diff --git a/README.md b/README.md index 3d8071ae8..5876fe714 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # 极客大学「算法训练营第四期 - 3 班」作业提交仓库 +## 讲师课件下载地址 + +请大家通过该链接查看讲师课件并进行下载:链接:https://pan.baidu.com/s/1uoU_2toXHYw_eQOHMpogfQ 密码:94kd + ## 仓库目录结构说明 @@ -24,4 +28,4 @@ ## 注意事项 1. **作业公布地址:** 我们会在置顶的 issue 中公布当周的算法练习题以及其他注意事项。 -2. 如果对 Git 和 GitHub 不太了解,请参考 [Git 官方文档](https://git-scm.com/book/zh/v2) 或者极客时间的[《玩转 Git 三剑客》](https://time.geekbang.org/course/intro/145)视频课程。 +2. 如果对 Git 和 GitHub 不太了解,请参考 [Git 官方文档](https://git-scm.com/book/zh/v2) 或者极客时间的[《玩转 Git 三剑客》](https://time.geekbang.org/course/intro/145)视频课程。从来没用过github的学员看这里的git_quick_guide,或许会对你有帮助奥。https://github.com/algorithm004-01/algorithm004-01/tree/master/Utils diff --git a/Week 01/id_003/189_rotateArrav.js b/Week 01/id_003/189_rotateArrav.js new file mode 100644 index 000000000..8d43fe5b8 --- /dev/null +++ b/Week 01/id_003/189_rotateArrav.js @@ -0,0 +1,59 @@ +//方法一 暴力法 +var rotate = function(nums, k) { + let previous; + for (let i = 0; i < k; i++) { + previous = nums[nums.length - 1]; + for ( let j = 0; j < nums.length; j++ ){ + [nums[j],previous]=[previous,nums[j]]; + } + } +}; +//方法2 辅助空间 +var rotate = function(nums, k) { + let previous; + for (let i = 0; i < k; i++) { + previous = nums[nums.length - 1]; + for ( let j = 0; j < nums.length; j++ ){ + [nums[j],previous]=[previous,nums[j]]; + } + } +}; + +//方法3 使用环状替换 + +var rotate = function(nums, k) { + k %= nums.length; + let start = 0; + for(let count = 0; count < nums.length; count++){ + let current = start; + let prev = nums[start]; + do{ + let next = (current + k) % nums.length; + let temp = nums[next]; + nums[next] = prev; + prev = temp; + current = next; + count++; + }while(start != current) + } + +} + +//方法4 反转数组法 +var rotate = function(nums, k) { + k %= nums.length; + revrse(nums, 0, nums.length -1); + revrse(nums, 0, k - 1); + revrse(nums, k - 1, nums.length -1); +} + +var revrse = function(nums, start, end) { + while ( start < end ) { + let temp = nums[start]; + nums[start] = nums[ennd]; + nums[end] = temp; + start++; + end--; + } +} + diff --git a/Week 01/id_003/26removeDuplicates.js b/Week 01/id_003/26removeDuplicates.js new file mode 100644 index 000000000..197dac54d --- /dev/null +++ b/Week 01/id_003/26removeDuplicates.js @@ -0,0 +1,12 @@ +var removeDuplicates = function(nums) { + if (nums.length == 0) return 0; + var i = 0; + var j = 1; + nums.map((item,index,arr)=>{ + if( item !== nums[i] ) { + i++; + nums[i] = item; + } + }) + return i+1; +}; \ No newline at end of file diff --git a/Week 01/id_003/NOTE.md b/Week 01/id_003/NOTE.md index a6321d6e2..269d7a7ea 100644 --- a/Week 01/id_003/NOTE.md +++ b/Week 01/id_003/NOTE.md @@ -1,4 +1,68 @@ # NOTE +## 学习总结 - +1. 数组:[源码](http://developer.classpath.org/doc/java/util/ArrayList-source.html) + 在内存中是连续的地址,访问时间非常快。 + 插入(insert)操作O(n):元素插入时,将插入的位置之后的元素全部向后移动一个位置,再放入该元素 + 删除(delete)操作O(n):元素删除时,拿出该元素,后面的元素都向前移一个位置 + 不适合增加删除 + 查看(lookup)O(1) + +2. Linked List[源码](https://www.geeksforgeeks.org/implementing-a-linked-list-in-java-using-class/) [Linked List 示例代码](http://www.cs.cmu.edu/~adamchik/15-121/lectures/Linked%20Lists/code/LinkedList.java) + 通过链表实现的空间不连续的结构 + 增加(prepend、append)、删除(delete)操作、插入操作(insert):O(1)的复杂度 newnode.next = Node.next, Node.next = newNode, + 查找(lookup)操作:O(n); + +3. skip List(跳表) + 运用于Redis,为了补足链表的缺陷而设计出来的。加速的中心思想(升维,空间换时间) + 跳表查询的时间复杂度分析O(logn) + n/2、n/4、n/8、第k级索引节点的个数就是n/(2^k) + 假设索引有h级,最高级的索引有2个结点。n/(2^h) = 2,从而求得h = log2(n) - 1 + + 跳表的空间复杂度分析O(n): + 原始链表大小为n,每2个结点抽1个,每层索引的结点数: + n/2、n/4,n/8,...,8,4,2 + 原始俩表大小为n,每3个结点抽1个,每层索引的结点数: + n/3、n/9,n/27,...,9,3,1 + 空间复杂度O(n) +实际应用: + 1. [LRU Cache - Linked list](https://leetcode-cn.com/problems/lru-cache/) + 2. [Redis - Skip List](https://www.zhihu.com/question/20202931) + +4. 栈 + stack: 先入后出;添加、删除O(1),查询O(n) +5. 队列 + Queue: 先入后出;添加、删除O(1),查询O(n) + Deque: 添加、删除O(1),查询O(n) + [Priority Queue](http://fuseyism.com/classpath/doc/java/util/PriorityQueue-source.html): 插入操作O(1),取出操作:O(logN)-按照元素的优先级取出,底层具体实现的数据结构较为多样和复杂:heap、bst、treap + +## 源码分析 + +### Queue + + 通过分析[Queue源码](http://fuseyism.com/classpath/doc/java/util/Queue-source.html)学习到以下几点。 + 1. Queue在java中定义为接口 + 2. 它的方法有 + add(E e)、offer(E e)、remove()、poll()、element()、peek() + add、offer:用于添加元素 + remove: 删除队头元素,当要移除的元素为空时抛出异常 + poll: 删除队头元素,当要移除的元素为空时返回null + element: 检索队头元素,为空时返回异常 + peek: 检索队头元素,为空时返回null +### PriorityQueue + 关于PriorityQUeue源码没看太懂,但是其手册内容关于这些方法的介绍看了一些 + 通过分析[PriorityQueue](http://fuseyism.com/classpath/doc/java/util/PriorityQueue-source.html)学习到以下几点。 + 1. PriorityQueue继承自AbstractQueue实现了Serializable定义的接口 + 2. 它的方法有: + add(E e)、clear()、comparator()、contains(Object c)、iterator()、offer()、remove(Object o)、spliterator()、toArray()、toArray(T[] a) + add(E e):将指定的元素插入此优先级队列。 + clear():删除优先队列中的所有元素。 + comparator(): 返回用于对队列中的元素进行排序的比较器;如果此队列是根据其元素的自然顺序排序的,则返回null。 + contains(Object c): 判断队列中是否包含该元素,包含返回true + iterator():返回一个迭代器用于对该队列中的所有元素进行迭代。 + offer(): 将制定的元素插入此优先队列。 + remove(): 从该队列中删除指定元素的单个实例(如果存在)。 + spliterator(): 在次队列的元素上创建一个fail-fasth和late-binding的spliterator(不明白什么意思) + toArray(): 返回一个包含队列中所有元素的数组 + toArray(T[] a):返回一个包含队列中所有元素的数组 diff --git "a/Week 01/id_003/dQueue\346\224\271\345\206\231\344\273\243\347\240\201.java" "b/Week 01/id_003/dQueue\346\224\271\345\206\231\344\273\243\347\240\201.java" new file mode 100644 index 000000000..630bd62c0 --- /dev/null +++ "b/Week 01/id_003/dQueue\346\224\271\345\206\231\344\273\243\347\240\201.java" @@ -0,0 +1,28 @@ +package algorithm; + +import java.util.Deque; +import java.util.LinkedList; + +public class dQueue { + + public static void main(String[] args) { + // TODO Auto-generated method stub + Deque deque = new LinkedList(); + deque.addLast("a"); + deque.addLast("b"); + deque.addLast("c"); + + + System.out.println(deque); + + String Str = deque.peekFirst(); + System.out.println(Str); + System.out.println(deque); + + while(deque.size()>0){ + System.out.println(deque.removeLast()); + } + System.out.println(deque); + } + +} \ No newline at end of file diff --git a/Week 01/id_003/mergeTwoLists.js b/Week 01/id_003/mergeTwoLists.js new file mode 100644 index 000000000..91432e295 --- /dev/null +++ b/Week 01/id_003/mergeTwoLists.js @@ -0,0 +1,20 @@ +var mergeTwoLists = function(l1, l2) { + var prehead = { + val:null, + next:null + }; + var pre = prehead; + while (l1 != null && l2 != null) { + if ( l1.val <= l2.val ) { + pre.next = l1; + l1 = l1.next; + } else { + pre.next = l2; + l2 = l2.next; + } + pre = pre.next; + + } + pre.next = l1 == null ? l2 : l1; + return prehead.next; +}; \ No newline at end of file diff --git a/Week 01/id_008/LeetCode_11_008.js b/Week 01/id_008/LeetCode_11_008.js new file mode 100644 index 000000000..be943304f --- /dev/null +++ b/Week 01/id_008/LeetCode_11_008.js @@ -0,0 +1,44 @@ +/** + * @param {number[]} height + * @return {number} + */ +var maxArea = function(height) { + let max = 0, x1, x2, current, min, prevHeight; + + for (x1 = 0; x1 < height.length - 1; ++x1) { + for (x2 = height.length - 1; x2 >= x1 + 1; --x2) { + if (height[x1] <= prevHeight) { + continue; + } + + min = height[x1] > height[x2] ? height[x2] : height[x1]; + max = Math.max(max, min * (x2 - x1)); + prevHeight = min; + } + } + + return max; +}; + +/** + * @param {number[]} height + * @return {number} + */ +var maxArea = function(height) { + var max = 0, x1 = 0, x2 = height.length - 1, current, low; + + while (x1 < x2) { + low = height[x1] > height[x2] ? height[x2] : height[x1]; + current = low * (x2 - x1); + max = max > current ? max : current; + + if (height[x1] >= height[x2]) { + --x2; + } + else if (height[x1] < height[x2]) { + ++x1; + } + } + + return max; +}; diff --git a/Week 01/id_008/LeetCode_141_008.js b/Week 01/id_008/LeetCode_141_008.js new file mode 100644 index 000000000..0be4a349e --- /dev/null +++ b/Week 01/id_008/LeetCode_141_008.js @@ -0,0 +1,39 @@ +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ + +/** + * @param {ListNode} head + * @return {boolean} + */ +var hasCycle = function(head) { + if (!head) { + return false; + } + + var a = head; + var b = head; + var start = true; + + while (a.next && b.next) { + if (!start && a === b) { + return true; + } + + start = false; + a = a.next; + b = b.next; + + if (!b.next) { + return false; + } + + b = b.next; + } + + return false; +}; diff --git a/Week 01/id_008/LeetCode_142_008.js b/Week 01/id_008/LeetCode_142_008.js new file mode 100644 index 000000000..2ca43157b --- /dev/null +++ b/Week 01/id_008/LeetCode_142_008.js @@ -0,0 +1,55 @@ +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ + +/** + * @param {ListNode} head + * @return {ListNode} + */ +var detectCycle = function(head) { + if (!head) { + return null; + } + + var a = head; + var b = head; + var start = true; + var meet = null; + + while (a.next && b.next) { + if (!start && a === b) { + meet = a; + break; + } + + start = false; + a = a.next; + b = b.next; + + if (!b.next) { + return null; + } + + b = b.next; + } + + if (meet) { + a = head; + + while (1) { + if (a === meet) { + return meet; + } + + a = a.next; + meet = meet.next; + } + } + else { + return null; + } +}; diff --git a/Week 01/id_008/LeetCode_155_008.js b/Week 01/id_008/LeetCode_155_008.js new file mode 100644 index 000000000..034ff8b8f --- /dev/null +++ b/Week 01/id_008/LeetCode_155_008.js @@ -0,0 +1,59 @@ +/** + * initialize your data structure here. + */ +var MinStack = function() { + this.stack = []; + this.min = []; + this._top = null; +}; + +/** + * @param {number} x + * @return {void} + */ +MinStack.prototype.push = function(x) { + this.stack.push(x); + this._top = x; + + if (this.min.length) { + var pop = this.min[this.min.length - 1]; + this.min.push(x < pop ? x : pop); + } + else { + this.min.push(x); + } + + return x; +}; + +/** + * @return {void} + */ +MinStack.prototype.pop = function() { + this.min.pop(); + this._top = this.stack.length >= 2 ? this.stack[this.stack.length - 2] : null; + return this.stack.pop(); +}; + +/** + * @return {number} + */ +MinStack.prototype.top = function() { + return this._top; +}; + +/** + * @return {number} + */ +MinStack.prototype.getMin = function() { + return this.min[this.min.length - 1]; +}; + +/** + * Your MinStack object will be instantiated and called as such: + * var obj = new MinStack() + * obj.push(x) + * obj.pop() + * var param_3 = obj.top() + * var param_4 = obj.getMin() + */ diff --git a/Week 01/id_008/LeetCode_15_008.js b/Week 01/id_008/LeetCode_15_008.js new file mode 100644 index 000000000..32e194f94 --- /dev/null +++ b/Week 01/id_008/LeetCode_15_008.js @@ -0,0 +1,43 @@ +/** + * @param {number[]} nums + * @return {number[][]} + */ +var threeSum = function(nums) { + nums.sort(function(a, b){ return a - b; }); + var prevI, prevJ, prevK, I = 0, J, K, sum, result = []; + + while (I < nums.length - 2 && nums[I] <= 0) { + J = I + 1; + K = nums.length - 1; + prevI = nums[I]; + prevJ = nums[J]; + prevK = nums[K]; + + while (J < K) { + sum = nums[I] + nums[J] + nums[K]; + + if (sum > 0) { + while (nums[K] === prevK) { + --K; + } + + prevK = nums[K]; + } + else { + sum === 0 && result.push([nums[I], nums[J], nums[K]]); + + while (nums[J] === prevJ) { + ++J; + } + + prevJ = nums[J]; + } + } + + while (nums[I] === prevI) { + ++I; + } + } + + return result; +}; diff --git a/Week 01/id_008/LeetCode_189_008.js b/Week 01/id_008/LeetCode_189_008.js new file mode 100644 index 000000000..1f4cf880b --- /dev/null +++ b/Week 01/id_008/LeetCode_189_008.js @@ -0,0 +1,33 @@ +/** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ +var rotate = function(nums, k) { + for (var i = 0; i < k; ++i) { + nums.unshift(nums.pop()); + } + + return nums; +}; + +/** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ +var rotate = function(nums, k) { + while (k >= nums.length) { + k -= nums.length; + } + + var len = nums.length; + var clone = nums.slice(0); + clone = clone.concat(clone.slice(0)); + + for (var i = 0; i < len; ++i) { + nums[i] = clone[len + i - k]; + } + + return nums; +}; diff --git a/Week 01/id_008/LeetCode_1_008.js b/Week 01/id_008/LeetCode_1_008.js new file mode 100644 index 000000000..51f176b6e --- /dev/null +++ b/Week 01/id_008/LeetCode_1_008.js @@ -0,0 +1,54 @@ +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = function(nums, target) { + for (var i = 0; i < nums.length - 1; ++i) { + for (var j = i + 1; j < nums.length; ++j) { + if (nums[i] + nums[j] === target) { + return [i, j] + } + } + } + + return []; +}; + +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = function(nums, target) { + var clone = nums.slice(0); + nums.sort(function(a, b){ return a - b; }); + + var A = 0, B = nums.length - 1, sum = 0; + + while (A < B) { + sum = nums[A] + nums[B]; + + if (sum > target) { + --B; + } + else if (sum < target) { + ++A; + } + else { + var a = 0, b = clone.length - 1; + + while (clone[a] != nums[A]) { + ++a; + } + + while (clone[b] != nums[B]) { + --b; + } + + return [a, b]; + } + } + + return []; +}; diff --git a/Week 01/id_008/LeetCode_206_008.js b/Week 01/id_008/LeetCode_206_008.js new file mode 100644 index 000000000..51cfdc6d2 --- /dev/null +++ b/Week 01/id_008/LeetCode_206_008.js @@ -0,0 +1,38 @@ +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ +/** + * @param {ListNode} head + * @return {ListNode} + */ +var reverseList = function(head) { + if (!head) { + return null; + } + + var list = [head.val]; + + while (head.next) { + head = head.next; + list.push(head.val); + } + + var pop, newList = {}; + var result = newList; + + while (list.length) { + pop = list.pop(); + newList.val = pop; + + if (list.length) { + newList.next = {}; + newList = newList.next; + } + } + + return result; +}; diff --git a/Week 01/id_008/LeetCode_20_008.js b/Week 01/id_008/LeetCode_20_008.js new file mode 100644 index 000000000..1cfd858dc --- /dev/null +++ b/Week 01/id_008/LeetCode_20_008.js @@ -0,0 +1,31 @@ +/** + * @param {string} s + * @return {boolean} + */ +var isValid = function(s) { + if (s.length % 2) { + return false; + } + + var map = { "(": ")", "[": "]", "{": "}" }; + var sArr = s.split(""); + var stack = []; + var half = s.length / 2; + + for (var i = 0; i < sArr.length; ++i) { + if (sArr[i] in map) { + stack.push(map[sArr[i]]); + + if (stack.length > half) { + return false; + } + } + else { + if (stack.pop() !== sArr[i]) { + return false; + } + } + } + + return stack.length === 0; +}; diff --git a/Week 01/id_008/LeetCode_21_008.js b/Week 01/id_008/LeetCode_21_008.js new file mode 100644 index 000000000..0b9fc1751 --- /dev/null +++ b/Week 01/id_008/LeetCode_21_008.js @@ -0,0 +1,51 @@ +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ +/** + * @param {ListNode} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ +var mergeTwoLists = function(l1, l2) { + if (!l1 && !l2) { + return null; + } + + var list = []; + var current = l1; + + while (current) { + list.push(current.val); + current = current.next; + } + + var current = l2; + + while (current) { + list.push(current.val); + current = current.next; + } + + list.sort(function(a, b){ return a - b; }); + + var result = {}; + current = result; + + for (var i = 0; i < list.length; ++i) { + current.val = list[i]; + + if (i >= list.length - 1) { + current.next = null + } + else { + current.next = {}; + current = current.next; + } + } + + return result; +}; diff --git a/Week 01/id_008/LeetCode_239_008.js b/Week 01/id_008/LeetCode_239_008.js new file mode 100644 index 000000000..4074be53b --- /dev/null +++ b/Week 01/id_008/LeetCode_239_008.js @@ -0,0 +1,30 @@ +/** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ +var maxSlidingWindow = function(nums, k) { + if (nums.length <= 0) { + return []; + } + + var result = []; + + for (var i = 0; i < nums.length - k + 1; ++i) { + result.push(findMax(i, i + k - 1)); + } + + return result; + + function findMax(L, R){ + var max = nums[L]; + + for (var i = L + 1; i <= R; ++i) { + if (nums[i] > max) { + max = nums[i]; + } + } + + return max; + } +}; diff --git a/Week 01/id_008/LeetCode_24_008.js b/Week 01/id_008/LeetCode_24_008.js new file mode 100644 index 000000000..0dc1b52d4 --- /dev/null +++ b/Week 01/id_008/LeetCode_24_008.js @@ -0,0 +1,46 @@ +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ +/** + * @param {ListNode} head + * @return {ListNode} + */ +var swapPairs = function(head) { + if (!head) { + return null; + } + + var list = [head.val]; + + while (head.next) { + head = head.next; + list.push(head.val); + } + + var temp = 0; + + for (var i = 1; i < list.length; i += 2) { + temp = list[i]; + list[i] = list[i - 1]; + list[i - 1] = temp; + } + + var pop, newList = {}; + var result = newList; + + while (list.length) { + pop = list.shift(); + newList.val = pop; + + if (list.length) { + newList.next = {}; + newList = newList.next; + } + } + + return result; +}; diff --git a/Week 01/id_008/LeetCode_25_008.js b/Week 01/id_008/LeetCode_25_008.js new file mode 100644 index 000000000..58881e25a --- /dev/null +++ b/Week 01/id_008/LeetCode_25_008.js @@ -0,0 +1,77 @@ +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ +/** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ +var reverseKGroup = function(head, k) { + if (!head) { + return null; + } + + if (k < 2) { + return head; + } + + // 先统计总结点数 + var total = 0; + var current = head; + + while (current) { + ++total; + current = current.next; + } + + if (k > total) { + return head; + } + + // 计算组数 + var group = (total - total % k) / k; + + // 重新迭代一次 + var currentGroup = 0; + var currentIndex = 0; + var node = { next: head }; + var prev = node, current = head, next = head.next; + var _current, _next; + var prevTail = node, currentTail; + + while (current) { + _current = current; + _next = current.next; + + if (currentIndex === 0) { + currentTail = current; + ++currentIndex; + } + else { + _next = current.next; + current.next = prev; + ++currentIndex; + + if (currentIndex >= k) { + prevTail.next = current; + prevTail = currentTail; + currentIndex = 0; + ++currentGroup; + + if (currentGroup >= group) { + prevTail.next = _next; + break; + } + } + } + + prev = _current; + current = _next; + } + + return node.next; +}; diff --git a/Week 01/id_008/LeetCode_26_008.js b/Week 01/id_008/LeetCode_26_008.js new file mode 100644 index 000000000..5be29fd14 --- /dev/null +++ b/Week 01/id_008/LeetCode_26_008.js @@ -0,0 +1,17 @@ +/** + * @param {number[]} nums + * @return {number} + */ +var removeDuplicates = function(nums) { + var compare = -Infinity, total = 0; + + for (var i = 0; i < nums.length; ++i) { + if (nums[i] !== compare) { + nums[total++] = nums[i]; + compare = nums[i]; + } + } + + nums.length = total; + return total; +}; diff --git a/Week 01/id_008/LeetCode_283_008.js b/Week 01/id_008/LeetCode_283_008.js new file mode 100644 index 000000000..503332de0 --- /dev/null +++ b/Week 01/id_008/LeetCode_283_008.js @@ -0,0 +1,16 @@ +/** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ +var moveZeroes = function(nums) { + var target = 0, i; + + for (i = 0; i < nums.length; ++i) { + if (nums[i] !== 0) { + nums[target] = nums[i]; + target++ === i || (nums[i] = 0); + } + } + + return nums; +}; diff --git a/Week 01/id_008/LeetCode_66_008.js b/Week 01/id_008/LeetCode_66_008.js new file mode 100644 index 000000000..3b31a5c63 --- /dev/null +++ b/Week 01/id_008/LeetCode_66_008.js @@ -0,0 +1,23 @@ +/** + * @param {number[]} digits + * @return {number[]} + */ +var plusOne = function(digits) { + var exp = 1, bit = 0; + + for (i = digits.length - 1; i >= 0; --i) { + bit = digits[i] + exp; + + if (bit > 9) { + digits[i] = bit - 10; + exp = 1; + } + else { + digits[i] = bit; + exp = 0; + } + } + + exp && digits.unshift(1); + return digits; +}; diff --git a/Week 01/id_008/LeetCode_70_008.js b/Week 01/id_008/LeetCode_70_008.js new file mode 100644 index 000000000..5177ebc23 --- /dev/null +++ b/Week 01/id_008/LeetCode_70_008.js @@ -0,0 +1,28 @@ +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function(n) { + var t = Math.sqrt(5); + return Math.round(1/t * (Math.pow((1+t)/2, n+1) - Math.pow((1-t)/2, n+1))); +}; + +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function(n) { + if (n < 2) { + return 1; + } + + var a = 1; + var b = 1; + + for (var i = 1; i < n; ++i) { + b += a; + a = b - a; + } + + return b; +}; diff --git a/Week 01/id_008/LeetCode_84_008.js b/Week 01/id_008/LeetCode_84_008.js new file mode 100644 index 000000000..47233740e --- /dev/null +++ b/Week 01/id_008/LeetCode_84_008.js @@ -0,0 +1,42 @@ +/** + * @param {number[]} heights + * @return {number} + */ +var largestRectangleArea = function(heights) { + if (heights.length <= 0) { + return 0; + } + + return findMaxS(0, heights.length - 1); + + function findMaxS(L, R){ + var minK = findMinH(L, R); + var max = heights[minK] * (R - L + 1); + + if (minK > L) { + var SL = findMaxS(L, minK - 1); + SL > max && (max = SL); + } + + if (minK < R) { + var SR = findMaxS(minK + 1, R); + SR > max && (max = SR); + } + + return max; + } + + // 方法待优化 + function findMinH(L, R){ + var min = heights[L], minK = L; + + for (var i = L + 1; i <= R; ++i) { + if (heights[i] < min) { + min = heights[i]; + minK = i; + } + } + + return minK; + } +}; diff --git a/Week 01/id_008/LeetCode_88_008.js b/Week 01/id_008/LeetCode_88_008.js new file mode 100644 index 000000000..ac16c14a8 --- /dev/null +++ b/Week 01/id_008/LeetCode_88_008.js @@ -0,0 +1,16 @@ +/** + * @param {number[]} nums1 + * @param {number} m + * @param {number[]} nums2 + * @param {number} n + * @return {void} Do not return anything, modify nums1 in-place instead. + */ +var merge = function(nums1, m, nums2, n) { + for (var i = 0; i < n; ++i) { + nums1[m + i] = nums2[i]; + } + + nums1.length = m + n; + nums1.sort(function(a, b){ return a - b; }); + return nums1; +}; diff --git a/Week 01/id_013/LeetCode_01_013.py b/Week 01/id_013/LeetCode_01_013.py new file mode 100644 index 000000000..5f4949d57 --- /dev/null +++ b/Week 01/id_013/LeetCode_01_013.py @@ -0,0 +1,16 @@ +''' +第三课第一题 +https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/ +''' +class Solution_1: + # 双指针遍历,遇到重复的删除第二个 + def removeDuplicates(self, nums): + left= 0 + right = 1 + while right < len(nums): + if nums[left] == nums[right]: + nums.pop(right) + else: + right +=1 + left += 1 + return len(nums) diff --git a/Week 01/id_013/LeetCode_02_013.py b/Week 01/id_013/LeetCode_02_013.py new file mode 100644 index 000000000..61209e133 --- /dev/null +++ b/Week 01/id_013/LeetCode_02_013.py @@ -0,0 +1,24 @@ +''' +第三课第二题 +189. 旋转数组 +https://leetcode-cn.com/problems/rotate-array/ +''' +class Solution_1: + """ + 解法:遍历移位,很直观的解法,时间复杂度为O(k*n) + """ + def rotate(self,nums,k): + for i in range(k): + last = nums[-1] + for j in range(len(nums)-1,0,-1): + nums[j] = nums[j-1] + nums[0] = last + + +class Solution_2: + """ + 解法:数组整体后移 + """ + def rotate(self,nums,k): + k %= len(nums) + nums[:] = nums[-k:] + nums[:-k] \ No newline at end of file diff --git a/Week 01/id_013/LeetCode_05_013.py b/Week 01/id_013/LeetCode_05_013.py new file mode 100644 index 000000000..0e10c87c3 --- /dev/null +++ b/Week 01/id_013/LeetCode_05_013.py @@ -0,0 +1,26 @@ +class Solution_1: + def twoSum(self, nums: List[int], target: int) -> List[int]: + for i in range(len(nums)-1): + for r in range(i+1,len(nums)): + if nums[i] + nums[r] == target: #注意这里的相等是==号 + return [i,r] #返回一个列表 + +class Solution_2: + # 可用一遍遍历,即根据当前遍历得到的元素index, + # 查找target-index是否在剩余数组里出现 + # 如果找得到,则返回其下标值;反之则说明没有该答案 + def twoSum(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: List[int] + """ + answer = [] + for left_index in range(len(nums)): + right = target - nums[left_index] + if right in nums[left_index+1:]: + nums_right = nums[left_index+1:] #往右截断,构建一个新的数组 + right_index = nums_right.index(right)+left_index+1 #注意这个+1 + answer.extend([left_index, right_index]) + break + return answer \ No newline at end of file diff --git a/Week 01/id_013/LeetCode_08_013.py b/Week 01/id_013/LeetCode_08_013.py new file mode 100644 index 000000000..2fe27a714 --- /dev/null +++ b/Week 01/id_013/LeetCode_08_013.py @@ -0,0 +1,39 @@ +""" +第二周第一题 +https://leetcode-cn.com/problems/trapping-rain-water/solution/ +""" +class Solution_1: + # 动态规划 + def trap(self,height): + if not height: + return 0 + n = len(height) + max_left = [0] * n + max_right = [0] * n + max_left[0] = height[0] + max_right[-1] = height[-1] + for i in range(1,n): + max_left[i] = max(height[i],max_left[i-1]) + for i in range(n-2,-1,-1): + max_right[i] = max(height[i],max_right[i+1]) + res = 0 + for i in range(n): + res += min(max_left[i],max_right[i])-height[i] + return res + +class Solution_2: + # 栈来实现 + def trap(self,height): + if not height: + return 0 + n = len(height) + stack = [] + res = 0 + for i in range(n): + while stack and height[stack[-1]] < height[i]: + tmp = stack.pop() + if not stack: + break + res += (min(height[i],height[stack[-1]])-height[tmp]) * (i-stack[-1]-1) + stack.append(i) + return res \ No newline at end of file diff --git a/Week 01/id_013/NOTE.md b/Week 01/id_013/NOTE.md index a6321d6e2..356230d14 100644 --- a/Week 01/id_013/NOTE.md +++ b/Week 01/id_013/NOTE.md @@ -1,4 +1,114 @@ -# NOTE - - - +# 笔记(更新中) + + + +### Linked List + +#### 各种操作的时间复杂度 + +| 操作 | 时间复杂度 | +| :-----: | :--------: | +| prepend | O(1) | +| append | O(1) | +| lookup | O(n) | +| insert | O(1) | +| delete | O(1) | + +#### 链表的优化 + +**核心思想:升维思想,空间换时间** + +解决方法:增加多级索引,log(2n)个索引 + +### Array + +#### 各种操作的时间复杂度 +| 操作 | 时间复杂度 | +| :-----: | :--------: | +| prepend | O(1) | +| append | O(1) | +| lookup | O(1) | +| insert | O(n) | +| delete | O(n) | + + + +# 队列和栈的Python实现形式的思考 + +> 个人博客有总结过: https://kanghaov.com + +## 队列 + + 队列 (queue) 是一种特殊的线性表,特殊之处在于它只允许在表的**前端(front)进行删除**操作,而在表的**后端(rear)进行插入**操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。 + +```python +class Node(object): + def __init__(self,elem,next=None): + self.elem = elem #表示对应的元素值 + self.next=next #表示下一个链接的链点 +class Queue(object): + def __init__(self): + self.head = None #头部链点为 None + self.rear = None #尾部链点为 None + def is_empty(self): + return self.head is None #判断队列是否为空 + def enqueue(self, elem): + p = Node(elem) #初始化一个新的点 + if self.is_empty(): + self.head = p #队列头部为新的链点 + self.rear = p #队列尾部为新的链点 + else: + self.rear.next = p #队列尾部的后继是这个新的点 + self.rear =p #然后让队列尾部指针指向这个新的点 + def dequeue(self): + if self.is_empty(): #判断队列是否为空 + print('Queue_is_empty') #若队列为空,则退出 dequeue 操作 + else: + result = self.head.elem #result为队列头部元素 + self.head = self.head.next #改变队列头部指针位置 + return result #返回队列头部元素 + def peek(self): + if self.is_empty(): #判断队列是否为空 + print('NOT_FOUND') #为空则返回 NOT_FOUND + else: + return self.head.elem #返回队列头部元素 + def print_queue(self): + print("queue:") + temp=self.head + myqueue=[] #暂时存放队列数据 + while temp is not None: + myqueue.append(temp.elem) + temp=temp.next + print(myqueue) +``` + + + +## 栈 + + 栈允许进行插入和删除操作的一端称为**栈顶(top)**,另一端为**栈底(bottom)**;**栈底固定,而栈顶浮动**;栈中元素个数为零时称为空栈。**插入一般称为进栈(PUSH),删除则称为退栈(POP)**。 + +```python +class Stack(object): + def __init__(self, limit=10): + self.stack = [] #存放元素 + self.limit = limit #栈容量极限 + def push(self, data): #判断栈是否溢出 + if len(self.stack) >= self.limit: + print('StackOverflowError') + pass + self.stack.append(data) + def pop(self): + if self.stack: + return self.stack.pop() + else: + raise IndexError('pop from an empty stack') #空栈不能被弹出 + def peek(self): #查看堆栈的最上面的元素 + if self.stack: + return self.stack[-1] + def is_empty(self): #判断栈是否为空 + return not bool(self.stack) + def size(self): #返回栈的大小 + return len(self.stack) +``` + diff --git a/Week 01/id_023/leetCode_026_023.js b/Week 01/id_023/leetCode_026_023.js new file mode 100644 index 000000000..53b9bbda9 --- /dev/null +++ b/Week 01/id_023/leetCode_026_023.js @@ -0,0 +1,15 @@ +/** + * @param {number[]} nums + * @return {number} + */ +var removeDuplicates = function (nums) { + var j = 0; + var n = nums.length; + for (let i = 1; i < n; i++) { + if (nums[i] != nums[i - 1]) { + j++; + nums[j] = nums[i]; + } + } + return j + 1; +}; \ No newline at end of file diff --git a/Week 01/id_023/leetCode_066_023.js b/Week 01/id_023/leetCode_066_023.js new file mode 100644 index 000000000..813bddec6 --- /dev/null +++ b/Week 01/id_023/leetCode_066_023.js @@ -0,0 +1,16 @@ +/** + * @param {number[]} digits + * @return {number[]} + */ +var plusOne = function (digits) { + const len = digits.length; + for (let i = len - 1; i >= 0; i--) { + digits[i]++; + digits[i] %= 10; + if (digits[i] != 0) + return digits; + } + digits = [...Array(len + 1)].map(_ => 0);; + digits[0] = 1; + return digits; +}; \ No newline at end of file diff --git a/Week 01/id_028/LeetCode_189_028.java b/Week 01/id_028/LeetCode_189_028.java new file mode 100644 index 000000000..83c9cbfa7 --- /dev/null +++ b/Week 01/id_028/LeetCode_189_028.java @@ -0,0 +1,56 @@ +import org.testng.annotations.Test; + +/** + * 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。 + * + * 示例 1: + * + * 输入: [1,2,3,4,5,6,7] 和 k = 3 + * 输出: [5,6,7,1,2,3,4] + * 解释: + * 向右旋转 1 步: [7,1,2,3,4,5,6] + * 向右旋转 2 步: [6,7,1,2,3,4,5] + * 向右旋转 3 步: [5,6,7,1,2,3,4] + * 示例 2: + * + * 输入: [-1,-100,3,99] 和 k = 2 + * 输出: [3,99,-1,-100] + * 解释: + * 向右旋转 1 步: [99,-1,-100,3] + * 向右旋转 2 步: [3,99,-1,-100] + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/rotate-array + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +public class Solution { + public void rotate(int[] nums, int k) { + k %= nums.length; + reverse(nums, 0, nums.length - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, nums.length - 1); + } + + public void reverse(int[] nums, int start, int end) { + while (start < end) { + int temp = nums[start]; + nums[start] = nums[end]; + nums[end] = temp; + start++; + end--; + } + } + + @Test + public void test1() { + int[] a = {1, 2, 3, 4, 5, 6, 7}; + rotate(a, 3); + } + + @Test + public void test2() { + int[] a = {-1, -100, 3, 99}; + rotate(a, 2); + } +} diff --git a/Week 01/id_028/LeetCode_1_028.java b/Week 01/id_028/LeetCode_1_028.java new file mode 100644 index 000000000..7a115a1a0 --- /dev/null +++ b/Week 01/id_028/LeetCode_1_028.java @@ -0,0 +1,64 @@ +package com.jane.part1; + +import java.util.HashMap; +import java.util.Map; + +/** + * 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 + * + * 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 + * + * 示例: + * + * 给定 nums = [2, 7, 11, 15], target = 9 + * + * 因为 nums[0] + nums[1] = 2 + 7 = 9 + * 所以返回 [0, 1] + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/two-sum + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +public class Solution { + public static void main(String[] args) { + int[] a = {2, 7, 11, 15}; + int target = 26; + + int[] result = twoSum(a, target); + for (int i = 0; i < result.length; i++) { + System.out.print(result[i] + " "); + } + } + + /* + public static int[] twoSum(int[] nums, int target) { + int[] a = new int[2]; + + for (int i = 0; i < nums.length; i++) { + for (int j = i + 1; j < nums.length; j++) { + if (nums[i] + nums[j] == target) { + a[0] = i; + a[1] = j; + break; + } + } + } + + return a; + }*/ + + public static int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + int data = target - nums[i]; + if (map.containsKey(data)){ + return new int[] {map.get(data),i}; + } + + map.put(nums[i],i); + } + + return null; + } +} diff --git a/Week 01/id_028/LeetCode_21_028.java b/Week 01/id_028/LeetCode_21_028.java new file mode 100644 index 000000000..477c3a2bb --- /dev/null +++ b/Week 01/id_028/LeetCode_21_028.java @@ -0,0 +1,33 @@ +package com.jane.part21; + +/** + * 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。  + * + * 示例: + * + * 输入:1->2->4, 1->3->4 + * 输出:1->1->2->3->4->4 + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/merge-two-sorted-lists + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +public class Solution { + public ListNode mergeTwoLists(ListNode l1, ListNode 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; + } + } +} diff --git a/Week 01/id_028/LeetCode_26_028.java b/Week 01/id_028/LeetCode_26_028.java new file mode 100644 index 000000000..6ac8534e3 --- /dev/null +++ b/Week 01/id_028/LeetCode_26_028.java @@ -0,0 +1,38 @@ +/** + * 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 + * + * 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 + * + * 示例 1: + * + * 给定数组 nums = [1,1,2], + * + * 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 + * + * 你不需要考虑数组中超出新长度后面的元素。 + * 示例 2: + * + * 给定 nums = [0,0,1,1,1,2,2,3,3,4], + * + * 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 + * + * 你不需要考虑数组中超出新长度后面的元素。 + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +public class Solution { + public int removeDuplicates(int[] nums) { + int j = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != nums[j]) { + j++; + nums[j] = nums[i]; + } + } + + return j + 1; + } +} diff --git a/Week 01/id_028/LeetCode_283_028.java b/Week 01/id_028/LeetCode_283_028.java new file mode 100644 index 000000000..a93850eee --- /dev/null +++ b/Week 01/id_028/LeetCode_283_028.java @@ -0,0 +1,42 @@ +package com.jane.part283; + +import org.testng.annotations.Test; + +/** + * 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 + * + * 示例: + * + * 输入: [0,1,0,3,12] + * 输出: [1,3,12,0,0] + * 说明: + * + * 必须在原数组上操作,不能拷贝额外的数组。 + * 尽量减少操作次数。 + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/move-zeroes + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +public class Solution { + public void moveZeroes(int[] nums) { + int count = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != 0) { + nums[count] = nums[i]; + count++; + } + } + + for (int i = count; i < nums.length; i++) { + nums[i] = 0; + } + } + + @Test + public void test() { + int[] a = {0, 1, 0, 3, 12}; + moveZeroes(a); + } +} diff --git a/Week 01/id_028/LeetCode_42_028.java b/Week 01/id_028/LeetCode_42_028.java new file mode 100644 index 000000000..314e36eb3 --- /dev/null +++ b/Week 01/id_028/LeetCode_42_028.java @@ -0,0 +1,53 @@ +package com.jane.part42; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.Stack; + +/** + * 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 + * + * + * + * 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。 + * + * 示例: + * + * 输入: [0,1,0,2,1,0,1,3,2,1,2,1] + * 输出: 6 + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/trapping-rain-water + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +public class Solution { + public int trap(int[] height) { + int maxArea = 0; + int current = 0; + + Stack stack = new Stack<>(); + while(current < height.length){ + while (!stack.empty() && height[current] > height[stack.peek()]) { + int top = stack.peek(); + stack.pop(); + if (stack.empty()) { + break; + } + int distance = current - stack.peek() - 1; + int boundedHeight = Math.min(height[current], height[stack.peek()]) - height[top]; + maxArea += distance * boundedHeight; + } + stack.push(current++); + } + + return maxArea; + } + + @Test + public void test(){ + int[] height = {0,1,0,2,1,0,1,3,2,1,2,1}; + Assert.assertEquals(6, trap(height)); + } +} diff --git a/Week 01/id_028/LeetCode_641_028.java b/Week 01/id_028/LeetCode_641_028.java new file mode 100644 index 000000000..b2da3e43e --- /dev/null +++ b/Week 01/id_028/LeetCode_641_028.java @@ -0,0 +1,130 @@ +package com.jane.part641; + +/** + * 设计实现双端队列。 + * 你的实现需要支持以下操作: + * + * MyCircularDeque(k):构造函数,双端队列的大小为k。 + * insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true。 + * insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。 + * deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true。 + * deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。 + * getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。 + * getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。 + * isEmpty():检查双端队列是否为空。 + * isFull():检查双端队列是否满了。 + * 示例: + * + * MyCircularDeque circularDeque = new MycircularDeque(3); // 设置容量大小为3 + * circularDeque.insertLast(1); // 返回 true + * circularDeque.insertLast(2); // 返回 true + * circularDeque.insertFront(3); // 返回 true + * circularDeque.insertFront(4); // 已经满了,返回 false + * circularDeque.getRear(); // 返回 2 + * circularDeque.isFull(); // 返回 true + * circularDeque.deleteLast(); // 返回 true + * circularDeque.insertFront(4); // 返回 true + * circularDeque.getFront(); // 返回 4 + *   + *   + * + * 提示: + * + * 所有值的范围为 [1, 1000] + * 操作次数的范围为 [1, 1000] + * 请不要使用内置的双端队列库。 + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/design-circular-deque + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +public class MyCircularDeque { + private int size; + private int[] data; + private int head; + private int tail; + + public MyCircularDeque(int k) { + this.data = new int[k]; + this.size = 0; + this.head = -1; + this.tail = -1; + } + + + public boolean insertFront(int value) { + if(isFull()){ + return false; + } + if(head - 1 < 0){ + head = data.length -1; + } else{ + head--; + } + if(size == 0){ + tail = head; + } + data[head] = value; + size++; + return true; + } + + public boolean insertLast(int value) { + if(isFull()){ + return false; + } + tail = (tail+1)%data.length; + data[tail] = value; + if(size == 0){ + head = tail; + } + size++; + return true; + } + + public boolean deleteFront() { + if(isEmpty()){ + return false; + } + head = (head+1)%data.length; + size--; + return true; + } + + public boolean deleteLast() { + if(isEmpty()){ + return false; + } + if(tail - 1 < 0){ + tail = data.length-1; + }else{ + tail--; + } + size--; + return true; + } + + public int getFront() { + if(isEmpty()){ + return -1; + } + return data[head]; + } + + public int getRear() { + if(isEmpty()){ + return -1; + } + return data[tail]; + } + + public boolean isEmpty() { + return size == 0; + } + + public boolean isFull() { + return size == data.length; + } + +} \ No newline at end of file diff --git a/Week 01/id_028/LeetCode_66_028.java b/Week 01/id_028/LeetCode_66_028.java new file mode 100644 index 000000000..b4555ee68 --- /dev/null +++ b/Week 01/id_028/LeetCode_66_028.java @@ -0,0 +1,75 @@ +package com.jane.part66; + +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * 给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。 + * + * 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 + * + * 你可以假设除了整数 0 之外,这个整数不会以零开头。 + * + * 示例 1: + * + * 输入: [1,2,3] + * 输出: [1,2,4] + * 解释: 输入数组表示数字 123。 + * 示例 2: + * + * 输入: [4,3,2,1] + * 输出: [4,3,2,2] + * 解释: 输入数组表示数字 4321。 + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/plus-one + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +public class Solution { + public int[] plusOne(int[] digits) { + for (int i = digits.length - 1; i >= 0; i--) { + digits[i]++; + digits[i] %= 10; + if (digits[i] != 0) { + return digits; + } + } + + digits = new int[digits.length + 1]; + digits[0] = 1; + return digits; + } + + @Test + public void test1() { + int[] digits = {1, 2, 3}; + int[] expect = {1, 2, 4}; + + Assert.assertEquals(plusOne(digits), expect); + } + + @Test + public void test2() { + int[] digits = {4, 3, 2, 9}; + int[] expect = {4, 3, 3, 0}; + + Assert.assertEquals(plusOne(digits), expect); + } + + @Test + public void test3() { + int[] digits = {9}; + int[] expect = {1, 0}; + + Assert.assertEquals(plusOne(digits), expect); + } + + @Test + public void test4() { + int[] digits = {8, 9}; + int[] expect = {9, 0}; + + Assert.assertEquals(plusOne(digits), expect); + } +} diff --git a/Week 01/id_028/LeetCode_88_028.java b/Week 01/id_028/LeetCode_88_028.java new file mode 100644 index 000000000..e35c3c938 --- /dev/null +++ b/Week 01/id_028/LeetCode_88_028.java @@ -0,0 +1,40 @@ +package com.jane.part88; + +import org.testng.annotations.Test; + +import java.util.Arrays; + +/** + * 给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。 + * + * 说明: + * + * 初始化 nums1 和 nums2 的元素数量分别为 m 和 n。 + * 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。 + * 示例: + * + * 输入: + * nums1 = [1,2,3,0,0,0], m = 3 + * nums2 = [2,5,6], n = 3 + * + * 输出: [1,2,2,3,5,6] + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/merge-sorted-array + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +public class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + System.arraycopy(nums2, 0, nums1, m, n); + Arrays.sort(nums1); + } + + @Test + public void test() { + int[] s1 = {1, 2, 3, 0, 0, 0}; + int[] s2 = {2, 5, 6}; + + merge(s1, 3, s2, 3); + } +} diff --git a/Week 01/id_028/NOTE.md b/Week 01/id_028/NOTE.md index a6321d6e2..1e1f1377b 100644 --- a/Week 01/id_028/NOTE.md +++ b/Week 01/id_028/NOTE.md @@ -1,4 +1,24 @@ # NOTE +## 第一周 + +### 第一课 数组、链表、跳表的基本实现和特性 +#### 数组 +>优点:支持O(1)的随机访问 +> +>缺点:平均为O(n)的插入和删除 + + +#### 链表 +>优点:时间复杂度为O(1)的插入、删除操作 +> +>缺点:不支持随机访问 + +#### 跳表 +>升维 空间换时间 +> +>时间复杂度:O(logn) + + + - diff --git a/Week 01/id_033/LeetCode_0001_033.js b/Week 01/id_033/LeetCode_0001_033.js new file mode 100644 index 000000000..a1146fdda --- /dev/null +++ b/Week 01/id_033/LeetCode_0001_033.js @@ -0,0 +1,20 @@ +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = function(nums, target) { + let map = new Map(); + + for (let i = 0, l = nums.length; i < l; i++) { + const num = target - nums[i]; + + if (map.has(num)) { + return [map.get(num), i]; + } + + map.set(nums[i], i); + } + + return []; +}; diff --git a/Week 01/id_033/LeetCode_0189_033.js b/Week 01/id_033/LeetCode_0189_033.js new file mode 100644 index 000000000..66a2bd615 --- /dev/null +++ b/Week 01/id_033/LeetCode_0189_033.js @@ -0,0 +1,25 @@ +/** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ +var rotate = function(nums, k) { + const l = nums.length; + + if (k > l - 1 || k < 1) { + return nums; + } + + const arr = []; + + for (let i = 0; i < l; i++) { + if (i < l - k) { + arr[i + k] = nums[i]; + } else { + arr[i + k - l] = nums[i]; + } + } + + // console.log(arr); + return arr; +}; diff --git a/Week 01/id_038/NOTE.md b/Week 01/id_038/NOTE.md index a6321d6e2..6c2fe81a9 100644 --- a/Week 01/id_038/NOTE.md +++ b/Week 01/id_038/NOTE.md @@ -1,4 +1,4 @@ # NOTE - +进入目录 `cd week-01-038` 后,运行 `mvn clean test` 即可运行所有题解测试。 diff --git a/Week 01/id_038/week-01-038/.gitignore b/Week 01/id_038/week-01-038/.gitignore new file mode 100644 index 000000000..a47486b2b --- /dev/null +++ b/Week 01/id_038/week-01-038/.gitignore @@ -0,0 +1,164 @@ +.vertx +*.ipr +# log files +*.log +*.log.2* +.log +out +output +dist +dist-3rd +*.txt +!agent-shell-banner.txt +!shell-banner.txt +!VERSION.txt + +# For Eclipse + +.metadata +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders +*.iml +.DS_Store + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + + +# For Java + +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# For Maven + +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# For JetBrains + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +#data module ignore +node_modules + +static +public + +BETHUNE_PRO_HOST_PID +BETHUNE_PRO_AGENT_PID +*PID +APP_PID +PID_* +alert-rules-*.yml +altmng-configs.yml +alertmanager.yml +app-info-*.properties +app-info.properties +inspectResult/ + +/file-uploads/ diff --git a/Week 01/id_038/week-01-038/pom.xml b/Week 01/id_038/week-01-038/pom.xml new file mode 100644 index 000000000..52da15911 --- /dev/null +++ b/Week 01/id_038/week-01-038/pom.xml @@ -0,0 +1,95 @@ + + 4.0.0 + com.github.kylefeng + week-01-038 + jar + 1.0-SNAPSHOT + week-01-038 + http://maven.apache.org + + UTF-8 + 1.8 + 4.12 + 5.0.0 + ${junit.version}.0 + 1.0.0 + + + + + + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + + + + maven-surefire-plugin + 2.19.1 + + + **/Test*.java + **/*Test.java + **/*Tests.java + **/*TestCase.java + + + + slow + + + + + + org.junit.platform + junit-platform-surefire-provider + ${junit.platform.version} + + + + + + + + + com.google.guava + guava + 28.1-jre + + + + org.assertj + assertj-core + 3.13.2 + test + + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test + + + + org.junit.platform + junit-platform-launcher + ${junit.platform.version} + test + + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + diff --git a/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_1_038.java b/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_1_038.java new file mode 100644 index 000000000..ae7cdd174 --- /dev/null +++ b/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_1_038.java @@ -0,0 +1,33 @@ +package com.github.kylefeng.week01; + +import java.util.HashMap; +import java.util.Map; + +/** + * 1. 两数之和 + * + * @author kylefeng + * @time 2019/10/20 16:39 + */ +public class LeetCode_1_038 { + + public static int[] twoSum(int[] nums, int target) { + if (nums == null || nums.length < 2) { + return new int[]{}; + } + + if (nums.length == 2) { + return new int[]{0, 1}; + } + + Map hash = new HashMap<>(nums.length); + for (int i = 0; i < nums.length; i++) { + Integer elem = hash.get(nums[i]); + if (elem != null) { + return new int[]{elem, i}; + } + hash.put(target - nums[i], i); + } + return new int[]{}; + } +} diff --git a/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_21_038.java b/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_21_038.java new file mode 100644 index 000000000..471257bb3 --- /dev/null +++ b/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_21_038.java @@ -0,0 +1,103 @@ +package com.github.kylefeng.week01; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 21. 合并两个有序链表 + * + * @author kylefeng + * @time 2019/10/19 21:09 + */ +public class LeetCode_21_038 { + + static class ListNode { + int val; + ListNode next; + + ListNode(int x) { + val = x; + } + } + + public static ListNode newLinkedList(int... nums) { + if (nums == null || nums.length == 0) { + throw new IllegalArgumentException("Nums to be used to init linked list cannot be empty!"); + } + + ListNode head = new ListNode(Integer.MIN_VALUE); + for (int i = 0; i < nums.length; i++) { + ListNode node = new ListNode(nums[i]); + if (head.next == null) { + head.next = node; + } else { + ListNode next = head.next; + head.next = node; + node.next = next; + } + } + return head.next; + } + + public static List linkedListToList(ListNode head) { + List list = new ArrayList<>(); + while (head != null) { + list.add(head.val); + head = head.next; + } + return list; + } + + + public static ListNode mergeTwoLists(ListNode l1, ListNode l2) { + if (l1 == null && l2 == null) { + return null; + } + + if (l1 != null && l2 == null) { + return l1; + } + + if (l1 == null && l2 != null) { + return l2; + } + + if (l1.next == null && l2.next == null) { + if (l1.val > l2.val) { + l2.next = l1; + return l2; + } else { + l1.next = l2; + return l1; + } + } + + List list = new ArrayList(); + while (l1 != null) { + list.add(l1.val); + l1 = l1.next; + } + + while (l2 != null) { + list.add(l2.val); + l2 = l2.next; + } + + Collections.sort(list, (o1, o2) -> -(o1 - o2)); + + ListNode head = new ListNode(Integer.MIN_VALUE); + for (Integer n : list) { + ListNode node = new ListNode(n); + if (head.next == null) { + head.next = node; + } else { + ListNode next = head.next; + head.next = node; + node.next = next; + } + } + return head.next; + } +} + diff --git a/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_26_038.java b/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_26_038.java new file mode 100644 index 000000000..16a66d435 --- /dev/null +++ b/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_26_038.java @@ -0,0 +1,34 @@ +package com.github.kylefeng.week01; + +/** + * 26. 删除排序数组中的重复项 + * + * @author kylefeng + * @time 2019/10/19 09:20 + */ +public class LeetCode_26_038 { + + public static int removeDuplicates(int[] nums) { + if (nums == null) { + return 0; + } + + if (nums.length <= 1) { + return nums.length; + } + + int i = 0; + int j = 1; + + while (j < nums.length) { + if (nums[i] != nums[j]) { + nums[i + 1] = nums[j]; + ++i; + } else { + ++j; + } + } + return i + 1; + } + +} diff --git a/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_283_038.java b/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_283_038.java new file mode 100644 index 000000000..faceccc92 --- /dev/null +++ b/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_283_038.java @@ -0,0 +1,28 @@ +package com.github.kylefeng.week01; + +/** + * 283. 移动零 + * + * @author kylefeng + * @time 2019/10/20 16:06 + */ +public class LeetCode_283_038 { + + public static void moveZeroes(int[] nums) { + if (nums == null || nums.length <= 1) { + return; + } + + int zeroIdx = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != 0) { + nums[zeroIdx] = nums[i]; + if (i != zeroIdx) { + nums[i] = 0; + } + zeroIdx++; + } + } + } + +} diff --git a/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_641_038.java b/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_641_038.java new file mode 100644 index 000000000..c7c54e836 --- /dev/null +++ b/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_641_038.java @@ -0,0 +1,174 @@ +package com.github.kylefeng.week01; + +/** + * 641. 设计循环双端队列 + * + * @author kylefeng + * @time 2019/10/19 10:54 + */ +public class LeetCode_641_038 { + public static class MyCircularDeque { + /** + * 队列中的节点,采用双向链表实现 + */ + class Node { + /** + * 在节点存储的数据 + */ + int data; + + /** + * 后继节点指针 + */ + Node next; + + /** + * 前驱节点指针 + */ + Node prev; + } + + /** + * 头节点 + */ + private Node head = new Node(); + + /** + * 尾节点 + */ + private Node tail = new Node(); + + /** + * 最大容量 + */ + private int maxSize; + + /** + * 当前队列大小 + */ + private int size = 0; + + /** + * Initialize your data structure here. Set the size of the deque to be k. + */ + public MyCircularDeque(int k) { + if (k <= 0) { + throw new IllegalArgumentException("Size of MyCircularDeque must great than zero"); + } + this.maxSize = k; + this.head.next = tail; + this.tail.prev = head; + } + + /** + * Adds an item at the front of Deque. Return true if the operation is successful. + */ + public boolean insertFront(int value) { + if (this.size == this.maxSize) { + return false; + } + + // setup new node + Node node = new Node(); + node.data = value; + node.prev = this.head; + node.next = this.head.next; + + // setup head + this.head.next.prev = node; + this.head.next = node; + this.size++; + return true; + } + + /** + * Adds an item at the rear of Deque. Return true if the operation is successful. + */ + public boolean insertLast(int value) { + if (this.size == this.maxSize) { + return false; + } + + // setup new node + Node node = new Node(); + node.data = value; + node.next = this.tail; + node.prev = this.tail.prev; + + // setup tail + this.tail.prev.next = node; + this.tail.prev = node; + this.size++; + return true; + } + + /** + * Deletes an item from the front of Deque. Return true if the operation is successful. + */ + public boolean deleteFront() { + if (this.size == 0) { + return false; + } + + Node toBeDeleted = this.head.next; + this.head.next = toBeDeleted.next; + toBeDeleted.next.prev = this.head; + toBeDeleted.next = toBeDeleted; + toBeDeleted.prev = toBeDeleted; + this.size--; + return true; + } + + /** + * Deletes an item from the rear of Deque. Return true if the operation is successful. + */ + public boolean deleteLast() { + if (this.size == 0) { + return false; + } + + Node toBeDeleted = this.tail.prev; + this.tail.prev = toBeDeleted.prev; + toBeDeleted.prev.next = this.tail; + toBeDeleted.next = toBeDeleted; + toBeDeleted.prev = toBeDeleted; + this.size--; + return true; + } + + /** + * Get the front item from the deque. + */ + public int getFront() { + if (this.size == 0) { + return -1; + } + return this.head.next.data; + } + + /** + * Get the last item from the deque. + */ + public int getRear() { + if (this.size == 0) { + return -1; + } + return this.tail.prev.data; + } + + /** + * Checks whether the circular deque is empty or not. + */ + public boolean isEmpty() { + return this.size == 0; + } + + /** + * Checks whether the circular deque is full or not. + */ + public boolean isFull() { + return this.size == this.maxSize; + } + } + +} diff --git a/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_66_038.java b/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_66_038.java new file mode 100644 index 000000000..7a2db0829 --- /dev/null +++ b/Week 01/id_038/week-01-038/src/main/java/com/github/kylefeng/week01/LeetCode_66_038.java @@ -0,0 +1,85 @@ +package com.github.kylefeng.week01; + +/** + * 66. 加一 + * + * @author kylefeng + * @time 2019/10/19 23:40 + */ +public class LeetCode_66_038 { + + public static int[] plusOne(int[] digits) { + // Handle special cases + if (digits == null || digits.length == 0) { + return digits; + } + + if (digits.length == 1) { + ++digits[0]; + if (digits[0] == 10) { + return new int[]{1, 0}; + } else { + return digits; + } + } + ////////////////////////////////////////// + int last = digits[digits.length - 1]; + ++last; + + + if (last < 10) { + digits[digits.length - 1] = last; + return digits; + } + + boolean promote = false; + // 等于 10 了,要进位 + if (last == 10) { + digits[digits.length - 1] = 0; + promote = true; + } + + for (int i = digits.length - 2; i >= 0; i--) { + if (!promote) { + break; + } + + int d = digits[i]; + ++d; + + if (d < 10) { + digits[i] = d; + break; + } + + if (d == 10 && i != 0) { + digits[i] = 0; + promote = true; + continue; + } + + if (d >= 10 && i == 0) { + digits[i] = d; + } + } + + if (digits[0] >= 10) { + return handleFirstDigitGreatThanTen(digits); + } + + return digits; + } + + private static int[] handleFirstDigitGreatThanTen(int[] digits) { + int num = digits[0]; + int first = num / 10; + int second = num % 10; + + int[] newDigits = new int[digits.length + 1]; + System.arraycopy(digits, 1, newDigits, 2, digits.length - 1); + newDigits[0] = first; + newDigits[1] = second; + return newDigits; + } + +} diff --git a/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_1_038_Test.java b/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_1_038_Test.java new file mode 100644 index 000000000..e994ef049 --- /dev/null +++ b/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_1_038_Test.java @@ -0,0 +1,33 @@ +package com.github.kylefeng.week01; + +import org.junit.jupiter.api.Test; + +import static com.github.kylefeng.week01.LeetCode_1_038.twoSum; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author kylefeng + * @time 2019/10/20 16:39 + */ +public class LeetCode_1_038_Test { + + @Test + void testCases() { + // Given + int[] nums1 = new int[]{2, 7, 11, 15}; + int target1 = 9; + int[] expected1 = new int[]{0, 1}; + + int[] nums2 = new int[]{1, 1}; + int target2 = 2; + int[] expected2 = new int[]{0, 1}; + + // When + int[] r1 = twoSum(nums1, target1); + int[] r2 = twoSum(nums2, target2); + + // Then + assertThat(r1).isEqualTo(expected1); + assertThat(r2).isEqualTo(expected2); + } +} diff --git a/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_21_038_Test.java b/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_21_038_Test.java new file mode 100644 index 000000000..bd9930a50 --- /dev/null +++ b/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_21_038_Test.java @@ -0,0 +1,22 @@ +package com.github.kylefeng.week01; + +import com.github.kylefeng.week01.LeetCode_21_038.ListNode; +import com.google.common.collect.Lists; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author kylefeng + * @time 2019/10/19 22:25 + */ +public class LeetCode_21_038_Test { + + @Test + void testCases() { + ListNode list1 = LeetCode_21_038.newLinkedList(1, 2, 4); + ListNode list2 = LeetCode_21_038.newLinkedList(1, 3, 4); + ListNode merged = LeetCode_21_038.mergeTwoLists(list1, list2); + assertThat(LeetCode_21_038.linkedListToList(merged)).isEqualTo(Lists.newArrayList(1, 1, 2, 3, 4, 4)); + } +} diff --git a/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_26_038_Test.java b/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_26_038_Test.java new file mode 100644 index 000000000..587bf82b1 --- /dev/null +++ b/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_26_038_Test.java @@ -0,0 +1,59 @@ +package com.github.kylefeng.week01; + + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 26. 删除排序数组中的重复项 - 测试用例 + * + * @author kylefeng + * @time 2019/10/19 09:21 + */ +public class LeetCode_26_038_Test { + + @Test + void official_test_case_1() { + int[] input = new int[]{1, 1, 2}; + int[] expected = new int[]{1, 2}; + + int len = LeetCode_26_038.removeDuplicates(input); + + assertThat(len).isEqualTo(2); + assertThat(Arrays.copyOfRange(input, 0, len)).isEqualTo(expected); + } + + @Test + void official_test_case_2() { + int[] input = new int[]{0, 0, 1, 1, 1, 2, 2, 3, 3, 4}; + int[] expected = new int[]{0, 1, 2, 3, 4}; + + int len = LeetCode_26_038.removeDuplicates(input); + + assertThat(len).isEqualTo(5); + assertThat(Arrays.copyOfRange(input, 0, len)).isEqualTo(expected); + } + + @Test + void abnormal_test_cases() { + int[] nullInput = null; + assertThat(LeetCode_26_038.removeDuplicates(nullInput)).isZero(); + + int[] emptyInput = new int[]{}; + assertThat(LeetCode_26_038.removeDuplicates(emptyInput)).isZero(); + + int[] oneInput = new int[]{1}; + LeetCode_26_038.removeDuplicates(oneInput); + assertThat(oneInput).isEqualTo(new int[]{1}); + + int[] twoSameElements = new int[]{1, 1}; + int len = LeetCode_26_038.removeDuplicates(twoSameElements); + assertThat(len).isOne(); + assertThat(Arrays.copyOfRange(twoSameElements, 0, len)).isEqualTo(new int[]{1}); + } + + +} diff --git a/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_283_038_Test.java b/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_283_038_Test.java new file mode 100644 index 000000000..f19e8bb79 --- /dev/null +++ b/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_283_038_Test.java @@ -0,0 +1,46 @@ +package com.github.kylefeng.week01; + +import org.junit.jupiter.api.Test; + +import static com.github.kylefeng.week01.LeetCode_283_038.moveZeroes; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author kylefeng + * @time 2019/10/20 16:07 + */ +public class LeetCode_283_038_Test { + + @Test + void testCases() { + // Given + int[] input = new int[]{0, 1, 0, 3, 12}; + int[] expected = new int[]{1, 3, 12, 0, 0}; + + // When + moveZeroes(input); + + // Then + assertThat(input).isEqualTo(expected); + } + + @Test + void abnormalTestCases() { + // Given + int[] nullInput = null; + int[] emptyInput = new int[]{}; + int[] singleElement = new int[]{Integer.MAX_VALUE}; + + // When + moveZeroes(nullInput); + moveZeroes(emptyInput); + moveZeroes(singleElement); + + // Then + assertThat(nullInput).isNull(); + assertThat(emptyInput).isEmpty(); + assertThat(singleElement).isEqualTo(new int[]{Integer.MAX_VALUE}); + } + + +} diff --git a/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_641_038_Test.java b/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_641_038_Test.java new file mode 100644 index 000000000..f8bdf7c3d --- /dev/null +++ b/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_641_038_Test.java @@ -0,0 +1,27 @@ +package com.github.kylefeng.week01; + +import org.junit.jupiter.api.Test; + +import static com.github.kylefeng.week01.LeetCode_641_038.MyCircularDeque; +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author kylefeng + * @time 2019/10/19 10:56 + */ +public class LeetCode_641_038_Test { + + @Test + void test_circular_dequeue_with_maximum_size_3() { + MyCircularDeque deque = new MyCircularDeque(3); + assertTrue(deque.insertLast(1)); + assertTrue(deque.insertLast(2)); + assertTrue(deque.insertFront(3)); + assertFalse(deque.insertFront(4)); + assertEquals(2, deque.getRear()); + assertTrue(deque.isFull()); + assertTrue(deque.deleteLast()); + assertTrue(deque.insertFront(4)); + assertEquals(4, deque.getFront()); + } +} diff --git a/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_66_038_Test.java b/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_66_038_Test.java new file mode 100644 index 000000000..5bb9279a2 --- /dev/null +++ b/Week 01/id_038/week-01-038/src/test/java/com/github/kylefeng/week01/LeetCode_66_038_Test.java @@ -0,0 +1,44 @@ +package com.github.kylefeng.week01; + +import org.junit.jupiter.api.Test; + +import static com.github.kylefeng.week01.LeetCode_66_038.plusOne; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author kylefeng + * @time 2019/10/20 15:26 + */ +public class LeetCode_66_038_Test { + + @Test + void testCase() { + int[] input1 = new int[]{1, 2, 3}; + int[] expect1 = new int[]{1, 2, 4}; + + int[] input2 = new int[]{4, 3, 2, 1}; + int[] expect2 = new int[]{4, 3, 2, 2}; + + + assertThat(plusOne(input1)).isEqualTo(expect1); + assertThat(plusOne(input2)).isEqualTo(expect2); + } + + void abnormalTestCase() { + int[] input1 = new int[]{9, 9}; + int[] expect1 = new int[]{1, 0, 0}; + + int[] input2 = new int[]{9}; + int[] expect2 = new int[]{1, 0}; + + int[] nullInput = null; + int[] emptyInput = new int[]{}; + + assertThat(plusOne(input1)).isEqualTo(expect1); + assertThat(plusOne(input2)).isEqualTo(expect2); + assertThat(plusOne(nullInput)).isNull(); + assertThat(plusOne(emptyInput)).isEmpty(); + } + + +} diff --git a/Week 01/id_043/NOTE.md b/Week 01/id_043/NOTE.md index a6321d6e2..2d839b591 100644 --- a/Week 01/id_043/NOTE.md +++ b/Week 01/id_043/NOTE.md @@ -1,4 +1,3 @@ # NOTE - diff --git a/Week 01/id_048/Solution.java b/Week 01/id_048/Solution.java new file mode 100644 index 000000000..715d70efd --- /dev/null +++ b/Week 01/id_048/Solution.java @@ -0,0 +1,62 @@ +package com.leetcode.practice; + +/** + * Created by tim on 2019/10/20. + */ +public class Solution { + public static void main(String[] args) { + Solution solution = new Solution(); + int[] dits = {1,2,3,4}; + int[] dit = solution.plusOne(dits); + for (int i = 0; i < dit.length; i++) { + //System.out.println(dit[i]); + } + int[] nums = {1,2,0,3,0,0,5}; + solution.moveZeroes(nums); + for (int i = 0; i< nums.length; i++) { + System.out.println(nums[i]); + } + } + + //https://leetcode-cn.com/problems/plus-one/ + public int[] plusOne(int[] digits) { + //求出数字 + int len = digits.length; + int num = 0; + int resNum; + for (int i = 0; i < len; i++) { + num += digits[i] * (int)Math.pow(10,len-i-1); + } + //加1 + resNum = num + 1; + //求出数组 + String resStr = String.valueOf(resNum); + int[] dit = new int[resStr.length()]; + for (int i = 0; i < resStr.length(); i++) { + Character ch = resStr.charAt(i); + dit[i] = Integer.parseInt(ch.toString()); + } + return dit; + } + + // https://leetcode-cn.com/problems/move-zeroes/ + public void moveZeroes(int[] nums) { + int len = nums.length; + int current; + for (int i = 0; i< len; i++) { + current = nums[i]; + if (current == 0) { + if (i + 1 < len) { + for (int j= i +1; j < len;j++) { + if (nums[j] != 0) { + nums[i] = nums[j]; + nums[j] = current; + break; + } + } + } + } + } + } + +} diff --git a/Week 01/id_053/MyCircularDeque.java b/Week 01/id_053/MyCircularDeque.java new file mode 100644 index 000000000..e92cf90ff --- /dev/null +++ b/Week 01/id_053/MyCircularDeque.java @@ -0,0 +1,90 @@ +class MyCircularDeque { + int size; + int k; + DoubleListNode head; + DoubleListNode tail; + /** Initialize your data structure here. Set the size of the deque to be k. */ + public MyCircularDeque(int k) { + head = new DoubleListNode(-1); + tail = new DoubleListNode(-1); + head.pre = tail; + tail.next = head; + this.k = k; + this.size = 0; + } + + /** Adds an item at the front of Deque. Return true if the operation is successful. */ + public boolean insertFront(int value) { + if (size == k) + return false; + DoubleListNode node = new DoubleListNode(value); + node.next = head; + node.pre = head.pre; + head.pre.next = node; + head.pre = node; + size++; + return true; + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + public boolean insertLast(int value) { + if (size == k) + return false; + DoubleListNode node = new DoubleListNode(value); + node.next = tail.next; + tail.next.pre = node; + tail.next = node; + node.pre = tail; + size++; + return true; + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + public boolean deleteFront() { + if (size == 0) + return false; + head.pre.pre.next = head; + head.pre = head.pre.pre; + size--; + return true; + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + public boolean deleteLast() { + if (size == 0) + return false; + tail.next.next.pre = tail; + tail.next = tail.next.next; + size--; + return true; + } + + /** Get the front item from the deque. */ + public int getFront() { + return head.pre.val; + } + + /** Get the last item from the deque. */ + public int getRear() { + return tail.next.val; + } + + /** Checks whether the circular deque is empty or not. */ + public boolean isEmpty() { + return size == 0; + } + + /** Checks whether the circular deque is full or not. */ + public boolean isFull() { + return size == k; + } +} + +class DoubleListNode { + DoubleListNode pre; + DoubleListNode next; + int val; + public DoubleListNode(int val) { + this.val = val; + } +} \ No newline at end of file diff --git a/Week 01/id_053/Trap.java b/Week 01/id_053/Trap.java new file mode 100644 index 000000000..6533e817a --- /dev/null +++ b/Week 01/id_053/Trap.java @@ -0,0 +1,22 @@ +public class Trap{ + public int trap(int[] A){ + int a=0; + int b=A.length-1; + int max=0; + int leftmax=0; + int rightmax=0; + while(a<=b){ + leftmax=Math.max(leftmax,A[a]); + rightmax=Math.max(rightmax,A[b]); + if(leftmaxval, pos2_ptr->val)); + if (pos1_ptr->val <= pos2_ptr->val) { + pos1_ptr = pos1_ptr->next; + } else { + pos2_ptr = pos2_ptr->next; + } + } else if (pos1_ptr != nullptr) { + new_node_ptr = new ListNode(pos1_ptr->val); + pos1_ptr = pos1_ptr->next; + } else { + new_node_ptr = new ListNode(pos2_ptr->val); + pos2_ptr = pos2_ptr->next; + } + + if (!head_ptr) { + tail_ptr = head_ptr = new_node_ptr; + } else { + tail_ptr->next = new_node_ptr; + tail_ptr = new_node_ptr; + } + } + + return head_ptr; + } +}; \ No newline at end of file diff --git a/Week 01/id_063/LeetCode_021_063.java b/Week 01/id_063/LeetCode_021_063.java new file mode 100644 index 000000000..17ff05d89 --- /dev/null +++ b/Week 01/id_063/LeetCode_021_063.java @@ -0,0 +1,36 @@ +public class Solution { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + ListNode pos1 = l1; + ListNode pos2 = l2; + + ListNode head = null; + ListNode tail = null; + ListNode newNode = null; + + while (! ((pos1 == null) && (pos2 == null))) { + if ((pos1 != null) && (pos2 != null)) { + newNode = new ListNode(Math.min(pos1.val, pos2.val)); + if (pos1.val <= pos2.val) { + pos1 = pos1.next; + } else { + pos2 = pos2.next; + } + } else if (pos1 == null) { + newNode = new ListNode(pos2.val); + pos2 = pos2.next; + } else { + newNode = new ListNode(pos1.val); + pos1 = pos1.next; + } + + if (head == null) { + tail = head = newNode; + } else { + tail.next = newNode; + tail = newNode; + } + } + + return head; + } +} \ No newline at end of file diff --git a/Week 01/id_063/LeetCode_189_063.cpp b/Week 01/id_063/LeetCode_189_063.cpp new file mode 100644 index 000000000..b04e7dd8a --- /dev/null +++ b/Week 01/id_063/LeetCode_189_063.cpp @@ -0,0 +1,36 @@ +/* +思路 +整个数组左右对换,然后前k个左右对换对换,前k个后面的所有元素左右对换 + */ + + +#include +using namespace std; + +class Solution { +private: + void swap(int& num1, int& num2) { + int tmp; + tmp = num1; num1 = num2; num2 = tmp; + } + +public: + void rotate(vector& nums, int k) { + if ((nums.empty()) || (k%nums.size() == 0)) { + return; + } + + k %= nums.size(); + for (int i = 0, j = nums.size()-1; i < j; i++, j--) { + swap(nums[i], nums[j]); + } + + for (int i = 0, j = k-1; i < j; i++, j--) { + swap(nums[i], nums[j]); + } + + for (int i = k, j = nums.size()-1; i < j; i++, j--) { + swap(nums[i], nums[j]); + } + } +}; diff --git a/Week 01/id_063/LeetCode_189_063.java b/Week 01/id_063/LeetCode_189_063.java new file mode 100644 index 000000000..5b904aa9a --- /dev/null +++ b/Week 01/id_063/LeetCode_189_063.java @@ -0,0 +1,27 @@ +/* +思路 +整个数组左右对换,然后前k个左右对换对换,前k个后面的所有元素左右对换 + */ + +class Solution { + public void rotate(int[] nums, int k) { + if ((nums.length == 0) || (k == 0)) { + return; + } + + int tmp; + + k %= nums.length; + for (int i = 0, j = nums.length-1; i < j; i++, j--) { + tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; + } + + for (int i = 0, j = k-1; i < j; i++, j--) { + tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; + } + + for (int i = k, j = nums.length-1; i < j; i++, j--) { + tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; + } + } +} \ No newline at end of file diff --git a/Week 01/id_063/LeetCode_1_063.cpp b/Week 01/id_063/LeetCode_1_063.cpp new file mode 100644 index 000000000..5abac0535 --- /dev/null +++ b/Week 01/id_063/LeetCode_1_063.cpp @@ -0,0 +1,29 @@ +/* +从左到右迭代数组中元素,每一个元素迭代时候判断已经迭代的元素中有没有匹配的 +判断是否匹配需要额外的哈希结构缓存已经迭代过的元素的数值和下标 + +向Hash中插入数值或者是触发了Rehash会带来一定开销,严格讲时间复杂度比O(n)高 + */ + + +#include +#include +using namespace std; + +class Solution { +public: + vector twoSum(vector& nums, int target) { + unordered_map val2idx(nums.size()); + + val2idx[nums[0]] = 0; + for (int i = 1; i < nums.size(); i++) { + if (val2idx.find(target - nums[i]) != val2idx.end()) { + return vector { val2idx[target - nums[i]], i }; + } + + val2idx[nums[i]] = i; + } + + return vector { 0, 0 }; + } +}; \ No newline at end of file diff --git a/Week 01/id_063/LeetCode_1_063.java b/Week 01/id_063/LeetCode_1_063.java new file mode 100644 index 000000000..c616e61f5 --- /dev/null +++ b/Week 01/id_063/LeetCode_1_063.java @@ -0,0 +1,28 @@ +import java.util.HashMap; +import java.util.Map; + + +/* +从左到右迭代数组中元素,每一个元素迭代时候判断已经迭代的元素中有没有匹配的 +判断是否匹配需要额外的哈希结构缓存已经迭代过的元素的数值和下标 + +向Hash中插入数值或者是触发了Rehash会带来一定开销,严格讲时间复杂度比O(n)高 + */ + +class Solution { + public int[] twoSum(int[] nums, int target) { + Map val2idx = new HashMap<>(nums.length + 1, 1); + + val2idx.put(nums[0], 0); + + for (int i = 1; i < nums.length; i++) { + if (val2idx.containsKey(target - nums[i])) { + return new int[] { val2idx.get(target - nums[i]), i }; + } + + val2idx.put(nums[i], i); + } + + return new int[] {0, 0}; + } +} \ No newline at end of file diff --git a/Week 01/id_063/LeetCode_26_063.cpp b/Week 01/id_063/LeetCode_26_063.cpp new file mode 100644 index 000000000..dcec83a99 --- /dev/null +++ b/Week 01/id_063/LeetCode_26_063.cpp @@ -0,0 +1,22 @@ +#include +using namespace std; + + +class Solution { +public: + int removeDuplicates(vector& nums) { + if (nums.size() == 0) { + return 0; + } + + int curIdx = 1; + + for (int i = 1; i < nums.size(); i++) { + if (nums[i] != nums[curIdx-1]) { + nums[curIdx++] = nums[i]; + } + } + + return curIdx; + } +}; \ No newline at end of file diff --git a/Week 01/id_063/LeetCode_26_063.java b/Week 01/id_063/LeetCode_26_063.java new file mode 100644 index 000000000..3c2b518a2 --- /dev/null +++ b/Week 01/id_063/LeetCode_26_063.java @@ -0,0 +1,25 @@ +/* +思路 +从左到右依次迭代,用游标变量curIdx维护当前存放不重复数据的位置,nums[curIdx-1]位置就是上次刚发现的 +不重复数据,因为原序列是排序号的序列,所以当前迭代数据如果和nums[curIdx-1]不相同,必然是在前面迭代 +中没有出现过的数据,将其存入到新的存放位置,递增curIdx即可 + +*/ + +class Solution { + public int removeDuplicates(int[] nums) { + if (nums.length == 0) { + return 0; + } + + int curIdx = 1; + + for (int i = 1; i < nums.length; i++) { + if (nums[i] != nums[curIdx - 1]) { + nums[curIdx++] = nums[i]; + } + } + + return curIdx; + } +} \ No newline at end of file diff --git a/Week 01/id_063/LeetCode_283_063.cpp b/Week 01/id_063/LeetCode_283_063.cpp new file mode 100644 index 000000000..9ec165f13 --- /dev/null +++ b/Week 01/id_063/LeetCode_283_063.cpp @@ -0,0 +1,17 @@ +#include +using namespace std; + +class Solution { +public: + void moveZeroes(vector& nums) { + int idx_store = 0; + + for (int i = 0; i < nums.size(); i++) { + if (nums[i]) { + nums[idx_store++] = nums[i]; + } + } + + fill(nums.begin() + idx_store, nums.end(), 0); + } +}; \ No newline at end of file diff --git a/Week 01/id_063/LeetCode_283_063.java b/Week 01/id_063/LeetCode_283_063.java new file mode 100644 index 000000000..e3237b185 --- /dev/null +++ b/Week 01/id_063/LeetCode_283_063.java @@ -0,0 +1,20 @@ +/* + 就是简单的数组移动操作 +*/ + + +public class Solution { + public void moveZeroes(int[] nums) { + int idx1, idx2; + + for (idx1 = 0, idx2 = 0; idx2 < nums.length; idx2++) { + if (nums[idx2] != 0) { + nums[idx1++] = nums[idx2]; + } + } + + while (idx1 < nums.length) { + nums[idx1++] = 0; + } + } +} diff --git a/Week 01/id_063/LeetCode_42_063.cpp b/Week 01/id_063/LeetCode_42_063.cpp new file mode 100644 index 000000000..048d2d16e --- /dev/null +++ b/Week 01/id_063/LeetCode_42_063.cpp @@ -0,0 +1,51 @@ +/* +动态规划算法, 暴力算法迭代每一根柱子都需要左右两个方向扫描到底查找左边最高的柱子和右边最高柱子 +进行了重复计算,可以用动态规划的方式先把每个柱子左边最高的柱子高度和每个柱子右边最高的柱子高度算出来 + +设dpLeftMax[i] 表示柱子i左边最高的柱子高度, 则有如下递推关系 + 若 height[i-1] > dpLeftMax[i-1], 则dpLeftMax[i] = height[i-1] + 否则 dpLeftMax[i] = dpLeftMax[i-1] + +设dpRightMax[i] 表示柱子i右边最高的柱子的高度,有如下递推关系 + 若 height[i+1] > dpRightMax[i+1], 则dpRightMax[i] = height[i+1] + 否则 dpRightMax[i] = dpRightMax[i+1] + + 两次线性扫描可以更新完两个Dp数组,第三次从左到右扫描所有柱子计算每一个柱子的出水量,然后累加即可 +*/ + +#include +using namespace std; + +class Solution { +public: + int trap(vector& height) { + if (height.size() == 0) { + return 0; + } + + int len = height.size(); + vector dpLeftMax(len); + vector dpRightMax(len); + + dpLeftMax[0] = 0; + for (int i = 1; i < len; i++) { + dpLeftMax[i] = (height[i-1] > dpLeftMax[i-1]) ? height[i-1] : dpLeftMax[i-1]; + } + + dpRightMax[len-1] = 0; + for (int i = len - 2; i >= 0; i--) { + dpRightMax[i] = (height[i+1] > dpRightMax[i+1]) ? height[i+1] : dpRightMax[i+1]; + } + + int area = 0; + int water_height; + for (int i = 0; i < len; i++) { + water_height = (dpLeftMax[i] < dpRightMax[i]) ? dpLeftMax[i] : dpRightMax[i]; + if (water_height > height[i]) { + area += (water_height - height[i]); + } + } + + return area; + } +}; \ No newline at end of file diff --git a/Week 01/id_063/LeetCode_42_063.java b/Week 01/id_063/LeetCode_42_063.java new file mode 100644 index 000000000..a226ad9d2 --- /dev/null +++ b/Week 01/id_063/LeetCode_42_063.java @@ -0,0 +1,50 @@ +/* +动态规划算法, 暴力算法迭代每一根柱子都需要左右两个方向扫描到底查找左边最高的柱子和右边最高柱子 +进行了重复计算,可以用动态规划的方式先把每个柱子左边最高的柱子高度和每个柱子右边最高的柱子高度算出来 + +设dpLeftMax[i] 表示柱子i左边最高的柱子高度, 则有如下递推关系 + 若 height[i-1] > dpLeftMax[i-1], 则dpLeftMax[i] = height[i-1] + 否则 dpLeftMax[i] = dpLeftMax[i-1] + +设dpRightMax[i] 表示柱子i右边最高的柱子的高度,有如下递推关系 + 若 height[i+1] > dpRightMax[i+1], 则dpRightMax[i] = height[i+1] + 否则 dpRightMax[i] = dpRightMax[i+1] + + 两次线性扫描可以更新完两个Dp数组,第三次从左到右扫描所有柱子计算每一个柱子的出水量,然后累加即可 + */ + +class Solution { + public int trap(int[] height) { + if (height.length == 0) { + return 0; + } + + int totalLen = height.length; + int[] dpLeftMax = new int[totalLen]; + int[] dpRightMax = new int[totalLen]; + + // dp 算法递推 + dpLeftMax[0] = 0; + for (int i = 1; i < totalLen; i++) { + dpLeftMax[i] = Math.max(height[i-1], dpLeftMax[i-1]); + } + + // dp 算法递推 + dpRightMax[totalLen-1] = 0; + for (int i = totalLen-2; i >= 0; i--) { + dpRightMax[i] = Math.max(height[i+1], dpRightMax[i+1]); + } + + // 累加水量 + int area = 0; + int waterHeight; + for (int i = 1; i < totalLen-1; i++) { + waterHeight = Math.min(dpLeftMax[i], dpRightMax[i]); + if (waterHeight > height[i]) { + area += (waterHeight - height[i]); + } + } + + return area; + } +} \ No newline at end of file diff --git a/Week 01/id_063/LeetCode_641_063.cpp b/Week 01/id_063/LeetCode_641_063.cpp new file mode 100644 index 000000000..04d881358 --- /dev/null +++ b/Week 01/id_063/LeetCode_641_063.cpp @@ -0,0 +1,127 @@ +class Node { + int val; + Node *prev_ptr; + Node *next_ptr; + +public: + Node(int val) : val(val), prev_ptr(nullptr), next_ptr(nullptr) {} + + friend class MyCircularDeque; +}; + +class MyCircularDeque { +private: + int curSize; + Node *head_ptr; + Node *tail_ptr; + const int MAX_SIZE; + +public: + /** Initialize your data structure here. Set the size of the deque to be k. */ + MyCircularDeque(int k) : curSize(0), head_ptr(nullptr), tail_ptr(nullptr), MAX_SIZE(k) { + + } + + /** Adds an item at the front of Deque. Return true if the operation is successful. */ + bool insertFront(int value) { + if (curSize == MAX_SIZE) { + return false; + } + + Node *new_node_ptr = new Node(value); + + if (curSize == 0) { + head_ptr = tail_ptr = new_node_ptr; + } else { + head_ptr->prev_ptr = new_node_ptr; + new_node_ptr->next_ptr = head_ptr; + head_ptr = new_node_ptr; + } + + curSize++; + return true; + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + bool insertLast(int value) { + if (curSize == MAX_SIZE) { + return false; + } + + Node *new_node_ptr = new Node(value); + + if (curSize == 0) { + head_ptr = tail_ptr = new_node_ptr; + } else { + tail_ptr->next_ptr = new_node_ptr; + new_node_ptr->prev_ptr = tail_ptr; + tail_ptr = new_node_ptr; + } + + curSize++; + return true; + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + bool deleteFront() { + if (curSize == 0) { + return false; + } + + Node *del_ptr = head_ptr; + head_ptr = head_ptr->next_ptr; + if (head_ptr) { + head_ptr->prev_ptr = nullptr; + } + + delete del_ptr; + del_ptr = nullptr; + + curSize--; + return true; + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + bool deleteLast() { + if (curSize == 0) { + return false; + } + + Node *del_ptr = tail_ptr; + tail_ptr = tail_ptr->prev_ptr; + if (tail_ptr) { + tail_ptr->next_ptr = nullptr; + } + + curSize--; + return true; + } + + /** Get the front item from the deque. */ + int getFront() { + if (curSize == 0) { + return -1; + } + + return head_ptr->val; + } + + /** Get the last item from the deque. */ + int getRear() { + if (curSize == 0) { + return -1; + } + + return tail_ptr->val; + } + + /** Checks whether the circular deque is empty or not. */ + bool isEmpty() { + return (curSize == 0); + } + + /** Checks whether the circular deque is full or not. */ + bool isFull() { + return (curSize == MAX_SIZE); + } +}; \ No newline at end of file diff --git a/Week 01/id_063/LeetCode_641_063.java b/Week 01/id_063/LeetCode_641_063.java new file mode 100644 index 000000000..b3bc7c590 --- /dev/null +++ b/Week 01/id_063/LeetCode_641_063.java @@ -0,0 +1,136 @@ +/* +链表实现双向链表,无特殊算法,简单链表操作 +*/ + + +class MyCircularDeque { + private class Node { + int val; + Node prev; + Node next; + + Node(int val) { + this.val = val; + this.prev = null; + this.next = null; + } + } + + private Node head = null; + private Node tail = null; + private int curSize = 0; + private final int MAX_SIZE; + + /** Initialize your data structure here. Set the size of the deque to be k. */ + public MyCircularDeque(int k) { + MAX_SIZE = k; + } + + /** Adds an item at the front of Deque. Return true if the operation is successful. */ + public boolean insertFront(int value) { + if (curSize == MAX_SIZE) { + return false; + } + + Node newNode = new Node(value); + + if (head == null) { + head = newNode; + tail = newNode; + } else { + newNode.next = head; + head.prev = newNode; + head = newNode; + } + + curSize++; + return true; + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + public boolean insertLast(int value) { + if (curSize == MAX_SIZE) { + return false; + } + + Node newNode = new Node(value); + + if (head == null) { + head = newNode; + tail = newNode; + } else { + tail.next = newNode; + newNode.prev = tail; + tail = newNode; + } + + curSize++; + return true; + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + public boolean deleteFront() { + if (curSize == 0) { + return false; + } + + if (curSize == 1) { + head = null; + tail = null; + } else { + head = head.next; + head.prev.next = null; + head.prev = null; + } + + curSize--; + return true; + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + public boolean deleteLast() { + if (curSize == 0) { + return false; + } + + if (curSize == 1) { + head = null; + tail = null; + } else { + tail = tail.prev; + tail.next.prev = null; + tail.next = null; + } + + curSize--; + return true; + } + + /** Get the front item from the deque. */ + public int getFront() { + if (curSize == 0) { + return -1; + } + + return head.val; + } + + /** Get the last item from the deque. */ + public int getRear() { + if (curSize == 0) { + return -1; + } + + return tail.val; + } + + /** Checks whether the circular deque is empty or not. */ + public boolean isEmpty() { + return (curSize == 0); + } + + /** Checks whether the circular deque is full or not. */ + public boolean isFull() { + return (curSize == MAX_SIZE); + } +} \ No newline at end of file diff --git a/Week 01/id_063/LeetCode_66_063.cpp b/Week 01/id_063/LeetCode_66_063.cpp new file mode 100644 index 000000000..9e483f38e --- /dev/null +++ b/Week 01/id_063/LeetCode_66_063.cpp @@ -0,0 +1,37 @@ +#include +#include +using namespace std; + +class Solution { +public: + vector plusOne(vector& digits) { + stack s; + int sum = 0; + int len = digits.size(); + int carry = 0; + + sum = digits[len-1] + 1; + s.push(sum % 10); + carry = (sum >= 10) ? 1 : 0; + + for (int i = len-2; i >= 0; i--) { + sum = digits[i] + carry; + + s.push(sum % 10); + carry = (sum >= 10) ? 1 : 0; + } + + if (carry) { + s.push(carry); + } + + vector result(s.size()); + int idx = 0; + while (!s.empty()) { + result[idx++] = s.top(); + s.pop(); + } + + return result; + } +}; \ No newline at end of file diff --git a/Week 01/id_063/LeetCode_66_063.java b/Week 01/id_063/LeetCode_66_063.java new file mode 100644 index 000000000..27a78456f --- /dev/null +++ b/Week 01/id_063/LeetCode_66_063.java @@ -0,0 +1,40 @@ +import java.util.Arrays; +import java.util.LinkedList; +import java.util.Queue; + +class Solution { + public int[] plusOne(int[] digits) { + int len = digits.length; + int[] result = null; + + if (digits[len-1] != 9) { + result = Arrays.copyOf(digits, len); + result[len-1]++; + } else { + Queue q = new LinkedList<>(); + int carry = 0; + int sum; + + sum = digits[len-1] + 1; + carry = (sum >= 10) ? 1 : 0; + q.add(sum % 10); + + for (int i = len-2; i >= 0; i--) { + sum = digits[i] + carry; + carry = (sum >= 10) ? 1 : 0; + q.add(sum % 10); + } + + if (carry != 0) { + q.add(carry); + } + + result = new int[q.size()]; + for (int i = result.length-1; i >= 0; i--) { + result[i] = q.poll(); + } + } + + return result; + } +} \ No newline at end of file diff --git a/Week 01/id_063/LeetCode_88_063.cpp b/Week 01/id_063/LeetCode_88_063.cpp new file mode 100644 index 000000000..eac6c1508 --- /dev/null +++ b/Week 01/id_063/LeetCode_88_063.cpp @@ -0,0 +1,38 @@ +/* +思路 +两个数组都从右到左进行反向迭代,存储数据的位置初始设置在num1偏移两个数组长度和的位置 +每次取两个数组当前迭代元素中较大的一个存放在num1的最末端,元素较大的那个数组迭代游标 +左移,同时存储数据的位置进行左移 + +只要num1 和 num2两个数组中有一个先迭代完,循环就可以结束 +这个时候出现两种情况 +1. num1先迭代完 num2中还剩余一段内容,这段内容是现在所有数据中最小的,直接拷贝到num1开头位置 +1. num2先迭代完 num1中前面剩一段是所有数据中最小的,且有序,本来就该存贮在现在的位置,不用做任何处理 + +整个过程扫描一遍即可结束 时间复杂度O(m+n) 空间复杂度O(1) 要换位思考,逆向迭代能够巧妙节省掉中间缓存 + + */ + +#include +using namespace std; + +class Solution { +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + int idx_store = m + n - 1; + int idx1 = m - 1; + int idx2 = n - 1; + + while ((idx1 >= 0) && (idx2 >= 0)) { + if (nums1[idx1] > nums2[idx2]) { + nums1[idx_store--] = nums1[idx1--]; + } else { + nums1[idx_store--] = nums2[idx2--]; + } + } + + if (idx2 >= 0) { + copy(nums2.begin(), nums2.begin()+(idx2+1), nums1.begin()); + } + } +}; \ No newline at end of file diff --git a/Week 01/id_063/LeetCode_88_063.java b/Week 01/id_063/LeetCode_88_063.java new file mode 100644 index 000000000..ace2976fa --- /dev/null +++ b/Week 01/id_063/LeetCode_88_063.java @@ -0,0 +1,36 @@ +/* +思路 +两个数组都从右到左进行反向迭代,存储数据的位置初始设置在num1偏移两个数组长度和的位置 +每次取两个数组当前迭代元素中较大的一个存放在num1的最末端,元素较大的那个数组迭代游标 +左移,同时存储数据的位置进行左移 + +只要num1 和 num2两个数组中有一个先迭代完,循环就可以结束 +这个时候出现两种情况 +1. num1先迭代完 num2中还剩余一段内容,这段内容是现在所有数据中最小的,直接拷贝到num1开头位置 +1. num2先迭代完 num1中前面剩一段是所有数据中最小的,且有序,本来就该存贮在现在的位置,不用做任何处理 + +整个过程扫描一遍即可结束 时间复杂度O(m+n) 空间复杂度O(1) 要换位思考,逆向迭代能够巧妙节省掉中间缓存 + + */ + +import java.util.Arrays; + +class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + int idxStore = m + n -1; + int idx1 = m - 1; + int idx2 = n - 1; + + while ((idx1 >= 0) && (idx2 >= 0)) { + if (nums1[idx1] > nums2[idx2]) { + nums1[idxStore--] = nums1[idx1--]; + } else { + nums1[idxStore--] = nums2[idx2--]; + } + } + + if (idx2 >= 0) { + System.arraycopy(nums2, 0, nums1, 0, idx2+1); + } + } +} \ No newline at end of file diff --git a/Week 01/id_068/jie_yu_shui.cpp b/Week 01/id_068/jie_yu_shui.cpp new file mode 100644 index 000000000..d9c87f933 --- /dev/null +++ b/Week 01/id_068/jie_yu_shui.cpp @@ -0,0 +1,18 @@ +class Solution { +public: + int trap(vector& height) { + int n = height.size(); + int ans = 0; + for(int i = 1; i < n-1; ++i) { + int max_l = 0, max_r = 0; + for(int j = 0; j <= i; ++j) { + max_l = max(max_l, height[j]); + } + for(int j = i; j < n; ++j) { + max_r = max(max_r, height[j]); + } + ans += (min(max_l, max_r) - height[i]); + } + return ans; + } +}; diff --git a/Week 01/id_068/plus_one.cpp b/Week 01/id_068/plus_one.cpp new file mode 100644 index 000000000..1df58e550 --- /dev/null +++ b/Week 01/id_068/plus_one.cpp @@ -0,0 +1,18 @@ +class Solution { +public: + vector plusOne(vector& digits) { + int len = digits.size(); + digits[len-1]++; + for(int i = len-1; i > 0; i--) { + if (digits[i] == 10) { + digits[i-1]++; + digits[i] = 0; + } + } + if (digits[0] == 10) { + digits.push_back(0); + digits[0] = 1; + } + return digits; + } +}; diff --git a/Week 01/id_078/LeetCode_26_078.java b/Week 01/id_078/LeetCode_26_078.java new file mode 100644 index 000000000..07106ee45 --- /dev/null +++ b/Week 01/id_078/LeetCode_26_078.java @@ -0,0 +1,17 @@ +package com.example.untitled1; + +public class LeetCode_26_078 { + public int removeDuplicates(int[] nums) { + if(nums == null || nums.length == 0) return 0; + int p = 0; + int q = 1; + while(q < nums.length){ + if(nums[p] != nums[q]){ + nums[p + 1] = nums[q]; + p++; + } + q++; + } + return p + 1; + } +} diff --git a/Week 01/id_078/LeetCode_283_078.java b/Week 01/id_078/LeetCode_283_078.java new file mode 100644 index 000000000..db92e215f --- /dev/null +++ b/Week 01/id_078/LeetCode_283_078.java @@ -0,0 +1,14 @@ +class MoveZero { + public void moveZeroes(int[] nums) { + int j = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != 0) { + nums[j] = nums[i]; + if (i != j) { + nums[i] = 0; + } + j++; + } + } + } +} \ No newline at end of file diff --git a/Week 01/id_078/LeetCode_66_078.java b/Week 01/id_078/LeetCode_66_078.java new file mode 100644 index 000000000..fc5f95932 --- /dev/null +++ b/Week 01/id_078/LeetCode_66_078.java @@ -0,0 +1,44 @@ +//给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。 +// +// 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 +// +// 你可以假设除了整数 0 之外,这个整数不会以零开头。 +// +// 示例 1: +// +// 输入: [1,2,3] +//输出: [1,2,4] +//解释: 输入数组表示数字 123。 +// +// +// 示例 2: +// +// 输入: [4,3,2,1] +//输出: [4,3,2,2] +//解释: 输入数组表示数字 4321。 +// +// Related Topics 数组 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int[] plusOne(int[] digits) { + for (int i = digits.length-1; i >= 0; i--) { + if (digits[i] != 9) { + digits[i] += 1; + return digits; + } else { + if (i == 0) { + int[] answer = new int[digits.length+1]; + answer[0] = 1; + return answer; + } else { + digits[i] = 0; + } + } + } + return digits; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 01/id_083/DequeUpdate.java b/Week 01/id_083/DequeUpdate.java new file mode 100644 index 000000000..7e0ca45ca --- /dev/null +++ b/Week 01/id_083/DequeUpdate.java @@ -0,0 +1,25 @@ +import java.util.Deque; +import java.util.LinkedList; + +public class DequeUpdate { + + public static void main(String[] args) { + // TODO Auto-generated method stub + + Deque deque = new LinkedList(); + deque.addLast("a"); + deque.addLast("b"); + deque.addLast("c"); + System.out.println(deque); + + String str = deque.peekFirst(); + System.out.println(str); + System.out.println(deque); + + while (deque.size()>0) { + System.out.println(deque.removeFirst()); + } + System.out.println(deque); + } + +} diff --git a/Week 01/id_083/Leetcode42.java b/Week 01/id_083/Leetcode42.java new file mode 100644 index 000000000..1c9974396 --- /dev/null +++ b/Week 01/id_083/Leetcode42.java @@ -0,0 +1,51 @@ +import java.util.Scanner; +import java.util.Stack; +/* +*由于以前都是“暴力解决”这种算法问题,现在尝试用栈 +*第一遍还是没什么思路,只好学习优秀代码.题目如下: +* +* +给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 +上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。  +示例: +输入: [0,1,0,2,1,0,1,3,2,1,2,1] +输出: 6 +*/ +public class TrapRainWater { + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in);//此处不处理[],默认输入如:0,1,0,2,1,0,1,3,2,1,2,1 + String str = sc.nextLine().toString(); + String temp[] = str.split(","); + int array[] = new int[temp.length]; + for(int j = 0; j < array.length; j++) + { + array[j] = Integer.parseInt(temp[j]); + } + System.out.print(trap(array)); + } + + public static int trap(int[] height) { + int sum = 0; + Stack stack = new Stack<>(); + int current = 0; + while (current < height.length) { + //如果栈不空并且当前指向的高度大于栈顶高度就一直循环 + while (!stack.empty() && height[current] > height[stack.peek()]) { + int h = height[stack.peek()]; //取出要出栈的元素 + stack.pop(); //出栈 + if (stack.empty()) { // 栈空就出去 + break; + } + + int distance = current - stack.peek() - 1; //两堵墙之前的距离。 + int min = Math.min(height[stack.peek()], height[current]); + sum = sum + distance * (min - h); + } + stack.push(current); //当前指向的墙入栈 + current++; //指针后移 + } + return sum; + } + +} diff --git a/Week 01/id_083/Leetcode641.java b/Week 01/id_083/Leetcode641.java new file mode 100644 index 000000000..422e2ebc1 --- /dev/null +++ b/Week 01/id_083/Leetcode641.java @@ -0,0 +1,130 @@ +/* + * 力扣算法题641 + * 设计实现双端队列。 + * +你的实现需要支持以下操作: + +MyCircularDeque(k):构造函数,双端队列的大小为k。 +insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true。 +insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。 +deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true。 +deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。 +getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。 +getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。 +isEmpty():检查双端队列是否为空。 +isFull():检查双端队列是否满了。 +示例: + +MyCircularDeque circularDeque = new MycircularDeque(3); // 设置容量大小为3 +circularDeque.insertLast(1); // 返回 true +circularDeque.insertLast(2); // 返回 true +circularDeque.insertFront(3); // 返回 true +circularDeque.insertFront(4); // 已经满了,返回 false +circularDeque.getRear(); // 返回 2 +circularDeque.isFull(); // 返回 true +circularDeque.deleteLast(); // 返回 true +circularDeque.insertFront(4); // 返回 true +circularDeque.getFront(); // 返回 4 +  +提示: + +所有值的范围为 [1, 1000] +操作次数的范围为 [1, 1000] +请不要使用内置的双端队列库。 +*/ +public class MyCircularDeque { + int[] myqueue ; + int front;//队头指针 + int rear;//队尾指针 + int size;//队列当前的大小 + int capacity;//队列的容量 + + /** Initialize your data structure here. Set the size of the deque to be k. */ + public MyCircularDeque(int k) { + this.myqueue = new int[k]; + this.front = 0; + this.rear = 0; + this.size = 0; + this.capacity = k; + } + + /** Adds an item at the front of Deque. Return true if the operation is successful. */ + public boolean insertFront(int value) { + if(rear==front && size == capacity) return false;//如果队列满,插入失败 + else { + front = (front + capacity -1)% capacity; + myqueue[front] = value; + size++; + return true; + } + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + public boolean insertLast(int value) { + if(rear==front && size == capacity) return false;//如果队列满,插入失败 + else { + myqueue[rear] = value; + rear = (rear+1+capacity)%capacity; + size++; + return true; + } + + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + public boolean deleteFront() { + if( rear == front && size == 0) return false; + else { + front = (front+1) % capacity; + size--; + return true; + } + + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + public boolean deleteLast() { + if( rear == front && size == 0) return false; + else { + rear = (rear - 1 + capacity) % capacity; + size--; + return true; + } + + } + + /** Get the front item from the deque. */ + public int getFront() { + if((rear == front) && size==0) return -1; + else { + int frontE = myqueue[front]; + //front = (front + 1) % capacity; + //size--; + return frontE; + } + } + + /** Get the last item from the deque. */ + public int getRear() { + if((rear == front) && size==0) return -1; + else { + int rearE = myqueue[(rear-1+capacity)%capacity]; + // rear = (rear - 1 +capacity)%capacity; + //size--; + return rearE; + } + + } + + /** Checks whether the circular deque is empty or not. */ + public boolean isEmpty() { + return (rear == front) && size==0; + } + + /** Checks whether the circular deque is full or not. */ + public boolean isFull() { + return rear==front && size == capacity; + + } + } + diff --git a/Week 01/id_088/NOTE.md b/Week 01/id_088/NOTE.md index a6321d6e2..7c42b2082 100644 --- a/Week 01/id_088/NOTE.md +++ b/Week 01/id_088/NOTE.md @@ -1,4 +1,24 @@ -# NOTE - - - +# 【088-Week 01】第一周总结 +简单的总结一下经过了预习周和第一周的学习的一些想法。 +对于我个人而言,作为一个前端入行已近两年了,虽然不是计算机专业的毕业生但是还是期待自己可以专业起来,所以首次尝试与数据结构算法相关的专项练习。旨在提高自己最最基础的变成思维和能力。 +从预习周开始到陆陆续续刷了大概10道题左右,感慨颇多。但这里并不是应该长篇大论的地方所以就简单阐述一下。 +### 关于覃超老师的一些话 +#### 量变到质变 + 算法的学习遵循了大部分的学习规律,从量变到质变。大部分的解法应该是有规律的。在我过去刷的多道题中有多次遇到双指针、hash表等解法,只是根据不同的题目采用了不同方式求解。说明有一些数据结构和解题方式是高效的,有一定规律的,可以重复尝试的,老师也说过有一些场景是可套用公式的。因此,大量的积累必定会有所提高的。同时老师还是总结了刷题的方法所以顺着这个思路来刷题总不会太差。由于没有更好的积累,所以当下并不能较为完整的列举哪种解题方式是更好的,哪种数据结构是更高效的,但这也正是我来这的理由,我会在之后的每一周学习中尽量的总结和发现。 + #### 算法一些本质的东西 + 在这部分中我只是复述了覃超老师的几句话: + ``` + 1. 解题方法 + if else + for loop, while loop + recursion + 2. 解题思路 + 寻找事物的最近相关性 + 3. 解题技巧 + 升维、 + 空间换时间 + ``` + 其实很多类似 javascript 这样后出现语言 都已经提供了很多可以直接使用的API方法,相当于提供一些捷径。但是每一个方法的 polyfill 的实现本质上并没有捷径可言。正如覃超老师所说的 哪怕是最基础的解法也是思维训练和基本功的方法。所以在解题的时候,尽可能少的使用语言本身已经实现的方法在初学阶段是必要。而循环和递归,所做的就是一遍一遍重复的行为。那么需要找到的就是1. 重复项 2. 结束循环的出口,应该就是老师所说的 '寻找事物的相关性'。对于**升维**和**空间换时间**的想法是我最新接触的一个思维模式,再加上对于跳表的理解也有限所以也没法说太多(跳表确实是一个很好的例子)。 + 由于实在缺乏计算机基础知识, 无法表述更全面更值得分享的知识,然后缩减大量的流水账之后呈现的内容。 + ### 最后 + 覃超老师常说'授之以鱼,不如授之以渔',所以对于这篇总结也肯定不仅仅应该是对课堂内容的简单复述。而以我的能力和笔力却只能如此,聊聊赘述,表达二三。 \ No newline at end of file diff --git a/Week 01/id_088/removeDuplicates.js b/Week 01/id_088/removeDuplicates.js new file mode 100644 index 000000000..7879a8e8b --- /dev/null +++ b/Week 01/id_088/removeDuplicates.js @@ -0,0 +1,20 @@ +// 26 删除排序数组中的重复项 +/** + * @description 1. 原数组删除 2. O(1) 条件下 + * @return 返回数组长度 + */ +const removeDuplicates = nums => { + const l = nums.length + let j = 1 + // 从 1 开始比较 + for (let i = 1; i < l; i++) { + // 不重复直接 添加到nums中 + if (nums[i] !== nums[i - 1]) { + nums[j++] = nums[i] + } + } + return (nums.length = j) +} + +// 解题 思路 双指针 数组双指针 确实是非常常用的解题思路 +// 唯一的疑问是 社区的题解 貌似并没有真正改变数组的长度 而是返回了 重排的长度 如果有人 review 这套代码 如果您能解答这个问题 真是感激不尽 diff --git a/Week 01/id_088/rotate.js b/Week 01/id_088/rotate.js new file mode 100644 index 000000000..3c07f6ff6 --- /dev/null +++ b/Week 01/id_088/rotate.js @@ -0,0 +1,61 @@ +// 189. 旋转数组 +/** + * @description 1. 三种方法 2. O(1) + * @return null + */ +const rotate = (nums, k) => { + // 循环嵌套 + const l = nums.length + k = k % nums.length + let temp = nums[l - 1] + // 不变的元素需要移动的距离 + for (let i = 0; i < k; i++) { + temp = nums[l - 1] + // 需要移动的次数 + for (let j = 0; j < l - 1; j++) { + // 每次只需要 移动 l-1 次 + nums[l - 1 - j] = nums[l - 2 - j] + } + nums[0] = temp + } + // 这个时间复杂度 应该是 O(n^2) +} + +// 两次循环 +const rotate = (nums, k) => { + const l = nums.length + k = k % nums.length + // 将所有的元素 先向后 统一移动 k 的距离 + for (let i = 0; i < l; i++) { + nums[l - 1 - i + k] = nums[l - 1 - i] + } + // 再将多余的元素 填充回来 + for (let i = 0; i < k; i++) { + nums[i] = nums[l + i] + } + nums.length = l + // 1. 这种方法 并不是空间上的O(1) 如果 k 是最后一个元素 或者 倒数第二个元素 的话 实际上数组相当于 又多 开辟了 n/n-1 个内存 个人觉得是 O(n) 的 空间复杂度 + // 2. 不是很理解 题例 问什么要提出来 k > nums.length 这种情况 挺无语的 +} + +// leetcode上的高票答案 三次反转 +const rotate = (nums, k) => { + const l = nums.length + if (!nums || l < 2) return + k = k % nums.length + // 进行三次反转 + reverse(nums, 0, nums.length - k - 1) + reverse(nums, nums.length - k, nums.length - 1) + reverse(nums, 0, nums.length - 1) +} + +const reverse = (nums, st, ed) => { + let temp = 0 + while (st < ed) { + temp = nums[st] + nums[st] = nums[ed] + nums[ed] = temp + st++ + ed-- + } +} diff --git a/Week 01/id_093/Remove_Duplicates_26.java b/Week 01/id_093/Remove_Duplicates_26.java new file mode 100644 index 000000000..88f77e930 --- /dev/null +++ b/Week 01/id_093/Remove_Duplicates_26.java @@ -0,0 +1,68 @@ +import java.util.Arrays; + +class RemoveDuplicates_26 { + //思路1.暴力求解(如果不考虑O(1)的限制): + //(1)建立新数组,遍历老数组,每读取一个老数组都和新数组所有元素对比,不存在则存入新数组,同时count+1 + //(2)遍历数组,每一个遍历的值和前面的数对比,按照从小到大排序。重复的则放到最后面; + //思路2.利用栈先进先出的方式,读取原数组,同样的,则不进入栈,不同样的,再给进,最后输出栈内元素(3)遍历数组, + //问题:有思路不会写代码。 + public static int removeDuplicates1(int[] nums) { + //LeetCode-cn官方解析 + //第一步:排除特殊情况,空数组 + if (nums.length == 0) return 0; + // i用于记录非重复值的位置和数量,j用于遍历数组 + int i = 0; + for (int j = 1; j < nums.length; j++) { + //如果遍历的数不等于上一个数,则i增加,同时,让非重复值换位到非重复值这,反之跳过; + // (注意,这种计算非重复值的前提是有序数组,非有序则不成立) + if (nums[j] != nums[i]) { + i++; + nums[i] = nums[j]; + } + } + //因为非重复值都放在前面,所以最后一个非重复值下标+1就是非重复值数量 + return i + 1; + } + +// public static int removeDuplicates2(int[] nums){ +// if (nums.length == 0) return 0; +// int j = 0; +// for (i = 0,i nums.length) k = k%nums.length; + //如果k不大于数组的length,则处理下标; + if (k <= nums.length) { + //遍历数组 + for (int i = 0; i < nums.length; i++ ){ + //如果i=k,那就直接是从左边移动k个位置过来的 + if(i>=k){ + nums[i] = nums2[i - k]; + } + } + } + } + + public static void main(String[] args){ + int[] nums = {1,2,3,4,5,6}; + int k = 100; + rotateArray1(nums,k); + System.out.println(Arrays.toString(nums)+ " k=" +k); + //System.out.println(nums2); + + } + +} \ No newline at end of file diff --git a/Week 01/id_098/Deque_with_newApi.java b/Week 01/id_098/Deque_with_newApi.java new file mode 100644 index 000000000..b90fdba09 --- /dev/null +++ b/Week 01/id_098/Deque_with_newApi.java @@ -0,0 +1,34 @@ + //jdk 1.8 + + Deque dequeOld = new LinkedList(); + Deque dequeNew = new LinkedList(); + + dequeOld.push("a"); + dequeOld.push("b"); + dequeOld.push("c"); + + System.out.println(dequeOld); + + dequeNew.addFirst("a"); + dequeNew.addFirst("b"); + dequeNew.addFirst("c"); + + System.out.println(dequeNew); + + String str = dequeOld.peek(); + System.out.println(str); + System.out.println(dequeOld); + + str = dequeNew.peekFirst(); + System.out.println(str); + System.out.println(dequeOld); + + while (dequeOld.size()>0){ + System.out.println(dequeOld.pop()); + } + System.out.println(dequeOld); + + while (dequeNew.size()>0){ + System.out.println(dequeNew.removeFirst()); + } + System.out.println(dequeNew); diff --git a/Week 01/id_098/LeetCode_26_098.java b/Week 01/id_098/LeetCode_26_098.java new file mode 100644 index 000000000..54a793d44 --- /dev/null +++ b/Week 01/id_098/LeetCode_26_098.java @@ -0,0 +1,49 @@ +//暴力法(双循环) +class Solution1 { + public int removeDuplicates(int[] nums) { + int newLength = nums.length; + int skip = 1; + for (int i=0; i 0 ? 1 : 0; + for (int n : nums) + if (n > nums[i-1]) + nums[i++] = n; + return i; + + } +} + diff --git a/Week 01/id_098/LeetCode_42_098.java b/Week 01/id_098/LeetCode_42_098.java new file mode 100644 index 000000000..028818f3e --- /dev/null +++ b/Week 01/id_098/LeetCode_42_098.java @@ -0,0 +1,70 @@ +//找中值,左右分治法 +class Solution { + public static int trap(int[] height) { + + int max = 0; + int flagIndex = 0; + + if(height.length == 0){ + return 0; + } + + //找出最高点 + for(int index = 0 ; index < height.length; index++){ + if(height[index] > max){ + max = height[index]; + } + } + + int totalWater = 0; + int sectionWater = 0; + int flag = height[0]; + + //计算最高点左边的雨水 + for(int i = 1; i < height.length; i++){ + + //当在最高点左边时,这么计算 + if(height[i] == max){ + //直接得出当前的sectionWater + totalWater = totalWater + sectionWater; + //得到第一次找到的最高点的下标 + flagIndex = i; + break; + } + + //当后一位小于前一位,可以蓄水 + if(height[i] < flag){ + //开始蓄水 + sectionWater = sectionWater + (flag - height[i]); + //否则蓄水结束,此时计算此区间内的蓄水量 + }else { + totalWater = totalWater + sectionWater; + //将区间蓄水量设为0 + sectionWater = 0; + //从height[i]开始往后查询 + flag = height[i]; + } + + } + + sectionWater = 0; + flag = height[height.length-1]; + //计算最高点右边的雨水 + for(int index = height.length-1; index >= flagIndex; index--){ + + //当右边的数大于左边的数,可以存雨水 + if(height[index] < flag){ + //开始蓄水 + sectionWater = sectionWater + (flag - height[index]); + }else { + totalWater = totalWater + sectionWater; + //将区间蓄水量设为0 + sectionWater = 0; + //从height[i]开始往后查询 + flag = height[index]; + } + } + + return totalWater; + } +} \ No newline at end of file diff --git a/Week 01/id_098/LeetCode_641_098.java b/Week 01/id_098/LeetCode_641_098.java new file mode 100644 index 000000000..717dce84e --- /dev/null +++ b/Week 01/id_098/LeetCode_641_098.java @@ -0,0 +1,151 @@ +class MyCircularDeque { + + private int[] array; + private int front; + private int last; + private int size; + + /** Initialize your data structure here. Set the size of the deque to be k. */ + public MyCircularDeque(int k) { + size = k + 1; + array= new int[size]; + front = 0; + last = 0; + } + + /** Adds an item at the front of Deque. Return true if the operation is successful. */ + public boolean insertFront(int value) { + if (isFull()) return false; + front = (front - 1 + size) % size; + array[front] = value; + return true; + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + public boolean insertLast(int value) { + if (isFull()) return false; + array[last] = value; + last = (last + 1) % size; + return true; + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + public boolean deleteFront() { + if (isEmpty()) return false; + front = (front + 1) % size; + return true; + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + public boolean deleteLast() { + if (isEmpty()) return false; + last = (last - 1 + size) % size; + return true; + + } + + /** Get the front item from the deque. */ + public int getFront() { + if (isEmpty()) return -1; + return array[front]; + } + + /** Get the last item from the deque. */ + public int getRear() { + if (isEmpty()) return -1; + return array[(last - 1 + size) % size]; + } + + /** Checks whether the circular deque is empty or not. */ + public boolean isEmpty() { + return front == last; + } + + /** Checks whether the circular deque is full or not. */ + public boolean isFull() { + return front == (last + 1) % size; + } +} + +/** + * Your MyCircularDeque object will be instantiated and called as such: + * MyCircularDeque obj = new MyCircularDeque(k); + * boolean param_1 = obj.insertFront(value); + * boolean param_2 = obj.insertLast(value); + * boolean param_3 = obj.deleteFront(); + * boolean param_4 = obj.deleteLast(); + * int param_5 = obj.getFront(); + * int param_6 = obj.getRear(); + * boolean param_7 = obj.isEmpty(); + * boolean param_8 = obj.isFull(); + */ + +//最优化 双端循环队列方案,设置另外变量count 数组是否满或者空,无空间浪费!! (他人答案) +class MyCircularDeque { + + private int[] q; + private int head, tail, len; + + public MyCircularDeque(int k) { + this.q = new int[k]; + } + + public boolean insertFront(int value) { + if (isFull()) return false; + if (!isEmpty()) head = (head - 1 + q.length) % q.length; + q[head] = value; + len++; + return true; + } + + public boolean insertLast(int value) { + if (isFull()) return false; + if (!isEmpty()) tail = (tail + 1) % q.length; + q[tail] = value; + len++; + return true; + } + + public boolean deleteFront() { + if (isEmpty()) return false; + if (len > 1) head = (head + 1) % q.length; + len--; + return true; + } + + public boolean deleteLast() { + if (isEmpty()) return false; + if (len > 1) tail = (tail - 1 + q.length) % q.length; + len--; + return true; + } + + public int getFront() { + return isEmpty() ? -1: q[head]; + } + + public int getRear() { + return isEmpty() ? -1: q[tail]; + } + + public boolean isEmpty() { + return len == 0; + } + + public boolean isFull() { + return len == q.length; + } +} + +/** + * Your MyCircularDeque object will be instantiated and called as such: + * MyCircularDeque obj = new MyCircularDeque(k); + * boolean param_1 = obj.insertFront(value); + * boolean param_2 = obj.insertLast(value); + * boolean param_3 = obj.deleteFront(); + * boolean param_4 = obj.deleteLast(); + * int param_5 = obj.getFront(); + * int param_6 = obj.getRear(); + * boolean param_7 = obj.isEmpty(); + * boolean param_8 = obj.isFull(); + */ \ No newline at end of file diff --git a/Week 01/id_098/Queue_PriorityQueue_Source_Analysis_Summary.md b/Week 01/id_098/Queue_PriorityQueue_Source_Analysis_Summary.md new file mode 100644 index 000000000..ef8b84184 --- /dev/null +++ b/Week 01/id_098/Queue_PriorityQueue_Source_Analysis_Summary.md @@ -0,0 +1,73 @@ +Queue 源码分析 + +Queue 是接口 继承自 java.util.Collection + +boolean add(E e): +添加元素进队列,当成功添加元素则返回 true 否则返回 false +当队列容量满时返回 IllegalStateException +当待加入元素和队列元素不一致返回 ClassCastException +当试图添加 Null 进入队列时返回 NullPointerException +当因添加元素的属性导致该元素无法添加至队列时返回 IllegalArgumentException (没想到例子) + +boolean offer(E e): +与add 的区别是当队列满的时候仅返回 false 而不会抛出 IllegalStateException 异常 + +E remove(): +移除头部元素, 当队列为空抛出 NoSuchElementException 异常 + +E poll(): +移除头部元素, 当队列为空返回null + +E element(): +返回头部元素,但不移除,当队列为空抛出 NoSuchElementException 异常 + +E peek(): +返回头部元素,但不移除,当队列为空则返回null + +关于时间复杂度, +链表实现,则这些方法的时间复杂度都是O(1)的 +数组实现 如果非循环队列,添加元素为O(n) 循环队列为 O(1) + +Priority Queue 源码分析 + +优先队列基于堆,优先级基于元素的自然顺序,或者通过Comparator指定的排序规则 +优先队列不允许插入null,或者无法排序的元素 +优先队列的头元素永远是最小的元素 +优先队列无届,但是可以指定大小 +spliterator()不能保证有序遍历,推荐使用Arrays.sort(pq.toArray())进行有序遍历 +入队,出队的时间复杂度为 O(logn) 删除和查找操作时间复杂度 O(n) 查头部元素和队列大小的时间复杂度为 O(1) + + +第一周总结: + +本周学习了 数组,链表,跳表 以及 栈,队列,优先队列和双端队列 + +数组和链表很好理解,并且实现不难,应用很广泛 +栈,队列,优先队列,概念很好理解 但是实现感觉有些困难 +双端队列,虽然是新的 java 推荐的栈的应用结构,但是直接利用双端队列的场景比较限定 +大部分是用双端队列来模拟栈及普通队列 +这周开始第一次刷算法题,感觉有些烧脑,特别是接雨水的题,容易陷入一些误区拔不出来 +而且当以水位这个概念去解这道题的时候,容易造成时间复杂度增长太快,从而超时 + +发散拓展: +对于计算机来讲,数据结构只有数组和链表两种,其他任何的数据结构的实现其实都是以数组 +或者链表为基础,追加了一些规则实现更快的存取或者遍历 +数组和链表最大的不同是数组是连续的,而链表是不连续的,数组的随机访问性能更好,而链表的 +插入删除更好,那么有没有可能存在一种数据结构,能把数组和链表的优势结合起来,比如数组 +能够不开辟连续的存储空间,或者链表可以带下标以实现随机访问为O(1), 这个可能需要更加 +强大的理论基础知识了 + +问题: +双端循环队列的题,我看到很多思路都是牺牲一个单位用来区分队列是否为空,或者为满, +但是如果用另外一个变量来记录是否为空或者是否为满的话,那就不用牺牲一个存储空间了 +循环队列在实际应用中非常广泛,那么实际上是用另置标志位来实现的多,还是用k+1这种 +牺牲一个单位来实现的多呢? 从各方面看(速度,空间复杂度)另置标志位是最优的 +(LeetCode641结果分析)那为什么还需要用 k+1 这种方法来实现呢? + + + + + + + + diff --git a/Week 01/id_103/LeeCode_189_103.java b/Week 01/id_103/LeeCode_189_103.java new file mode 100644 index 000000000..ecf966d56 --- /dev/null +++ b/Week 01/id_103/LeeCode_189_103.java @@ -0,0 +1,46 @@ +package com.weekwork; + +/* +给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。 + +示例 1: + +输入: [1,2,3,4,5,6,7] 和 k = 3 +输出: [5,6,7,1,2,3,4] +解释: +向右旋转 1 步: [7,1,2,3,4,5,6] +向右旋转 2 步: [6,7,1,2,3,4,5] +向右旋转 3 步: [5,6,7,1,2,3,4] + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/rotate-array +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 +* */ + + +class RotatedArray { + + //Method 1 Explain:计算需要移动的位置,在开始移动点切割数组,将切割后需要旋转的的部分与剩余部分换位拼合 + public void rotateFuncOne(int[] nums, int k) { + int length = nums.length; + int newk = k % length; + int[] newArray = new int[length]; + System.arraycopy(nums,nums.length-newk,newArray,0,newk); + System.arraycopy(nums,0,newArray,newk,nums.length-newk); + System.arraycopy(newArray,0,nums,0,length); + } + + //Method 2 Explain:计算当前数组在最终移动后各节点需要移动的具体位置,并将计算好位置的元素按照新位置插入新的数组中 + public void rotateFuncTwo(int[] nums, int k) { + int length = nums.length; + int newk = k % length; + int[] newArray = new int[length]; + for (int i = 0; i < length; i++) { + int newPosition = (i + newk) % length; + newArray[newPosition] = nums[i]; + } + System.arraycopy(newArray, 0, nums, 0, length); + } +} + + diff --git a/Week 01/id_103/LeeCode_21_103.java b/Week 01/id_103/LeeCode_21_103.java new file mode 100755 index 000000000..afbd76c0f --- /dev/null +++ b/Week 01/id_103/LeeCode_21_103.java @@ -0,0 +1,77 @@ +package com.weekwork; + +/* +将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。  + +示例: + +输入:1->2->4, 1->3->4 +输出:1->1->2->3->4->4 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/merge-two-sorted-lists +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 +* */ + + +//class ListNode { +// int val; +// ListNode next; +// ListNode(int x) { val = x; } +//} + +public class MergeTwoLists { + + //Method 1 Explain:通过移动指针获取当前节点的下一个节点 + public ListNode mergeTwoListsFuncOne(ListNode l1, ListNode l2) { + if (l1==null && l2!=null) { + return l2; + }else if (l1!=null && l2==null) { + return l1; + } + else { + ListNode l1node=l1; + ListNode l2node=l2; + + ListNode first=new ListNode(-1); + ListNode cur=first; + while (l1node!=null) { + while (l2node!=null) { + if (l2node.val<=l1node.val) { + cur.next=l2node; + cur=l2node; + l2node=l2node.next; + } + else { + break; + } + } + cur.next=l1node; + cur=l1node; + l1node=l1node.next; + } + if (l2node!=null) { + cur.next=l2node; + } + return first.next; + } + } + + //Method 2 Explain:递归获取最小节点 + public ListNode mergeTwoListsFuncTwo(ListNode l1, ListNode l2) { + if(l1 == null) return l2; + if(l2 == null) return l1; + + if(l1.val < l2.val){ + l1.next = mergeTwoListsFuncTwo(l1.next, l2); + return l1; + }else{ + l2.next = mergeTwoListsFuncTwo(l1, l2.next); + return l2; + } + + } +} + + + diff --git a/Week 01/id_108/LeeCode_001_108.java b/Week 01/id_108/LeeCode_001_108.java new file mode 100644 index 000000000..f5eac519e --- /dev/null +++ b/Week 01/id_108/LeeCode_001_108.java @@ -0,0 +1,37 @@ +package study; + +public class LeeCode_001_108 { + + public static int[] twoSum(int[] nums, int target) { + + int len = nums.length; + int resultArray[] = new int[2]; + int indextemp[]= new int[2]; + for (int i = 0; i < len - 1; i++) { + for (int j = i+1; j < len; j++) { + int sum = nums[i] + nums[j]; + if (target == sum) { + //resultArray[0] = nums[i]; + //resultArray[1] = nums[j]; + indextemp[0]=i; + indextemp[1]=j; + } + + } + + } + + return indextemp; + + } + + public static void main(String[] args) { + + int nums[] = { 2, 7, 11, 15 }; + int result[] = new int[2]; + result = twoSum(nums, 9); + System.out.println("The result is " + result[0] + " and " + result[1]); + + } + +} diff --git a/Week 01/id_108/LeeCode_641_108.java b/Week 01/id_108/LeeCode_641_108.java new file mode 100644 index 000000000..a0bc78512 --- /dev/null +++ b/Week 01/id_108/LeeCode_641_108.java @@ -0,0 +1,95 @@ +package study; + +public class LeeCode_641_108 { + + + private int[] nums; // 定义一个顺序队列,用数组来存储队列元素 + private int count = 0; // count 来表示队列中当前存在的元素个数 + private int head = 0; // head 表示队列的头 + private int tail =0; // tail 表示队列的尾 + + public LeeCode_641_108(int k) { + nums = new int[k]; // 根据 k 来初始化数组的长度 + } + + + public boolean insertFront(int value) { + if(count>=nums.length) return false; // 队列元素个数超出数组长度则无法再入队 + if(head==0) + head=nums.length-1; + else + head-=1; + nums[head] = value; // 队列未满则将元素入队,尾指针以及数组元素 +1 + count++; + return true; + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + public boolean insertLast(int value) { + if(count>=nums.length) return false; // 队列元素个数超出数组长度则无法再入队 + nums[tail++] = value; // 队列未满则将元素入队,尾指针以及数组元素 +1 + count++; + tail = tail % nums.length; + return true; + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + public boolean deleteFront() { + if(count==0) return false; // 队列为空则无法删除元素 + head++; + head = head % nums.length; // 减少数组搬运操作直接将队列头移向下一元素 + count--; + return true; + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + public boolean deleteLast() { + if(count==0) return false; // 队列为空则无法删除元素 + if(tail==0) + tail=nums.length-1; // 减少数组搬运操作直接将队列尾移向上一元素 + else + tail-=1; + count--; + return true; + } + + /** Get the front item from the deque. */ + public int getFront() { + if(count==0) return -1; + return nums[head]; + } + + /** Get the last item from the deque. */ + public int getRear() { + if(count==0) return -1; + if(tail==0) return nums[nums.length-1]; + return nums[tail-1]; + } + + /** Checks whether the circular deque is empty or not. */ + public boolean isEmpty() { + return count==0; + } + + /** Checks whether the circular deque is full or not. */ + public boolean isFull() { + return count == nums.length; + } + + + public static void main(String args[]){ + LeeCode_641_108 circularDeque = new LeeCode_641_108(8); + boolean param_1 = circularDeque.insertFront(8); + boolean param_2 = circularDeque.insertLast(7); + boolean param_3 = circularDeque.deleteFront(); + boolean param_4 = circularDeque.deleteLast(); + int param_5 = circularDeque.getFront(); + int param_6 = circularDeque.getRear(); + boolean param_7 = circularDeque.isEmpty(); + boolean param_8 = circularDeque.isFull(); + + //System.out.println("param_5"+param_5.); + } + + +} diff --git a/Week 01/id_113/LeetCode21.py b/Week 01/id_113/LeetCode21.py new file mode 100644 index 000000000..d45a1c748 --- /dev/null +++ b/Week 01/id_113/LeetCode21.py @@ -0,0 +1,15 @@ +# Definition for singly-linked list. +class ListNode: + def __init__(self, x): + self.val = x + self.next = None + + +class Solution: + def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode: + if l1 and l2: + if l1.val > l2.val: + l1, l2 = l2, l1 + l1.next = self.mergeTwoLists(l1.next, l2) + + return l1 or l2 diff --git a/Week 01/id_113/LeetCode26.py b/Week 01/id_113/LeetCode26.py new file mode 100644 index 000000000..3ff6a6e6f --- /dev/null +++ b/Week 01/id_113/LeetCode26.py @@ -0,0 +1,10 @@ +class Solution: + def removeDuplicates(self, nums: list[int]) -> int: + p, q = 0, 1 + while q < len(nums): + if nums[p] != nums[q]: + nums[p + 1] = nums[q] + p += 1 + q += 1 + + return p + 1 diff --git a/Week 01/id_113/LeetCode641.py b/Week 01/id_113/LeetCode641.py new file mode 100644 index 000000000..3f64431ab --- /dev/null +++ b/Week 01/id_113/LeetCode641.py @@ -0,0 +1,91 @@ +class MyCircularDeque: + + def __init__(self, k: int): + """ + Initialize your data structure here. Set the size of the deque to be k. + """ + self.size = k + self.array = [] + + def insertFront(self, value: int) -> bool: + """ + Adds an item at the front of Deque. Return true if the operation is successful. + """ + if not self.isFull(): + self.array.insert(0, value) + return True + + return False + + def insertLast(self, value: int) -> bool: + """ + Adds an item at the rear of Deque. Return true if the operation is successful. + """ + if not self.isFull(): + self.array.append(value) + return True + return False + + def deleteFront(self) -> bool: + """ + Deletes an item from the front of Deque. Return true if the operation is successful. + """ + if not self.isEmpty(): + self.array.pop(0) + return True + return False + + def deleteLast(self) -> bool: + """ + Deletes an item from the rear of Deque. Return true if the operation is successful. + """ + if not self.isEmpty(): + self.array.pop() + return True + return False + + def getFront(self) -> int: + """ + Get the front item from the deque. + """ + if not self.isEmpty(): + return self.array[0] + return -1 + + def getRear(self) -> int: + """ + Get the last item from the deque. + """ + if not self.isEmpty(): + count = len(self.array) + return self.array[count - 1] + return -1 + + def isEmpty(self) -> bool: + """ + Checks whether the circular deque is empty or not. + """ + if len(self.array) == 0: + return True + return False + + def isFull(self) -> bool: + """ + Checks whether the circular deque is full or not. + """ + if len(self.array) == self.size: + return True + return False + + +# Your MyCircularDeque object will be instantiated and called as such: +if __name__ == "__main__": + obj = MyCircularDeque(k) + param_1 = obj.insertFront(value) + param_2 = obj.insertLast(value) + param_3 = obj.deleteFront() + param_4 = obj.deleteLast() + param_5 = obj.getFront() + param_6 = obj.getRear() + param_7 = obj.isEmpty() + param_8 = obj.isFull() diff --git a/Week 01/id_118/LeetCode_189_118.py b/Week 01/id_118/LeetCode_189_118.py new file mode 100644 index 000000000..bd7210adc --- /dev/null +++ b/Week 01/id_118/LeetCode_189_118.py @@ -0,0 +1,60 @@ +from typing import List + + +class Solution1: + """ + First solution is brute force + """ + + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + Time complexity: O(n * k) + """ + for _ in range(k): + # the slice to be rotated is [-k:] + # start with the last num in [-k:] + prev = nums[-1] + for i in range(len(nums)): + # there are two steps: + # 1. swap first element with prev + # e.g. [7,2,3,4,5,6,1] + # 2. loop thru to arrange the rest of the elements + # to its original order + # e.g. [7,1,3,4,5,6,2] => [7,1,2,4,5,6,3] =>...=>[7,1,2,3,4,5,6] + nums[i], prev = prev, nums[i] + + print(nums) # for testing + + +class Solution2: + """ + A very Pythonic solution, with list slicing and pythonic swapping + Be super careful with the edge cases + Time complexity: O(n), as slicing a list will create new list + Check https://github.com/python/cpython/blob/master/Objects/listobject.c + line 473 to 493 + """ + + def rotate(self, nums: List[int], k: int) -> None: + # here k is updated as the mod of len(nums) + # this will be used when k is greater than len(nums) + # e.g. for array of 7, when k = 7 is the same as k = 0, or don't rotate + # when k = 8 is the same as k = 1 + k %= len(nums) + + if (len(nums) <= 1) or (k == 0): + # when the list is either empty or only has 1 element + # or k % len(nums) == 0, don't move + return + + nums[:k], nums[k:] = nums[-k:], nums[:-k] + print(nums) + + +if __name__ == '__main__': + # Create two test cases + sol1 = Solution1() + sol1.rotate([1, 2, 3, 4, 5, 6, 7], 3) + sol2 = Solution2() + sol2.rotate([1, 2, 3, 4, 5, 6, 7], 3) diff --git a/Week 01/id_118/LeetCode_1_118.py b/Week 01/id_118/LeetCode_1_118.py new file mode 100644 index 000000000..5aa9aa130 --- /dev/null +++ b/Week 01/id_118/LeetCode_1_118.py @@ -0,0 +1,72 @@ +from typing import List + + +class Solution1: + """ + Brute force, use the two pointers technique to iterate thru the array + Time complexity is O(n^2) due to nested loop + """ + + def two_sum(self, nums: List[int], target: int) -> List[int]: + for i in range(len(nums) - 1): + for j in range(i + 1, len(nums)): + if nums[i] + nums[j] == target: + return [i, j] + + +class Solution2: + """ + for each number in nums, check if target - number is in nums + Time complexity is not great in this solution as nums.index() also has + a BigO of O(n), so this is also a nested for loop: -> O(n^2) + """ + + def two_sum(self, nums: List[int], target: int) -> List[int]: + for num in nums: + if ((target - num) in nums) and (nums.index(num) != + nums.index(target - num)): + return [nums.index(num), nums.index(target - num)] + + +class Solution3: + """ + Use enumerate to iterate both index and num + Use a hash table (dict) to store {key: val} -> {num: index} + """ + + def two_sum(self, nums: List[int], target: int) -> List[int]: + d = {} + # in this dictionary, the key is num, val is index + # need to make sure the list is unique, duplicates will cause + # hash conflicting + for idx, num in enumerate(nums): + # if target-num exists in d, return both indices + if target - num in d: + return [d[target - num], idx] + else: + d[num] = idx + + +test_nums = [2, 7, 11, 15] +test_target = 9 + + +def test1(): + sol1 = Solution1() + print(sol1.two_sum(test_nums, test_target)) + + +def test2(): + sol2 = Solution2() + print(sol2.two_sum(test_nums, test_target)) + + +def test3(): + sol3 = Solution3() + print(sol3.two_sum(test_nums, test_target)) + + +if __name__ == '__main__': + test1() + test2() + test3() diff --git a/Week 01/id_118/LeetCode_206_118.py b/Week 01/id_118/LeetCode_206_118.py new file mode 100644 index 000000000..0c79cd9bd --- /dev/null +++ b/Week 01/id_118/LeetCode_206_118.py @@ -0,0 +1,65 @@ +class Solution1: + """ + Solve iteratively + """ + + class ListNode: + def __init__(self, x): + self.val = x + self.next = None + + def reverseList(self, head: ListNode) -> ListNode: + prev = None + curr = head + + while curr: + # store the next + next = curr.next + # reference original next to prev + curr.next = prev + + # Below is iteration + # prev move to the current + prev = curr + # loop thru the LL + curr = next + + return prev + + +class Solution2: + """ + A more Pythonic writing + """ + + class ListNode: + def __init__(self, x): + self.val = x + self.next = None + + def reverseList(self, head: ListNode) -> ListNode: + prev, curr = None, head + while curr: + curr.next, prev, curr = prev, curr, curr.next + return prev + + +class Solution3: + """ + Solve it recursively + """ + + class ListNode: + def __init__(self, x): + self.val = x + self.next = None + + def reverseList(self, head: ListNode) -> ListNode: + return self._reverseList(head) + + def _reverseList(self, head, prev=None): + if not head: + return prev + next = head.next + head.next = prev + return self._reverseList(next, head) diff --git a/Week 01/id_118/LeetCode_21_118.py b/Week 01/id_118/LeetCode_21_118.py new file mode 100644 index 000000000..56fe66347 --- /dev/null +++ b/Week 01/id_118/LeetCode_21_118.py @@ -0,0 +1,70 @@ +# Definition for singly-linked list. +class ListNode: + def __init__(self, e): + self.val = e + self.next = None + + +class Solution1: + """ + 1. Iterative Solution + The time complexity for this solution will be O(n^2) due to nexted iteration + it will have TLE (Time Limit Exceeded) error + """ + + def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode: + """ + Solve this problem iteratively + :param l1: + :param l2: + :return: + """ + # Create a dummy head node + dummy_head = curr = ListNode(None) + + # while l1 and l2 are not empty LL + # compare the value and connect smaller node to dummy_head + # Time Complexity is O(n) + while l1 and l2: + if l1.val <= l2.val: + # If l1.val is no greater than l2, connect l1 with dummy_head + # iterate to l1's next + curr.next = l1 # nexted iteration, time complexity is O(n) + l1 = l1.next + else: + # If l1.val is greater than l2, connect l2 with curr + # iterate to l2's next + curr.next = l2 + l2 = l2.next + + # if l1 or l2 is empty + # connect to the remainder either rest of l1, rest of l2 or None + curr.next = l1 or l2 + # iterate thru the new list + curr = curr.next + + return dummy_head.next + + +class Solution2: + """ + 2. Solve the problem with recursion + """ + + def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode: + # edge case: if any list is empty + # return either one of the list or empty + if None in (l1, l2): + return l1 or l2 + # this is a more Pythonic way of checking if either l1 or l2 is empty + # same as below + # if not l1 or not l2: + # return l1 or l2 + + # if neither lists is empty + if l1.val <= l2.val: + l1.next = self.mergeTwoLists(l1.next, l2) + return l1 + else: + l2.next = self.mergeTwoLists(l1, l2.next) + return l2 diff --git a/Week 01/id_118/LeetCode_26_118.py b/Week 01/id_118/LeetCode_26_118.py new file mode 100644 index 000000000..c73ef9f99 --- /dev/null +++ b/Week 01/id_118/LeetCode_26_118.py @@ -0,0 +1,63 @@ +# import type hint, otherwise IDE will report error +from typing import List + + +class Solution1: + """ + Solution 1: below is the most intuitive solution + Use del function to do in-place deletion + However this solution is not optimal as del itself has O(n) time complexity + Also remember to check edge cases, empty list and list with only 1 element + """ + + def removeDuplicates(self, nums: List[int]) -> int: + # handle the edge cases first + # if nums is empty + if len(nums) == 0: + return 0 + # if nums only has 1 element + if len(nums) == 1: + return 1 + i = 1 + while i < len(nums): + if nums[i - 1] == nums[i]: + del nums[i] + else: + i += 1 + return i + + +class Solution2: + """ + Solution 2: use a pointer to check if the next element is duplicated in the existing + list + """ + + def removeDuplicates(self, nums: List[int]) -> int: + # set a pointer _len + # its final index position is the len of new nums + # nums[_len] serves as confirmation to accept unique value + _len = 1 + # if nums is empty + if len(nums) == 0: + return 0 + # if nums is not empty + for i in range(1, len(nums)): + # nums[i-1] is the first unique value + # if consecutive two elements are unique + # nums[_len] will confirm nums[i] is a non-duplicate value + # and accept it to the updated nums + if nums[i] != nums[i - 1]: + nums[_len] = nums[i] + _len += 1 + return _len + + +if __name__ == '__main__': + arr = [1, 1, 2] + + sol1 = Solution1() + print(sol1.removeDuplicates(arr)) + + sol2 = Solution2() + print(sol2.removeDuplicates(arr)) diff --git a/Week 01/id_118/LeetCode_283_118.py b/Week 01/id_118/LeetCode_283_118.py new file mode 100644 index 000000000..c78f6b888 --- /dev/null +++ b/Week 01/id_118/LeetCode_283_118.py @@ -0,0 +1,74 @@ +from typing import List + + +class Solution1: + """ + This solution uses a lot of python specific methods + """ + + def move_zeroes(self, nums: List[int]) -> None: + # length of the original array + l = len(nums) + # filter out all the zeros + nums[:] = filter(lambda x: x != 0, nums) + # length of the modified array + l2 = len(nums) + # fill 0s to the right + nums[l2:] = [0] * (l - l2) + # print to test + print(nums) + + +class Solution2: + """ + the most traditional solution + """ + + def move_zeroes(self, nums: List[int]) -> None: + # index for zero element + zero = 0 + for i in range(len(nums)): + if nums[i] != 0: + nums[zero], nums[i] = nums[i], nums[zero] + zero += 1 + print(nums) + + +class Solution3: + """ + following Olsh's snowball solution + """ + + def move_zeros(self, nums: List[int]) -> None: + snowball = 0 + for i in range(len(nums)): + if nums[i] == 0: + snowball += 1 + elif snowball > 0: + # swap with the most left 0 + nums[i], nums[i - snowball] = nums[i - snowball], nums[i] + print(nums) + + +test_nums = [0, 1, 0, 3, 12] + + +def test1(): + sol = Solution1() + sol.move_zeroes(test_nums) + + +def test2(): + sol = Solution2() + sol.move_zeroes(test_nums) + + +def test3(): + sol = Solution3() + sol.move_zeros(test_nums) + + +if __name__ == '__main__': + test1() + test2() + test3() diff --git a/Week 01/id_118/LeetCode_42_118.py b/Week 01/id_118/LeetCode_42_118.py new file mode 100644 index 000000000..0411940e4 --- /dev/null +++ b/Week 01/id_118/LeetCode_42_118.py @@ -0,0 +1,73 @@ +from typing import List + + +class Solution1: + """ + solution with 2 pointers + the amount of water will always be + the difference between the max height and current height, otherwise water will spill + Time Complexity: O(n) + """ + + def trap(self, height: List[int]) -> int: + # Initialize left and right pointer + left, right = 0, len(height) - 1 + # Initialize left max, right max and water + l_max, r_max, water = 0, 0, 0 + # keep looping as long as two pointers don't meet + while left <= right: + if l_max <= r_max: + # on the left side + # if the current height is no greater than left max, store water + # otherwise water will spill + # if the current height is greater than left max + # swap left max and height, move to the next iteration + if l_max >= height[left]: + water += l_max - height[left] + else: + l_max = height[left] + left += 1 + else: + # same on the right + if r_max >= height[right]: + water += r_max - height[right] + else: + r_max = height[right] + right -= 1 + return water + + +class Solution2: + """ + Solve the problem with 'dynamic programming' + Not sure if it's truly DP, but use two hash tables to store the + calculated results in the caches + """ + + def trap(self, height: List[int]) -> int: + # create two hash tables lmax and rmax to store the max val + res, lmax, rmax = 0, {}, {} + if len(height) == 0: + return res + + lmax[0] = height[0] + for left in range(1, len(height)): + lmax[left] = max(lmax[left-1], height[left]) + + rmax[len(height)-1] = height[-1] + for right in range(len(height)-2, -1, -1): + rmax[right] = max(rmax[right+1], height[right]) + + for i in range(len(height)): + res += (min(lmax[i], rmax[i]) - height[i]) + + return res + + +if __name__ == '__main__': + sol1 = Solution1() + l = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1] + print(sol1.trap(l)) + + sol2 = Solution2() + print(sol2.trap(l)) diff --git a/Week 01/id_118/LeetCode_641_118.py b/Week 01/id_118/LeetCode_641_118.py new file mode 100644 index 000000000..636bf312b --- /dev/null +++ b/Week 01/id_118/LeetCode_641_118.py @@ -0,0 +1,89 @@ +class MyCircularDeque: + """ + This problem is quite straightforward, just be careful with the boundaries + It will make the solution clear once we separate actual size (_size) from + capacity (size) + """ + def __init__(self, k: int): + """ + Initialize your data structure here. Set the size of the deque to be k. + """ + self.data = [] + # this is the actual size + self._size = 0 + # this is the capacity + self.size = k + + def insertFront(self, value: int) -> bool: + """ + Adds an item at the front of Deque. Return true if the operation is successful. + """ + if not self.isFull(): + self.data.insert(0, value) + self._size += 1 + return True + else: + return False + + def insertLast(self, value: int) -> bool: + """ + Adds an item at the rear of Deque. Return true if the operation is successful. + """ + if not self.isFull(): + self.data.append(value) + self._size += 1 + return True + else: + return False + + def deleteFront(self) -> bool: + """ + Deletes an item from the front of Deque. Return true if the operation is successful. + """ + if not self.isEmpty(): + self.data.pop(0) + self._size -= 1 + return True + else: + return False + + def deleteLast(self) -> bool: + """ + Deletes an item from the rear of Deque. Return true if the operation is successful. + """ + if not self.isEmpty(): + self.data.pop() + self._size -= 1 + return True + else: + return False + + def getFront(self) -> int: + """ + Get the front item from the deque. + """ + if not self.isEmpty(): + return self.data[0] + else: + return -1 + + def getRear(self) -> int: + """ + Get the last item from the deque. + """ + if not self.isEmpty(): + return self.data[self._size - 1] + else: + return -1 + + def isEmpty(self) -> bool: + """ + Checks whether the circular deque is empty or not. + """ + return self._size == 0 + + def isFull(self) -> bool: + """ + Checks whether the circular deque is full or not. + """ + return self._size == self.size \ No newline at end of file diff --git a/Week 01/id_118/LeetCode_66_118.py b/Week 01/id_118/LeetCode_66_118.py new file mode 100644 index 000000000..703093d13 --- /dev/null +++ b/Week 01/id_118/LeetCode_66_118.py @@ -0,0 +1,58 @@ +from typing import List + + +class Solution1: + """ + The idea is simple, array -> string -> int + then: int + 1 + finally: int -> string -> array + """ + + def plusOne(self, digits: List[int]) -> List[int]: + # convert list to an int + a = "".join(map(lambda x: str(x), digits)) + a = int(a) + b = a + 1 + res = [int(digit) for digit in str(b)] + return res + + +class Solution2: + """ + I think a more proper solution would be using digit operation + """ + + def plusOne(self, digits: List[int]) -> List[int]: + num = 0 + # reverse order as hundreds is at 0, tens at 1... + for idx, digit in enumerate(digits[::-1]): + num += digit * (10 ** idx) + # plus one + num += 1 + + res = [] + while num > 0: + # start append digit into res from units, tens, hundreds etc + res.append(num % 10) + num = num // 10 + + # need to reverse the list as hundreds, tens, units + return res[::-1] + + +test_array = [1, 2, 3, 9] + + +def test1(): + sol = Solution1() + print(sol.plusOne(test_array)) + + +def test2(): + sol = Solution2() + print(sol.plusOne(test_array)) + + +if __name__ == '__main__': + test1() + test2() diff --git a/Week 01/id_118/LeetCode_88_118.py b/Week 01/id_118/LeetCode_88_118.py new file mode 100644 index 000000000..ebb2cedac --- /dev/null +++ b/Week 01/id_118/LeetCode_88_118.py @@ -0,0 +1,69 @@ +from typing import List + + +class Solution1: + """ + A very pythonic solution, though it uses built-in sort function + Python uses mergeSort to implement list sort method + Time complexity is O(nlogn) + """ + + def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: + # merge two list first, nums1 only takes the first m elements + # nums2 fill the remainder + nums1[m:] = nums2[:n] + # nums1 sort + nums1.sort() + print(nums1) # for testing + + +class Solution2: + """ + This solution has a time complexity of O(n) + """ + + def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: + """ + in-place change nums1 + :param nums1: + :param m: + :param nums2: + :param n: + :return: + """ + # compare from last to first element + # the bigger element will remain it's original position + # the smaller element will be used for the next comparison + # the new list will have the length of n+m-1 + while m > 0 and n > 0: + if nums1[m - 1] > nums2[n - 1]: + nums1[n + m - 1] = nums1[m - 1] + m -= 1 + else: + nums1[n + m - 1] = nums2[n - 1] + n -= 1 + + # edge case if there are still values in nums2, put all in front of + # nums1 as they are sorted and compared with all the elements in nums1 + if n >= 0: + nums1[:n] = nums2[:n] + print(nums1) # for testing + + +def test1(): + sol1 = Solution1() + l1 = [1, 2, 3, 0, 0, 0] + l2 = [2, 5, 6] + sol1.merge(l1, 3, l2, 3) + + +def test2(): + sol2 = Solution2() + l1 = [1, 2, 3, 0, 0, 0] + l2 = [2, 5, 6] + sol2.merge(l1, 3, l2, 3) + + +if __name__ == '__main__': + test1() + test2() diff --git a/Week 01/id_118/NOTE.md b/Week 01/id_118/NOTE.md index a6321d6e2..39e9ff41a 100644 --- a/Week 01/id_118/NOTE.md +++ b/Week 01/id_118/NOTE.md @@ -1,4 +1,309 @@ -# NOTE +# [118-Week01] Recap + +## 1. Practice + +- There's really no shortcut to it, just practice relentlessly by following the 5-step method. Peace out AI... + + ![AI]( https://i.ytimg.com/vi/K9ZQhyOZCNE/maxresdefault.jpg ) + +## 2. Time-Space Tradeoff + +- Using more space for better performance is a common practice + +- Skip List is a good example here, use additional references to speed up the lookup process + + ![skiplist](https://upload.wikimedia.org/wikipedia/commons/8/86/Skip_list.svg) + +- Definition from wikipedia: + + - A space–time or time–memory trade-off in computer science is a case where an algorithm or program trades increased space usage with decreased time. + - Here, space refers to the data storage consumed in performing a given task, and time refers to the time consumed in performing a given task. + + + +## 3. Common Techniques. + +- Iterate an array with two pointers without lookup elements repetitively + + - It uses nested loop, so its time complexity is at least O(n^2) + + ```python + for i in range(len(arr) - 1): + for j in range(i+1, len(arr)): + # do something + pass + ``` + +- Use two pointers on either side of array, then converge to the middle + + - It doesn't use nested loop, so its time complexity starts from O(n) + + ```python + i = 0 + for j in range(len(arr)-1, i, -1): + #do something + i += 1 + ``` + + - Alternatively use `while` loop + + ```python + i, j = 0, len(arr)-1 + while i < j: + # do something + i += 1 + j -= 1 + ``` +- Fast and Slow Pointers + + - Used to find if there's a loop in the linked list + - Space complexity is O(1) + + ```python + fast = node.nxt + slow = node.nxt.nxt + # check if fast and slow will meet + ``` + + + +- Find all the solutions, start with Brute Force if possible + +- Try to find a solution progressively (Climbing Stairs): + - What's the simplest case + - Generalization: find the most recent and repeatable sub-problem and its solution + - In the end, all the algorithms can be converted to `if...else`, `for`, `while` or recursion + +## 4. Linked List + +- A truly dynamic linear data structure + +- Time complexity of common operations + + | Operations | Linked List | Array | + | ------------- | ----------- | :---- | + | Add Last | O(1) | O(1) | + | Remove Last | O(1) | O(1) | + | Add any | O(1) | O(n) | + | Remove any | O(1) | O(n) | + | Random Lookup | O(n) | O(1) | + +- Implement a Linked List in Python + + - Will only implement initialization, delete and add + - Add Last, Remove Last can be built from general add and remove methods + + ```python + class LinkedList: + + class _Node: + # internal Node class + def __init__(self, val=None, nxt=None): + self.val = val + # use nxt here for next as next is a keyword in Python + self.nxt = nxt + + def __init__(self): + self._dummy_head = self._Node() + # create a dummy head of None, pointing to None + self._size = 0 + # size of the Linked List initiated to 0 + + def add(self, idx, e): + # report error if index is illegal + if not(0 <= idx < self._size): + raise ValueError("Add Failed. Illegal Index Position") + + # iterate from the dummy head to idx + curr = self._dummy_head + for i in range(idx): + curr = curr.next + new_node = self._Node(e) + # key: first connect new_node next, then reconnect original idx + new_node.nxt = curr.nxt + curr.nxt = new_node + # resize the linked list + self._size += 1 + + def remove(self, idx): + # report error if index is illegal + if not(0 <= idx < self._size): + raise ValueError("Add Failed. Illegal Index Position") + # iterate from the dummy head to idx + curr = self._dummy_head + for i in range(idx): + curr = curr.nxt + delete_node = curr.nxt + # jump over delete_node + curr.nxt = curr.nxt.nxt + delete_node.nxt = None + self._size -= 1 + + ``` + + + +## 5. Stack + +- FILO + +- Time complexity is O(1) for pop and append + +- Time complexity is O(n) for lookup, as elements in stacks are orderless + +- In Python, list can be used to implement a stack [Python Doc]( https://docs.python.org/3/tutorial/datastructures.html?highlight=stack ) + +- Implement a stack in Python using list + - `list.pop()`remove the top element from the stack + - `list.append()` add the element to the top of the stack + +- Implement a stack in Python + + ```python + class Stack: + def __init__(self): + self._data = list() + + def push(self, ele): + self._data.append(ele) + + def pop(self): + if not len(self._data) > 0: + raise ValueError("Stack is empty") + self._data.pop() + ``` + + + + + +## 6. Queue +- FIFO +- Time complexity is O(1) for enqueue and dequeue +- Time complexity is O(n) for lookup, as elements in queues are orderless +- Python has a built-in [queue library]( https://docs.python.org/3/library/queue.html ) + - Queue classes are usually used in threaded programming + - `queue.Queue()` FIFO queue, the regular queue as we know + - `queue.SimpleQueue` it's also a FIFO queue, but it cannot be used for task tracking + - `queue.LifoQueue()` LIFO queue, similar to a stack + - `queue.PriorityQueue()` , Priority queue, kept sorted with [heapq]( https://docs.python.org/3/library/heapq.html#module-heapq ) + +- Common built-in Queue methods + + - `Queue.qsize()` get the size of the queue + - `Queue.empty()` check if a queue is empty + - `Queue.full()`check if a queue is full + - `Queue.put()`Put item into the queue => enqueue + - `Queue.get()`Remove and return the removed element from queue => dequeue + +- A queue can also be simply implemented with list in python + + ```python + class Queue: + def __init__(self): + self._data = list() + + def enqueue(self, ele): + # add data to the front of queue + self._data.insert(0, ele) + + def dequeue(self): + if not len(self._data) > 0: + raise ValueError("Queue is empty") + self._data.pop() + ``` + + + +## 7. Deque + +- double-end queue + +- Can perform enqueue and dequeue on both top and tail + +- Python has a built-in deque data structure in collections library [Deque]( https://docs.python.org/2/library/collections.html#collections.deque ) + +- Common methods + + - `append(ele)`, add element to the right side of the deque + + - `appendleft(ele)`, add element to the left side of the deque + + - `pop()` Remove and return an element from the right side of the deque. If deque is empty, raise IndexError + + - `popleft()` Remove and return an element from the left size of the deque. If deque is empty, raise IndexError + + Additionally, python deque and extend iterables (list, tuple etc.) + + - `extend(iterable)` extend the right side of the deque by appending elements from the iterable + + - `extendleft(iterable)` extend the left side of the deque by appending elements form the iterable + +- Use Python to rewrite Deque, it's quite different from Java, so just for reference + + ```python + from collections import deque + d = deque() + # python can extend a whole iterable into a deque + d.extend(["a", "b", "c"]) + print(d) + # for peek, python deque doesn't have built-in method, but there's no need + # simply look it up as a list + # peekFirst is equivalent to d[0], peekLast is equivalent to d[-1] + str = d[0] + + while not d.empty(): + d.pop() + print(d) + ``` + + + +## 8. Priority Queue + +- Elements in Priority Queues have priorities, it's more similar to a tree-like data structure. +- There are multiple ways to implement it, e.g. heap, BST, AVL, Red-Black Tree etc. +- Time Complexity is O(1) to add element +- Time Complexity is O(logN) to remove element +- For priority queues, it's important to keep the elements in order. + - Assume using a maxheap to represent a priority queue + - Enqueue: + - step1: when add an element, add it to the last of queue + + - step2: then need to use sift up to make sure all elements are in order + +```python +def _sift_up(self, k): + while k > 0 and self._data.get(k) > self._data.get(self._parent(k)): + self._data.swap(k, self._parent(k)) + k = self._parent(k) +``` + + - Dequeue + - step 1: remove the the max element + + - step 2: after removing max element, perform sift down to make sure the elements are in order + +```python +def _sift_down(self, k): + while self._left_child(k) < self._data.get_size(): + j = self._left_child(k) + if j + 1 < self._data.get_size() and self._data.get(j + 1) > self._data.get(j): + # right child is greater + j = self._right_child(k) + # self._data.get(j) is the max(left.child, right.child) + if self._data.get(k) > self._data.get(j): + break + self._data.swap(k, j) + k = j +``` + +- Python uses `heapq` to keep the priority queues in order. Here, they use the min heap rather than max heap. some commonly useful methods + + - `heapq.heapify(x)`: transform a list into a heap, in-place, complexity is O(n) + + - `heapq.heappush(heap, item)`: push the item into the heap, keeping the heap in order + - `heapq.headpop(heap)`Pop and return the smallest item from the heap, keeping the heap in order diff --git a/Week 01/id_123/LeetCode_1_123.c b/Week 01/id_123/LeetCode_1_123.c new file mode 100644 index 000000000..ae055367a --- /dev/null +++ b/Week 01/id_123/LeetCode_1_123.c @@ -0,0 +1,25 @@ +/** + * Note: The returned array must be malloced, assume caller calls free(). + */ +int* twoSum(int* nums, int numsSize, int target, int* returnSize){ + int *result = (int *)malloc(sizeof(int)*2); + bool found = false; + for(int i = 0; i map = new Dictionary(nums.Length); + for (int i = 0; i < nums.Length; i++) { + if (map.ContainsKey(target - nums[i])) { + return new int[] {map[target - nums[i]], i}; + } + if (!map.ContainsKey(nums[i])) { + map.Add(nums[i], i); + } + } + throw new ArgumentException(); + } +} \ No newline at end of file diff --git a/Week 01/id_128/LeetCode_26_128.cs b/Week 01/id_128/LeetCode_26_128.cs new file mode 100644 index 000000000..879dd589e --- /dev/null +++ b/Week 01/id_128/LeetCode_26_128.cs @@ -0,0 +1,15 @@ +public class Solution { + public int RemoveDuplicates(int[] nums) { + //bounds checking + if (nums.Length < 2) + return nums.Length; + //double pointers + int j = 0; + for (int i = 1; i < nums.Length; i++) { + if (nums[i] != nums[j] && i > ++j) { + nums[j] = nums[i]; + } + } + return j + 1; + } +} diff --git a/Week 01/id_128/LeetCode_88_128.cs b/Week 01/id_128/LeetCode_88_128.cs new file mode 100644 index 000000000..429714e48 --- /dev/null +++ b/Week 01/id_128/LeetCode_88_128.cs @@ -0,0 +1,35 @@ +public class Solution { + public void Merge(int[] nums1, int m, int[] nums2, int n) { + //bounds checking + if (nums2.Length == 0) + return; + + int newLength = m + n; + int k = newLength - 1; + + //nums1 index + int i = m - 1; + + //nums2 index + for (int j = n - 1; j >= 0;) { + if (i >= 0) { + if (nums2[j] > nums1[i]) { + nums1[k--] = nums2[j--]; + } + else { + nums1[k--] = nums1[i--]; + } + } + //copy rest of nums2 + else { + nums1[k--] = nums2[j--]; + } + + } + //copy rest of nums1 + while (i >= 0) { + nums1[k--] = nums1[i--]; + } + } +} + diff --git a/Week 01/id_128/NOTE.md b/Week 01/id_128/NOTE.md index a6321d6e2..0e6dc4d88 100644 --- a/Week 01/id_128/NOTE.md +++ b/Week 01/id_128/NOTE.md @@ -1,4 +1,79 @@ # NOTE - +深度学习内容 +Array, LinkedList, Stack (interface), Queue (interface), Deque - Double Ended Queue +了解内容 +SkipList, Priority Queue (interface) - 多样实现方法 + +解题策略 +1. 考虑暴力方法。 理清基本解题逻辑 为找重复子问题打基础 +2. 找 最近 重复子问题。 + 数学推导法 - 从简单情况列举。 为什么? 因为 计算机只能处理if else loop, 所以所有思路只能变成去找重复性 + + +解题思想: +1. 左右夹逼 (从中间到两边) +2. 快慢指针 - 通过速度不同的多指针, 实现O(1)空间操作 +3. 空间换时间, 升维 + a. Deque (实现中间值的线性有序记录) + b. Dynamic programming aka 表格编程 (可预先记录计算过程中的中间值,随时取用)Deque的升维版本 +4. 空间换时间的技巧 + a. 栈 - 具有最近相关性。 洋葱结构 + b. 队列 - 强调先来后到 + + +学习方法 +1. 5遍刷题法 + 过遍数 很重要 + 看题解 做题 都要过遍数 + +2. 5分钟思考没思路直接看答案, 不需要发明创造 + + + +Deque 改写 + +Deque deque = new LinkedList(); +deque.addFirst("a"); +deque.addFirst("b"); +deque.addFirst("c"); +System.out.println(deque); + +String str = deque.peekFirst(); +System.out.println(str); +System.out.println(deque); + +while (deque.size() > 0) { + System.out.println(deque.removeFirst()); +} +System.out.println(deque); + + + +不熟悉java 简单源码分析 + +Queue source code anlysis + +first in first out + +Time Complexity +add, delete O(1) +find O(n) +Queue is top level interface + +Supported Methods +Operation Category Throw Return Value +Insert add() offer(e) false +Remove remove() poll() null +Examine element() peek() null + +Priority Queue source code analysis + +Unbounded +Based on Heap +Support custom element comparator upon construction + +Time Complexity +add O(1) +get O(logN) diff --git a/Week 01/id_133/leetcode_189_133.java b/Week 01/id_133/leetcode_189_133.java new file mode 100644 index 000000000..75b3c133a --- /dev/null +++ b/Week 01/id_133/leetcode_189_133.java @@ -0,0 +1,28 @@ + +//leetcode 题号189 旋转数组问题 + +import java.io.*; +import java.util.Arrays; + +public class Solution { + public int[] rotate(int[] nums, int k) { + int currentNums,temp; + for (int i = 1; i<= k; i++) { + currentNums = nums[nums.length - 1]; + for (int j = 0; j < nums.length; j++) { + temp = nums[j]; + nums[j] = currentNums; + currentNums = temp; + } + } + return nums; + } + + public static void main(String[] args) { + int[] targetBox = {1,1,2,3,3,5};//有序数组 + int k = 4; + Solution solution = new Solution(); + int result[] = solution.rotate(targetBox,k); + System.out.println("输出:" + Arrays.toString(result)); + } +} \ No newline at end of file diff --git a/Week 01/id_133/leetcode_26_133.java b/Week 01/id_133/leetcode_26_133.java new file mode 100644 index 000000000..3a58fbce6 --- /dev/null +++ b/Week 01/id_133/leetcode_26_133.java @@ -0,0 +1,31 @@ + +//leetcode 题号26 有序数组去重问题 +//思路:查看了官方题解答案,理解后,默写完成 + +import java.io.*; +import java.util.Arrays; + +public class Solution { + public int removeDuplicates(int[] targetBox) { + //首先判断目标数组是否为空 + if (targetBox.length == 0) return 0; + //定义左卡尺 + int left = 0; + //定义右卡尺并循环数组 + for (int right = 1; right < targetBox.length; right++) { + if (targetBox[right] != targetBox[left]) { + left++; + targetBox[left] = targetBox[right]; + } + } + // System.out.println("输出:" + Arrays.toString(targetBox)); //真实输出:[1,2,2] + return left + 1; + } + + public static void main(String[] args) { + int[] targetBox = {1,1,2,3,3,5};//有序数组 + Solution solution = new Solution(); + int result = solution.removeDuplicates(targetBox); + System.out.println(result); //输出最终结果,去重后数组的长度 + } +} \ No newline at end of file diff --git a/Week 01/id_138/LeetCode_189_138.java b/Week 01/id_138/LeetCode_189_138.java new file mode 100644 index 000000000..d966cb8ff --- /dev/null +++ b/Week 01/id_138/LeetCode_189_138.java @@ -0,0 +1,49 @@ +/** + * 旋转数组 + * @author Lukas + * @since 2019/10/19 0:29 + **/ +public class LeetCode_189_138 { + /** + * 将数组向右移动k位 + * 暴力法:每个元素移动k次 + * @param nums + * @param k + */ + public void rotate(int[] nums, int k) { + int tail ,temp; + for (int i = 0; i < k; i++) {//1 2 3 4 5 -> 5 1 2 3 4 -> 4 5 1 2 3 ->3 4 5 1 2 + tail = nums[nums.length - 1]; + for (int j = 0; j < nums.length; j++) { + temp = nums[j]; + nums[j] = tail; + tail = temp; + } + } + } + + /** + * 反转数组:先反转所有元素,类似倒序;然后将前k个元素和剩余的n-k个元素倒排 + * @param nums + * @param k + */ + public void rotate2(int[] nums, int k) { + if (nums==null || nums.length==0) + return ; + int len = nums.length; + k = k%len; + reverseArray(nums, 0, len-1); + reverseArray(nums, 0,k-1); + reverseArray(nums,k,len-1); + } + + public void reverseArray(int[] nums, int start, int end) { + while (start < end) { + int temp = nums[start]; + nums[start] = nums[end]; + nums[end] = temp; + start++; + end--; + } + } +} diff --git a/Week 01/id_138/LeetCode_24_138.java b/Week 01/id_138/LeetCode_24_138.java new file mode 100644 index 000000000..72a3df2d3 --- /dev/null +++ b/Week 01/id_138/LeetCode_24_138.java @@ -0,0 +1,47 @@ +/** + * 两两交换链表中的相邻节点 + * @author Lukas + * @since 2019/10/19 20:10 + **/ +public class LeetCode_24_138 { + + /** + * 递归调用方式 + * @param head + * @return + */ + public ListNode swapPairs(ListNode head) { + if (head == null || head.next == null) { + return head; + } + ListNode next = head.next;//下一个节点指针 + head.next = swapPairs(next.next);//递归调用 + next.next = head;//交换 + return next; + } + + /** + * 非递归调用方式 + * @param head + * @return + */ + public ListNode swapPairs2(ListNode head) { + ListNode first = new ListNode(0); + first.next = head;//重新设置需要交换的链表的头节点 + ListNode pre = first;//前一个遍历的节点 + ListNode cur = pre.next;//当前遍历的节点 + while (cur != null && cur.next != null) { + ListNode temp = cur.next;//当前遍历节点的下个节点 + ListNode next = temp.next;//下次流程交换的节点 + pre.next = temp;//设置已经完成遍历的节点的next + temp.next = cur;//交换节点 + + //重新开始下一轮的交换 + pre = cur; + cur = next; + } + + return first.next; + } + +} diff --git a/Week 01/id_138/LeetCode_26_138.java b/Week 01/id_138/LeetCode_26_138.java new file mode 100644 index 000000000..1434153ad --- /dev/null +++ b/Week 01/id_138/LeetCode_26_138.java @@ -0,0 +1,27 @@ +/** + * 删除排序数组中的重复项 + * @author Lukas + * @since 2019/10/18 23:38 + **/ +public class LeetCode_26_138 { + /** + * 双指针法:时间复杂度O(n),空间复杂度:O(1) + * @param nums + * @return + */ + public int removeDuplicates(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + + int L = 0; + int R = 1; + while (R < nums.length) { + if (nums[L] != nums[R]) { + nums[++L] = nums[R]; + } + R++; + } + return L+1; + } +} diff --git a/Week 01/id_138/LeetCode_283_138.java b/Week 01/id_138/LeetCode_283_138.java new file mode 100644 index 000000000..aab0d1e07 --- /dev/null +++ b/Week 01/id_138/LeetCode_283_138.java @@ -0,0 +1,26 @@ +/** + * + * @Author Lukas + * @Description 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 + * @Date 14:06 2019/10/14 + * @Param + * @return + **/ +public class LeetCode_283_138 { + + public void moveZeroes(int[] nums) { + if (nums != null || nums.length == 0) { + return ; + } + int k = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != 0) {//交换条件1 记录非0元素个数 + if (i != k) {//交换条件2 + nums[k] = nums[i]; + nums[i] = 0; + } + k++; + } + } + } +} diff --git a/Week 01/id_138/LeetCode_42_138.java b/Week 01/id_138/LeetCode_42_138.java new file mode 100644 index 000000000..3d514f9a6 --- /dev/null +++ b/Week 01/id_138/LeetCode_42_138.java @@ -0,0 +1,66 @@ +/** + * 接雨水 + * @author Lukas + * @since 2019/10/19 22:08 + **/ +public class LeetCode_42_138 { + + /** + * 暴力法求解:按行求解 + * + * @param height + * @return + */ + public int trap1(int[] height) { + int sum = 0; + int max = getMaxHeight(height);//获取最高度 + for (int row = 1; row <= max; row++) {//一行行遍历,从1开始 + boolean isStart = false; + int temp_sum = 0; + for (int j = 0; j < height.length; j++) { + if (isStart && height[j] < row) { + temp_sum++; + } + if (height[j] >= row) { + sum += temp_sum; + temp_sum = 0; + isStart = true; + } + } + } + return sum; + } + + private int getMaxHeight(int[] height) { + int max = 0; + for (int i = 0; i < height.length; i++) { + if (height[i] > max) { + max = height[i]; + } + } + return max; + } + + + public int trap2(int[] height) { + int sum = 0; + final int length = height.length; + int[] max_left = new int[length];//第i列左侧最高的值 + int[] max_right = new int[length];//第i列右侧最高度值 + for (int i = 1; i < length - 1; i++) { + max_left[i] = Math.max(max_left[i - 1], height[i-1]); + } + + for (int i = length - 2; i >= 0; i--) { + max_right[i] = Math.max(max_right[i+1], height[i+1]); + } + + for (int i = 1; i < length-1; i++) { + int min = Math.min(max_left[i], max_right[i]); + if (min > height[i]) { + sum += min - height[i]; + } + } + return sum; + } +} diff --git a/Week 01/id_138/LeetCode_641_138.java b/Week 01/id_138/LeetCode_641_138.java new file mode 100644 index 000000000..acc885133 --- /dev/null +++ b/Week 01/id_138/LeetCode_641_138.java @@ -0,0 +1,109 @@ +/** + * 设计双端队列 + * @author Lukas + * @since 2019/10/19 20:36 + **/ +public class LeetCode_641_138 { + + /** + * 双链表 + */ + class CircleListNode { + CircleListNode pre; + CircleListNode next; + int val; + + public CircleListNode(int v) { + this.val = v; + } + } + + /** + * 双端队列 + */ + class MyCircularDeque { + int size;//队列当前元素个数 + int length;//队列初始化大小 + CircleListNode head;//队列头 + CircleListNode tail;//队列尾 + + /** Initialize your data structure here. Set the size of the deque to be k. */ + public MyCircularDeque(int k) { + head = new CircleListNode(-1); + tail = new CircleListNode(-1); + head.pre = tail; + tail.next = head; + this.size = 0; + this.length = k; + } + + /** Adds an item at the front of Deque. Return true if the operation is successful. */ + public boolean insertFront(int value) { + if (size == length) { + return false; + } + CircleListNode node = new CircleListNode(value); + node.next = head; + node.pre = head.pre; + head.pre.next = node; + head.pre = node; + size++; + return true; + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + public boolean insertLast(int value) { + if (size == length) { + return false; + } + CircleListNode node = new CircleListNode(value); + node.next = tail.next; + tail.next.pre = node; + tail.next = node; + node.pre = tail; + size++; + return true; + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + public boolean deleteFront() { + if (size == 0) { + return false; + } + head.pre.pre.next = head; + head.pre = head.pre.pre; + size--; + return true; + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + public boolean deleteLast() { + if (size == 0) + return false; + tail.next.next.pre = tail; + tail.next = tail.next.next; + size--; + return true; + } + + /** Get the front item from the deque. */ + public int getFront() { + return head.pre.val; + } + + /** Get the last item from the deque. */ + public int getRear() { + return tail.next.val; + } + + /** Checks whether the circular deque is empty or not. */ + public boolean isEmpty() { + return size==0; + } + + /** Checks whether the circular deque is full or not. */ + public boolean isFull() { + return size == length; + } + } +} diff --git a/Week 01/id_138/LeetCode_66_138.java b/Week 01/id_138/LeetCode_66_138.java new file mode 100644 index 000000000..c87ffd599 --- /dev/null +++ b/Week 01/id_138/LeetCode_66_138.java @@ -0,0 +1,61 @@ +/** + * @author Lukas + * @since 2019/10/20 22:33 + **/ +public class LeetCode_66_138 { + public int[] plusOne(int[] digits) { + int length = digits.length; + int[] rs = new int[length + 1]; + + int temp = 0; + boolean up = false;//是否存在进位 + for (int i = length - 1; i >= 0; i--) { + if (i == length - 1) {//个位数加1 + temp = digits[i] + 1; + } else { + if (up) { + temp = digits[i] + 1; + } else { + temp = digits[i]; + } + } + + + if (temp == 10) { + rs[i + 1] = 0; + up = true; + } else { + rs[i + 1] = temp; + up = false; + } + } + if (up) { + rs[0] = 1; + } + if (rs[0] != 0) { + return rs; + } else { + int[] dest = new int[length]; + System.arraycopy(rs, 1, dest, 0, length); + return dest; + } + } + + /** + * 优化 + * @param digits + * @return + */ + public int[] plusOne_best(int[] digits) { + for (int i = digits.length-1; i >=0; i--) { + digits[i]++; + digits[i] %= 10; + if (digits[i] != 0)//从低位到高位遍历,如果不存在进位则返回当前数组 + return digits; + } + //特殊处理 9...99 + 1 + digits = new int[digits.length+1]; + digits[0] = 1; + return digits; + } +} diff --git a/Week 01/id_138/ListNode.java b/Week 01/id_138/ListNode.java new file mode 100644 index 000000000..86fb00382 --- /dev/null +++ b/Week 01/id_138/ListNode.java @@ -0,0 +1,9 @@ +/** + * @author Lukas + * @since 2019/10/19 20:11 + **/ +public class ListNode { + int val; + ListNode next; + ListNode(int x) { val = x; } +} diff --git a/Week 01/id_138/NOTE.md b/Week 01/id_138/NOTE.md index a6321d6e2..e01891802 100644 --- a/Week 01/id_138/NOTE.md +++ b/Week 01/id_138/NOTE.md @@ -1,4 +1,84 @@ # NOTE - +第一周学习总结: +PartA: 本周学习内容总结 +1. 学习数组、链表、队列、栈等数据结构,了解各种数据结构的优缺点。 +2. 学习数组的操作方法:例如:循环遍历(多重遍历注意指针下标)、快慢指针、左右指针收敛。 +3. 关于链表:快慢指针,递归。对于链表,必须通过理解背诵记忆。 +4.队列、栈:主要还是考虑在递归能够解决问题的时候能否通过栈或队列做优化。 +5.跳表:优化链表查询的时间,中心思想是通过空间换时间,为每个节点创建索引,缺点是数据发生变化后需要触发索引或者缓存的重建,空间开销大。 +6.算法还是很考验记忆力的,但是记忆需要多理解,需要多动手,多思考。 + +PartB: PriorityQueue实现 +1.PriorityQueue(JDK版本1.8)是一个基于优先级堆的无界优先队列。类图参考:PriorityQueue-uml.png。默认通过自然顺序进行排序,也可以通过构造方法指定排序比较器。 +排队和出队方法(offer、poll、remove() 和 add)提供 O(log(n)) 时间;为 remove(Object) 和 contains(Object) 方法提供线性时间;为获取方法(peek、element 和 size)提供固定时间。 +此外该类是非线程安全的,如果是多线程推荐使用PriorityBlockingQueue. + 构造方法: public PriorityQueue() { + this(DEFAULT_INITIAL_CAPACITY, null); + } + public PriorityQueue(Comparator comparator) { + this(DEFAULT_INITIAL_CAPACITY, comparator); + } + 优先级队列不允许插入null元素,同样不允许插入不可比较的对象。队列的头是按照比较顺序的最小元素。但是允许重复元素。内部使用数组Object[]存放元素。 + 示例代码: PriorityQueue queue = new PriorityQueue<>(8); + queue.add(7); + queue.add(6); + queue.add(1); + queue.add(1); + //queue.add(null); + System.out.println(queue); + 输出: [1, 1, 6, 7] +2.主要的方法摘要: + 1)boolean add(E e) + 将元素插入到此优先队列,调用的是offer方法,重写父类的add方法,可能会抛出ClassCastException/NullPointerException + 2)boolean offer(E e) + 将元素插入到此优先队列,可能会抛出ClassCastException/NullPointerException + 内部使用变量size存放元素个数,添加元素的时候,如果元素个数超过原数组长度则扩充数组。 + 具体调用方法:void grow(int minCapacity),扩充的策略:原数组长度小于64,则长度加2;否则,长度扩充50%。 + // Double size if small; else grow by 50% + int newCapacity = oldCapacity + ((oldCapacity < 64) ? + (oldCapacity + 2) : + (oldCapacity >> 1)); + 扩充数组后通过System.arraycopy方法完成数组的拷贝。 + T[] copy = ((Object)newType == (Object)Object[].class) + ? (T[]) new Object[newLength] + : (T[]) Array.newInstance(newType.getComponentType(), newLength); + System.arraycopy(original, 0, copy, 0, + Math.min(original.length, newLength)); + 最后将元素插入到队列里,调用siftUpComparable/siftUpUsingComparator方法,完成比较后插入到对应的队列位置。 + 3)E peek() + 获取但不移除此队列的头,队列为空,则返回null + 由代码分析可知 return (size == 0) ? null : (E) queue[0];其实返回的就是内部数组的首个元素,也就是最小的元素。但是没有附加操作去修改数组长度,因此队列不变。 + 4)E poll() + 获取并移除队列的头,队列空则返回null + 该方法首先将内部数组的长度-1,记录数组的首元素。 + 5)boolean remove(Object o) + 从此队列中移除指定元素的单个实例(如果此队列包含一个或多个满足 o.equals(e) 的元素 e,则移除一个这样的元素)。 + 具体实现方式先通过indexOf(遍历比较是否相等)方法定位下标,然后调用removeAt方法移除指定下标的元素。 + +3.应用场景: + 优先队列以及DelayQueue实现 + /** + * 从输入的n个数里获取top k + * @param args + * @param size + * @return + */ + public Object[] testPriorityQueue(int[] args, int size) { + PriorityQueue pq = new PriorityQueue<>(size); + if (args == null || args.length == 0) + return null; + for (int arg : args) { + if (pq.size() > size) { + if (pq.peek() < arg) { + pq.poll(); + pq.add(arg); + } + }else { + pq.add(arg); + } + } + + return pq.toArray(); + } diff --git a/Week 01/id_138/PriorityQueue-uml.png b/Week 01/id_138/PriorityQueue-uml.png new file mode 100644 index 000000000..e6465e050 Binary files /dev/null and b/Week 01/id_138/PriorityQueue-uml.png differ diff --git a/Week 01/id_143/LeetCode_189_143.java b/Week 01/id_143/LeetCode_189_143.java new file mode 100644 index 000000000..2bdd5e11b --- /dev/null +++ b/Week 01/id_143/LeetCode_189_143.java @@ -0,0 +1,23 @@ +/* + * @lc app=leetcode.cn id=189 lang=java + * + * [189] 旋转数组 + * 思路1,每次转一位,转K次。 + * 思路2,每次换K位,空间K交换。 + * 思路3,循环处理。 + * 思路4,双指针,与临时变量 + */ + +// @lc code=start +class Solution { + public void rotate(int[] nums, int k) { + if (nums.length >0){ + k = k % nums.length; + int[] temp = new int[k]; + System.arraycopy(nums, nums.length - k, temp, 0, k); + System.arraycopy(nums, 0, nums, k, nums.length - k); + System.arraycopy(temp, 0, nums, 0, temp.length); + } + } +} +// @lc code=end \ No newline at end of file diff --git a/Week 01/id_143/LeetCode_1_143.java b/Week 01/id_143/LeetCode_1_143.java new file mode 100644 index 000000000..26ef62903 --- /dev/null +++ b/Week 01/id_143/LeetCode_1_143.java @@ -0,0 +1,49 @@ +import java.util.HashMap; +import java.util.Map; +/* + * @lc app=leetcode.cn id=1 lang=java + * + * [1] 两数之和 + * 方法一:暴力双循环 + * 方法二:缓存+循环 + * 方法三:边循环边缓存 + * 方法四:主体思想 两边向中夹迫,但原始数组无序,需要处理好多事情。 + * 方法五:&位运算+数组缓存(其实不推荐) + */ + +// @lc code=start +class Solution { + public int[] twoSum(int[] nums, int target) { + int [] b = new int[nums.length]; + + System.arraycopy(nums,0,b,0,nums.length); + Arrays.sort(nums); + + int res[] = new int[2]; + int k1 = nums.length - 1; + for (int i = 0; i >= 0 && i <= k1; ) { + if(nums[i] + nums[k1] == target){ + for(int j=0;j=0;j--){ + if(b[j]==nums[k1]){ + res[1] = j; + break; + } + } + return res; + } else if (nums[i] + nums[k1] < target) { + i++; + } else { + k1--; + } + } + return null; + } +} +// @lc code=end + diff --git a/Week 01/id_143/LeetCode_21_143.java b/Week 01/id_143/LeetCode_21_143.java new file mode 100644 index 000000000..a9e69de4c --- /dev/null +++ b/Week 01/id_143/LeetCode_21_143.java @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode.cn id=21 lang=java + * + * [21] 合并两个有序链表 + */ + +// @lc code=start +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +class Solution { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + if(l1==null){ + return l2; + }else if(l2 == null){ + return l1; + }else if(l1.val= 0; i--) { + + digits[i]++; + if(digits[i] !=10){ + return digits; + }else{ + digits[i]=0; + } + + } + digits = new int[digits.length + 1]; + digits[0] = 1; + return digits; + } +} +// @lc code=end + diff --git a/Week 01/id_143/LeetCode_88_143.java b/Week 01/id_143/LeetCode_88_143.java new file mode 100644 index 000000000..1a11d5fe7 --- /dev/null +++ b/Week 01/id_143/LeetCode_88_143.java @@ -0,0 +1,21 @@ +/* + * @lc app=leetcode.cn id=88 lang=java + * + * [88] 合并两个有序数组 + */ + +// @lc code=start +class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + + int p1 = m-1; + int p2 = n-1; + int p = m+n-1; + while( p2>=0 && p1>=0){ + nums1[p--] = (nums1[p1] > nums2[p2] )? nums1[p1--] : nums2[p2--]; + } + System.arraycopy(nums2,0,nums1,0,p2+1); + } +} +// @lc code=end + diff --git a/Week 01/id_143/NOTE.md b/Week 01/id_143/NOTE.md index a6321d6e2..db862792e 100644 --- a/Week 01/id_143/NOTE.md +++ b/Week 01/id_143/NOTE.md @@ -1,4 +1,1925 @@ -# NOTE +# 学习 +# 第一周 +## 第三课 数组、链表、跳表 +### 数组特性 ---ArrayList代表 +1. 每当申请一个数组 实际上是在内存中开辟了一段连续的内存空间 +2. 随机访问时间复杂度为O(1) +3. 增、删平均时间复杂度为O(n) +- ArrayList源码学习总结: + - 特性: + - 底层于Object数组 + - 非线程安全 + - 实现了RandomAccess接口。 + - 源码分析-主成员 + - 主成员变量 + ``` + /** + * 默认初始化容量值. + */ + private static final int DEFAULT_CAPACITY = 10; - + /** + * 空实例时,这个空对象传给elementData. + */ + private static final Object[] EMPTY_ELEMENTDATA = {}; + /** + * 默认构造方法时,传这个空对象给elementData. + */ + private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; + + /** + * 用于存放实际元素。默认构造方法得到的 ArrayList 在添加第一个元素时 DEFAULTCAPACITY_EMPTY_ELEMENTDATA + * will be expanded to DEFAULT_CAPACITY . + */ + transient Object[] elementData; // non-private to simplify nested class access + + /** + * The size of the ArrayList (the number of elements it contains). + * + * @serial + */ + private int size; + ``` + - 构造方法 + ``` + /** + * Constructs an empty list with the specified initial capacity. + * + * @param initialCapacity the initial capacity of the list + * @throws IllegalArgumentException if the specified initial capacity + * is negative + */ + public ArrayList(int initialCapacity) { + if (initialCapacity > 0) { + this.elementData = new Object[initialCapacity]; + } else if (initialCapacity == 0) { + this.elementData = EMPTY_ELEMENTDATA; + } else { + throw new IllegalArgumentException("Illegal Capacity: "+ + initialCapacity); + } + } + + /** + * Constructs an empty list with an initial capacity of ten. + */ + public ArrayList() { + this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; + } + + /** + * Constructs a list containing the elements of the specified + * collection, in the order they are returned by the collection's + * iterator. + * + * @param c the collection whose elements are to be placed into this list + * @throws NullPointerException if the specified collection is null + */ + public ArrayList(Collection c) { + elementData = c.toArray(); + if ((size = elementData.length) != 0) { + // c.toArray might (incorrectly) not return Object[] (see 6260652) + if (elementData.getClass() != Object[].class) + elementData = Arrays.copyOf(elementData, size, Object[].class); + } else { + // replace with empty array. + this.elementData = EMPTY_ELEMENTDATA; + } + } + ``` + - 添加元素 + ``` + /** + * Appends the specified element to the end of this list. + * + * @param e element to be appended to this list + * @return true (as specified by {@link Collection#add}) + */ + public boolean add(E e) { + ensureCapacityInternal(size + 1); // Increments modCount!! + elementData[size++] = e; + return true; + } + + /** + * Inserts the specified element at the specified position in this + * list. Shifts the element currently at that position (if any) and + * any subsequent elements to the right (adds one to their indices). + * + * @param index index at which the specified element is to be inserted + * @param element element to be inserted + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public void add(int index, E element) { + rangeCheckForAdd(index); + + ensureCapacityInternal(size + 1); // Increments modCount!! + System.arraycopy(elementData, index, elementData, index + 1, + size - index); + elementData[index] = element; + size++; + } + /** + * Increases the capacity of this ArrayList instance, if + * necessary, to ensure that it can hold at least the number of elements + * specified by the minimum capacity argument. + * + * @param minCapacity the desired minimum capacity + */ + public void ensureCapacity(int minCapacity) { + int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) + // any size if not default element table + ? 0 + // larger than default for default empty table. It's already + // supposed to be at default size. + : DEFAULT_CAPACITY; + + if (minCapacity > minExpand) { + ensureExplicitCapacity(minCapacity); + } + } + + private void ensureCapacityInternal(int minCapacity) { + if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { + minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); + } + + ensureExplicitCapacity(minCapacity); + } + + private void ensureExplicitCapacity(int minCapacity) { + modCount++; + + // overflow-conscious code + if (minCapacity - elementData.length > 0) + grow(minCapacity); + } + /** + * Increases the capacity to ensure that it can hold at least the + * number of elements specified by the minimum capacity argument. + * + * @param minCapacity the desired minimum capacity + */ + private void grow(int minCapacity) { + // overflow-conscious code + int oldCapacity = elementData.length; + int newCapacity = oldCapacity + (oldCapacity >> 1); + if (newCapacity - minCapacity < 0) + newCapacity = minCapacity; + if (newCapacity - MAX_ARRAY_SIZE > 0) + newCapacity = hugeCapacity(minCapacity); + // minCapacity is usually close to size, so this is a win: + elementData = Arrays.copyOf(elementData, newCapacity); + } + ``` + - 删除元素 + ``` + /** + * Removes the element at the specified position in this list. + * Shifts any subsequent elements to the left (subtracts one from their + * indices). + * + * @param index the index of the element to be removed + * @return the element that was removed from the list + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E remove(int index) { + rangeCheck(index); + + modCount++; + E oldValue = elementData(index); + + int numMoved = size - index - 1; + if (numMoved > 0) + System.arraycopy(elementData, index+1, elementData, index, + numMoved); + elementData[--size] = null; // clear to let GC do its work + + return oldValue; + } + + /** + * Removes the first occurrence of the specified element from this list, + * if it is present. If the list does not contain the element, it is + * unchanged. More formally, removes the element with the lowest index + * i such that + * (o==null ? get(i)==null : o.equals(get(i))) + * (if such an element exists). Returns true if this list + * contained the specified element (or equivalently, if this list + * changed as a result of the call). + * + * @param o element to be removed from this list, if present + * @return true if this list contained the specified element + */ + public boolean remove(Object o) { + if (o == null) { + for (int index = 0; index < size; index++) + if (elementData[index] == null) { + fastRemove(index); + return true; + } + } else { + for (int index = 0; index < size; index++) + if (o.equals(elementData[index])) { + fastRemove(index); + return true; + } + } + return false; + } + + /* + * Private remove method that skips bounds checking and does not + * return the value removed. + */ + private void fastRemove(int index) { + modCount++; + int numMoved = size - index - 1; + if (numMoved > 0) + System.arraycopy(elementData, index+1, elementData, index, + numMoved); + elementData[--size] = null; // clear to let GC do its work + } + + /** + * Removes all of the elements from this list. The list will + * be empty after this call returns. + */ + public void clear() { + modCount++; + + // clear to let GC do its work + for (int i = 0; i < size; i++) + elementData[i] = null; + + size = 0; + } + ``` + - get|set + ``` + /** + * Returns the element at the specified position in this list. + * + * @param index index of the element to return + * @return the element at the specified position in this list + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E get(int index) { + rangeCheck(index); + return elementData(index); + } + + /** + * Replaces the element at the specified position in this list with + * the specified element. + * + * @param index index of the element to replace + * @param element element to be stored at the specified position + * @return the element previously at the specified position + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E set(int index, E element) { + rangeCheck(index); + E oldValue = elementData(index); + elementData[index] = element; + return oldValue; + } + ``` + + +### 链表特性 ---LinkedList代表 +1. 非连续内存空间,适合内存碎片较多场景。注:jdk1.7采用双向循环链表,jdk1.8采用双向链表 +2. 随机访问时间复杂度O(n),增/删时间复杂度为O(1),但值得一提的是:linkedList仅在增加/删除确定位置下的结点时,时间复杂度为O(1)。因为链表不存在实际下标,若要在指定位置增加或删除某个元素时,必需先通过O(n)的遍历确定node位置再进行操作 +3. 优化:链表上面加HashMap。主要思想:空间换时间。升维。如redis为代表的的跳表 +- 学习LinkedList源码。LinkedList的工程应用:LRUCache(链表+HashMap,也可以是LinkedHashMap) +- LinkedList的源码学习总结: + - 特性 + - 底层基于Node对象 + - 双向链表 + - 非线程安全 + - 实现了Deque + - 主成员变量 + ``` + transient int size = 0; + + /** + * Pointer to first node. + * Invariant: (first == null && last == null) || + * (first.prev == null && first.item != null) + */ + transient Node first; + + /** + * Pointer to last node. + * Invariant: (first == null && last == null) || + * (last.next == null && last.item != null) + */ + transient Node last; + // 内部类 + private static class Node { + E item; + Node next; + Node prev; + + Node(Node prev, E element, Node next) { + this.item = element; + this.next = next; + this.prev = prev; + } + } + ``` + - 构造函数 + ``` + /** + * Constructs an empty list. + */ + public LinkedList() { + } + + /** + * Constructs a list containing the elements of the specified + * collection, in the order they are returned by the collection's + * iterator. + * + * @param c the collection whose elements are to be placed into this list + * @throws NullPointerException if the specified collection is null + */ + public LinkedList(Collection c) { + this(); + addAll(c); + } + ``` + - 添加函数 + ``` + /** + * Appends the specified element to the end of this list. + * + *

This method is equivalent to {@link #addLast}. + * + * @param e element to be appended to this list + * @return {@code true} (as specified by {@link Collection#add}) + */ + public boolean add(E e) { + linkLast(e); + return true; + } + /** + * Links e as last element. + */ + void linkLast(E e) { + final Node l = last; + final Node newNode = new Node<>(l, e, null); + last = newNode; + if (l == null) + first = newNode; + else + l.next = newNode; + size++; + modCount++; + } + /** + * Adds the specified element as the tail (last element) of this list. + * + * @param e the element to add + * @return {@code true} (as specified by {@link Queue#offer}) + * @since 1.5 + */ + public boolean offer(E e) { + return add(e); + } + /** + * Inserts the specified element at the end of this list. + * + * @param e the element to insert + * @return {@code true} (as specified by {@link Deque#offerLast}) + * @since 1.6 + */ + public boolean offerLast(E e) { + addLast(e); + return true; + } + /** + * Appends the specified element to the end of this list. + * + *

This method is equivalent to {@link #add}. + * + * @param e the element to add + */ + public void addLast(E e) { + linkLast(e); + } + /** + * Inserts the specified element at the specified position in this list. + * Shifts the element currently at that position (if any) and any + * subsequent elements to the right (adds one to their indices). + * + * @param index index at which the specified element is to be inserted + * @param element element to be inserted + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public void add(int index, E element) { + checkPositionIndex(index); + + if (index == size) + linkLast(element); + else + linkBefore(element, node(index)); + } + /** + * Inserts element e before non-null Node succ. + */ + void linkBefore(E e, Node succ) { + // assert succ != null; + final Node pred = succ.prev; + final Node newNode = new Node<>(pred, e, succ); + succ.prev = newNode; + if (pred == null) + first = newNode; + else + pred.next = newNode; + size++; + modCount++; + } + // Deque operations + /** + * Inserts the specified element at the front of this list. + * + * @param e the element to insert + * @return {@code true} (as specified by {@link Deque#offerFirst}) + * @since 1.6 + */ + public boolean offerFirst(E e) { + addFirst(e); + return true; + } + /** + * Inserts the specified element at the beginning of this list. + * + * @param e the element to add + */ + public void addFirst(E e) { + linkFirst(e); + } + private void linkFirst(E e) { + final Node f = first; + final Node newNode = new Node<>(null, e, f); + first = newNode; + if (f == null) + last = newNode; + else + f.prev = newNode; + size++; + modCount++; + } + /** + * Pushes an element onto the stack represented by this list. In other + * words, inserts the element at the front of this list. + * + *

This method is equivalent to {@link #addFirst}. + * + * @param e the element to push + * @since 1.6 + */ + public void push(E e) { + addFirst(e); + } + ``` + - 删除元素 + ``` + /** + * Removes the element at the specified position in this list. Shifts any + * subsequent elements to the left (subtracts one from their indices). + * Returns the element that was removed from the list. + * + * @param index the index of the element to be removed + * @return the element previously at the specified position + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E remove(int index) { + checkElementIndex(index); + return unlink(node(index)); + } + /** + * Unlinks non-null node x. + */ + E unlink(Node x) { + // assert x != null; + final E element = x.item; + final Node next = x.next; + final Node prev = x.prev; + + if (prev == null) { + first = next; + } else { + prev.next = next; + x.prev = null; + } + + if (next == null) { + last = prev; + } else { + next.prev = prev; + x.next = null; + } + + x.item = null; + size--; + modCount++; + return element; + } + /** + * Removes the first occurrence of the specified element from this list, + * if it is present. If this list does not contain the element, it is + * unchanged. More formally, removes the element with the lowest index + * {@code i} such that + * (o==null ? get(i)==null : o.equals(get(i))) + * (if such an element exists). Returns {@code true} if this list + * contained the specified element (or equivalently, if this list + * changed as a result of the call). + * + * @param o element to be removed from this list, if present + * @return {@code true} if this list contained the specified element + */ + public boolean remove(Object o) { + if (o == null) { + for (Node x = first; x != null; x = x.next) { + if (x.item == null) { + unlink(x); + return true; + } + } + } else { + for (Node x = first; x != null; x = x.next) { + if (o.equals(x.item)) { + unlink(x); + return true; + } + } + } + return false; + } + /** + * Removes and returns the first element from this list. + * + * @return the first element from this list + * @throws NoSuchElementException if this list is empty + */ + public E removeFirst() { + final Node f = first; + if (f == null) + throw new NoSuchElementException(); + return unlinkFirst(f); + } + + /** + * Removes and returns the last element from this list. + * + * @return the last element from this list + * @throws NoSuchElementException if this list is empty + */ + public E removeLast() { + final Node l = last; + if (l == null) + throw new NoSuchElementException(); + return unlinkLast(l); + } + /** + * Retrieves and removes the first element of this list, + * or returns {@code null} if this list is empty. + * + * @return the first element of this list, or {@code null} if + * this list is empty + * @since 1.6 + */ + public E pollFirst() { + final Node f = first; + return (f == null) ? null : unlinkFirst(f); + } + + /** + * Retrieves and removes the last element of this list, + * or returns {@code null} if this list is empty. + * + * @return the last element of this list, or {@code null} if + * this list is empty + * @since 1.6 + */ + public E pollLast() { + final Node l = last; + return (l == null) ? null : unlinkLast(l); + } + /** + * Unlinks non-null first node f. + */ + private E unlinkFirst(Node f) { + // assert f == first && f != null; + final E element = f.item; + final Node next = f.next; + f.item = null; + f.next = null; // help GC + first = next; + if (next == null) + last = null; + else + next.prev = null; + size--; + modCount++; + return element; + } + + /** + * Unlinks non-null last node l. + */ + private E unlinkLast(Node l) { + // assert l == last && l != null; + final E element = l.item; + final Node prev = l.prev; + l.item = null; + l.prev = null; // help GC + last = prev; + if (prev == null) + first = null; + else + prev.next = null; + size--; + modCount++; + return element; + } + /** + * Pops an element from the stack represented by this list. In other + * words, removes and returns the first element of this list. + * + *

This method is equivalent to {@link #removeFirst()}. + * + * @return the element at the front of this list (which is the top + * of the stack represented by this list) + * @throws NoSuchElementException if this list is empty + * @since 1.6 + */ + public E pop() { + return removeFirst(); + } + ``` + - get|set + ``` + // Positional Access Operations + + /** + * Returns the element at the specified position in this list. + * + * @param index index of the element to return + * @return the element at the specified position in this list + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E get(int index) { + checkElementIndex(index); + return node(index).item; + } + + /** + * Replaces the element at the specified position in this list with the + * specified element. + * + * @param index index of the element to replace + * @param element element to be stored at the specified position + * @return the element previously at the specified position + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E set(int index, E element) { + checkElementIndex(index); + Node x = node(index); + E oldVal = x.item; + x.item = element; + return oldVal; + } + /** + * Returns the first element in this list. + * + * @return the first element in this list + * @throws NoSuchElementException if this list is empty + */ + public E getFirst() { + final Node f = first; + if (f == null) + throw new NoSuchElementException(); + return f.item; + } + + /** + * Returns the last element in this list. + * + * @return the last element in this list + * @throws NoSuchElementException if this list is empty + */ + public E getLast() { + final Node l = last; + if (l == null) + throw new NoSuchElementException(); + return l.item; + } + /** + * Retrieves, but does not remove, the head (first element) of this list. + * + * @return the head of this list, or {@code null} if this list is empty + * @since 1.5 + */ + public E peek() { + final Node f = first; + return (f == null) ? null : f.item; + } + /** + * Retrieves, but does not remove, the first element of this list, + * or returns {@code null} if this list is empty. + * + * @return the first element of this list, or {@code null} + * if this list is empty + * @since 1.6 + */ + public E peekFirst() { + final Node f = first; + return (f == null) ? null : f.item; + } + + /** + * Retrieves, but does not remove, the last element of this list, + * or returns {@code null} if this list is empty. + * + * @return the last element of this list, or {@code null} + * if this list is empty + * @since 1.6 + */ + public E peekLast() { + final Node l = last; + return (l == null) ? null : l.item; + } + ``` + + +### 跳表 +1. 在linkedList的基础上进行了优化,添加了索引指针层,即升维,空间换时间。 +2. 跳表增加的索引级数为logn个 +3. 随机访问的时间复杂度O(logn) +4. 实际场景下使用跳表,会因为元素增加/删除导致其索引不工整,造成维护成本较高,每增加/删除一次导致索引都要更新一遍。 +- redis跳表的总结资料:极客时间专栏 王争老师的 数据结构与算法之美 第17讲。知乎:https://www.zhihu.com/question/20202931 + +## 第四课 栈,队列,优先队列,双端队列 +### 栈 +1. 先进后出。添加,删除时间复杂度为O(1),查询时间复杂度O(n) +2. api + 1. peek() 查看栈顶元素 + 2. pop() 删除并返回栈顶元素 + 3. push(E e) 添加至栈顶 +3. Stack源码学习总结 + - 特性 + - 继承Vector + - 线程安全 + - 内部由数组实现 + - search()时间复杂度为O(1) + - 主成员变量(继承Vector) + ``` + /** + * The array buffer into which the components of the vector are + * stored. The capacity of the vector is the length of this array buffer, + * and is at least large enough to contain all the vector's elements. + * + *

Any array elements following the last element in the Vector are null. + * + * @serial + */ + protected Object[] elementData; + + /** + * The number of valid components in this {@code Vector} object. + * Components {@code elementData[0]} through + * {@code elementData[elementCount-1]} are the actual items. + * + * @serial + */ + protected int elementCount; + + /** + * The amount by which the capacity of the vector is automatically + * incremented when its size becomes greater than its capacity. If + * the capacity increment is less than or equal to zero, the capacity + * of the vector is doubled each time it needs to grow. + * + * @serial + */ + protected int capacityIncrement; + ``` + - 构造器 + ``` + /** + * Creates an empty Stack. + */ + public Stack() { + } + /** + * Constructs an empty vector with the specified initial capacity and + * capacity increment. + * + * @param initialCapacity the initial capacity of the vector + * @param capacityIncrement the amount by which the capacity is + * increased when the vector overflows + * @throws IllegalArgumentException if the specified initial capacity + * is negative + */ + public Vector(int initialCapacity, int capacityIncrement) { + super(); + if (initialCapacity < 0) + throw new IllegalArgumentException("Illegal Capacity: "+ + initialCapacity); + this.elementData = new Object[initialCapacity]; + this.capacityIncrement = capacityIncrement; + } + /** + * Constructs an empty vector with the specified initial capacity and + * with its capacity increment equal to zero. + * + * @param initialCapacity the initial capacity of the vector + * @throws IllegalArgumentException if the specified initial capacity + * is negative + */ + public Vector(int initialCapacity) { + this(initialCapacity, 0); + } + ``` + - 添加元素 + ``` + /** + * Pushes an item onto the top of this stack. This has exactly + * the same effect as: + *

+     * addElement(item)
+ * + * @param item the item to be pushed onto this stack. + * @return the item argument. + * @see java.util.Vector#addElement + */ + public E push(E item) { + addElement(item); + + return item; + } + //Vector中的addElement方法 + /** + * Adds the specified component to the end of this vector, + * increasing its size by one. The capacity of this vector is + * increased if its size becomes greater than its capacity. + * + *

This method is identical in functionality to the + * {@link #add(Object) add(E)} + * method (which is part of the {@link List} interface). + * + * @param obj the component to be added + */ + public synchronized void addElement(E obj) { + modCount++; + ensureCapacityHelper(elementCount + 1); + elementData[elementCount++] = obj; + } + ``` + - 删除元素 + ``` + /** + * Removes the object at the top of this stack and returns that + * object as the value of this function. + * + * @return The object at the top of this stack (the last item + * of the Vector object). + * @throws EmptyStackException if this stack is empty. + */ + public synchronized E pop() { + E obj; + int len = size(); + + obj = peek(); + removeElementAt(len - 1); + + return obj; + } + //Vector 中的移除元素方法 + /** + * Deletes the component at the specified index. Each component in + * this vector with an index greater or equal to the specified + * {@code index} is shifted downward to have an index one + * smaller than the value it had previously. The size of this vector + * is decreased by {@code 1}. + * + *

The index must be a value greater than or equal to {@code 0} + * and less than the current size of the vector. + * + *

This method is identical in functionality to the {@link #remove(int)} + * method (which is part of the {@link List} interface). Note that the + * {@code remove} method returns the old value that was stored at the + * specified position. + * + * @param index the index of the object to remove + * @throws ArrayIndexOutOfBoundsException if the index is out of range + * ({@code index < 0 || index >= size()}) + */ + public synchronized void removeElementAt(int index) { + modCount++; + if (index >= elementCount) { + throw new ArrayIndexOutOfBoundsException(index + " >= " + + elementCount); + } + else if (index < 0) { + throw new ArrayIndexOutOfBoundsException(index); + } + int j = elementCount - index - 1; + if (j > 0) { + System.arraycopy(elementData, index + 1, elementData, index, j); + } + elementCount--; + elementData[elementCount] = null; /* to let gc do its work */ + } + ``` + - 查看|查找 + ``` + /** + * Looks at the object at the top of this stack without removing it + * from the stack. + * + * @return the object at the top of this stack (the last item + * of the Vector object). + * @throws EmptyStackException if this stack is empty. + */ + public synchronized E peek() { + int len = size(); + + if (len == 0) + throw new EmptyStackException(); + return elementAt(len - 1); + } + //下面两方法为Vector中实现的 + /** + * Returns the component at the specified index. + * + *

This method is identical in functionality to the {@link #get(int)} + * method (which is part of the {@link List} interface). + * + * @param index an index into this vector + * @return the component at the specified index + * @throws ArrayIndexOutOfBoundsException if the index is out of range + * ({@code index < 0 || index >= size()}) + */ + public synchronized E elementAt(int index) { + if (index >= elementCount) { + throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount); + } + + return elementData(index); + } + /** + * Returns the 1-based position where an object is on this stack. + * If the object o occurs as an item in this stack, this + * method returns the distance from the top of the stack of the + * occurrence nearest the top of the stack; the topmost item on the + * stack is considered to be at distance 1. The equals + * method is used to compare o to the + * items in this stack. + * + * @param o the desired object. + * @return the 1-based position from the top of the stack where + * the object is located; the return value -1 + * indicates that the object is not on the stack. + */ + public synchronized int search(Object o) { + int i = lastIndexOf(o); + + if (i >= 0) { + return size() - i; + } + return -1; + } + //下面两方法是Vector中方法 + /** + * Returns the index of the last occurrence of the specified element + * in this vector, or -1 if this vector does not contain the element. + * More formally, returns the highest index {@code i} such that + * (o==null ? get(i)==null : o.equals(get(i))), + * or -1 if there is no such index. + * + * @param o element to search for + * @return the index of the last occurrence of the specified element in + * this vector, or -1 if this vector does not contain the element + */ + public synchronized int lastIndexOf(Object o) { + return lastIndexOf(o, elementCount-1); + } + + /** + * Returns the index of the last occurrence of the specified element in + * this vector, searching backwards from {@code index}, or returns -1 if + * the element is not found. + * More formally, returns the highest index {@code i} such that + * (i <= index && (o==null ? get(i)==null : o.equals(get(i)))), + * or -1 if there is no such index. + * + * @param o element to search for + * @param index index to start searching backwards from + * @return the index of the last occurrence of the element at position + * less than or equal to {@code index} in this vector; + * -1 if the element is not found. + * @throws IndexOutOfBoundsException if the specified index is greater + * than or equal to the current size of this vector + */ + public synchronized int lastIndexOf(Object o, int index) { + if (index >= elementCount) + throw new IndexOutOfBoundsException(index + " >= "+ elementCount); + + if (o == null) { + for (int i = index; i >= 0; i--) + if (elementData[i]==null) + return i; + } else { + for (int i = index; i >= 0; i--) + if (o.equals(elementData[i])) + return i; + } + return -1; + } + ``` +### 队列 +1. 先进先出。添加、删除时间复杂度O(1),查找时间复杂度为O(n) +2. api + 1. 添加 add() --遇错抛出 offer --遇错返回特殊值 + 2. 删除并返回 remove --遇错抛出 poll --遇错返回特殊值 + 3. 查看 element --遇错抛出 peek --遇错返回特殊值 +3.Queue 源码分析 + - Queue接口 + ``` + /** + * Inserts the specified element into this queue if it is possible to do so + * immediately without violating capacity restrictions, returning + * {@code true} upon success and throwing an {@code IllegalStateException} + * if no space is currently available. + * + * @param e the element to add + * @return {@code true} (as specified by {@link Collection#add}) + * @throws IllegalStateException if the element cannot be added at this + * time due to capacity restrictions + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null and + * this queue does not permit null elements + * @throws IllegalArgumentException if some property of this element + * prevents it from being added to this queue + */ + boolean add(E e); + + /** + * Inserts the specified element into this queue if it is possible to do + * so immediately without violating capacity restrictions. + * When using a capacity-restricted queue, this method is generally + * preferable to {@link #add}, which can fail to insert an element only + * by throwing an exception. + * + * @param e the element to add + * @return {@code true} if the element was added to this queue, else + * {@code false} + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null and + * this queue does not permit null elements + * @throws IllegalArgumentException if some property of this element + * prevents it from being added to this queue + */ + boolean offer(E e); + + /** + * Retrieves and removes the head of this queue. This method differs + * from {@link #poll poll} only in that it throws an exception if this + * queue is empty. + * + * @return the head of this queue + * @throws NoSuchElementException if this queue is empty + */ + E remove(); + + /** + * Retrieves and removes the head of this queue, + * or returns {@code null} if this queue is empty. + * + * @return the head of this queue, or {@code null} if this queue is empty + */ + E poll(); + + /** + * Retrieves, but does not remove, the head of this queue. This method + * differs from {@link #peek peek} only in that it throws an exception + * if this queue is empty. + * + * @return the head of this queue + * @throws NoSuchElementException if this queue is empty + */ + E element(); + + /** + * Retrieves, but does not remove, the head of this queue, + * or returns {@code null} if this queue is empty. + * + * @return the head of this queue, or {@code null} if this queue is empty + */ + E peek(); + ``` +### 优先队列 +1. 特性 + 1. 插入操作O(1) + 2. 增加删除操作:O(logN),因为需要按照元素的优先级取出 + 3. 底层具体实现的数据结构较为多样和复杂,可以用heap/bst/avl/treap + 4. Java中对队列数据结构的工程实现采用了PriorityQueue +2. PriorityQueue 源码分析 + - PriorityQueue主成员变量 + ``` + private static final int DEFAULT_INITIAL_CAPACITY = 11; + + /** + * Priority queue represented as a balanced binary heap: the two + * children of queue[n] are queue[2*n+1] and queue[2*(n+1)]. The + * priority queue is ordered by comparator, or by the elements' + * natural ordering, if comparator is null: For each node n in the + * heap and each descendant d of n, n <= d. The element with the + * lowest value is in queue[0], assuming the queue is nonempty. + */ + transient Object[] queue; // non-private to simplify nested class access + + /** + * The number of elements in the priority queue. + */ + private int size = 0; + + /** + * The comparator, or null if priority queue uses elements' + * natural ordering. + */ + private final Comparator comparator; + + /** + * The number of times this priority queue has been + * structurally modified. See AbstractList for gory details. + */ + transient int modCount = 0; // non-private to simplify nested class access + ``` + - PriorityQueue 构造函数 + ``` + /** + * Creates a {@code PriorityQueue} with the default initial + * capacity (11) that orders its elements according to their + * {@linkplain Comparable natural ordering}. + */ + public PriorityQueue() { + this(DEFAULT_INITIAL_CAPACITY, null); + } + + /** + * Creates a {@code PriorityQueue} with the specified initial + * capacity that orders its elements according to their + * {@linkplain Comparable natural ordering}. + * + * @param initialCapacity the initial capacity for this priority queue + * @throws IllegalArgumentException if {@code initialCapacity} is less + * than 1 + */ + public PriorityQueue(int initialCapacity) { + this(initialCapacity, null); + } + + /** + * Creates a {@code PriorityQueue} with the default initial capacity and + * whose elements are ordered according to the specified comparator. + * + * @param comparator the comparator that will be used to order this + * priority queue. If {@code null}, the {@linkplain Comparable + * natural ordering} of the elements will be used. + * @since 1.8 + */ + public PriorityQueue(Comparator comparator) { + this(DEFAULT_INITIAL_CAPACITY, comparator); + } + + /** + * Creates a {@code PriorityQueue} with the specified initial capacity + * that orders its elements according to the specified comparator. + * + * @param initialCapacity the initial capacity for this priority queue + * @param comparator the comparator that will be used to order this + * priority queue. If {@code null}, the {@linkplain Comparable + * natural ordering} of the elements will be used. + * @throws IllegalArgumentException if {@code initialCapacity} is + * less than 1 + */ + public PriorityQueue(int initialCapacity, + Comparator comparator) { + // Note: This restriction of at least one is not actually needed, + // but continues for 1.5 compatibility + if (initialCapacity < 1) + throw new IllegalArgumentException(); + this.queue = new Object[initialCapacity]; + this.comparator = comparator; + } + + /** + * Creates a {@code PriorityQueue} containing the elements in the + * specified collection. If the specified collection is an instance of + * a {@link SortedSet} or is another {@code PriorityQueue}, this + * priority queue will be ordered according to the same ordering. + * Otherwise, this priority queue will be ordered according to the + * {@linkplain Comparable natural ordering} of its elements. + * + * @param c the collection whose elements are to be placed + * into this priority queue + * @throws ClassCastException if elements of the specified collection + * cannot be compared to one another according to the priority + * queue's ordering + * @throws NullPointerException if the specified collection or any + * of its elements are null + */ + @SuppressWarnings("unchecked") + public PriorityQueue(Collection c) { + if (c instanceof SortedSet) { + SortedSet ss = (SortedSet) c; + this.comparator = (Comparator) ss.comparator(); + initElementsFromCollection(ss); + } + else if (c instanceof PriorityQueue) { + PriorityQueue pq = (PriorityQueue) c; + this.comparator = (Comparator) pq.comparator(); + initFromPriorityQueue(pq); + } + else { + this.comparator = null; + initFromCollection(c); + } + } + + /** + * Creates a {@code PriorityQueue} containing the elements in the + * specified priority queue. This priority queue will be + * ordered according to the same ordering as the given priority + * queue. + * + * @param c the priority queue whose elements are to be placed + * into this priority queue + * @throws ClassCastException if elements of {@code c} cannot be + * compared to one another according to {@code c}'s + * ordering + * @throws NullPointerException if the specified priority queue or any + * of its elements are null + */ + @SuppressWarnings("unchecked") + public PriorityQueue(PriorityQueue c) { + this.comparator = (Comparator) c.comparator(); + initFromPriorityQueue(c); + } + + /** + * Creates a {@code PriorityQueue} containing the elements in the + * specified sorted set. This priority queue will be ordered + * according to the same ordering as the given sorted set. + * + * @param c the sorted set whose elements are to be placed + * into this priority queue + * @throws ClassCastException if elements of the specified sorted + * set cannot be compared to one another according to the + * sorted set's ordering + * @throws NullPointerException if the specified sorted set or any + * of its elements are null + */ + @SuppressWarnings("unchecked") + public PriorityQueue(SortedSet c) { + this.comparator = (Comparator) c.comparator(); + initElementsFromCollection(c); + } + ``` + - 添加 + ``` + /** + * Inserts the specified element into this priority queue. + * + * @return {@code true} (as specified by {@link Collection#add}) + * @throws ClassCastException if the specified element cannot be + * compared with elements currently in this priority queue + * according to the priority queue's ordering + * @throws NullPointerException if the specified element is null + */ + public boolean add(E e) { + return offer(e); + } + + /** + * Inserts the specified element into this priority queue. + * + * @return {@code true} (as specified by {@link Queue#offer}) + * @throws ClassCastException if the specified element cannot be + * compared with elements currently in this priority queue + * according to the priority queue's ordering + * @throws NullPointerException if the specified element is null + */ + public boolean offer(E e) { + if (e == null) + throw new NullPointerException(); + modCount++; + int i = size; + if (i >= queue.length) + grow(i + 1); + size = i + 1; + if (i == 0) + queue[0] = e; + else + siftUp(i, e); + return true; + } + /** + * Inserts item x at position k, maintaining heap invariant by + * promoting x up the tree until it is greater than or equal to + * its parent, or is the root. + * + * To simplify and speed up coercions and comparisons. the + * Comparable and Comparator versions are separated into different + * methods that are otherwise identical. (Similarly for siftDown.) + * + * @param k the position to fill + * @param x the item to insert + */ + private void siftUp(int k, E x) { + if (comparator != null) + siftUpUsingComparator(k, x); + else + siftUpComparable(k, x); + } + + @SuppressWarnings("unchecked") + private void siftUpComparable(int k, E x) { + Comparable key = (Comparable) x; + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = queue[parent]; + if (key.compareTo((E) e) >= 0) + break; + queue[k] = e; + k = parent; + } + queue[k] = key; + } + + @SuppressWarnings("unchecked") + private void siftUpUsingComparator(int k, E x) { + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = queue[parent]; + if (comparator.compare(x, (E) e) >= 0) + break; + queue[k] = e; + k = parent; + } + queue[k] = x; + } + ``` + - 删除 + ``` + /** + * Removes a single instance of the specified element from this queue, + * if it is present. More formally, removes an element {@code e} such + * that {@code o.equals(e)}, if this queue contains one or more such + * elements. Returns {@code true} if and only if this queue contained + * the specified element (or equivalently, if this queue changed as a + * result of the call). + * + * @param o element to be removed from this queue, if present + * @return {@code true} if this queue changed as a result of the call + */ + public boolean remove(Object o) { + int i = indexOf(o); + if (i == -1) + return false; + else { + removeAt(i); + return true; + } + } + private int indexOf(Object o) { + if (o != null) { + for (int i = 0; i < size; i++) + if (o.equals(queue[i])) + return i; + } + return -1; + } + + /** + * Version of remove using reference equality, not equals. + * Needed by iterator.remove. + * + * @param o element to be removed from this queue, if present + * @return {@code true} if removed + */ + boolean removeEq(Object o) { + for (int i = 0; i < size; i++) { + if (o == queue[i]) { + removeAt(i); + return true; + } + } + return false; + } + /** + * Removes the ith element from queue. + * + * Normally this method leaves the elements at up to i-1, + * inclusive, untouched. Under these circumstances, it returns + * null. Occasionally, in order to maintain the heap invariant, + * it must swap a later element of the list with one earlier than + * i. Under these circumstances, this method returns the element + * that was previously at the end of the list and is now at some + * position before i. This fact is used by iterator.remove so as to + * avoid missing traversing elements. + */ + @SuppressWarnings("unchecked") + private E removeAt(int i) { + // assert i >= 0 && i < size; + modCount++; + int s = --size; + if (s == i) // removed last element + queue[i] = null; + else { + E moved = (E) queue[s]; + queue[s] = null; + siftDown(i, moved); + if (queue[i] == moved) { + siftUp(i, moved); + if (queue[i] != moved) + return moved; + } + } + return null; + } + public E poll() { + if (size == 0) + return null; + int s = --size; + modCount++; + E result = (E) queue[0]; + E x = (E) queue[s]; + queue[s] = null; + if (s != 0) + siftDown(0, x); + return result; + } + /** + * Inserts item x at position k, maintaining heap invariant by + * demoting x down the tree repeatedly until it is less than or + * equal to its children or is a leaf. + * + * @param k the position to fill + * @param x the item to insert + */ + private void siftDown(int k, E x) { + if (comparator != null) + siftDownUsingComparator(k, x); + else + siftDownComparable(k, x); + } + + @SuppressWarnings("unchecked") + private void siftDownComparable(int k, E x) { + Comparable key = (Comparable)x; + int half = size >>> 1; // loop while a non-leaf + while (k < half) { + int child = (k << 1) + 1; // assume left child is least + Object c = queue[child]; + int right = child + 1; + if (right < size && + ((Comparable) c).compareTo((E) queue[right]) > 0) + c = queue[child = right]; + if (key.compareTo((E) c) <= 0) + break; + queue[k] = c; + k = child; + } + queue[k] = key; + } + + @SuppressWarnings("unchecked") + private void siftDownUsingComparator(int k, E x) { + int half = size >>> 1; + while (k < half) { + int child = (k << 1) + 1; + Object c = queue[child]; + int right = child + 1; + if (right < size && + comparator.compare((E) c, (E) queue[right]) > 0) + c = queue[child = right]; + if (comparator.compare(x, (E) c) <= 0) + break; + queue[k] = c; + k = child; + } + queue[k] = x; + } + ``` + - 获取 + ``` + public E peek() { + return (size == 0) ? null : (E) queue[0]; + } + ``` +### 双端队列 +1. 特性 + 1. 在工程应用中一般不直接使用queue或stack,而是采用deque双端队列 + 2. 插入删除时间复杂度O(1),查询时间复杂度O(n) + 3. 可以在首部push/pop,也可以在尾部push/pop,LinkedList就实现了Deque的接口 +2. Deque 源码总结 + - Deque接口(注意 throws) + ``` + /** + * 头部插入. + * + * @param e the element to add + * @throws IllegalStateException if the element cannot be added at this + * time due to capacity restrictions + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + void addFirst(E e); + + /** + * 尾部插入. + * + *

This method is equivalent to {@link #add}. + * + * @param e the element to add + * @throws IllegalStateException if the element cannot be added at this + * time due to capacity restrictions + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + void addLast(E e); + + /** + * 头部插入. + * + * @param e the element to add + * @return {@code true} if the element was added to this deque, else + * {@code false} + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + boolean offerFirst(E e); + + /** + * 尾部插入. + * + * @param e the element to add + * @return {@code true} if the element was added to this deque, else + * {@code false} + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + boolean offerLast(E e); + + /** + * 移除第一个元素. + * + * @return the head of this deque + * @throws NoSuchElementException if this deque is empty + */ + E removeFirst(); + + /** + * 移除最后一个元素. + * + * @return the tail of this deque + * @throws NoSuchElementException if this deque is empty + */ + E removeLast(); + + /** + * 移除第一个元素. + * + * @return the head of this deque, or {@code null} if this deque is empty + */ + E pollFirst(); + + /** + * 移除最后一个元素. + * + * @return the tail of this deque, or {@code null} if this deque is empty + */ + E pollLast(); + + /** + * 获取第一个元素. + * + * @return the head of this deque + * @throws NoSuchElementException if this deque is empty + */ + E getFirst(); + + /** + * 获取最后一个元素. + * + * @return the tail of this deque + * @throws NoSuchElementException if this deque is empty + */ + E getLast(); + + /** + * 获取第一个元素. + * + * @return the head of this deque, or {@code null} if this deque is empty + */ + E peekFirst(); + + /** + * 获取最后一个元素. + * + * @return the tail of this deque, or {@code null} if this deque is empty + */ + E peekLast(); + + /** + * Removes the first occurrence of the specified element from this deque. + * If the deque does not contain the element, it is unchanged. + * More formally, removes the first element {@code e} such that + * (o==null ? e==null : o.equals(e)) + * (if such an element exists). + * Returns {@code true} if this deque contained the specified element + * (or equivalently, if this deque changed as a result of the call). + * + * @param o element to be removed from this deque, if present + * @return {@code true} if an element was removed as a result of this call + * @throws ClassCastException if the class of the specified element + * is incompatible with this deque + * (optional) + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * (optional) + */ + boolean removeFirstOccurrence(Object o); + + /** + * Removes the last occurrence of the specified element from this deque. + * If the deque does not contain the element, it is unchanged. + * More formally, removes the last element {@code e} such that + * (o==null ? e==null : o.equals(e)) + * (if such an element exists). + * Returns {@code true} if this deque contained the specified element + * (or equivalently, if this deque changed as a result of the call). + * + * @param o element to be removed from this deque, if present + * @return {@code true} if an element was removed as a result of this call + * @throws ClassCastException if the class of the specified element + * is incompatible with this deque + * (optional) + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * (optional) + */ + boolean removeLastOccurrence(Object o); + + // *** Queue methods *** + + /** + * Inserts the specified element into the queue represented by this deque + * (in other words, at the tail of this deque) if it is possible to do so + * immediately without violating capacity restrictions, returning + * {@code true} upon success and throwing an + * {@code IllegalStateException} if no space is currently available. + * When using a capacity-restricted deque, it is generally preferable to + * use {@link #offer(Object) offer}. + * + *

This method is equivalent to {@link #addLast}. + * + * @param e the element to add + * @return {@code true} (as specified by {@link Collection#add}) + * @throws IllegalStateException if the element cannot be added at this + * time due to capacity restrictions + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + boolean add(E e); + + /** + * Inserts the specified element into the queue represented by this deque + * (in other words, at the tail of this deque) if it is possible to do so + * immediately without violating capacity restrictions, returning + * {@code true} upon success and {@code false} if no space is currently + * available. When using a capacity-restricted deque, this method is + * generally preferable to the {@link #add} method, which can fail to + * insert an element only by throwing an exception. + * + *

This method is equivalent to {@link #offerLast}. + * + * @param e the element to add + * @return {@code true} if the element was added to this deque, else + * {@code false} + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + boolean offer(E e); + + /** + * Retrieves and removes the head of the queue represented by this deque + * (in other words, the first element of this deque). + * This method differs from {@link #poll poll} only in that it throws an + * exception if this deque is empty. + * + *

This method is equivalent to {@link #removeFirst()}. + * + * @return the head of the queue represented by this deque + * @throws NoSuchElementException if this deque is empty + */ + E remove(); + + /** + * Retrieves and removes the head of the queue represented by this deque + * (in other words, the first element of this deque), or returns + * {@code null} if this deque is empty. + * + *

This method is equivalent to {@link #pollFirst()}. + * + * @return the first element of this deque, or {@code null} if + * this deque is empty + */ + E poll(); + + /** + * Retrieves, but does not remove, the head of the queue represented by + * this deque (in other words, the first element of this deque). + * This method differs from {@link #peek peek} only in that it throws an + * exception if this deque is empty. + * + *

This method is equivalent to {@link #getFirst()}. + * + * @return the head of the queue represented by this deque + * @throws NoSuchElementException if this deque is empty + */ + E element(); + + /** + * Retrieves, but does not remove, the head of the queue represented by + * this deque (in other words, the first element of this deque), or + * returns {@code null} if this deque is empty. + * + *

This method is equivalent to {@link #peekFirst()}. + * + * @return the head of the queue represented by this deque, or + * {@code null} if this deque is empty + */ + E peek(); + + + // *** Stack methods *** + + /** + * Pushes an element onto the stack represented by this deque (in other + * words, at the head of this deque) if it is possible to do so + * immediately without violating capacity restrictions, throwing an + * {@code IllegalStateException} if no space is currently available. + * + *

This method is equivalent to {@link #addFirst}. + * + * @param e the element to push + * @throws IllegalStateException if the element cannot be added at this + * time due to capacity restrictions + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + void push(E e); + + /** + * Pops an element from the stack represented by this deque. In other + * words, removes and returns the first element of this deque. + * + *

This method is equivalent to {@link #removeFirst()}. + * + * @return the element at the front of this deque (which is the top + * of the stack represented by this deque) + * @throws NoSuchElementException if this deque is empty + */ + E pop(); + + + // *** Collection methods *** + + /** + * Removes the first occurrence of the specified element from this deque. + * If the deque does not contain the element, it is unchanged. + * More formally, removes the first element {@code e} such that + * (o==null ? e==null : o.equals(e)) + * (if such an element exists). + * Returns {@code true} if this deque contained the specified element + * (or equivalently, if this deque changed as a result of the call). + * + *

This method is equivalent to {@link #removeFirstOccurrence(Object)}. + * + * @param o element to be removed from this deque, if present + * @return {@code true} if an element was removed as a result of this call + * @throws ClassCastException if the class of the specified element + * is incompatible with this deque + * (optional) + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * (optional) + */ + boolean remove(Object o); + + /** + * Returns {@code true} if this deque contains the specified element. + * More formally, returns {@code true} if and only if this deque contains + * at least one element {@code e} such that + * (o==null ? e==null : o.equals(e)). + * + * @param o element whose presence in this deque is to be tested + * @return {@code true} if this deque contains the specified element + * @throws ClassCastException if the type of the specified element + * is incompatible with this deque + * (optional) + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * (optional) + */ + boolean contains(Object o); + + /** + * Returns the number of elements in this deque. + * + * @return the number of elements in this deque + */ + public int size(); + + /** + * Returns an iterator over the elements in this deque in proper sequence. + * The elements will be returned in order from first (head) to last (tail). + * + * @return an iterator over the elements in this deque in proper sequence + */ + Iterator iterator(); + + /** + * Returns an iterator over the elements in this deque in reverse + * sequential order. The elements will be returned in order from + * last (tail) to first (head). + * + * @return an iterator over the elements in this deque in reverse + * sequence + */ + Iterator descendingIterator(); + ``` + - Deque实现类 LinkedList 详见 第3课 第二节 链表 章节 + + - 课后作业改写 + ``` + Deque deque = new LinkedList<>(); + deque.addLast("a"); + deque.addLast("b"); + deque.addLast("c"); + System.out.println(deque); + String last = deque.getLast(); + System.out.println(last);//c + System.out.println(deque);//a b c + String first = deque.getFirst(); + System.out.println(first);//a + while (deque.size() > 0) { + System.out.println(deque.removeLast()); + //c + //b + // a + } + System.out.println(deque);//[] + ``` +## 个人感悟 +- **时间问题**: 无论如何都要抽出时间,平均保证每天保证有1个小时。 +- **练习问题**: 结合自己情况,当天练的题、隔天练、隔四再练。 +- 团队问题: 团队的讨论的风气还未形成,一步一步来,咱们先形成相互提醒学习的"**叫醒**"服务。另居然还有组员还没报github帐号,我只能天天催一催。 diff --git a/Week 01/id_148/DequeChange.java b/Week 01/id_148/DequeChange.java new file mode 100644 index 000000000..0a23c19b1 --- /dev/null +++ b/Week 01/id_148/DequeChange.java @@ -0,0 +1,47 @@ +package com.ning.test; + +import org.junit.Test; + +import java.util.Deque; +import java.util.LinkedList; + +/** + * 描述: + * + * @author yu.shao@okcoin.com + * @create 2019-10-23 下午11:18 + */ +public class DequeChange { + + @Test + public void test() { + Deque deque = new LinkedList<>(); + + deque.addFirst("a"); + deque.addFirst("b"); + deque.addFirst("c"); + + System.out.println(deque); + + String str = deque.peek(); + System.out.println(str); + System.out.println(deque); + + while(deque.size() > 0) { + System.out.println(deque.pop()); + } + + System.out.println(deque); + + + deque.addLast("a"); + deque.addLast("b"); + deque.addLast("c"); + + System.out.println(deque); + + while(deque.size() > 0) { + System.out.println(deque.poll()); + } + } +} diff --git a/Week 01/id_148/LeetCode_1_148.java b/Week 01/id_148/LeetCode_1_148.java new file mode 100644 index 000000000..181b639a8 --- /dev/null +++ b/Week 01/id_148/LeetCode_1_148.java @@ -0,0 +1,36 @@ +package com.ning.test; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +/** + * 描述: + * + * @author shaoyu + * @create 2019-09-25 下午7:49 + */ +public class LeetCode_1_148 { + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(); + + for (int i = 0; i < nums.length; i++) { + if (map.containsKey(target - nums[i])) { + return new int[]{map.get(target - nums[i]), i}; + } + map.put(nums[i], i); + } + return null; + } + + @Test + public void test() { + int[] nums = {2,3,4,5,9,10,17}; + int[] arr = twoSum(nums, 19); + + for(int n : arr) { + System.out.println(n); + } + } +} diff --git a/Week 01/id_148/LeetCode_21_148.java b/Week 01/id_148/LeetCode_21_148.java new file mode 100644 index 000000000..4515bfcfd --- /dev/null +++ b/Week 01/id_148/LeetCode_21_148.java @@ -0,0 +1,86 @@ +package com.ning.test; + +/** + * 描述:头条面试的时候接触过 + * + * @create 2019-10-20 下午10:53 + */ +class ListNode21 { + int val; + ListNode21 next; + + ListNode21(int x) { + val = x; + } + + public ListNode21(int[] nums) { + if (nums == null || nums.length == 0) { + throw new IllegalArgumentException("arr can not be empty"); + } + this.val = nums[0]; + ListNode21 curr = this; + for (int i = 1; i < nums.length; i++) { + curr.next = new ListNode21(nums[i]); + curr = curr.next; + } + } + + @Override + public String toString() { + StringBuilder s = new StringBuilder(); + ListNode21 cur = this; + while (cur != null) { + s.append(cur.val + " -> "); + cur = cur.next; + } + s.append("NULL"); + return s.toString(); + } +} + +public class LeetCode_21_148 { + + /** + * + * @param l1 有序链表 + * @param l2 有序链表 + * @return 有序链表 + */ + public ListNode21 mergeTwoLists(ListNode21 l1, ListNode21 l2) { + ListNode21 dummyNode = new ListNode21(-1); + ListNode21 p1 = l1; + ListNode21 p2 = l2; + ListNode21 curNode = dummyNode; + while (p1 != null && p2 != null) { + if (p1.val < p2.val) { + curNode.next = p1; + p1 = p1.next; + } else { + curNode.next = p2; + p2 = p2.next; + } + curNode = curNode.next; + } + + if (p1 == null) { + curNode.next = p2; + } + if (p2 == null) { + curNode.next = p1; + } + return dummyNode.next; + } + + public static void main(String[] args) { + int[] nums1 = {1, 3, 5, 7}; + int[] nums2 = {2, 4, 6}; + + ListNode21 l1 = new ListNode21(nums1); + ListNode21 l2 = new ListNode21(nums2); + + LeetCode_21_148 leetCode_21_148 = new LeetCode_21_148(); + ListNode21 mergeTwoLists = leetCode_21_148.mergeTwoLists(l1, l2); + System.out.println(mergeTwoLists); + + } +} diff --git a/Week 01/id_148/LeetCode_66_148.java b/Week 01/id_148/LeetCode_66_148.java new file mode 100644 index 000000000..80269186b --- /dev/null +++ b/Week 01/id_148/LeetCode_66_148.java @@ -0,0 +1,29 @@ +package com.ning.test; + +import org.junit.Test; + +/** + * 描述: + * + * @author yu.shao@okcoin.com + * @create 2019-10-20 下午11:40 + */ +public class LeetCode_66_148 { + public int[] plusOne(int[] digits) { + int len = digits.length; + for(int i = len - 1; i >= 0; i--) { + digits[i]++; + digits[i] %= 10; + if(digits[i]!=0) + return digits; + } + digits = new int[len + 1]; + digits[0] = 1; + return digits; + } + + @Test + public void test () { + System.out.println((1003213 >> 2)% 10); + } +} diff --git a/Week 01/id_153/LeetCode_189_153.java b/Week 01/id_153/LeetCode_189_153.java new file mode 100644 index 000000000..ee287a101 --- /dev/null +++ b/Week 01/id_153/LeetCode_189_153.java @@ -0,0 +1,45 @@ + +/** + * LeetCode_189_153 + * + * @desc LeetCode 189 题 https://leetcode-cn.com/problems/rotate-array/ + */ + +public class LeetCode_189_153 { + + /** + * 暴力法求解,两层嵌套循环 每次只先移动一个元素 + * + * @param nums + * @param k + */ + public void rotate1(int[] nums, int k) { + for (int i = 0; i < k; i++) { + int last = nums[nums.length - 1]; + for (int j = 0; j < nums.length; j++) { + int temp = nums[j]; + nums[j] = last; + last = temp; + } + } + } + + /** + * 可以先算出每个元素移动 k 次之后,最终的位置,存到一个新数组 然后循环将元素一一复制过去即可。 最开始就想到的是这个办法,在推到计算公式时,把数组 + * nums 的长度看错了, 导致推导出来的公式是错的。 + * + * @param nums + * @param k + */ + public void rotate2(int[] nums, int k) { + int[] arr = new int[nums.length]; + for (int i = 0; i < arr.length; i++) { + int end = (i + k) % nums.length; + arr[end] = nums[i]; + } + + for (int i = 0; i < arr.length; i++) { + nums[i] = arr[i]; + } + } +} \ No newline at end of file diff --git a/Week 01/id_153/LeetCode_21_153.java b/Week 01/id_153/LeetCode_21_153.java new file mode 100644 index 000000000..a3d7c9052 --- /dev/null +++ b/Week 01/id_153/LeetCode_21_153.java @@ -0,0 +1,59 @@ +/** + * LeetCode_21_153 + * + * @desc LeetCode 21 题 https://leetcode-cn.com/problems/merge-two-sorted-lists/ + */ +public class LeetCode_21_153 { + + public class ListNode { + int val; + ListNode next; + + ListNode(int x) { + val = x; + } + } + + public ListNode mergeTwoLists1(ListNode l1, ListNode l2) { + if (l1 == null) { + return l2; + } else if (l2 == null) { + return l1; + } else if (l1.val < l2.val) { + l1.next = mergeTwoLists1(l1.next, l2); + return l1; + } else { + l2.next = mergeTwoLists1(l1, l2.next); + return l2; + } + } + + /** + * 维护一个新的链表,定义一个指针用于每一次比较指向下一个元素 + * + * @param l1 + * @param l2 + * @return + */ + public ListNode mergeTwoLists2(ListNode l1, ListNode l2) { + ListNode prevhead = new ListNode(-1); + ListNode prev = prevhead; + while (l1 != null && l2 != null) { + if (l1.val <= l2.val) { + // l1 小于等于 l2 的值,将 prev 指针指向 l1,把 l1 的后继结点指向 l1 + // 相当于 l1 往前挪一个元素 + prev.next = l1; + l1 = l1.next; + } else { + // l1 大于 l2,将 prev 指针指向 l2,l2 的后继结点指向 l2 + prev.next = l2; + l2 = l2.next; + } + // 指针 prev 向前走一次,开始下一次的比较 + prev = prev.next; + } + // 判断 l1 是否为空,将非空链表合并的链表尾部 + prev.next = l1 == null ? l2 : l1; + return prevhead.next; + } +} \ No newline at end of file diff --git a/Week 01/id_153/LeetCode_26_153.java b/Week 01/id_153/LeetCode_26_153.java new file mode 100644 index 000000000..9a522df22 --- /dev/null +++ b/Week 01/id_153/LeetCode_26_153.java @@ -0,0 +1,28 @@ +/** + * LeetCode_26_153 + * + * @desc LeetCode 26 题 + * https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/ + */ +public class LeetCode_26_153 { + + /** + * 注意审题,给定排序数组,删除重复元素 根据题意最后输出数组,可知不是真的删除元素, 而是将不同元素移到数组前面,返回有个多少个元素即可 + * + * 解法:快慢指针,将第一个元素与后面的元素比较,如果不同,将元素迁移, + * 同时指针指向这个元素,然后再开始往后面比较,依次类推,这样就把所有不同元素都挪到前边了 + * + * @param nums + * @return + */ + public int removeDuplicates(int[] nums) { + int i = 0; + for (int j = 1; j < nums.length; j++) { + if (nums[j] != nums[i]) { + i++; + nums[i] = nums[j]; + } + } + return i + 1; + } +} \ No newline at end of file diff --git a/Week 01/id_153/LeetCode_283_153.java b/Week 01/id_153/LeetCode_283_153.java new file mode 100644 index 000000000..2f6ed188a --- /dev/null +++ b/Week 01/id_153/LeetCode_283_153.java @@ -0,0 +1,26 @@ +/** + * LeetCode_283_153 + * + * @desc LeetCode 283 题 https://leetcode-cn.com/problems/move-zeroes/ + */ +public class LeetCode_283_153 { + + /** + * 将不为 0 的元素往前挪,然后后面的元素直接赋值为 0 即可 + * + * @param nums + */ + public void moveZeros(int[] nums) { + int countZero = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != 0) { + nums[countZero] = nums[i]; + countZero++; + } + } + + for (int i = countZero; i < nums.length; i++) { + nums[i] = 0; + } + } +} \ No newline at end of file diff --git a/Week 01/id_153/LeetCode_66_153.java b/Week 01/id_153/LeetCode_66_153.java new file mode 100644 index 000000000..b96b0eed4 --- /dev/null +++ b/Week 01/id_153/LeetCode_66_153.java @@ -0,0 +1,28 @@ +/** + * LeetCode_66_153 + * + * @desc LeetCode 66 题 https://leetcode-cn.com/problems/plus-one/ + */ +public class LeetCode_66_153 { + + /** + * 最后一位是 9 的话需要进位,前一位加 1 即可 + * + * @param digits + * @return + */ + public int[] plusOne(int[] digits) { + for (int i = digits.length - 1; i >= 0; i--) { + digits[i]++; + digits[i] = digits[i] % 10; + if (digits[i] != 0) { + return digits; + } + } + // 如果是 99、999 这种数组,可以直接初始化一个数组,首位加 1,其余默认为 0 + // 看了网友的题解,茅塞顿开,还有这种操作,我还在一直想怎么处理后面的加 1 操作 + digits = new int[digits.length + 1]; + digits[0] = 1; + return digits; + } +} \ No newline at end of file diff --git a/Week 01/id_153/NOTE.md b/Week 01/id_153/NOTE.md index a6321d6e2..e0ba798b5 100644 --- a/Week 01/id_153/NOTE.md +++ b/Week 01/id_153/NOTE.md @@ -1,4 +1,99 @@ # NOTE - +## 总结 +经过一周的的 leetcode 的做题训练,以前没有进行过像这种算法题的训练,作为一个计算机专业毕业的,上学的时候也没有进行过系统的训练,而是写业务代码多一点,学过 Android,做过 python,总是去写一些小项目,这个系统那个系统的,忽略了写代码的基本功。做了老师布置的题目,刚开始完全没有思路,有时候连题目的意思都没有理解清楚,操作的数组的方法,例如快慢指针,完全不知道。不过这几道题下来,慢慢的感觉到了里面的一些套路,还需自己多总结,多练习,自古深情留不住,唯有套路得人心。这周对快慢指针法影响比较深刻,需要多进行专项训练,熟练掌握这种方法。 +做题思路 +1. 先暴力法解一遍。 +2. 空间换时间,找一个中间变量,例如重新定义一个数组。 +3. 把掌握的其他方法套一遍,看能不能解。 +4. 想不出来就直接看答案。 + +目前工作中主要以 `Node.js` 为主,好多 `Java` 的语法都忘光了,老师课后布置的分析优先级队列的源码,还没有完全看懂,留个坑吧,后面再填。 + +## 数组 + +| 操作 | 时间复杂度 | +| ------- | :--------: | +| prepend | O(1) | +| append | O(1) | +| lookup | O(1) | +| insert | O(n) | +| delete | O(n) | + +数组,连续的存储空间,可以根据下标实现随机访问,在数组的前面和后面删除或增加元素不需要移动其他元素,所以时间复杂度是 `O(1)` + +## 链表 + +| 操作 | 时间复杂度 | +| ------- | :--------: | +| prepend | O(1) | +| append | O(1) | +| lookup | O(n) | +| insert | O(1) | +| delete | O(1) | + +链表应为在存储是不连续的地址空间存储,每个结点包括值和后继指针,表示下一个元素的位置。所以在删除,插入等操作的时间复杂度是 `O(1)`,不需要移动元素,但是因为元素的不连续存储,只能遍历整个链表才能查询到元素,所以查询时间复杂度是`O(1)` + +## 跳表 + +| 操作 | 时间复杂度 | 空间复杂度 | +| ------ | :--------: | :--------: | +| lookup | O(logn) | O(n) | + +为了给链表的查询加速,从而设计了跳表。 +思想:升维 + 以空间换时间 +在链表的基础上增加索引,可以增加多级索引。每一个结点的后继不一定只指向下一个,可以是指向下下个,跳着指向。第一级索引指向下下个,第二级索引指向下下下个,依次类推。 + +## 栈 + +| 操作 | 时间复杂度 | +| ------ | :--------: | +| lookup | O(n) | +| insert | O(1) | +| delete | O(1) | + +特点:先进后出 + +## 队列 +特点:先进先出 +双端队列 队头和队尾都可以进行入队和出队操作 +优先级队列 按照元素优先级进行出队 + +| 操作 | 时间复杂度 | +| ------ | :--------: | +| search | O(n) | +| insert | O(1) | +| delete | O(1) | + +## 用 `addFirst` 或`addLast`改写`Deque`代码 +```java +Deque deque = new LinkedList(); + +deque.addFirst("a"); +deque.addFirst("b"); +deque.addFirst("c"); + +String str = deque.peek(); +System.out.println(str); +System.out.println(deque); + +while (deque.size() > 0) { + System.out.println(deque.pop()); +} +System.out.println(deque); +``` + +## `Queue` 源码分析 +在 `Java`中`Queue`是接口形式存在的,继承自`Collection`,`LinkedList`实现了`Queue`接口中的方法。 + +| 操作 | 失败时抛出异常 | 失败时返回特殊值 | +| ---------------- | :------------: | :--------------: | +| 队列尾部添加元素 | add(E e) | offer(E e) | +| 队列头部删除元素 | remove() | poll() | +| 查看队列头部元素 | element() | peek() | + +## `PriorityQueue` 源码分析 +基于堆这种数据结构实现的,堆分为大根堆和小根堆,大根堆就是子节点小于根节点,小根堆就是子节点大于根节点。`Java` 实现的默认为我们创建了大小为 11 的数组,你也可以指定队列大小。 +### 插入队列 +`boolean add(E e)` 源码中 `add()` 方法调用的是 `offer()` 方法,首先插入元素为 `null`,会报空指针异常,然后`modCount++`,如果大小超过队列的长度,会进行扩容操作,否则大小加 1,如果队列为空,直接给第一个元素赋值,否则调用`siftUp()`,判断`comparator`是否为空,为空调用`siftUpComparable()`,不为空调用`siftUpUsingComparator`,这两个方法目前为止还没看懂,先留下坑,待填。 \ No newline at end of file diff --git a/Week 01/id_158/PlusOne.cs b/Week 01/id_158/PlusOne.cs new file mode 100644 index 000000000..006b22766 --- /dev/null +++ b/Week 01/id_158/PlusOne.cs @@ -0,0 +1,24 @@ +// [66] 加一 +public class Solution { + public int[] PlusOne(int[] digits) { + for (int i = digits.Length-1; i >=0; i--) + { + digits[i]++; + if(digits[i]<10) { + return digits; + } else{ + digits[i]=0; + } + } + if(digits[0]!=0){ + return digits; + } + int[] res = new int[digits.Length+1]; + res[0] = 1; + for (int i = 0; i < digits.Length; i++) + { + res[i+1] = digits[i]; + } + return res; + } +} \ No newline at end of file diff --git a/Week 01/id_158/RemoveDuplicates.cs b/Week 01/id_158/RemoveDuplicates.cs new file mode 100644 index 000000000..752d69498 --- /dev/null +++ b/Week 01/id_158/RemoveDuplicates.cs @@ -0,0 +1,19 @@ +/* + * @lc app=leetcode.cn id=26 lang=csharp + * + * [26] 删除排序数组中的重复项 + */ + +public class Solution { + public int RemoveDuplicates(int[] nums) { + if (nums.Length == 0) return 0; + int i = 0; + for (int j = 1; j < nums.Length; j++) + { + if(nums[j]!=nums[i]) { + nums[++i] =nums[j]; + } + } + return i+1; + } +} \ No newline at end of file diff --git a/Week 01/id_158/RotateArray.cs b/Week 01/id_158/RotateArray.cs new file mode 100644 index 000000000..af2379d1c --- /dev/null +++ b/Week 01/id_158/RotateArray.cs @@ -0,0 +1,22 @@ +/* + * @lc app=leetcode.cn id=189 lang=csharp + * + * [189] 旋转数组 + */ + + public class Solution { + public void Rotate(int[] nums, int k) { + k %= nums.Length; + Reverse(nums,0,nums.Length-1); + Reverse(nums,0,k-1); + Reverse(nums,k,nums.Length-1); + } + + private void Reverse(int[] nums,int start,int end){ + while(start dict = new Dictionary(); + for (int i = 0; i < nums.Length; i++) + { + int remainder = target - nums[i]; + if(dict.ContainsKey(remainder) && dict[remainder]!=i){ + return new int[2]{dict[remainder], i}; + } else { + if(!dict.ContainsKey(nums[i])) + dict.Add(nums[i],i); + } + } + return new int[0]; + } +} +// @lc code=end \ No newline at end of file diff --git a/Week 01/id_173/LeetCode_189_173.cpp b/Week 01/id_173/LeetCode_189_173.cpp new file mode 100644 index 000000000..63daa3a4c --- /dev/null +++ b/Week 01/id_173/LeetCode_189_173.cpp @@ -0,0 +1,21 @@ +/* + * 189. 旋转数组 + */ + +class Solution { +public: + void reverse(vector& nums, int start, int end) { + while(start < end) { + swap(nums[start++], nums[end--]); + } + } + + void rotate(vector& nums, int k) { + int size = nums.size(); + k = k%size; + + reverse(nums, 0, size-1); + reverse(nums, 0, k-1); + reverse(nums, k, size-1); + } +}; \ No newline at end of file diff --git a/Week 01/id_173/LeetCode_1_173.cpp b/Week 01/id_173/LeetCode_1_173.cpp new file mode 100644 index 000000000..effe5bd2d --- /dev/null +++ b/Week 01/id_173/LeetCode_1_173.cpp @@ -0,0 +1,22 @@ +/* + * 1. 两数之和 + */ + +class Solution { +public: + vector twoSum(vector& nums, int target) { + vector res; + unordered_map hashTable; + + for(int i=0; ival <= l2->val) { + prev->next = l1; + l1 = l1->next; + } + else { + prev->next = l2; + l2 = l2->next; + } + + prev = prev->next; + } + + prev->next = (l1==NULL)?l2:l1; + + return tempHead->next; + } +}; \ No newline at end of file diff --git a/Week 01/id_173/LeetCode_26_173.cpp b/Week 01/id_173/LeetCode_26_173.cpp new file mode 100644 index 000000000..39b2b46e6 --- /dev/null +++ b/Week 01/id_173/LeetCode_26_173.cpp @@ -0,0 +1,20 @@ +/* + * 26. 删除排序数组中的重复项 + */ + +class Solution { +public: + int removeDuplicates(vector& nums) { + if(nums.empty()) + return 0; + + int j = 0; + for(int i=1; i& nums) { + for(int i=0, j=0; i& height) { + int res=0; + int maxLeft=0, maxRight=0; + int left=0, right=height.size()-1; + + while(left < right) { + if(height[left] <= height[right]) { + (height[left] >= maxLeft) ? (maxLeft = height[left]) : (res += maxLeft-height[left]); + ++left; + } + else { + (height[right] >= maxRight) ? (maxRight = height[right]) : (res += maxRight-height[right]); + --right; + } + } + + return res; + } +}; \ No newline at end of file diff --git a/Week 01/id_173/LeetCode_641_173.cpp b/Week 01/id_173/LeetCode_641_173.cpp new file mode 100644 index 000000000..834182cf9 --- /dev/null +++ b/Week 01/id_173/LeetCode_641_173.cpp @@ -0,0 +1,77 @@ +/* + * 641. 设计循环双端队列 + */ + +class MyCircularDeque { +private: + int* data; + int capacity; + int front; + int rear; + +public: + /** Initialize your data structure here. Set the size of the deque to be k. */ + MyCircularDeque(int k) { + data = new int[k+1]; + capacity = k+1; + front = rear = 0; + } + + /** Adds an item at the front of Deque. Return true if the operation is successful. */ + bool insertFront(int value) { + if(isFull()) + return false; + front = (front-1+capacity)%capacity; + data[front] = value; + return true; + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + bool insertLast(int value) { + if(isFull()) + return false; + data[rear] = value; + rear = (rear+1)%capacity; + return true; + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + bool deleteFront() { + if(isEmpty()) + return false; + front = (front+1)%capacity; + return true; + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + bool deleteLast() { + if(isEmpty()) + return false; + rear = (rear-1+capacity)%capacity; + return true; + } + + /** Get the front item from the deque. */ + int getFront() { + if(isEmpty()) + return -1; + return data[front]; + } + + /** Get the last item from the deque. */ + int getRear() { + if(isEmpty()) + return -1; + return data[(rear-1+capacity)%capacity]; + } + + /** Checks whether the circular deque is empty or not. */ + bool isEmpty() { + return front==rear; + } + + /** Checks whether the circular deque is full or not. */ + bool isFull() { + return ((rear+1)%capacity)==front; + } +}; \ No newline at end of file diff --git a/Week 01/id_173/LeetCode_66_173.cpp b/Week 01/id_173/LeetCode_66_173.cpp new file mode 100644 index 000000000..a69ddaa64 --- /dev/null +++ b/Week 01/id_173/LeetCode_66_173.cpp @@ -0,0 +1,24 @@ +/* + * 66. 加一 + */ + +class Solution { +public: + vector plusOne(vector& digits) { + int i = digits.size()-1; + int carry = 0; + + do { + int number = digits[i]+1; + digits[i] = number%10; + carry = number/10; + i--; + }while(i>=0 && carry>0); + + if(carry>0) { + digits.insert(digits.begin(), 1); + } + + return digits; + } +}; \ No newline at end of file diff --git a/Week 01/id_173/LeetCode_88_173.cpp b/Week 01/id_173/LeetCode_88_173.cpp new file mode 100644 index 000000000..8c39385f8 --- /dev/null +++ b/Week 01/id_173/LeetCode_88_173.cpp @@ -0,0 +1,23 @@ +/* + * 88. 合并两个有序数组 + */ + +class Solution { +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + int i = m-1; + int j = n-1; + int k = m+n-1; + + while(i>=0 && j>=0) { + if(nums1[i] >= nums2[j]) + nums1[k--] = nums1[i--]; + else + nums1[k--] = nums2[j--]; + } + + while(j>=0) { + nums1[k--] = nums2[j--]; + } + } +}; \ No newline at end of file diff --git a/Week 01/id_173/NOTE.md b/Week 01/id_173/NOTE.md index a6321d6e2..ca4fc68b4 100644 --- a/Week 01/id_173/NOTE.md +++ b/Week 01/id_173/NOTE.md @@ -1,4 +1,39 @@ -# NOTE +### 1. 数组 +- 插入操作:O(n) +- 删除操作:O(n) +- 支持随机访问,时间复杂度为 O(1) +### 2. 链表 +- 插入操作:O(1) +- 删除操作:O(1) +- 不支持随机访问,时间复杂度为 O(n) + +### 3. 跳表 +- 在链表的基础上,通过添加多级索引以提高链表线性查找的效率,其中心思想就是利用空间来换取时间。 +- 跳表查询的时间复杂度为 O(logn) ,空间复杂度为 O(n) 。 +- 工程应用:redis + - [跳表 —— Redis 设计与实现](https://redisbook.readthedocs.io/en/latest/internal-datastruct/skiplist.html) + - [为啥 redis 使用跳表而不是红黑树?](https://www.zhihu.com/question/20202931) + +### 4. 栈 +- 先入后出 +- 添加和删除的时间复杂度都为 O(1) + +### 5. 队列 +- 先入先出 +- 添加和删除的时间复杂度都为 O(1) +### 6. 双端队列 +- 两端都可以出队、入队 +- 添加和删除的时间复杂度都为 O(1) + +### 7. 优先队列 +- 插入操作:O(1) +- 取出操作:O(logN),按照元素的优先级取出 +- 底层具体实现的数据结构:heap(堆)、BST(二叉搜索树)、treap(树堆) + +
+
+
+**【附加】**:[常见数据结构复杂度清单](https://www.bigocheatsheet.com) diff --git a/Week 01/id_178/LeetCode_11_178.java b/Week 01/id_178/LeetCode_11_178.java new file mode 100644 index 000000000..e28b29e8c --- /dev/null +++ b/Week 01/id_178/LeetCode_11_178.java @@ -0,0 +1,93 @@ +package org.geektime.weak01; + +/** + * Created by fukan on 2019/10/22. + * 一维数组的变换 + */ +public class LeetCode_11_178 { + public static void main(String[] args) { + int[] height = {1,8,6,2,5,4,8,3,7}; +// maxArea1(height); +// maxArea2(height); + LeetCode_11_178 instance = new LeetCode_11_178(); + instance.maxArea3(height); + } + + /** + * 我的想法: + * 枚举:left bar,right bar.(x-y)*hight_diff + * 1.循环计算出每个矩阵的面积 + * 2.选取最大值 + * 执行用时 : + * 539 ms, 在所有 java 提交中击败了 7.97% 的用户 + * O(n^2) + */ + public static int maxArea1(int[] height){ + int maxArea = 0; + int r_height = 0; + int width = 0; + + for (int i= 0;i < height.length ;i++){ + for(int j = height.length-1;j>=0;j--){ + r_height = Math.min(height[i],height[j]); + width = j - i; + int area = r_height*width; + if (area > maxArea){ + maxArea = area; + } + } + } + return maxArea; + } + + + /** + * 执行用时 :268 ms, 在所有 java 提交中击败了 32.34% 的用户 + * Question:为什么同样是O(n^2)的时间复杂度,maxArea2比maxArea1 运行时间少了这么多 + */ + public static int maxArea2(int[] height){ + int maxArea = 0; + //left bar loop + for (int i = 0;i < height.length - 1 ; i++){ + //right bar loop + for (int j = i+1 ; j < height.length ;j++ ){ + int area = (j-i)*Math.min(height[i],height[j]); + maxArea = Math.max(maxArea,area); + } + } + return maxArea; + } + + + /** + * 执行用时 :3 ms, 在所有 java 提交中击败了 97.07% 的用户 + * 左右收敛,中间夹逼 + */ + public static int maxArea3(int[] height){ + int maxArea = 0; + for (int i = 0,j=height.length-1;i nodesSeen = new HashSet<>(); + while(head != null){ + if(nodesSeen.contains(head)){ + return true; + }else { + nodesSeen.add(head); + } + head = head.next; + } + + + return false; + } + + class ListNode { + int val; + ListNode next; + + ListNode(int x) { + val = x; + next = null; + } + } +} + + + + + diff --git a/Week 01/id_178/LeetCode_15_178.java b/Week 01/id_178/LeetCode_15_178.java new file mode 100644 index 000000000..3a99fa5dc --- /dev/null +++ b/Week 01/id_178/LeetCode_15_178.java @@ -0,0 +1,149 @@ +package org.geektime.weak01; + +import java.util.*; + + +/** + * Created by fukan on 2019/10/22. + * 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c , + * 使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。 + * 排序 加 双指针。在遍历的过程中注意减枝和去重 + */ +public class LeetCode_15_178 { + public static void main(String[] args) { + int nums[] = {-1, 0, 1, 2, -1, -4}; + LeetCode_15_178 instance = new LeetCode_15_178(); + //instance.threeSum1(nums); +// instance.threeSum2(nums); +// instance.threeSum3(nums); +// instance.threeSum4(nums); + + } + + + /** + * 暴力破解AC超出时间限制了T_T + */ + public List> threeSum1(int[] nums) { + List> answer = new ArrayList(); + List item = new ArrayList<>(); + for (int i = 0; i < nums.length; ++i) { + for (int j = i + 1; j < nums.length; ++j) { + for (int z = j + 1; z < nums.length; ++z) { + if (nums[i] + nums[j] + nums[z] == 0) { + item = Arrays.asList(nums[i], nums[j], nums[z]); + Collections.sort(item); + if (!answer.contains(item)) { + answer.add(item); + } + + } + } + } + } + return answer; + } + + /** + * 暴力法搜索为 O(N^3)的时间复杂度,可通过双指针动态消去无效解来优化效率。 + * 双指针法铺垫: 先将给定 nums 排序,复杂度为 O(NlogN)。 + * 双指针法思路: 固定 3 个指针中最左(最小)数字的指针 k,双指针 i,j 分设在数组索引 (k, len(nums))两端, + * 通过双指针交替向中间移动,记录对于每个固定指针 k 的所有满足 nums[k] + nums[i] + nums[j] == 0 的 i,j 组合: + * 当 nums[k] > 0 时直接break跳出:因为 nums[j] >= nums[i] >= nums[k] > 0,即 3 个数字都大于 0 , + * 在此固定指针 k 之后不可能再找到结果了。 + * 当 k > 0且nums[k] == nums[k - 1]时即跳过此元素nums[k]:因为已经将 nums[k - 1] 的所有组合加入到结果中, + * 本次双指针搜索只会得到重复组合。 + * i,j 分设在数组索引 (k, len(nums)) 两端,当i < j时循环计算s = nums[k] + nums[i] + nums[j], + * 并按照以下规则执行双指针移动: + * 当s < 0时,i += 1并跳过所有重复的nums[i]; + * 当s > 0时,j -= 1并跳过所有重复的nums[j]; + * 当s == 0时,记录组合[k, i, j]至res,执行i += 1和j -= 1并跳过所有重复的nums[i]和nums[j],防止记录到重复组合。 + * 左右夹逼,中心迫近的思想 + * 摘自leetcode + */ + public List> threeSum2(int[] nums) { + Arrays.sort(nums); + List> res = new ArrayList<>(); + + // - 2 减去左指针 和 右指针的长度 + for (int k = 0; k < nums.length - 2; k++) { + if (nums[k] > 0) break; + + //>0 防止数组下标越界,假如K值与前一个K值相等,则不处理,应为效果相同,节约计算资源 + if (k > 0 && nums[k] == nums[k - 1]) continue; + + //i是左指针位置 j是右指针位置 + int i = k + 1, j = nums.length - 1; + + while (i < j) { + int sum = nums[k] + nums[i] + nums[j]; + //小于目标值时,左指针向右迫近。 + if (sum < 0) { + while (i < j && nums[i] == nums[++i]) ; + //大于目标值时,右指针向左迫近。 + } else if (sum > 0) { + while (i < j && nums[j] == nums[--j]) ; + } else { + res.add(new ArrayList(Arrays.asList(nums[k], nums[i], nums[j]))); + while (i < j && nums[i] == nums[++i]) ; + while (i < j && nums[j] == nums[--j]) ; + } + } + } + + return res; + } + + + /** + * 暴力破解 性能极差。 + */ + public List> threeSum3(int[] nums) { + if (nums == null || nums.length == 0) return Collections.EMPTY_LIST; + Arrays.sort(nums); + Map map = new HashMap<>(); + Set> hashSet = new HashSet<>(); + for (int i = 0; i < nums.length; i++) map.put(nums[i], i); + List> list = new ArrayList<>(); + for (int i = 0; i < nums.length; i++) { + for (int j = i + 1; j < nums.length; j++) { + if (map.containsKey(-nums[i] - nums[j]) + && map.get(-nums[i] - nums[j]) != i + && map.get(-nums[i] - nums[j]) != j + && -nums[i] - nums[j] >= nums[j]) { + if (!hashSet.contains(Arrays.asList(nums[i], nums[j], -nums[i] - nums[j]))) { + hashSet.add(Arrays.asList(nums[i], nums[j], -nums[i] - nums[j])); + list.add(Arrays.asList(nums[i], nums[j], -nums[i] - nums[j])); + } + } + } + } + + return list; + } + + /** + * 使用linkedlist在增加数组时,减少了所用资源 + */ + public List> threeSum4(int[] nums) { + Arrays.sort(nums); + List> res = new LinkedList<>(); + for (int i = 0; i < nums.length - 2; i++) { + if (1 == 0 || (i > 0) && nums[i] != nums[i - 1]) { + int lo = i + 1, hi = nums.length - 1, sum = 0 - nums[i]; + while (lo < hi) { + if (nums[lo] + nums[hi] == sum) { + res.add(Arrays.asList(nums[i], nums[lo], nums[hi])); + while (lo < hi && nums[lo] == nums[lo + 1]) lo++; + while (lo < hi && nums[hi] == nums[hi - 1]) hi--; + lo++; + hi--; + } else if (nums[lo] + nums[hi] < sum) lo++; + else hi--; + } + } + } + return res; + } + +} diff --git a/Week 01/id_178/LeetCode_189_178.java b/Week 01/id_178/LeetCode_189_178.java new file mode 100644 index 000000000..32971f054 --- /dev/null +++ b/Week 01/id_178/LeetCode_189_178.java @@ -0,0 +1,182 @@ +package org.geektime.weak01; + +/** + * 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。 + */ +public class LeetCode_189_178 { + public static void main(String[] args) { + int nums[] = {1,2,3,4,5,6}; + LeetCode_189_178 instance = new LeetCode_189_178(); + instance.rotate3(nums,3); + + } + + /** + * 暴力破解 + */ + public void rotate1(int[] nums, int k) { + int temp,previous =0; + for (int i = 0;i 目的地位置(nums[next]) + nums[next] = prev; + //prev 移动至目的地后,接受目的地原住户的信息 + prev = temp; + //当前指针位置为 next + current = next; + //count计算器 +1 + count++; + }while (start != current);//让一次轮循完成 + } + + } + + /** + * 使用反转算法 + * + * 这个方法基于这个事实:当我们旋转数组 k 次, k%n 个尾部元素会被移动到头部,剩下的元素会被向后移动。 + * 在这个方法中,我们首先将所有元素反转。然后反转前 k 个元素,再反转后面 n-k 个元素,就能得到想要的结果。 + * + * 假设 n=7 且 k=3。 + * 原始数组 : 1 2 3 4 5 6 7 + * 反转所有数字后 : 7 6 5 4 3 2 1 + * 反转前 k 个数字后 : 5 6 7 4 3 2 1 + * 反转后 n-k 个数字后 : 5 6 7 1 2 3 4 --> 结果 + * + * 作者:LeetCode + * 链接:https://leetcode-cn.com/problems/rotate-array/solution/xuan-zhuan-shu-zu-by-leetcode/ + * 来源:力扣(LeetCode) + * 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 + * 个人理解: + * 先做出原数组的镜像,镜像 与 原数组 组成环,k=为镜像中需要遍历的长度,镜像索引 与 原数组索引位置相同,依次放入 mirror_Array[--(k-1)],--(k-1) >=0 + */ + public void rotate4(int[] nums, int k) { + k %= nums.length; + reverse(nums,0,nums.length - 1); + reverse(nums,0,k-1); + reverse(nums,0,nums.length-1); + } + + public void reverse(int[] nums, int start, int end) { + while (start < end){ + int temp = nums[start]; + nums[start] = nums[end]; + nums[end] = temp; + start++; + end--; + } + } + + /** + * 别人家的暴力破解法 + * @param nums + * @param k + */ + public void rotate5(int[] nums, int k) { + if (nums.length <= 1) return; + k = k % nums.length; + int[] temp = new int[k]; + System.arraycopy(nums, nums.length - k, temp, 0, k); + System.arraycopy(nums, 0, nums, k, nums.length - k); + System.arraycopy(temp, 0, nums, 0, temp.length); + + } + + /** + * 反转法优化 + * @param nums + * @param k + */ + public void rotate6(int[] nums, int k) { + if (nums == null || nums.length == 0) { + return; + } + k = k % nums.length; + if (k == 0) { + return; + } + swap(nums, 0, nums.length - k - 1); + swap(nums, nums.length - k, nums.length - 1); + swap(nums, 0, nums.length - 1); + +} + + private void swap(int[] nums, int start, int end) { + while (start < end) { + int tmp = nums[start]; + nums[start] = nums[end]; + nums[end] = tmp; + start++; + end--; + } + } + + + +} diff --git a/Week 01/id_178/LeetCode_1_178.java b/Week 01/id_178/LeetCode_1_178.java new file mode 100644 index 000000000..7b8422f1c --- /dev/null +++ b/Week 01/id_178/LeetCode_1_178.java @@ -0,0 +1,96 @@ +package org.geektime.weak01; + +import java.util.HashMap; +import java.util.Map; + +/** + * 给定一个整数数组 nums 和一个目标值 target, + * 请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。 + * 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 + * + * Created by fukan on 2019/10/22. + */ +public class LeetCode_1_178 { + public static void main(String[] args) { + int[]nums = {2, 7, 11, 15}; + LeetCode_1_178 instance = new LeetCode_1_178(); + instance.twoSum5(nums,9); + } + + /** + * 暴力破解法 + */ + public int[] twoSum1(int[] nums, int target) { + int[] result = {}; + for (int i = 0 ; i < nums.length ; i++){ + for (int j = 0 ; j< nums.length;j++){ + if(i != j){ + if( nums[i] + nums[j] == target){ + result = new int[]{i,j}; + } + } + } + } + return result; + } + + /** + * 官方暴力破解法 + */ + public int[] twoSum2(int[] nums, int target) { + for (int i = 0;i < nums.length;i++){ + for (int j = i+1 ;j < nums.length;j++){ + if (nums[j] == target - nums[i]){ + return new int[]{i,j}; + } + } + } + + throw new IllegalArgumentException("No Two Sum Solution"); + } + + /** + * 两遍哈希表 + */ + public int[] twoSum3(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0;i < nums.length;i++){ + map.put(nums[i],i); + } + for (int i = 0;i < nums.length ;i++){ + int complement = target - nums[i]; + if (map.containsKey(complement) && map.get(complement) != i ){ + return new int[]{i,map.get(complement)}; + } + } + throw new IllegalArgumentException("No Two sum solution"); + } + + /** + * 一遍哈希表 + */ + public int[] twoSum4(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0;i < nums.length; i++){ + int complement = target - nums[i]; + if (map.containsKey(complement)){ + return new int[]{map.get(complement),i}; + } + map.put(nums[i],i); + } + throw new IllegalArgumentException("There No Two Sum Solution"); + } + + /** + * + */ + public int[] twoSum5(int[] nums, int target) { + Map map = new HashMap(); + for (int i = 0;i < nums.length; map.put(nums[i], ++i)){ + if (map.containsKey(target - nums[i])){ + return new int[]{map.get(target-nums[i])-1,i}; + } + } + throw new IllegalArgumentException("No Solution"); + } +} diff --git a/Week 01/id_178/LeetCode_21_178.java b/Week 01/id_178/LeetCode_21_178.java new file mode 100644 index 000000000..7fdd559ca --- /dev/null +++ b/Week 01/id_178/LeetCode_21_178.java @@ -0,0 +1,29 @@ +package org.geektime.weak01; + +/** + * 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。  + * + * 示例: + * + * 输入:1->2->4, 1->3->4 + * 输出:1->1->2->3->4->4 + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/merge-two-sorted-lists + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_21_178 { + public static void main(String[] args) { + //Question:ListNode类型不知道如何定义输入及测试类编写 + + } + + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + if (l1 == null) + {return l2;} + else if (l2 == null) + {return l1;} + else if(l1.val < l2.val) {l1.next = mergeTwoLists(l1,l2.next);return l1;} + else l2.next = mergeTwoLists(l1,l2.next);return l2; + } +} diff --git a/Week 01/id_178/LeetCode_239_178.java b/Week 01/id_178/LeetCode_239_178.java new file mode 100644 index 000000000..454ddee2f --- /dev/null +++ b/Week 01/id_178/LeetCode_239_178.java @@ -0,0 +1,62 @@ +package org.geektime.weak01; + +/** + * 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。 + * 你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 + *

+ * 返回滑动窗口中的最大值。 + *

+ *

+ * 示例: + *

+ * 输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3 + * 输出: [3,3,5,5,6,7] + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/sliding-window-maximum + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_239_178 { + public static void main(String[] args) { + LeetCode_239_178 instance = new LeetCode_239_178(); + int nums[] = {1, 3, -1, -3, 5, 3, 6, 7}; + int k = 3; + instance.maxSlidingWindow(nums, k); + } + + /** + * 好难理解!!! + * @param nums + * @param k + * @return + */ + + public int[] maxSlidingWindow(int[] nums, int k) { + int n = nums.length; + if (n * k == 0) return new int[0]; + if (k == 1) return nums; + + int[] left = new int[n]; + left[0] = nums[0]; + int[] right = new int[n]; + right[n - 1] = nums[n - 1]; + for (int i = 1; i < n; i++) { + + if (i % k == 0) left[i] = nums[i]; // block_start + else left[i] = Math.max(left[i - 1], nums[i]); + + // from right to left + int j = n - i - 1; + if ((j + 1) % k == 0) right[j] = nums[j]; // block_end + else right[j] = Math.max(right[j + 1], nums[j]); + } + + int[] output = new int[n - k + 1]; + for (int i = 0; i < n - k + 1; i++) + output[i] = Math.max(left[i + k - 1], right[i]); + + return output; + } + + +} diff --git a/Week 01/id_178/LeetCode_26_178.java b/Week 01/id_178/LeetCode_26_178.java new file mode 100644 index 000000000..9596ddf4e --- /dev/null +++ b/Week 01/id_178/LeetCode_26_178.java @@ -0,0 +1,74 @@ +package org.geektime.weak01; + +/** + * 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 + * + * 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_26_178 { + public static void main(String[] args) { + int nums[]= {0,0,1,1,2,21,31,41,41}; + LeetCode_26_178 instance = new LeetCode_26_178(); + instance.removeDuplicates1(nums); + } + + /** + * 双指针法 + * 算法 + * + * 数组完成排序后,我们可以放置两个指针 i 和 j,其中 i 是慢指针,而 j 是快指针。 + * 只要 nums[i] = nums[j] 我们就增加 j 以跳过重复项。 + * + * 当我们遇到 nums[j] <> nums[i]时, + * 跳过重复项的运行已经结束,因此我们必须把它(nums[j])的值复制到 nums[i + 1]。 + * 然后递增 i,接着我们将再次重复相同的过程,直到 j 到达数组的末尾为止。 + * + * 作者:LeetCode + * 链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/solution/shan-chu-pai-xu-shu-zu-zhong-de-zhong-fu-xiang-by-/ + * 来源:力扣(LeetCode) + * 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 + */ + public int removeDuplicates1(int[] nums) { + if (nums.length == 0) return 0; + int i =0; + for (int j = 1;j < nums.length;j++){ + if (nums[j] != nums[i]) { + i++; + nums[i] = nums[j]; + } + } + return i+1; + } + + public int removeDuplicates2(int[] nums) { + if (nums == null || nums.length == 0) return 0; + int p = 0; + int q = 1; + while(q < nums.length){ + if(nums[p] != nums[q]){ + if(q - p > 1){ + nums[p + 1] = nums[q]; + } + p++; + } + q++; + } + return p + 1; + } + + + public int removeDuplicates3(int[] nums) { + int i = nums.length > 0 ? 1 : 0; + for (int n:nums) + if (n>nums[i-1]) + nums[i++] = n; + return i; + } + + +} + diff --git a/Week 01/id_178/LeetCode_283_178.java b/Week 01/id_178/LeetCode_283_178.java new file mode 100644 index 000000000..e2b741079 --- /dev/null +++ b/Week 01/id_178/LeetCode_283_178.java @@ -0,0 +1,96 @@ +package org.geektime.weak01; + +/** + * Created by fukan on 2019/10/22. + */ + + +public class LeetCode_283_178 { + public static void main(String[] args) { + int nums[] = {0,1,0,3,12}; + LeetCode_283_178 instance = new LeetCode_283_178(); + //instance.solution1(nums); + //instance.solution2(nums); + //instance.solution3(nums); + instance.solution4(nums); + } + + /** + 解法一:双指针解法 + 1、维护一个总是指向0的动态指针 i + 2、每次遇到非0位置的数将其位置的数与0位置指针索引上的数进行交换值并更新1的指针i++ + 两处交换 一处总为0 所以直接赋值为0 不用存储临时变量 但如此就需要判断 i 是否等于 j 去掉为自己的情况 + */ + public static int[] solution1(int[] nums){ + int index = 0; + for (int i = 0 ;i < nums.length;i++){ + if(nums[i] != 0){ + if(index != i){ + nums[index] = nums[i]; + nums[i] = 0; + } + index++; + } + + } + return nums; + } + + /** + * 遍历取非0值 && push 0 + * @param nums + * @return + */ + public static int[] solution2(int[] nums){ + int insertZero = 0; + int n = nums.length; + for(int i = 0;i= 0 ; i--){ + digits[i]++; + digits[i] = digits[i] % 10; + if (digits[i] != 0 ) return digits; + } + digits = new int[digits.length+1]; + digits[0] = 1; + + return digits; + } + + public int[] plusOne2(int[] digits) { + for (int i = digits.length - 1;i >= 0 ; i--){ + if (digits[i] != 9 ){ + digits[i]++; + break; + }else { + digits[i] = 0; + } + } + + if (digits[0] == 0){ + int[] res = new int[digits.length+1]; + res[0] = 1; + return res; + } + + return digits; + } + + public int[] plusOne3(int[] digits) { + int carry = 1; + for (int i = digits.length-1;i>=0;--i){ + digits[i] += carry; + if (digits[i] <= 9){ + return digits; + } + digits[i] = 0; + } + digits = new int[digits.length+1]; + digits[0] = 1; + return digits; + } +} + + diff --git a/Week 01/id_178/LeetCode_70_178.java b/Week 01/id_178/LeetCode_70_178.java new file mode 100644 index 000000000..2f6edae15 --- /dev/null +++ b/Week 01/id_178/LeetCode_70_178.java @@ -0,0 +1,41 @@ +package org.geektime.weak01; + +/** + * 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 + * + * 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? + * + * 注意:给定 n 是一个正整数。 + * Created by fukan on 2019/10/22. + */ +public class LeetCode_70_178 { + public static void main(String[] args) { + LeetCode_70_178 instance = new LeetCode_70_178(); + int a = instance.climbStairs1(1); + System.out.print(a); + + } + + /** + * 3 |1,1,1|1,2|2,1| + * 4 |1,1,1,1|2,1,1|1,2,1|1,1,2|2,2| + * 5 |1,1,1,1,1|2,1,1,1|1,1,2,1|1,1,1,2|2,2,1| + * 推出答案是一组斐波那契数列 1 2 3 5 8... + * 即本题答案为第N个斐波那契数是几? + * 误区:做题只做一遍 + */ + public int climbStairs1(int n){ + if(n<=2){ + return n; + } + + int first = 1,second = 2,third = 3; + for (int i = 3;i<=n;i++){ + third = first + second; + first = second; + second = third; + } + + return third; + } +} diff --git a/Week 01/id_178/LeetCode_84_178.java b/Week 01/id_178/LeetCode_84_178.java new file mode 100644 index 000000000..665966e99 --- /dev/null +++ b/Week 01/id_178/LeetCode_84_178.java @@ -0,0 +1,44 @@ +package org.geektime.weak01; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 + * 求在该柱状图中,能够勾勒出来的矩形的最大面积。 + */ +public class LeetCode_84_178 { + public static void main(String[] args) { + + } + + public int largestRectangleArea1(int[] heights) { + int largestRectangleArea = 0; + for (int i = 0 ;i < heights.length;i++){ + int minHeight = Integer.MAX_VALUE; + for(int j = i;j < heights.length;j++){ + minHeight = Math.min(minHeight,heights[j]); + largestRectangleArea = Math.max(largestRectangleArea,minHeight*(j - i + 1)); + } + } + return largestRectangleArea; + } + + public int largestRectangleArea2(int[] heights) { + return calculateArea(heights, 0, heights.length - 1); + } + + private int calculateArea(int[] heights, int start, int end) { + if (start > end) + return 0; + int minindex = start; + for (int i = start; i <= end; i++) + if (heights[minindex] > heights[i]) + minindex = i; + return Math.max(heights[minindex] * (end - start + 1), Math.max(calculateArea(heights, start, minindex - 1), calculateArea(heights, minindex + 1, end))); + + } + + +} diff --git a/Week 01/id_178/LeetCode_88_178.java b/Week 01/id_178/LeetCode_88_178.java new file mode 100644 index 000000000..b21c50659 --- /dev/null +++ b/Week 01/id_178/LeetCode_88_178.java @@ -0,0 +1,51 @@ +package org.geektime.weak01; + +import java.util.Arrays; + +/** + * 给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。 + * + * 说明: + * + * 初始化 nums1 和 nums2 的元素数量分别为 m 和 n。 + * 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。 + * 示例: + * + * 输入: + * nums1 = [1,2,3,0,0,0], m = 3 + * nums2 = [2,5,6], n = 3 + * + * 输出: [1,2,2,3,5,6] + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/merge-sorted-array + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_88_178 { + public static void main(String[] args) { + + } + + public void merge1(int[] nums1, int m, int[] nums2, int n) { + System.arraycopy(nums2, 0, nums1, m, n); + Arrays.sort(nums1); + } + + public void merge2(int[] nums1, int m, int[] nums2, int n) { + // two get pointers for nums1 and nums2 + int p1 = m - 1; + int p2 = n - 1; + // set pointer for nums1 + int p = m + n - 1; + + // while there are still elements to compare + while ((p1 >= 0) && (p2 >= 0)) + // compare two elements from nums1 and nums2 + // and add the largest one in nums1 + nums1[p--] = (nums1[p1] < nums2[p2]) ? nums2[p2--] : nums1[p1--]; + + // add missing elements from nums2 + System.arraycopy(nums2, 0, nums1, 0, p2 + 1); + + } +} diff --git a/Week 01/id_178/ListNode.java b/Week 01/id_178/ListNode.java new file mode 100644 index 000000000..9e45b6ddc --- /dev/null +++ b/Week 01/id_178/ListNode.java @@ -0,0 +1,21 @@ +package org.geektime.weak01; + +/** + * Created by fukan on 2019/10/25. + */ +public class ListNode { + int val; + ListNode next; + + ListNode(int x) + { + val = x; + } + + @Override + public String toString() + { + return "" + val; + } +} + diff --git a/Week 01/id_178/NOTE.md b/Week 01/id_178/NOTE.md index a6321d6e2..c3a9b0158 100644 --- a/Week 01/id_178/NOTE.md +++ b/Week 01/id_178/NOTE.md @@ -1,4 +1,19 @@ # NOTE +1.ListNode类按如下编写后,不会进行Test Case的编写 +2.链表的知识和题目比较模糊,未有一个清晰的认识 +public class ListNode { + int val; + ListNode next; - + ListNode(int x) + { + val = x; + } + @Override + public String toString() + { + return "" + val; + } +} +3.在艾宾浩斯遗忘曲线中进行5遍刷题法,是否更为高效 diff --git a/Week 01/id_183/LeetCode_11_183.cpp b/Week 01/id_183/LeetCode_11_183.cpp new file mode 100644 index 000000000..1a2330260 --- /dev/null +++ b/Week 01/id_183/LeetCode_11_183.cpp @@ -0,0 +1,26 @@ +/* + * @lc app=leetcode id=11 lang=cpp + * + * [11] Container With Most Water + */ + +// @lc code=start +class Solution { +public: + int maxArea(vector& height) { + //pointer i: from front; + //pointer j: from rear; + int i,j; + int res =0 ; + for(i = 0, j=height.size()-1;i& nums, int k) { + //if k==nunms.size()-> do not need to rotate?? + k %= nums.size(); + reverse(nums.begin(),nums.end()); + reverse(nums.begin(),nums.begin()+k); + reverse(nums.begin()+k,nums.end()); + } +}; +// @lc code=end + diff --git a/Week 01/id_183/LeetCode_1_bf_183.cpp b/Week 01/id_183/LeetCode_1_bf_183.cpp new file mode 100644 index 000000000..c72226bd4 --- /dev/null +++ b/Week 01/id_183/LeetCode_1_bf_183.cpp @@ -0,0 +1,28 @@ +/* + * @lc app=leetcode id=1 lang=cpp + * + * [1] Two Sum + */ + +// @lc code=start +class Solution { +public: + vector twoSum(vector& nums, int target) { + vector res(2,0); + for(int i=0; ivalval) + { + l1->next = mergeTwoLists(l1->next,l2); + return l1; + } + else{ + l2->next = mergeTwoLists( l2->next,l1); + return l2; + } + } +}; +// @lc code=end + diff --git a/Week 01/id_183/LeetCode_26_183.cpp b/Week 01/id_183/LeetCode_26_183.cpp new file mode 100644 index 000000000..d35d20775 --- /dev/null +++ b/Week 01/id_183/LeetCode_26_183.cpp @@ -0,0 +1,37 @@ +/* + * @lc app=leetcode id=26 lang=cpp + * + * [26] Remove Duplicates from Sorted Array + */ + +// @lc code=start +class Solution { +public: + int removeDuplicates(vector& nums) { + if(nums.size()==0) return 0; + // int i = 0; + // //if nums [j] == nums[i]--> ++j + // for(int j=1;j& nums) { + //pointer i:current position ready to insert; + //pointer j:traverse all elements in the vector + int i,j; + i=0; + for(j = 0;j nums[i]=nums[j];++i; + if(nums[j]!=0) + { + nums[i] = nums[j]; + ++i; + } + } + //add zeros + for(int k = i;k& height) { + int i,j=0,n=height.size(); + if(!n) return 0; + vectorleft(n,0); // left-max + vectorright(n,0); // right-max + + left[0]=height[0]; + for(i=1;i=0;i--) + right[i] = max(height[i], right[i+1]); + + for(i=0;i plusOne(vector& digits) { + //two situation: no need to add one digit || 999->1000 + for(int i = digits.size()-1;i>=0;i--) + { + digits[i]++; + digits[i] = digits[i] % 10; + if(digits[i] !=0) return digits; + } + int size = digits.size(); + vector new_v(size+1,0); + new_v[0] = 1; + return new_v; + } +}; +// @lc code=end + diff --git a/Week 01/id_183/LeetCode_70_183.cpp b/Week 01/id_183/LeetCode_70_183.cpp new file mode 100644 index 000000000..44d29497f --- /dev/null +++ b/Week 01/id_183/LeetCode_70_183.cpp @@ -0,0 +1,26 @@ +/* + * @lc app=leetcode id=70 lang=cpp + * + * [70] Climbing Stairs + */ + +// @lc code=start +class Solution { +public: + int climbStairs(int n) { + if(n<=2) + return n; + //f3=f1+f2;f4=f2+f3;f5=f3+f4 + int first = 1; + int second = 2; + for(int i=3;i<=n;++i) + { + int third = first + second; + first = second; + second = third; + } + return second; + · } +}; +// @lc code=end + diff --git a/Week 01/id_183/LeetCode_88_183.cpp b/Week 01/id_183/LeetCode_88_183.cpp new file mode 100644 index 000000000..ab7985e74 --- /dev/null +++ b/Week 01/id_183/LeetCode_88_183.cpp @@ -0,0 +1,26 @@ +/* + * @lc app=leetcode id=88 lang=cpp + * + * [88] Merge Sorted Array + */ + +// @lc code=start +class Solution { +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + //sort from the largest element and store it in num1 + int pos = m+n-1; + while(n>0)//while num2 still has elements ready to be traverse + { + //start from the rightmost + if(m>0 && nums1[m-1]>nums2[n-1]) + { + nums1[pos--] = nums1[--m]; + }else{ + nums1[pos--] = nums2[--n]; + } + } + } +}; +// @lc code=end + diff --git a/Week 01/id_188/LeetCode_11_188.go b/Week 01/id_188/LeetCode_11_188.go new file mode 100644 index 000000000..cd4cb858f --- /dev/null +++ b/Week 01/id_188/LeetCode_11_188.go @@ -0,0 +1,38 @@ +/* + * @lc app=leetcode.cn id=11 lang=golang + * + * [11] 盛最多水的容器 + */ + +// @lc code=start +func maxArea(height []int) int { + var ( + maxCapacity = 0 + leftIndex = 0 + rightIndex = len(height) - 1 + ) + + for leftIndex < rightIndex { + leftHegiht, rightHeight := height[leftIndex], height[rightIndex] + + minHeight := leftHegiht + if minHeight > rightHeight { + minHeight = rightHeight + } + + capacity := minHeight * (rightIndex - leftIndex) + if capacity > maxCapacity { + maxCapacity = capacity + } + + if minHeight == leftHegiht { + leftIndex++ + } else { + rightIndex-- + } + } + + return maxCapacity +} + +// @lc code=end diff --git a/Week 01/id_188/LeetCode_15_188.go b/Week 01/id_188/LeetCode_15_188.go new file mode 100644 index 000000000..8045e243a --- /dev/null +++ b/Week 01/id_188/LeetCode_15_188.go @@ -0,0 +1,57 @@ +import "sort" + +/* + * @lc app=leetcode.cn id=15 lang=golang + * + * [15] 三数之和 + */ + +// @lc code=start +func threeSum(nums []int) [][]int { + ret := make([][]int, 0, 10) + + if len(nums) < 3 { + return ret + } + + sort.Ints(nums) + + for index, value := range nums { + if index > len(nums)-2 || value > 0 || (value+nums[index+1]) > 0 { + break + } + + if index > 0 && value == nums[index-1] { + continue + } + + leftIndex, rightIndex := index+1, len(nums)-1 + + for leftIndex < rightIndex { + if leftIndex > index+1 && nums[leftIndex] == nums[index+1] { + leftIndex++ + continue + } + + if rightIndex < len(nums)-1 && nums[rightIndex] == nums[rightIndex+1] { + rightIndex-- + continue + } + + v := value + nums[leftIndex] + nums[rightIndex] + + if v < 0 { + leftIndex++ + } else if v > 0 { + rightIndex-- + } else { + ret = append(ret, []int{value, nums[leftIndex], nums[rightIndex]}) + leftIndex++ + rightIndex-- + } + } + } + return ret +} + +// @lc code=end diff --git a/Week 01/id_188/LeetCode_1_188.go b/Week 01/id_188/LeetCode_1_188.go new file mode 100644 index 000000000..1565fee96 --- /dev/null +++ b/Week 01/id_188/LeetCode_1_188.go @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode.cn id=1 lang=golang + * + * [1] 两数之和 + */ + +// @lc code=start +func twoSum(nums []int, target int) []int { + ret := make([]int, 0, 2) + + if len(nums) < 2 { + return ret + } + + maps := make(map[int]int, len(nums)) + + for index, value := range nums { + needValue := target - value + + if _, exist := maps[needValue]; exist { + ret = append(ret, maps[needValue]) + ret = append(ret, index) + break + } + + maps[value] = index + } + + return ret +} + +// @lc code=end diff --git a/Week 01/id_188/LeetCode_283_188.go b/Week 01/id_188/LeetCode_283_188.go new file mode 100644 index 000000000..cf92af08e --- /dev/null +++ b/Week 01/id_188/LeetCode_283_188.go @@ -0,0 +1,20 @@ +/* + * @lc app=leetcode.cn id=283 lang=golang + * + * [283] 移动零 + */ + +// @lc code=start +func moveZeroes(nums []int) { + lastIndex := 0 + for index, value := range nums { + if value == 0 { + continue + } + + nums[lastIndex], nums[index] = nums[index], nums[lastIndex] + lastIndex++ + } +} + +// @lc code=end diff --git a/Week 01/id_188/LeetCode_70_188.go b/Week 01/id_188/LeetCode_70_188.go new file mode 100644 index 000000000..e05fca8a1 --- /dev/null +++ b/Week 01/id_188/LeetCode_70_188.go @@ -0,0 +1,22 @@ +/* + * @lc app=leetcode.cn id=70 lang=golang + * + * [70] 爬楼梯 + */ + +// @lc code=start +func climbStairs(n int) int { + if n < 3 { + return n + } + + f1, f2, ret := 1, 2, 0 + for index := 3; index <= n; index++ { + ret = f1 + f2 + f1, f2 = f2, ret + } + + return ret +} + +// @lc code=end diff --git a/Week 01/id_198/LeetCode_26_198.go b/Week 01/id_198/LeetCode_26_198.go new file mode 100644 index 000000000..904e0a192 --- /dev/null +++ b/Week 01/id_198/LeetCode_26_198.go @@ -0,0 +1,19 @@ + +package leetcode + +//leetcode submit region begin(Prohibit modification and deletion) +func removeDuplicates(nums []int) int { + i := 0 + for j := 0; j < len(nums); j++ { + + if nums[i] != nums[j] { + + i++ + } + nums[i] = nums[j] + } + return i + 1 + +} + +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 01/id_198/LeetCode_641_198.go b/Week 01/id_198/LeetCode_641_198.go new file mode 100644 index 000000000..af4e6d306 --- /dev/null +++ b/Week 01/id_198/LeetCode_641_198.go @@ -0,0 +1,108 @@ +package leetcode + +type MyCircularDeque struct { + elements []int + frontIndex int + rearIndex int +} + +/** Initialize your data structure here. Set the cap of the deque to be k. */ +func Constructor(k int) MyCircularDeque { + + return MyCircularDeque{elements: make([]int, k+1), frontIndex: 0, rearIndex: 0} +} + +/** Adds an item at the frontIndex of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) InsertFront(value int) bool { + + if this.IsFull() { + + return false + } + + this.frontIndex = (this.frontIndex - 1 + len(this.elements)) % len(this.elements) + this.elements[this.frontIndex] = value + return true; +} + +/** Adds an item at the rearIndex of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) InsertLast(value int) bool { + + if this.IsFull() { + + return false + } + this.elements[this.rearIndex] = value + this.rearIndex = (this.rearIndex + 1) % len(this.elements) + return true +} + +/** Deletes an item from the frontIndex of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) DeleteFront() bool { + + if this.IsEmpty() { + + return false + } + + this.frontIndex = (this.frontIndex + 1) % len(this.elements) + return true +} + +/** Deletes an item from the rearIndex of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) DeleteLast() bool { + + if this.IsEmpty() { + + return false + } + + this.rearIndex = ((this.rearIndex - 1) + len(this.elements)) % len(this.elements) + return true +} + +/** Get the frontIndex item from the deque. */ +func (this *MyCircularDeque) GetFront() int { + + if this.IsEmpty() { + + return -1 + } + return this.elements[this.frontIndex] +} + +/** Get the last item from the deque. */ +func (this *MyCircularDeque) GetRear() int { + + if this.IsEmpty() { + + return -1 + } + return this.elements[(this.rearIndex-1+len(this.elements))%len(this.elements)] +} + +/** Checks whether the circular deque is empty or not. */ +func (this *MyCircularDeque) IsEmpty() bool { + + return this.frontIndex == this.rearIndex +} + +/** Checks whether the circular deque is full or not. */ +func (this *MyCircularDeque) IsFull() bool { + + return (this.rearIndex+1)%len(this.elements) == this.frontIndex +} + +/** + * Your MyCircularDeque object will be instantiated and called as such: + * obj := Constructor(k); + * param_1 := obj.InsertFront(value); + * param_2 := obj.InsertLast(value); + * param_3 := obj.DeleteFront(); + * param_4 := obj.DeleteLast(); + * param_5 := obj.GetFront(); + * param_6 := obj.GetRear(); + * param_7 := obj.IsEmpty(); + * param_8 := obj.IsFull(); + */ +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 01/id_198/NOTE.md b/Week 01/id_198/NOTE.md index a6321d6e2..2219cfa0d 100644 --- a/Week 01/id_198/NOTE.md +++ b/Week 01/id_198/NOTE.md @@ -1,4 +1,99 @@ -# NOTE +# 【198-week 01】学习总结 - +## Java Queue 和 Priority Queue源码分析 +### 队列 + +> 队列(Queue),是一种基于先进先出(FIFO)策略的集合数据类型。 + +在java中,Queue接口继承与Collection接口(List,Set接口也继承与Collection); 定义方法如下: + +- add(offer): 插入元素, 若当前队列容量不足,add抛出IllegalStateException ,而offer返回false;其他行为等价;(若无边界队列,add和offer等价,如LinkedList) +- remove(poll): 获取并移除Queue头部元素,若当前队列为空,则remove前者抛出NoSuchElementException,poll返回null;其他行为等价; +- element(peek): 获取Queue头部元素(但不移除),element和peek的区别是,若当前队列为空,则前者抛出NoSuchElementException ,后者返回null;其他行为等价; + +Queue的实现包括: + +- ArrayBlockingQueue: 基于数组的有界阻塞队列 +- ArrayDeque: 基于数组实现的可扩容双端对垒 +- ConcurrentLinkedDeque: 基于双向链表的无边界并发安全的双端队列;在多线程环境中安全的插入,删除和访问 +- ConcurrentLinkedQueue:基于双向链表的无边界并发安全的队列;在多线程环境中安全的插入,删除和访问 +- DelayQueue:无边界的延迟阻塞队列 +- LinkedBlockingDeque: 基于双向链表的阻塞双端队列,该队列容量可在初始化时指定 +- LinkedBlockingQueue: 基于双向链表的阻塞队列,该队列容量可在初始化时指定 +- LinkedList: 基于双向列表实现List和Deque接口,实现所有的list和双端队列的操作,并允许元素为null +- LinkedTransferQueue: 基于链表实现的传输队列(TransferQueue,一种阻塞队列) +- PriorityQueue: 优先队列(非线程安全) +- PriorityBlockingQueue: 阻塞优先队列(线程安全) +- SynchronousQueue:同步队列,一个阻塞队列,每个插入操作必须等待另一个线程进行相应的删除操作,反之亦然。 + +### 优先队列 + +Java 优先队列基于Priority Heap实现; 在创建优先级队列时,可以指定元素比较器(`java.util.Comparator`),若不指定将使用队列元素的默认比较器。实现说明: + +- 插入元素不能为null +- 若为指定比较器,则插入元素必须实现java.util.Comparator接口 +- 该队列为无界队列 +- 非线程安全 +- offer、poll、remove和add的时间复杂度为O(log(n)), 进行排序和查找 +- remove(Object) and contains(Object)实现复杂度为O(n) +- peek、element、size时间复杂度为O(1) + +## 【198-week 01】Java Deque API改写 + +原代码如下: + +```java +Deque deque = new LinkedList<>(); +deque.push("a"); +deque.push("b"); +deque.push("c"); +System.out.println(deque); + +String str = deque.peek(); +System.out.println(str); +System.out.println(deque); +while (deque.size() > 0) { + + System.out.println(deque.pop()); +} +System.out.println(deque); +``` + +`addFirst`改写 + +```java +Deque deque = new LinkedList<>(); +deque.addFirst("a"); +deque.addFirst("b"); +deque.addFirst("c"); +System.out.println(deque); + +String str = deque.element(); +System.out.println(str); +System.out.println(deque); +while (deque.size() > 0) { + + System.out.println(deque.removeFirst()); +} +System.out.println(deque); +``` + +`addLast`改写 + +```java +Deque deque = new LinkedList<>(); +deque.addLast("a"); +deque.addLast("b"); +deque.addLast("c"); +System.out.println(deque); + +String str = deque.getLast(); +System.out.println(str); +System.out.println(deque); +while (deque.size() > 0) { + + System.out.println(deque.removeLast()); +} +System.out.println(deque); +``` diff --git a/Week 01/id_203/LeetCode_1_203.go b/Week 01/id_203/LeetCode_1_203.go new file mode 100644 index 000000000..ab63656a6 --- /dev/null +++ b/Week 01/id_203/LeetCode_1_203.go @@ -0,0 +1,16 @@ +package leetcode + +func twoSum(nums []int, target int) []int { + dict := make(map[int]int) + + res := make([]int, 2) + for i := 0; i < len(nums); i++ { + if x, ok := dict[nums[i]]; ok { + res = []int{x, i} + } else { + dict[target-nums[i]] = i + } + } + + return res +} \ No newline at end of file diff --git a/Week 01/id_203/LeetCode_283_203.go b/Week 01/id_203/LeetCode_283_203.go new file mode 100644 index 000000000..c667520bb --- /dev/null +++ b/Week 01/id_203/LeetCode_283_203.go @@ -0,0 +1,15 @@ +package leetcode + +func moveZeroes(nums []int) { + j := 0 + for i := 0; i < len(nums); i++ { + if nums[i] != 0 { + if i != j { + nums[j] = nums[i] + nums[i] = 0 + } + j++ + } + } + +} diff --git a/Week 01/id_203/LeetCode_641_203.go b/Week 01/id_203/LeetCode_641_203.go new file mode 100644 index 000000000..8220de9bc --- /dev/null +++ b/Week 01/id_203/LeetCode_641_203.go @@ -0,0 +1,169 @@ +package main + +import "fmt" + +type MyCircularDeque struct { + cap int + length int + head *Node + end *Node +} + +type Node struct { + value int + pre *Node + next *Node +} + + +/** Initialize your data structure here. Set the size of the deque to be k. */ +func Constructor(k int) MyCircularDeque { + deque := &MyCircularDeque{k, 0, nil, nil} + + return *deque +} + + +/** Adds an item at the front of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) InsertFront(value int) bool { + if this.IsFull() { + return false + } + + node := &Node{value:value} + + if this.length == 0 { + this.head = node + this.end = node + } else { + node.next = this.head + this.head.pre = node + this.head = node + } + + this.length++ + + return true +} + + +/** Adds an item at the rear of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) InsertLast(value int) bool { + if this.IsFull() { + return false + } + + node := &Node{value:value} + if this.length == 0 { + this.head = node + this.end = node + } else { + node.pre = this.end + this.end.next = node + this.end = node + } + this.length++ + + return true +} + + +/** Deletes an item from the front of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) DeleteFront() bool { + if this.IsEmpty() { + return false + } + + if this.length == 1 { + this.head = nil + this.end = nil + } else { + this.head = this.head.next + this.head.pre = nil + } + + this.length-- + + return true +} + + +/** Deletes an item from the rear of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) DeleteLast() bool { + if this.IsEmpty() { + return false + } + + if this.length == 1 { + this.head = nil + this.end = nil + } else { + this.end = this.end.pre + this.end.next = nil + } + + this.length-- + + return true +} + + +/** Get the front item from the deque. */ +func (this *MyCircularDeque) GetFront() interface{} { + if this.head == nil { + return -1 + } + return this.head.value +} + + +/** Get the last item from the deque. */ +func (this *MyCircularDeque) GetRear() interface{} { + if this.end == nil { + return -1 + } + return this.end.value +} + + +/** Checks whether the circular deque is empty or not. */ +func (this *MyCircularDeque) IsEmpty() bool { + return this.length == 0 +} + + +/** Checks whether the circular deque is full or not. */ +func (this *MyCircularDeque) IsFull() bool { + return this.length == this.cap +} + +func main() { + obj := Constructor(4) + param1 := obj.InsertFront(9) + param2 := obj.DeleteLast() + param3 := obj.GetRear() + param4 := obj.GetFront() + param5 := obj.DeleteFront() + param6 := obj.InsertFront(6) + param7 := obj.InsertLast(5) + param8 := obj.InsertFront(9) + param9 := obj.GetFront() + param10 := obj.InsertFront(6) + + + fmt.Println(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) +} + + +/** + * Your MyCircularDeque object will be instantiated and called as such: + * obj := Constructor(k); + * param_1 := obj.InsertFront(value); + * param_2 := obj.InsertLast(value); + * param_3 := obj.DeleteFront(); + * param_4 := obj.DeleteLast(); + * param_5 := obj.GetFront(); + * param_6 := obj.GetRear(); + * param_7 := obj.IsEmpty(); + * param_8 := obj.IsFull(); + */ diff --git a/Week 01/id_203/LeetCode_66_203.go b/Week 01/id_203/LeetCode_66_203.go new file mode 100644 index 000000000..5bfb61098 --- /dev/null +++ b/Week 01/id_203/LeetCode_66_203.go @@ -0,0 +1,18 @@ +package leetcode + +func plusOne(digits []int) []int { + for i := len(digits) - 1; i >= 0; i-- { + if digits[i] == 9 { + digits[i] = 0 + } else { + digits[i] ++ + break + } + } + + if digits[0] > 0 { + return digits + } + + return append([]int{1}, digits...) +} \ No newline at end of file diff --git a/Week 01/id_203/NOTE.md b/Week 01/id_203/NOTE.md index a6321d6e2..11eb083ca 100644 --- a/Week 01/id_203/NOTE.md +++ b/Week 01/id_203/NOTE.md @@ -1,4 +1,55 @@ # NOTE - +### 移动零 +原本老师课中所讲的代码,在每次循环中碰到非0的数就将 `i` 下标中的数赋值给 `j` 下标的位置,但我后面思考了一下,只有在 `i` 和 `j` 不相等,即两个指针的指向位置不同的时候,才需要执行这个操作,所以稍微做了一点修改,将这个赋值语句挪进判断内,如下 + +``` +func moveZeroes(nums []int) { + j := 0 + for i := 0; i < len(nums); i++ { + if nums[i] != 0 { + if i != j { + nums[j] = nums[i] //将这个语句挪进来了 + nums[i] = 0 + } + j++ + } + } + +} +``` + +### 加一 + +这题的解题思路只看了别人的一半,受到了启发,自己把后面的步骤补充出来,跟原来的算法不太一样,LeetCode上运行是 0ms,时间复杂度是 O(n),我的算法是 + +- 从数组的最后一个元素往前循环,如果碰到的值是 `9` 这将当前的元素置 `0` ,然后进入下一个循环 +- 如果碰到非 `9` 的元素,则把当前的元素加 `1` ,跳出循环 +- 判断数组的第一个元素(即下标为0的元素)是否大于0,如果大于0则说明没有进位,直接返回这个数组即可 +- 如果第一个元素为 `0` 则说明,原数组是 `9999...`,此时需要进位,而此时的数组里面元素全是 `0`,这时只要创建一个里面只包含一个元素 `1` 的数组,然后拼接上这个全是0的数组即可 + +``` +func plusOne(digits []int) []int { + for i := len(digits) - 1; i >= 0; i-- { + if digits[i] == 9 { + digits[i] = 0 + } else { + digits[i] ++ + break + } + } + + if digits[0] > 0 { + return digits + } + + return append([]int{1}, digits...) +} +``` + +### 设计循环链表 + +这题没有看题解,其实没有涉及到复杂的算法,这题的感觉就是老师所说的自顶向下的设计,主干方法都定义好了,自己填上内容,慢慢debug做出来,在debug过程中注意到以前没有注意到的细节 + +采用双端链表,head 和 end 指针分别指向队头和队尾,队列的插入删除时间复杂度都是O(1),我那个解法在golang中运行16ms,速度超过90%左右的人 \ No newline at end of file diff --git "a/Week 01/id_203/\346\200\273\347\273\223.md" "b/Week 01/id_203/\346\200\273\347\273\223.md" new file mode 100644 index 000000000..ee1bfed11 --- /dev/null +++ "b/Week 01/id_203/\346\200\273\347\273\223.md" @@ -0,0 +1,107 @@ +### 解题思路 + +- 找最近重复子问题 +- 升维+空间换时间(例:跳表) +- 左右夹逼:左右边界向中间收敛 +- 如果一个问题具有最近相关性,考虑用 `栈` 来解决 + +以上是老师讲的解题思路,特别是第一条,基本上贯穿在所有的算法中,计算机能处理的就是 `if else for loop recursion` 以不变应万变 + +### 改写代码 + +Java没有在工作中使用过,只是会了基本的语法,不过文档都基本上都能看懂,甚至不用看文档,只看Deque的类方法名就能猜出它的作用了,这块相对比较容易 + +``` + Deque deque = new LinkedList(); + deque.addFirst("a"); + deque.addFirst("b"); + deque.addFirst("c"); + System.out.println(deque); + + String str = deque.peekFirst(); + System.out.println(str); + System.out.println(deque); + + while (deque.size() > 0) { + System.out.println(deque.removeFirst()); + } + System.out.println(deque); +``` + +### 源码分析 + +1) Queue源码分析 + +`Queue` 在java中是一个接口,继承自 `Collection` 接口,咱就先不看继承的接口了,在源码里 `Queue` 只有6个方法,分别是: + +- `boolean add(E e)` : 向队列里添加元素 +- `boolean offer(E e)`: 也是向队列里添加元素,如果使用有容量限制的队列,那么使用这个方法更好 +- `E remove()`: 返回并删除队头元素,即队列的 `pop` 操作 +- `E poll()`: 也是返回并删除队头元素,与 `remove()` 方法的区别是,这个方法在找不到元素的时候不会抛出异常,只会返回 `null` +- `E element()`: 返回队头元素,但不删除,如果找不到则抛出异常 +- `E peek()`: 返回队头元素,但不删除,如果找不到则返回 `null` 不抛出异常 + +Java中很多基础类都是有实现这个接口,各种队列,链表,栈等等,说明这些数据结构都有相同的操作 + +2) PriorityQueue源码分析 + +PriorityQueue底层是一个对象数组 `transient Object[] queue;` 由于对Java不熟, `transient` 关键字不是很理解,后来查了资料,这个关键字是为了让成员变量不被序列化,不过这个不深究,目前也搞不懂为啥要加这个 + +PriorityQueue有重载6个不同的构造函数,要嘛初始化容量,要嘛初始化比较器,初始化容量是必须的,如果不指定的话,类本身有一个默认的容量 `DEFAULT_INITIAL_CAPACITY = 11` 至于这个初始化容量为什么是11就不懂了 + +如果没有指定 `Comparator` 的话,将会采用元素的自然排序 + +``` + /** + * The comparator, or null if priority queue uses elements' + * natural ordering. + */ + private final Comparator comparator; +``` + +PriorityQueue的 `add` 方法和 `offer` 方法是一样的,实际上是前者调用后者; + +``` + /** + * Inserts the specified element into this priority queue. + * + * @return {@code true} (as specified by {@link Collection#add}) + * @throws ClassCastException if the specified element cannot be + * compared with elements currently in this priority queue + * according to the priority queue's ordering + * @throws NullPointerException if the specified element is null + */ + public boolean add(E e) { + return offer(e); + } + + /** + * Inserts the specified element into this priority queue. + * + * @return {@code true} (as specified by {@link Queue#offer}) + * @throws ClassCastException if the specified element cannot be + * compared with elements currently in this priority queue + * according to the priority queue's ordering + * @throws NullPointerException if the specified element is null + */ + public boolean offer(E e) { + if (e == null) + throw new NullPointerException(); + modCount++; + int i = size; + if (i >= queue.length) + grow(i + 1); + size = i + 1; + if (i == 0) + queue[0] = e; + else + siftUp(i, e); + return true; + } +``` + +`offer()` 这个插入元素的方法,首先会计算队列的容量是不是够用,如果不够的话就 `grow` 一下,队列内为空的时候,直接插入元素,放在数组第一个位置,后面再插入其他的元素的话就要调用 `siftUp` 进行比较排序了,要嘛使用元素的自然排序,要嘛使用比较器排序,删除操作也要进行同样的处理,所以优先队列的插入和删除操作时间复杂度都是 O(logn) + +`peek()` 操作只是取得队头元素,并不删除元素,时间复杂度是O(1) + +`poll()` 操作既要取得队头元素,还要把队头元素从队列中删除,时间复杂度是O(logn) \ No newline at end of file diff --git a/Week 01/id_213/213_week02/Anagram.java b/Week 01/id_213/213_week02/Anagram.java new file mode 100644 index 000000000..de4ce53e5 --- /dev/null +++ b/Week 01/id_213/213_week02/Anagram.java @@ -0,0 +1,27 @@ +public class Solution{ + +//这个题目解法颇多。 +//思路1.可将字符串转为字符数组,然后排序。最后使用数组的比较方法即可求得。 + + public boolean isAnagram(String s,String t) { + char[] c = s.toCharArray(); + char[] c1 = t.toCharArray(); + Arrays.sort(c); + Arrays.sort(c1); + return Arrays.equals(c,c1); + + } +//思路2.使用字符串的ASC值做下标,记录26个字母出现的次数,两者相同返回true,否则返回false。 + public boolean isAnagram2(String s, String t) { + int[] counter = new int[26]; + for (int i = 0; i < s.length; i++) { + counter[s.charAt[i] - 'a']++; + counter[t.charAt[i] - 'a']--; + } + for(int count : counter) { + if(count !=0) return false; + } + return true; + } + +} \ No newline at end of file diff --git a/Week 01/id_213/213_week02/GroupAnagrams.java b/Week 01/id_213/213_week02/GroupAnagrams.java new file mode 100644 index 000000000..4ce57bea7 --- /dev/null +++ b/Week 01/id_213/213_week02/GroupAnagrams.java @@ -0,0 +1,19 @@ +class Solution { +//主要思路是:将字符串变成字符数组,并且排序。 +//让互为异位词的字符串以数组形式存入,然后以某一键值存入。 +//遍历结束,分类完毕。 +//获取map的值对象,得到的应该是一系列的值,存入ArrayList即可。 + public List> groupAnagrams(String[] strs) { + HashMap> map = new HashMap(); + for(int i = 0; i < strs.length; i++) { + char[] a = strs[i].toCharArray(); + Arrays.sort(a); + String key = String.valueOf(a); + if(!map.containsKey(key)) { + map.put(key,new ArrayList()); + }map.get(key).add(strs[i]); + } + return new ArrayList(map.values()); + } + +} \ No newline at end of file diff --git a/Week 01/id_213/213_week02/InorderTraversal.java b/Week 01/id_213/213_week02/InorderTraversal.java new file mode 100644 index 000000000..709e41e2e --- /dev/null +++ b/Week 01/id_213/213_week02/InorderTraversal.java @@ -0,0 +1,33 @@ +class Solution { +//二叉树的中序遍历,就是左根右遍历。 + public List inorderTraveesal(TreeNode root) { + List list = new ArrayList(); + sortt(list, root); + return list; + } + public void sortt(List list, TreeNode root) { + if (root != null) { + if(root.left != null) + sortt(list, root.left); + list.add(root.val); + if(root.right != null) + sortt(list, root.right); + } + } +//前序遍历,顾名思义就是根左右。 + public List inorderTraveesal(TreeNode root) { + List list = new ArrayList(); + sortt(list, root); + return list; + } + public void sortt(List list, TreeNode root) { + if (root != null) { + list.add(root.val); + if(root.left != null) + sortt(list, root.left); + if(root.right != null) + sortt(list, root.right); + } + } + +} \ No newline at end of file diff --git a/Week 01/id_213/213_week02/MajorityElement.java b/Week 01/id_213/213_week02/MajorityElement.java new file mode 100644 index 000000000..aaaf443fb --- /dev/null +++ b/Week 01/id_213/213_week02/MajorityElement.java @@ -0,0 +1,20 @@ +class Solution { +//个人理解的众数和题目中给出的众数是不一样的, +//个人理解是在一组树中出现次数最多的一个数。 +//思路:对于数组中出现的任何一个数,都应该有一个计数器。 +// 故得用键值对的形式的做题,以元素键,计数器为值。 + public int majorityElement(int[] nums) { + HashMap map = new HashMap(); + int num = 0; + int maxNum = 0; + for(int i = 0; i < nums.length; i++) { + int count = map.getOrDefault(nums[i],0) + 1; + if (count > num) { + num = count; + maxNum = nums[i]; + } + map.put(nums[i],count); + } + return maxNum; + } +} \ No newline at end of file diff --git a/Week 01/id_213/213_week02/N-Ary-Tree.java b/Week 01/id_213/213_week02/N-Ary-Tree.java new file mode 100644 index 000000000..ba6b1fa5f --- /dev/null +++ b/Week 01/id_213/213_week02/N-Ary-Tree.java @@ -0,0 +1,34 @@ +class Solution{ +// n叉树后续遍历,这个可以利用二叉树的遍历的思路来解这个题。 + public List postorder(Node root) { + List list = new ArrayList(); + sortt(list, root); + return list; + } + public void sortt(List list, Node root) { + if(root != null ) { + if(root.children != null) { + for(int i = 0; i < root.children.size(); i++){ + sortt(list,root.children.get(i)); + } + } + list.add(root.val); + } + } +// n叉树前序续遍历,如上 +// n叉树不不存在中序遍历的。 + public List preorder(Node root) { + List list = new ArrayList(); + sortt1(list, root); + return list; + } + public void sortt1(List list, Node root) { + if (root != null) { + list.add(root.val); + if (root.children != null) { + for(int i = 0; i < root.children.size(); i++) + sortt1(list, root.children.get(i)); + } + } + } +} \ No newline at end of file diff --git a/Week 01/id_213/NOTE.md b/Week 01/id_213/NOTE.md index a6321d6e2..21f7a5fd9 100644 --- a/Week 01/id_213/NOTE.md +++ b/Week 01/id_213/NOTE.md @@ -1,4 +1,43 @@ # NOTE +class MinStack { + private Stack stackData; + private Stack stackMin; + + public MinStack() { + this.stackData = new Stack(); + this.stackMin = new Stack(); + } + + public void push(int x) { + this.stackData.push(x); + if (this.stackMin.isEmpty() || x <= this.getMin()) { + this.stackMin.push(x); + } + } + + public void pop() { + if (this.stackData.isEmpty()) + throw new RuntimeException("Your stack is empty."); + int val = this.stackData.pop(); + if (val == this.getMin()) + this.stackMin.pop(); + } + + public int top() { + if (this.stackData.isEmpty()) + throw new RuntimeException("Your stack is empty."); + return this.stackData.peek(); + } + + public int getMin() { + if (this.stackMin.isEmpty()) + throw new RuntimeException("Your stack is empty."); + return this.stackMin.peek(); + } +} + + + + - diff --git a/Week 01/id_213/algorithm/ClimbStairs.java b/Week 01/id_213/algorithm/ClimbStairs.java new file mode 100644 index 000000000..77d5241ec --- /dev/null +++ b/Week 01/id_213/algorithm/ClimbStairs.java @@ -0,0 +1,23 @@ +class Solution { +//这是个题目用递归的思量来做 +// 要点:1.寻找重复的动作。可以从前面找,也可以从后面找。 +// 该题从后面开始从前面开始可以找到斐波拉契函数的规律。 +// 从后面可以找到最后一阶可以由前面一阶迈一步,前面俩阶迈两步这种方式。 +// 而前面一阶又可以分为………… +// 实现:该方法的实现比递归调用好的点是不会出现栈溢出。 +// 通过循环赋值的方式减小了对栈的压力。 + + public int climbStairs(int n) { + if(n <= 2 ) return n; + int s1 = 1; + int s2 = 2; + int s3 = 0; + for(int i = 3; i <= n; i++) { + s3 = s2 + s1; + s1 = s2; + s2 = s3; + + } + return s3; + } +} \ No newline at end of file diff --git a/Week 01/id_213/algorithm/LargestRectangleArea.java b/Week 01/id_213/algorithm/LargestRectangleArea.java new file mode 100644 index 000000000..d0cfc2a6b --- /dev/null +++ b/Week 01/id_213/algorithm/LargestRectangleArea.java @@ -0,0 +1,42 @@ +class Solution { +// 思路:(1)暴力解题法,遍历所有连续的块可以解该题。时间复杂度较高(1+2+……+n); +// (2) 1)递归的方式,利用类似快排的方法和最大盛水量的思路,每次去找到其中最小值,记录改下表i,并分区。 +// 2)分区区间 为 [p,i-1],[i+1,q]。并且记录在i位置得到的面积(i*该区间长度)。 +// 可行性 :1)我们首先选取整个数组,长度最长,再去找高度相对较高的。类似最大盛水一样。 +// 2)对于面积而言要连续,于是我思考到每次取出最小的。 +// (如果不取出最小的,移动下标的话,最小的始终存在,面积会越变越小,无意义。) +// 3)并将在改下表求得的面积赋值与改下表数组。类似于快排每次找出一个数。 +// (当然对相同数值也有处理,在取最小值的时候,从左至右遍历。) +// 收获:这道题算是解决了,觉得还有优化的地方,利用递归,能够运算的量并不大,容易引起栈内存溢出。 +// 关于用栈的方法还需要熟悉。 + public int largestRectangleArea(int[] heights) { + searchLargest(heights, 0, heights.length - 1); + return search(heights); + } + private int search(int[] heights) { + int k = 0; + for (int i = 0; i < heights.length; i++) { + if (heights[i] > k) + k = heights[i]; + } + return k; + } + public void searchLargest(int[] a, int p, int q) { + if (p >= q) return; + int l = searchMinest(a, p, q); + searchLargest(a, p, l - 1); + searchLargest(a, l + 1, q); + } + public int searchMinest(int[] a, int p, int q) { + int k = a[p]; + int j = p; + for (int i = p; i <= q; i++) { + if (a[i] < k) { + k = a[i]; + j = i; + } + } + a[j] = (q - p + 1) * k; + return j; + } +} \ No newline at end of file diff --git a/Week 01/id_213/algorithm/MaxArea.java b/Week 01/id_213/algorithm/MaxArea.java new file mode 100644 index 000000000..9bb1ee3bd --- /dev/null +++ b/Week 01/id_213/algorithm/MaxArea.java @@ -0,0 +1,22 @@ +class Solution { +//这个题目一开始用暴力解法需要两重循环,复杂度为O(n^2) +//后来经过视频观看以及思考得知可以利用先确定一个变量的情况下, +//再去改变另外一个变量。时间复杂度为O(n) +//思路:1.数组长度是确定的情况下,取两端值。获得一个最大值。 +// 2. 在数组元素中取两个元素中的较小元素。并用一个指针(—引用)记录取出最大值。 +// 3.遍历元素,返回指针所指位置即可。 +// 这个解题的关键点是,左右两边个下标同时移动,确定大的,小的移动,最后可以得到两个相对大的值。 + + public int maxArea(int[] height) { + if(height.length == 1) return 0; + int square = 0; + int i = 0; int j = height.length-1; + while(i != j){ + int min = (height[i] < height[j]) ? height[i++] : height[j--]; + if(square < (j - i + 1) *min) + square = (j - i + 1) * min; + } + return square; + } + +} \ No newline at end of file diff --git a/Week 01/id_213/algorithm/MoveZeros.java b/Week 01/id_213/algorithm/MoveZeros.java new file mode 100644 index 000000000..c02b602f0 --- /dev/null +++ b/Week 01/id_213/algorithm/MoveZeros.java @@ -0,0 +1,18 @@ +class Silution{ + +// moveZeros +// 这个问题可以用快排的思想来解决,将0作为中心点。利用一个计数下标,从0开始。 +// 遍历数组,数组中所有值与0比较,如果不等于0,就将其放入数组,下标加1。如果 +// 等于零,不做处理,继续遍历。遍历完成后,剩余空间用0填充。 + public void moveZeros(int[] nums) { + int j = 0; + for(int i = 0; i deque = new LinkedList(); + deque.add("eason"); + deque.addFirst("easaon_frist"); + deque.addLast("eason_last"); + System.out.println(deque); + + String str = deque.peek(); + System.out.println(str); + System.out.println(deque); + str = deque.peekFirst(); + System.out.println(deque); + while (deque.size() > 0) { + System.out.println(deque.pollFirst()); + } + System.out.println(deque); + PriorityQueue priorityQueue; + } +} diff --git a/Week 01/id_218/LeetCode_189_218.java b/Week 01/id_218/LeetCode_189_218.java new file mode 100644 index 000000000..6f7b6e294 --- /dev/null +++ b/Week 01/id_218/LeetCode_189_218.java @@ -0,0 +1,59 @@ +package leetcode; + +/** + * https://leetcode-cn.com/problems/rotate-array/submissions/ + * + * @author eason.feng at 2019/10/20/0020 16:39 + **/ +public class LeetCode_189_218 { + + /** + * by reversing array + * @param nums + * @param k + */ + public void rotate(int[] nums, int k) { + if (k < 0 || nums == null || nums.length <= 1 || nums.length == k) { + return; + } + if (k > nums.length) { + k = k % nums.length; + } + reverse(nums, 0, nums.length - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, nums.length - 1); + } + + private void reverse(int[] nums, int start, int end) { + while (start < end) { + int tmp = nums[start]; + nums[start] = nums[end]; + nums[end] = tmp; + start++; + end--; + } + } + + /** + * Brute force way + * @param nums + * @param k + */ + public void rotateBruteForce(int[] nums, int k) { + if (k < 0 || nums == null || nums.length <= 1 || nums.length == k) { + return; + } + if (k > nums.length) { + k = k % nums.length; + } + while (k > 0) { + int tmp = nums[nums.length - 1]; + for (int j = nums.length - 1; j > 0; j--) { + nums[j] = nums[j - 1]; + } + nums[0] = tmp; + k--; + } + } + +} diff --git a/Week 01/id_218/LeetCode_26_218.java b/Week 01/id_218/LeetCode_26_218.java new file mode 100644 index 000000000..90fdd5e82 --- /dev/null +++ b/Week 01/id_218/LeetCode_26_218.java @@ -0,0 +1,24 @@ +package leetcode; + +/** + * leetcode china address: https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/ + * + * @author eason.feng at 2019/10/20/0020 16:29 + **/ +public class LeetCode_26_218 { + + public final int removeDuplicates(final int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int i = 0; + for (int j = 1; j < nums.length; j++) { + if (nums[i] != nums[j]) { + i++; + nums[i] = nums[j]; + } + } + return i + 1; + } + +} diff --git a/Week 01/id_218/NOTE.md b/Week 01/id_218/NOTE.md index a6321d6e2..85fb17314 100644 --- a/Week 01/id_218/NOTE.md +++ b/Week 01/id_218/NOTE.md @@ -1,4 +1,185 @@ -# NOTE +### 知识点 +#### 一、数组、链表、跳表的基本实现和特性 - +##### 数组 +特性:地址连续(需要加深对内存管理器的学习)、访问速度O(1),随机插入和删除慢。常用:ArrayList,非线程安全的。 +##### 链表 +特性:地址可以不连续,随机查询慢,新增和删除快。LinkedList为双向链表。 +* 单链表:tail的next为空。 +* 循环链表:tail的next指向头。 +##### 跳表 +基于LinkedList的不足,SkipList做了补充。 +特性: +* 通过增加索引的方式实现。 +* 增加多级索引。 +* 维护成本较高。且发生多次增加和删除索引后,可能导致索引是不工整的,可多多跨,或者少跨。 +* 时间复杂度为O(logn),空间复杂度为O(n) +通过SkipList的学习,可以得到如下2个重要优化算法的思想: +``` +1:升维 +2:空间换时间 +``` +##### 工程中的应用: + +* LRU可用LinkedList实现。 +* Redis的ZSET可用SkipList实现。 + + +#### 二、栈和队列的基本实现和特性 +##### 栈(Stack) +特性:先进后出。添加删除时间复杂度为O(1),查询为O(n)。**不推荐使用,建议使用Deque** +##### 队列(Queue) +特性:先进先出。添加删除时间复杂度为O(1),查询为O(n)。 +##### 双端队列Deque(Double-End Queue) +特性:双端都可以进行添加和删除。添加删除时间复杂度为O(1),查询为O(n)。 +#### 栈队列作业一:使用新的API来重构之前的Deque的使用方式: +``` +public static void main(String[] args) {    + Deque deque = new LinkedList(); + deque.add("eason"); + deque.addFirst("easaon_frist"); + deque.addLast("eason_last"); + System.out.println(deque); + + String str = deque.peek(); + System.out.println(str); + System.out.println(deque); + str = deque.peekFirst(); + System.out.println(deque); + while (deque.size() > 0) { + System.out.println(deque.pollFirst()); + } + System.out.println(deque); + } +} +``` +执行结果如下: +``` +[easaon_frist, eason, eason_last] +easaon_frist +[easaon_frist, eason, eason_last] +[easaon_frist, eason, eason_last] +easaon_frist +eason +eason_last +[] +``` + + +tips: +常见数据结构的复杂度分析: +![3973f7c936f8bf325a61c28be45c0977.png](en-resource://database/2243:0) + + +##### 工程中的应用: + +* Priority Queue(按照元素的优先级取出) +1. 特性:插入O(1),取出操作O(logN)) +2. PriorityQueue特征如下: + + * 基于Priority堆实现的无限队列 + * 非线程安全。 + * 优先级可通过Comparator来排序或者通过它们Comparable的自然顺序。插入非comparable对象,会抛出ClassCaseException。 + * 不允许null元素的存在。 + * head元素为最小元素。 + * 表现为一个平衡二顶堆。数据结构为一个平衡二叉树。 + * 时间复杂度见下表 + +| 时间复杂度 | 函数 | +| --- | --- | +| O(1) | peek、element、size | +| O(n) | remove(object)、contains(object) | +| O(logN) | offer、poll、remove()、add | + + +#### 栈队列作业二PriorityQueue源码分析: +##### 1)属性分析: +``` +/** * Priority queue represented as a balanced binary heap: the two +* children of queue[n] are queue[2*n+1] and queue[2*(n+1)].  The +* priority queue is ordered by comparator, or by the elements' +* natural ordering, if comparator is null: For each node n in the +* heap and each descendant d of n, n <= d.  The element with the +* lowest value is in queue[0], assuming the queue is nonempty. +*/ +transient Object[] queue; // non-private to simplify nested class access +``` +优先级队列表现为一个平衡二顶对。队列默认为非空 +``` +/** +* The number of elements in the priority queue. +*/ +private int size = 0; +/** +* The comparator, or null if priority queue uses elements' +* natural ordering. +*/ +private final Comparator comparator; +/** +* The number of times this priority queue has been +* structurally modified.  See AbstractList for gory details. +*/ +transient int modCount = 0; // non-private to simplify nested class access +``` +size为队列的大小,comparator为比较器,当元素使用自然序列时,属性为空。 +``` +private static final int DEFAULT_INITIAL_CAPACITY = 11; +``` +在不指定大小时,默认的初识容量。 +##### 2)核心方法分析: +* add(E e) 同 offer(E e) +* offer(E e)插入元素到priority queue: +往优先级队列中插入元素,如果队列满了,则进行扩容。插入操作必要的话是会导致堆元素调整的,以满足父节点总是小于等于子节点的要求。插入操作的时间复杂度为O(log(n)); +通过siftUp方法来完成元素插入时的调整:siftUp(index, object)方法会升高待插入元素在树中的位置index,直到待插入的元素大于或等于它待插入位置的父节点,见如下代码: +``` +private void siftUp(int k, E x) { + if (comparator != null)    + siftUpUsingComparator(k, x); + else    + siftUpComparable(k, x); +} + +@SuppressWarnings("unchecked") +private void siftUpComparable(int k, E x) { + Comparable key = (Comparable) x; + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = queue[parent]; + if (key.compareTo((E) e) >= 0) { + break; + } + queue[k] = e; + k = parent; + } + queue[k] = key; +} +@SuppressWarnings("unchecked") +private void siftUpUsingComparator(int k, E x) { + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = queue[parent]; + if (comparator.compare(x, (E) e) >= 0) + break; + queue[k] = e; + k = parent; + } + queue[k] = x; +} +``` +通过“int parent = (k - 1) >>> 1;”获取到当前要插入节点位置的父节点,比较父节点和待插入节点,如果待插入节点小于父节点,则将父节点插入到子节点的位置,然后在获取父节点的父节点循环上面的操作,直到待插入节点大于等于父节点,则在相应位置插入这个节点。最终保证代表优先级队列的平衡二叉树中,所有的子节点都大于它们的父节点,但同一层的子节点间并不需要维护大小关系。 + + + +### 总结: +通过本周的学习主要学习到了如下几点: + +* 对基础数据结构:数组、链表、栈、队列有了一定的了解。还需要对一些常见的数据结构做进一步的学习。 +* 对算法的分析方式基本掌握了,可归纳出如下几个步骤: + * 1:拿到题目先分析清楚题目(一般暴力算法可以完成)。 + * 2:如果没有思路则看源码,或者完成后,查看官方及国际站上面的讨论,查找最优的方法。 +* 学会了几种小工具,在日后的算法分析上会用的上: + * 1:双指针法。收尾指针往中间汇聚。 + * 2:快慢指针法。可用于对数组的操作,可达到空间复杂度为O(1)的效果。 + * 3:尽可能的找到重复的片段,才是突破问题的关键。 + * 注意:在遇到问题时,能将上面的三种方法运用好才是需要日后算法锻炼时需要注意的地方。 diff --git a/Week 01/id_223/LeetCode_189_223.java b/Week 01/id_223/LeetCode_189_223.java new file mode 100644 index 000000000..03bb80fbf --- /dev/null +++ b/Week 01/id_223/LeetCode_189_223.java @@ -0,0 +1,19 @@ +class Solution { + public void rotate(int[] nums, int k) { + k%=nums.length; + reverse(nums, 0, nums.length-1); + reverse(nums, 0, k-1); + reverse(nums, k, nums.length-1); + } + + public void reverse(int[] nums, int start, int end) { + int temp; + while (start < end) { + temp = nums[start]; + nums[start] = nums[end]; + nums[end] = temp; + start++; + end--; + } + } +} \ No newline at end of file diff --git a/Week 01/id_223/LeetCode_21_223.java b/Week 01/id_223/LeetCode_21_223.java new file mode 100644 index 000000000..1486c94e2 --- /dev/null +++ b/Week 01/id_223/LeetCode_21_223.java @@ -0,0 +1,27 @@ +class Solution { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + if (l1 == null) { + return l2; + } + else if (l2 == null) { + return l1; + } + else if (l1.val < l2.val) { + l1.next = mergeTwoLists(l1.next, l2); + return l1; + } + else { + l2.next = mergeTwoLists(l1, l2.next); + return l2; + } + + } + +class ListNode { + int val; + ListNode next; + ListNode(int x) { + val = x; + } +} +} \ No newline at end of file diff --git a/Week 01/id_223/NOTE.md b/Week 01/id_223/NOTE.md index a6321d6e2..5f82ae51d 100644 --- a/Week 01/id_223/NOTE.md +++ b/Week 01/id_223/NOTE.md @@ -1,4 +1,27 @@ # NOTE +### 学习总结 +### 数组、链表、跳表的基本实现和特性: +#### Part1: +- 数组:连续空间,增删慢O(n),查找快O(1) +- 链表:离散空间, 增删块O(1),查找慢O(n),每个结点需要额外的空间存储指针,需要的内存比数组大 +- 跳表:对链表的改进,增加索引,升维思想 + 空间换时间,查找降低到O(logn),增加和删除时间复杂度增大为O(logn),因为需要额外存储索引,空间复杂度为O(n) +#### Part2: +##### 数组的操作方法: +1. 循环遍历(多重遍历时注意指针下标)、快慢指针、左右指针收敛(左右夹逼)。 +2. 利用HashMap,空间换时间 +##### 链表的操作方法:递归、迭代等。 +### 栈和队列的基本实现和特性: +#### Part1: +- Stack:先入后出;添加、删除皆为 O(1) +- Queue:先入先出;添加、删除皆为 O(1) +- Deque:双端队列 + * 两端都可以出队/入队: 添加、删除皆为O(1),查询为O(n) - +- 优先队列 Priority Queue + * 插入操作:O(1) + * 取出操作:O(logN) 按照元素的优先级取出 + * 底层具体实现的数据结构较为多样和复杂:heap(堆)、bst(binary search tree 平衡二叉搜索树)、treap +#### Part2: +1. 数据结构也是根据生活中的现实逻辑抽象出来的,比如洋葱(Stack),排队(Queue) +2. 2个队列实现一个栈,2个栈实现一个队列。 diff --git a/Week 01/id_228/LeetCode_189_228.js b/Week 01/id_228/LeetCode_189_228.js new file mode 100644 index 000000000..7d2a0c8f9 --- /dev/null +++ b/Week 01/id_228/LeetCode_189_228.js @@ -0,0 +1,25 @@ +// 解法一:双重遍历,时间复杂度O(n*k) +/** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ +var rotate = function(nums, k) { + let temEnd; + let temPrev; + for (let i = 0; i < k; i++) { + temEnd = nums[nums.length - 1]; + for (let j = 0; j < nums.length; j++) { + temPrev = nums[j]; + nums[j] = temEnd; + temEnd = temPrev; + } + } +}; + +// 解法二:遍历nums数组k次,每次讲最后一个元素插入到第一个 +var rotate = function(nums, k) { + for (var i = 0; i < k; i++) { + nums.unshift(nums.pop()); + } +}; diff --git a/Week 01/id_228/LeetCode_1_228.js b/Week 01/id_228/LeetCode_1_228.js new file mode 100644 index 000000000..bd439f601 --- /dev/null +++ b/Week 01/id_228/LeetCode_1_228.js @@ -0,0 +1,16 @@ +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = function(nums, target) { + let arr = []; + for (let i = 0; i < nums.length - 1; i++) { + for (let j = i + 1; j < nums.length; j++) { + if (nums[i] + nums[j] === target) { + arr = [i, j]; + } + } + } + return arr; +}; diff --git a/Week 01/id_228/LeetCode_21_228.js b/Week 01/id_228/LeetCode_21_228.js new file mode 100644 index 000000000..52b3074a8 --- /dev/null +++ b/Week 01/id_228/LeetCode_21_228.js @@ -0,0 +1,25 @@ +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ +/** + * @param {ListNode} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ +var mergeTwoLists = function(l1, l2) { + if (l1 == null) { + return l2; + } else if (l2 == null) { + return l1; + } else if (l1.val < l2.val) { + l1.next = mergeTwoLists(l1.next, l2); + return l1; + } else { + l2.next = mergeTwoLists(l1, l2.next); + return l2; + } +}; diff --git a/Week 01/id_228/LeetCode_26_228.js b/Week 01/id_228/LeetCode_26_228.js new file mode 100644 index 000000000..983a0114c --- /dev/null +++ b/Week 01/id_228/LeetCode_26_228.js @@ -0,0 +1,14 @@ +/** + * @param {number[]} nums + * @return {number} + */ +var removeDuplicates = function(nums) { + let i = 0; + for (let j = 1; j < nums.length; j++) { + if (nums[i] !== nums[j]) { + i++; + nums[i] = nums[j]; + } + } + return i + 1; +}; diff --git a/Week 01/id_228/LeetCode_283_288.js b/Week 01/id_228/LeetCode_283_288.js new file mode 100644 index 000000000..b7b636e58 --- /dev/null +++ b/Week 01/id_228/LeetCode_283_288.js @@ -0,0 +1,16 @@ +/** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ +var moveZeroes = function(nums) { + let noZeroIndex = 0; + for (let i = 0; i < nums.length; i++) { + if (nums[i] !== 0) { + nums[noZeroIndex] = nums[i]; + noZeroIndex++ + } + } + for (let j = noZeroIndex; j < nums.length; j++) { + nums[j] = 0; + } +}; diff --git a/Week 01/id_228/LeetCode_641_228.js b/Week 01/id_228/LeetCode_641_228.js new file mode 100644 index 000000000..aa413acc1 --- /dev/null +++ b/Week 01/id_228/LeetCode_641_228.js @@ -0,0 +1,109 @@ +/** + * Initialize your data structure here. Set the size of the deque to be k. + * @param {number} k + */ +var MyCircularDeque = function(k) { + this.length = k + this.queue = [] +}; + +/** + * 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.queue.unshift(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.push(value) + 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 + } + this.queue.shift() + 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.queue.pop() + return true +}; + +/** + * Get the front item from the deque. + * @return {number} + */ +MyCircularDeque.prototype.getFront = function() { + if (this.isEmpty()) { + return -1 + } + return this.queue[0] +}; + +/** + * Get the last item from the deque. + * @return {number} + */ +MyCircularDeque.prototype.getRear = function() { + if (this.isEmpty()) { + return -1 + } + return this.queue[this.queue.length - 1] +}; + +/** + * Checks whether the circular deque is empty or not. + * @return {boolean} + */ +MyCircularDeque.prototype.isEmpty = function() { + return this.queue.length === 0 +}; + +/** + * Checks whether the circular deque is full or not. + * @return {boolean} + */ +MyCircularDeque.prototype.isFull = function() { + return this.queue.length === this.length +}; + +/** + * Your MyCircularDeque object will be instantiated and called as such: + * var obj = new MyCircularDeque(k) + * var param_1 = obj.insertFront(value) + * var param_2 = obj.insertLast(value) + * var param_3 = obj.deleteFront() + * var param_4 = obj.deleteLast() + * var param_5 = obj.getFront() + * var param_6 = obj.getRear() + * var param_7 = obj.isEmpty() + * var param_8 = obj.isFull() + */ \ No newline at end of file diff --git a/Week 01/id_228/LeetCode_66_228.js b/Week 01/id_228/LeetCode_66_228.js new file mode 100644 index 000000000..b4868a1b3 --- /dev/null +++ b/Week 01/id_228/LeetCode_66_228.js @@ -0,0 +1,27 @@ +/** + * @param {number[]} digits + * @return {number[]} + */ + +/** + * 由于大数组精度丢失问题,该解法不能解决数据过大的问题 +*/ +var plusOne = function(digits) { + return `${digits.join('') + 1}`.split('') +}; + +/** + * 逆序遍历,然后+1,如果+1后大于10,则继续遍历,进一位,否则直接return当前数组,如果遍历到最后,则表示首位也要进1 + */ +var plusOne = function(digits) { + for (let i = digits.length - 1; i >= 0; i--) { + ++digits[i] + if (digits[i] < 10) { + return digits + } else { + digits[i] = digits[i] % 10 + } + } + arr[0] = arr[0] % 10 + arr.unshift(1) +}; \ No newline at end of file diff --git a/Week 01/id_228/LeetCode_88_228.js b/Week 01/id_228/LeetCode_88_228.js new file mode 100644 index 000000000..20fa6edd9 --- /dev/null +++ b/Week 01/id_228/LeetCode_88_228.js @@ -0,0 +1,12 @@ +/** + * @param {number[]} nums1 + * @param {number} m + * @param {number[]} nums2 + * @param {number} n + * @return {void} Do not return anything, modify nums1 in-place instead. + */ +var merge = function(nums1, m, nums2, n) { + let tempNums1 = nums1.slice(0, m); + let tempNums2 = nums2.slice(0, n); + Object.assign(nums1, tempNums1.concat(tempNums2).sort((a, b) => a - b)); +}; diff --git a/Week 01/id_233/LeetCode_1_233.java b/Week 01/id_233/LeetCode_1_233.java new file mode 100644 index 000000000..042b7e532 --- /dev/null +++ b/Week 01/id_233/LeetCode_1_233.java @@ -0,0 +1,29 @@ +//给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 +// +// 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 +// +// 示例: +// +// 给定 nums = [2, 7, 11, 15], target = 9 +// +//因为 nums[0] + nums[1] = 2 + 7 = 9 +//所以返回 [0, 1] +// +// Related Topics 数组 哈希表 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class LeetCode_1_233 { + public int[] twoSum(int[] nums, int target) { + HashMap content = new HashMap<>(); + for(int i=0;i nonzeroIndex) { + nums[nonzeroIndex] = nums[i]; + nums[i] = 0; + } + nonzeroIndex++; + } + } + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 01/id_233/NOTE.md b/Week 01/id_233/NOTE.md index a6321d6e2..0eb78d812 100644 --- a/Week 01/id_233/NOTE.md +++ b/Week 01/id_233/NOTE.md @@ -1,4 +1,296 @@ -# NOTE +# 233-Week 01 学习总结 +### 学习内容 +- 数组、链表、跳表、栈、队列、双端队列、优先队列 +- 阅读每个数据结构的 java 实现 +- 做题 leetCode - +### 牢记师傅的话 +- 做题最大误区是只做一遍题 +- 优化思想:升维即空间换时间 +### [Week 01 学习笔记链接](https://mubu.com/doc/x1I3Q84PQ0) + +### 改写Deque的代码 +``` +public class DequeTest { + public static void main(String[] args) { + Deque deque = new LinkedList(); + deque.addFirst("f1"); + deque.addFirst("f2"); + deque.addLast("l1"); + deque.addLast("l2"); + deque.addFirst("f3"); + System.out.println(deque); + System.out.println(deque.peekFirst()); + System.out.println(deque.getLast()); + + while (deque.size() > 0) { + System.out.println(deque.removeFirst()); + } + System.out.println(deque); + } +} +``` +## Queue 源码分析 +- 先进先出 first in fist out (FIFO) +### 时间复杂度 +- 添加、删除皆为O(1),数据无序查询为O(n) +#### Queue是所有队列的顶级接口,定义了6个方法 + +操作 | 抛出异常 | 返回特定值 +---|---|--- +Insert | add() | offer(e) false +Remove | remove() | poll() null +Examine | element() | peek() null +``` +public interface Queue extends Collection { + //向队列中插入一个元素,并返回true + //如果队列已满,抛出IllegalStateException异常 + boolean add(E e); + //向队列中插入一个元素,并返回true + //如果队列已满,返回false + boolean offer(E e); + //取出队列头部的元素,并从队列中移除 + //队列为空,抛出NoSuchElementException异常 + E remove(); + //取出队列头部的元素,并从队列中移除 + //队列为空,返回null + E poll(); + //取出队列头部的元素,但并不移除 + //如果队列为空,抛出NoSuchElementException异常 + E element(); + //取出队列头部的元素,但并不移除 + //队列为空,返回null + E peek(); +} +``` +## Priority Queue 源码分析 +- 优先级队列,是0个或多个元素的集合,集合中的每个元素都有一个权重值,每次出队都弹出优先级最大或最小的元素。 +- 底层实现的数据结构较为多样和复杂:heap、bst(二叉搜索树)、treap +### 时间复杂度 +- 添加 O(1);取出 O(logN) +### 成员变量 +``` +// 默认容量 +private static final int DEFAULT_INITIAL_CAPACITY = 11; +// 存储元素的地方 +transient Object[] queue; +// 元素个数 +private int size = 0; +// 比较器,在优先级队列中,也有两种方式比较元素,一种是元素的自然顺序,一种是通过比较器来比较 +private final Comparator comparator; +// 修改次数 修改次数,有这个属性表示PriorityQueue也是fast-fail的 +transient int modCount = 0; +``` +- queue[n] 的孩子节点是 queue[2*n+1] 和 queue[2*(n+1)] +### 构造函数 +- PriorityQueue的构造函数大致分为两类,一种是确定数组初始化大小和Comparator,另一种是由Collection对象构造 +``` +public PriorityQueue(Collection c) { + if (c instanceof SortedSet) { + SortedSet ss = (SortedSet) c; + this.comparator = (Comparator) ss.comparator(); + initElementsFromCollection(ss); + } + else if (c instanceof PriorityQueue) { + PriorityQueue pq = (PriorityQueue) c; + this.comparator = (Comparator) pq.comparator(); + initFromPriorityQueue(pq); + } + else { + this.comparator = null; + initFromCollection(c); + } +} +``` +- SortedSet和PriorityQueue这两种在构造的时候可以获取Comparator(也可能获取不到,因为是自然排序),其余的都是直接构造,如果这个对象没有实现Comparable接口,那么在使用的时候就会产生一个==ClassCastException== + +### 入队 +> 入队有两个方法,add(E e)和offer(E e),两者是一致的,add(E e)也是调用的offer(E e)。 + +``` +public boolean add(E e) { + return offer(e); +} + +public boolean offer(E e) { + // 不支持null元素 + if (e == null) + throw new NullPointerException(); + modCount++; + // 取size + int i = size; + // 元素个数达到最大容量了,扩容 + if (i >= queue.length) + grow(i + 1); + // 元素个数加1 + size = i + 1; + // 如果还没有元素 + // 直接插入到数组第一个位置 + // 这里跟我们之前讲堆不一样了 + // java里面是从0开始的 + // 我们说的堆是从1开始的 + if (i == 0) + queue[0] = e; + else + // 否则,插入元素到数组size的位置,也就是最后一个元素的下一位 + // 注意这里的size不是数组大小,而是元素个数 + // 然后,再做自下而上的堆化 + siftUp(i, e); + return true; +} + +private void siftUp(int k, E x) { + // 根据是否有比较器,使用不同的方法 + if (comparator != null) + siftUpUsingComparator(k, x); + else + siftUpComparable(k, x); +} + +@SuppressWarnings("unchecked") +private void siftUpComparable(int k, E x) { + Comparable key = (Comparable) x; + while (k > 0) { + // 找到父节点的位置 + // 因为元素是从0开始的,所以减1之后再除以2 + int parent = (k - 1) >>> 1; + // 父节点的值 + Object e = queue[parent]; + // 比较插入的元素与父节点的值 + // 如果比父节点大,则跳出循环 + // 否则交换位置 + if (key.compareTo((E) e) >= 0) + break; + // 与父节点交换位置 + queue[k] = e; + // 现在插入的元素位置移到了父节点的位置 + // 继续与父节点再比较 + k = parent; + } + // 最后找到应该插入的位置,放入元素 + queue[k] = key; +} +``` +### 出队 +> 出队有两个方法,remove()和poll(),remove()也是调用的poll(),只是没有元素的时候抛出异常。 + +``` +public E remove() { + // 调用poll弹出队首元素 + E x = poll(); + if (x != null) + // 有元素就返回弹出的元素 + return x; + else + // 没有元素就抛出异常 + throw new NoSuchElementException(); +} + +@SuppressWarnings("unchecked") +public E poll() { + // 如果size为0,说明没有元素 + if (size == 0) + return null; + // 弹出元素,元素个数减1 + int s = --size; + modCount++; + // 队列首元素 + E result = (E) queue[0]; + // 队列末元素 + E x = (E) queue[s]; + // 将队列末元素删除 + queue[s] = null; + // 如果弹出元素后还有元素 + if (s != 0) + // 将队列末元素移到队列首 + // 再做自上而下的堆化 + siftDown(0, x); + // 返回弹出的元素 + return result; +} + +private void siftDown(int k, E x) { + // 根据是否有比较器,选择不同的方法 + if (comparator != null) + siftDownUsingComparator(k, x); + else + siftDownComparable(k, x); +} + +@SuppressWarnings("unchecked") +private void siftDownComparable(int k, E x) { + Comparable key = (Comparable)x; + // 只需要比较一半就行了,因为叶子节点占了一半的元素 + int half = size >>> 1; // loop while a non-leaf + while (k < half) { + // 寻找子节点的位置,这里加1是因为元素从0号位置开始 + int child = (k << 1) + 1; // assume left child is least + // 左子节点的值 + Object c = queue[child]; + // 右子节点的位置 + int right = child + 1; + if (right < size && + ((Comparable) c).compareTo((E) queue[right]) > 0) + // 左右节点取其小者 + c = queue[child = right]; + // 如果比子节点都小,则结束 + if (key.compareTo((E) c) <= 0) + break; + // 如果比最小的子节点大,则交换位置 + queue[k] = c; + // 指针移到最小子节点的位置继续往下比较 + k = child; + } + // 找到正确的位置,放入元素 + queue[k] = key; +} +``` +### 扩容 +1. 当数组比较小(小于64)的时候每次扩容容量翻倍; +2. 当数组比较大的时候每次扩容只增加一半的容量 +``` +private void grow(int minCapacity) { + // 旧容量 + int oldCapacity = queue.length; + // Double size if small; else grow by 50% + // 旧容量小于64时,容量翻倍 + // 旧容量大于等于64,容量只增加旧容量的一半 + int newCapacity = oldCapacity + ((oldCapacity < 64) ? + (oldCapacity + 2) : + (oldCapacity >> 1)); + // overflow-conscious code + // 检查是否溢出 + if (newCapacity - MAX_ARRAY_SIZE > 0) + newCapacity = hugeCapacity(minCapacity); + + // 创建出一个新容量大小的新数组并把旧数组元素拷贝过去 + queue = Arrays.copyOf(queue, newCapacity); +} + +private static int hugeCapacity(int minCapacity) { + if (minCapacity < 0) // overflow + throw new OutOfMemoryError(); + return (minCapacity > MAX_ARRAY_SIZE) ? + Integer.MAX_VALUE : + MAX_ARRAY_SIZE; +} +``` +### 取队首元素 +> 取队首元素有两个方法,element()和peek(),element()也是调用的peek(),只是没取到元素时抛出异常 + +1. 如果有元素就取下标0的元素 +2. 如果没有元素就返回null,element()抛出异常 + +``` +public E element() { + E x = peek(); + if (x != null) + return x; + else + throw new NoSuchElementException(); +} +public E peek() { + return (size == 0) ? null : (E) queue[0]; +} +``` \ No newline at end of file diff --git a/Week 01/id_243/LeetCode_189_243.java b/Week 01/id_243/LeetCode_189_243.java new file mode 100644 index 000000000..766f9d87f --- /dev/null +++ b/Week 01/id_243/LeetCode_189_243.java @@ -0,0 +1,48 @@ +/** + * @author eazonshaw + * @date 2019/10/20 14:34 + * + * 题目:189. 旋转数组 + * 链接:https://leetcode-cn.com/problems/rotate-array/ + * 描述:给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。 + */ +public class LeetCode_189_243 { + + /** + * 1.暴力法:遍历所有元素,向右移动三次 + */ + public void rotate1(int[] nums, int k) { + for(int j=0;j 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 + * + * > 示例: + * 输入: [0,1,0,3,12] + * 输出: [1,3,12,0,0] + * + * > 说明: + * 必须在原数组上操作,不能拷贝额外的数组。 + * 尽量减少操作次数 + */ +public class LeetCode_238_243 { + /** + * 冒泡解法 + */ + public void moveZeroes1(int[] nums) { + int len = nums.length; + for(int i=0;i deque = new LinkedList<>(); + //入队 + deque.addFirst("a"); + deque.addFirst("b"); + deque.addFirst("c"); + System.out.println(deque); + //队尾 + String str = deque.getFirst(); + System.out.println(str); + System.out.println(deque); + //出队 + while (!deque.isEmpty()){ + System.out.println(deque.removeFirst()); + } + System.out.println(deque); + } +} +``` +#### 分析 Queue 和 Priority Queue 的源码 +> jdk:1.8 +##### Queue接口的方法 + +| Throws exception | Returns special value | +|---|---| +|add|offer| +|remove|poll| +|element|peek| + +##### 基于链表LinkedList的实现 + +1. 新增 +> add方法等同于addLast方法,add是基于List的实现,addLast是基于deque的实现。实现逻辑主要是将当前元素e标记置为链表的last节点。 +```java +public boolean add(E e) { + //此处将元素e标记为tail + linkLast(e); + return true; +} +``` +2. 删除 +> remove方法等同于removeFirst方法。实现逻辑是释放链表的first节点。 +``` +public E remove() { + return removeFirst(); +} +public E removeFirst() { + final Node f = first; + if (f == null) + throw new NoSuchElementException(); + return unlinkFirst(f); +} +private E unlinkFirst(Node f) { + //默认f就是first节点且不为空,因为在removeFirst已经验证过且抛异常了 + // assert f == first && f != null; + //将first节点的item置空且next指针置空,有助于gc + final E element = f.item; + final Node next = f.next; + f.item = null; + f.next = null; // help GC + //将first节点置为next指针 + first = next; + //若next指针为空的话,last节点也置空,否则将next的prev指针置空,这里才是彻彻底底与原来的first节点say bye bye~ + if (next == null) + last = null; + else + next.prev = null; + size--; + modCount++; + return element; +} +``` +3. 取第一个元素 +> 比较简单,就是取fist节点的item。 +```java +public E element() { + return getFirst(); +} +public E getFirst() { + final Node f = first; + if (f == null) + throw new NoSuchElementException(); + return f.item; +} +``` +##### 基于PriorityQueue的实现 +###### 优先级队列 PriorityQueue 的实现原理 +* Java中PriorityQueue通过二叉小顶堆实现,,可以用一棵完全二叉树表示。 +* 优先队列的作用是能保证每次取出的元素都是队列中权值最小的。元素大小的评判可以通过元素本身的自然顺序(natural ordering),也可以通过构造时传入的比较器comparator。 +> 小顶堆:任意一个非叶子节点的权值,都不大于其左右子节点的权值。 + +> 实验环境:https://visualgo.net/zh/heap + +初始化数组: +``` +/** + * Priority queue represented as a balanced binary heap: the two + * children of queue[n] are queue[2*n+1] and queue[2*(n+1)]. The + * priority queue is ordered by comparator, or by the elements' + * natural ordering, if comparator is null: For each node n in the + * heap and each descendant d of n, n <= d. The element with the + * lowest value is in queue[0], assuming the queue is nonempty. + */ +transient Object[] queue; +``` +父子节点的编号之间有如下关系: +* leftNo = parentNo * 2 + 1 +* rightNo = parentNo * 2 + 2 +* parentNo = (nodeNo - 1) / 2 + +++(后续)mark:对堆和树的内容不够了解,留个疑问?后续分析优先队列的实现算法++ - diff --git a/Week 01/id_253/LeetCode_189_253.c b/Week 01/id_253/LeetCode_189_253.c new file mode 100644 index 000000000..972934ed3 --- /dev/null +++ b/Week 01/id_253/LeetCode_189_253.c @@ -0,0 +1,34 @@ +void rotate(int* nums, int numsSize, int k){ + int temp[numsSize]; + if(numsSize == 1){ + return ; + } + for(int i=0;i k){ + for(int i=0;inumsSize); + for(int i=0;i map=new HashMap<>(); + for(int i=0;i= l2.val){ + prev.next=l2; + l2=l2.next; + } + else{ + prev.next=l1; + l1=l1.next; + } + prev=prev.next; + } + if(l1 == null){ + prev.next=l2; + } + else{ + prev.next=l1; + } + return prehead.next; + } +} diff --git a/Week 01/id_253/LeetCode_26_253.c b/Week 01/id_253/LeetCode_26_253.c new file mode 100644 index 000000000..01e81bf12 --- /dev/null +++ b/Week 01/id_253/LeetCode_26_253.c @@ -0,0 +1,11 @@ +int removeDuplicates(int* nums, int numsSize){ + int j=0,i; + if(numsSize == 0) return 0; + for(i=0;i stack = new Stack<>(); + int current=0; + while(current < height.length){ + while(!stack.empty() && height[current] > height[stack.peek()]){ + int h=height[stack.peek()]; + stack.pop(); + if(stack.empty()){ + break; + } + int distance = current - stack.peek() - 1; + int min=Math.min(height[stack.peek()],height[current]); + sum = sum + distance * (min-h); + } + stack.push(current); + current++; + } + return sum; + } +} diff --git a/Week 01/id_253/LeetCode_641_253.cpp b/Week 01/id_253/LeetCode_641_253.cpp new file mode 100644 index 000000000..43f49af16 --- /dev/null +++ b/Week 01/id_253/LeetCode_641_253.cpp @@ -0,0 +1,51 @@ +class MyCircularDeque { + private: + vector buffer; + int cnt; + int k; + int front; + int rear; +public: + MyCircularDeque(int k): buffer(k, 0), cnt(0), k(k), front(k - 1), rear(0) { + } + bool insertFront(int value) { + if(k==cnt) return false; + buffer[front] = value; + front=(front-1+k)%k; + cnt++; + return true; + } + bool insertLast(int value) { + if(k==cnt) return false; + buffer[rear]=value; + rear=(rear+1)%k; + cnt++; + return true; + } + bool deleteFront() { + if(cnt == 0) return false; + front=(front+1)%k; + cnt--; + return true; + } + bool deleteLast() { + if(cnt == 0) return false; + rear=(rear-1+k)%k; + cnt--; + return true; + } + int getFront() { + if(cnt == 0) return -1; + return buffer[(front+1)%k]; + } + int getRear() { + if(cnt == 0) return -1; + return buffer[(rear-1+k)%k]; + } + bool isEmpty() { + return cnt==0; + } + bool isFull() { + return cnt == k; + } +}; diff --git a/Week 01/id_253/LeetCode_66_253.java b/Week 01/id_253/LeetCode_66_253.java new file mode 100644 index 000000000..8efe55a1d --- /dev/null +++ b/Week 01/id_253/LeetCode_66_253.java @@ -0,0 +1,14 @@ +class Solution { + public int[] plusOne(int[] digits) { + for(int i=digits.length-1;i>=0;i--){ + digits[i]++; + digits[i]%=10; + if(digits[i] != 0){ + return digits; + } + } + digits = new int[digits.length + 1]; + digits[0]=1; + return digits; + } +} diff --git a/Week 01/id_253/LeetCode_88_253.java b/Week 01/id_253/LeetCode_88_253.java new file mode 100644 index 000000000..0010d1b21 --- /dev/null +++ b/Week 01/id_253/LeetCode_88_253.java @@ -0,0 +1,15 @@ +class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + int p1=m-1; + int p2=n-1; + int p=m+n-1; + while(p1>=0 && p2 >=0){ + nums1[p--]=(nums1[p1]=0){ + do{ + nums1[p--]=nums2[p2--]; + }while(p2>=0); + } + } +} diff --git a/Week 01/id_258/LeetCode_189_258.js b/Week 01/id_258/LeetCode_189_258.js new file mode 100644 index 000000000..9828bb88b --- /dev/null +++ b/Week 01/id_258/LeetCode_189_258.js @@ -0,0 +1,26 @@ +/* + * @lc app=leetcode.cn id=189 lang=javascript + * + * [189] 旋转数组 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ +var rotate = function (nums, k) { + let index = 0; + if (k > nums.length) { + k = k % nums.length; + } + index = nums.length - k; + nums.unshift(...nums.splice(index, k)) +}; + +let nums = [1, 2, 3, 4, 5, 6, 7] +let k = 3 +rotate(nums, k) +console.log(nums) +// @lc code=end \ No newline at end of file diff --git a/Week 01/id_258/LeetCode_1_258.js b/Week 01/id_258/LeetCode_1_258.js new file mode 100644 index 000000000..70ef020e1 --- /dev/null +++ b/Week 01/id_258/LeetCode_1_258.js @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode.cn id=1 lang=javascript + * + * [1] 两数之和 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = function (nums, target) { + for (let i = 0; i < nums.length; i++) { + let j = nums.indexOf(target - nums[i]) + if (j !== -1 && i !== j) { + return [i, j] + } + } + return [] +}; +// var twoSum = function (nums, target) { +// // 利用哈希表的key值唯一的特性 +// // 升维操作 空间换时间 +// let memory = new Map(); +// for (let i = 0; i < nums.length; i++) { +// if (memory.has(target - nums[i])) { +// return [memory.get(target - nums[i]), i]; +// } else { +// memory.set(nums[i], i); +// } +// } +// return [] +// }; +// @lc code=end \ No newline at end of file diff --git a/Week 01/id_258/LeetCode_26_258.js b/Week 01/id_258/LeetCode_26_258.js new file mode 100644 index 000000000..0e10f50e8 --- /dev/null +++ b/Week 01/id_258/LeetCode_26_258.js @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode.cn id=26 lang=javascript + * + * [26] 删除排序数组中的重复项 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +// var removeDuplicates = function (nums) { +// let memory = new Map(); +// for (let i = 0; i < nums.length; i++) { +// if (!memory.has(nums[i])) { +// memory.set(nums[i]) +// } +// } +// return memory.size; +// }; +var removeDuplicates = function (nums) { + // let deletIndex = []; + let index = 0; + for (let i = 0; i < nums.length; i++) { + if (nums[i] != nums[index]) { + nums[++index] = nums[i] + } + } + nums.splice(index, nums.length - ++index) + return index; +}; +let nums = [0, 0, 1, 1, 1, 2, 2, 3, 3, 4] +console.log(removeDuplicates(nums)) +console.log(nums) +// @lc code=end \ No newline at end of file diff --git a/Week 01/id_258/LeetCode_283_258.js b/Week 01/id_258/LeetCode_283_258.js new file mode 100644 index 000000000..598348211 --- /dev/null +++ b/Week 01/id_258/LeetCode_283_258.js @@ -0,0 +1,36 @@ +/* + * @lc app=leetcode.cn id=283 lang=javascript + * + * [283] 移动零 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ +var moveZeroes = function (nums) { + // 找到目标数组0项的开始下标 + let zeroIndex = 0; + for (let i = 0; i < nums.length; i++) { + if (nums[i]) { + nums[zeroIndex++] = nums[i]; + } + } + + // 将数组末端为0的元素置0 + for (let i = zeroIndex; i < nums.length; i++) { + nums[i] = 0; + } +}; + +// var moveZeroes = function (nums) { +// for (let i = nums.length; i--;) { +// if (nums[i] === 0) { +// nums.splice(i, 1); +// nums.push(0); +// } +// } +// return nums; +// } +// @lc code=end \ No newline at end of file diff --git a/Week 01/id_263/LeetCode_189_263.java b/Week 01/id_263/LeetCode_189_263.java new file mode 100644 index 000000000..ce199144d --- /dev/null +++ b/Week 01/id_263/LeetCode_189_263.java @@ -0,0 +1,46 @@ +package io.beansoft.pencil.leetcode; + + +/** + * @author beanlam + * @version 1.0 + * @date 2019-10-20 13:29 + */ +public class LeetCode_189_263 { + + public void rotate(int[] nums, int k) { + if (nums.length < 2) { + return; + } + + k = k % nums.length; + if (k == 0) { + return; + } + + + int beginIndex = 0; + int currentIndex = 0; + + int prevValue = nums[currentIndex]; + + int count = 1; // 限制总次数 + while (count <= nums.length) { + int nextIndex = (currentIndex + k) % nums.length; + + int temp = nums[nextIndex]; + nums[nextIndex] = prevValue; + prevValue = temp; + currentIndex = nextIndex; + + if (currentIndex == beginIndex) {//遇到循环,初始位置向前步进,同时更新缓存值 + currentIndex++; + beginIndex++; + prevValue = nums[currentIndex]; + } + + count++; + } + + } +} \ No newline at end of file diff --git a/Week 01/id_263/LeetCode_1_263.java b/Week 01/id_263/LeetCode_1_263.java new file mode 100644 index 000000000..0ca2aeb3a --- /dev/null +++ b/Week 01/id_263/LeetCode_1_263.java @@ -0,0 +1,54 @@ +package io.beansoft.pencil.leetcode; + + +/** + * @author beanlam + * @version 1.0 + * @date 2019-10-20 16:54 + */ +public class LeetCode_1_263 { + + public ListNode addTwoNumbers(ListNode l1, ListNode l2) { + ListNode result = null; + ListNode a = l1; + ListNode b = l2; + + int upValue = 0; + ListNode currentNodePointer = result; + while (a != null || b != null) { + int aval = 0; + int bval = 0; + if (a != null) { + aval = a.val; + } + if (b != null) { + bval = b.val; + } + int nextUpValue = (aval + bval + upValue) >= 10 ? 1 : 0; + int currentVal = ((aval + bval + upValue) % 10); + + upValue = nextUpValue; + + if (currentNodePointer == null) { + result = new ListNode(currentVal); + currentNodePointer = result; + } else { + currentNodePointer.next = new ListNode(currentVal); + currentNodePointer = currentNodePointer.next; + } + + if (a != null) { + a = a.next; + } + if (b != null) { + b = b.next; + } + } + if (upValue > 0) { + currentNodePointer.next = new ListNode(upValue); + } + + return result; + + } +} diff --git a/Week 01/id_263/LeetCode_21_263.java b/Week 01/id_263/LeetCode_21_263.java new file mode 100644 index 000000000..7f7ee3703 --- /dev/null +++ b/Week 01/id_263/LeetCode_21_263.java @@ -0,0 +1,86 @@ +package io.beansoft.pencil.leetcode; + + +/** + * @author beanlam + * @version 1.0 + * @date 2019-10-20 14:49 + */ +public class LeetCode_21_263 { + + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + ListNode resultHeeader = new ListNode(-1); + ListNode tail = resultHeeader; + + ListNode header1 = new ListNode(-1); + header1.next = l1; + + ListNode header2 = new ListNode(-1); + header2.next = l2; + + ListNode next1 = header1.next; + ListNode next2 = header2.next; + + while (true) { + + if (next1 == null) { + while(next2 != null) { + tail.next = new ListNode(next2.val); + tail = tail.next; + next2 = next2.next; + } + break; + } + + if (next2 == null) { + while(next1 != null) { + tail.next = new ListNode(next1.val); + tail = tail.next; + next1 = next1.next; + } + break; + } + + + if (next1.val == next2.val) { + tail.next = new ListNode(next2.val); + tail = tail.next; + tail.next = new ListNode(next1.val); + tail = tail.next; + + next1 = next1.next; + next2 = next2.next; + continue; + } + + if (next1.val < next2.val) { + tail.next = new ListNode(next1.val); + tail = tail.next; + + next1 = next1.next; + continue; + } + + if (next2.val < next1.val) { + tail.next = new ListNode(next2.val); + tail = tail.next; + + next2 = next2.next; + continue; + } + } + + + return resultHeeader.next; + } + +} + +class ListNode { + int val; + ListNode next; + + ListNode(int x) { + val = x; + } +} diff --git a/Week 01/id_263/LeetCode_26_263.java b/Week 01/id_263/LeetCode_26_263.java new file mode 100644 index 000000000..fb5892d59 --- /dev/null +++ b/Week 01/id_263/LeetCode_26_263.java @@ -0,0 +1,37 @@ +package io.beansoft.pencil.leetcode; + + + +/** + * + * + * @author beanlam + * @date 2019-10-20 13:03 + * @version 1.0 + */ +public class LeetCode_26_263 { + + public int removeDuplicates(int[] nums) { + // 边界 + if (nums.length == 0) { + return 0; + } + if (nums.length == 1) { + return 1; + } + + int previousUniqueIndex = 0; + + for (int i = 1; i < nums.length; i++) { + + if (nums[i] == nums[i-1]) { + continue; + } else { + nums[++previousUniqueIndex] = nums[i]; + } + } + + return previousUniqueIndex + 1; + } + +} diff --git a/Week 01/id_263/LeetCode_283_263.java b/Week 01/id_263/LeetCode_283_263.java new file mode 100644 index 000000000..ab7fae216 --- /dev/null +++ b/Week 01/id_263/LeetCode_283_263.java @@ -0,0 +1,25 @@ +package io.beansoft.pencil.leetcode; + + +/** + * @author beanlam + * @version 1.0 + * @date 2019-10-20 16:56 + */ +public class LeetCode_283_263 { + + public void moveZeroes(int[] nums) { + + int indexOfNoneZero = 0; + + for (int i = 0; i < nums.length; i++) { + if (nums[i] != 0) { + nums[indexOfNoneZero] = nums[i]; + if (i != indexOfNoneZero) { + nums[i] = 0; + } + indexOfNoneZero++; + } + } + } +} diff --git a/Week 01/id_263/LeetCode_641_263.java b/Week 01/id_263/LeetCode_641_263.java new file mode 100644 index 000000000..6b051dede --- /dev/null +++ b/Week 01/id_263/LeetCode_641_263.java @@ -0,0 +1,133 @@ +package io.beansoft.pencil.leetcode; + + +/** + * @author beanlam + * @version 1.0 + * @date 2019-10-20 17:37 + */ +public class LeetCode_641_263 { + +} + +class MyCircularDeque { + + private int limit; + + private int size = 0; + + private Node head; + private Node tail; + + /** Initialize your data structure here. Set the size of the deque to be k. */ + public MyCircularDeque(int k) { + this.limit = k; + this.head = new Node(); + this.tail = new Node(); + head.next = tail; + tail.prev = head; + } + + /** Adds an item at the front of Deque. Return true if the operation is successful. */ + public boolean insertFront(int value) { + + if (size == limit) { + return false; + } + + Node front = head.next; + + Node newNode = new Node(); + newNode.value = value; + newNode.prev = head; + newNode.next = front; + + head.next = newNode; + front.prev = newNode; + + size++; + return true; + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + public boolean insertLast(int value) { + if (size == limit) { + return false; + } + + Node last = tail.prev; + + Node newNode = new Node(); + newNode.value = value; + newNode.next = tail; + newNode.prev = last; + + last.next = newNode; + tail.prev = newNode; + + size++; + return true; + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + public boolean deleteFront() { + if (size == 0) { + return false; + } + + Node theSecond = head.next.next; + theSecond.prev = head; + head.next = theSecond; + + size--; + return true; + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + public boolean deleteLast() { + if (size == 0) { + return false; + } + + Node theLeastSecond = tail.prev.prev; + theLeastSecond.next = tail; + tail.prev = theLeastSecond; + + size--; + return true; + } + + /** Get the front item from the deque. */ + public int getFront() { + if (size == 0) { + return -1; + } + + return head.next.value; + } + + /** Get the last item from the deque. */ + public int getRear() { + if (size == 0) { + return -1; + } + + return tail.prev.value; + } + + /** Checks whether the circular deque is empty or not. */ + public boolean isEmpty() { + return size == 0; + } + + /** Checks whether the circular deque is full or not. */ + public boolean isFull() { + return size == limit; + } + + static class Node { + Node prev; + Node next; + int value; + } +} diff --git a/Week 01/id_263/LeetCode_66_263.java b/Week 01/id_263/LeetCode_66_263.java new file mode 100644 index 000000000..5a0031e6e --- /dev/null +++ b/Week 01/id_263/LeetCode_66_263.java @@ -0,0 +1,54 @@ +package io.beansoft.pencil.leetcode; + + +/** + * @author beanlam + * @version 1.0 + * @date 2019-10-20 17:10 + */ +public class LeetCode_66_263 { + + public int[] plusOne(int[] digits) { + + + boolean increment = false; + + int[] result = digits; + + for (int i = digits.length - 1; i >= -1; i--) { + + + if (i == -1) { + if (increment) { + result = new int[digits.length + 1]; + result[0] = 1; + System.arraycopy(digits, 0, result, 1, digits.length); + } + break; + } + + int currentValue = digits[i]; + if (i == digits.length - 1) { + currentValue++; + } + + int popupValue = currentValue + (increment ? 1 : 0) - 10; + if (popupValue >= 0) { + increment = true; + digits[i] = popupValue; + } else { + digits[i] = currentValue + (increment ? 1 : 0); + increment = false; + } + + } + + return result; + } + + public static void main(String[] args) { + for (int i : new LeetCode_66_263().plusOne(new int[]{8,9,9,9})) { + System.out.print(i + ","); + } + } +} diff --git a/Week 01/id_263/LeetCode_88_263.java b/Week 01/id_263/LeetCode_88_263.java new file mode 100644 index 000000000..24c6a7639 --- /dev/null +++ b/Week 01/id_263/LeetCode_88_263.java @@ -0,0 +1,52 @@ +package io.beansoft.pencil.leetcode; + + +/** + * @author beanlam + * @version 1.0 + * @date 2019-10-20 15:28 + */ +public class LeetCode_88_263 { + + public void merge(int[] nums1, int m, int[] nums2, int n) { + + int maxIndex1 = m - 1; + int maxIndex2 = n - 1; + + int currentMaxIndex = m + n -1; + + while (true) { + if (maxIndex1 < 0) { + while(maxIndex2 >= 0) { + nums1[currentMaxIndex--] = nums2[maxIndex2--]; + } + + break; + } + + if (maxIndex2 < 0) { + while (maxIndex1 >= 0) { + nums1[currentMaxIndex--] = nums1[maxIndex1--]; + } + break; + } + + if (nums1[maxIndex1] == nums2[maxIndex2]) { + nums1[currentMaxIndex--] = nums1[maxIndex1--]; + nums1[currentMaxIndex--] = nums2[maxIndex2--]; + continue; + } + + if (nums1[maxIndex1] > nums2[maxIndex2]) { + nums1[currentMaxIndex--] = nums1[maxIndex1--]; + continue; + } + + if (nums1[maxIndex1] < nums2[maxIndex2]) { + nums1[currentMaxIndex--] = nums2[maxIndex2--]; + continue; + } + } + + } +} diff --git a/Week 01/id_273/Deque_273.java b/Week 01/id_273/Deque_273.java new file mode 100644 index 000000000..919513ac3 --- /dev/null +++ b/Week 01/id_273/Deque_273.java @@ -0,0 +1,14 @@ +Deque deque = new LinkedList<>(); +deque.addLast("a"); +deque.addLast("b"); +deque.addLast("c"); +System.out.println(deque); + +String str = deque.getLast(); +System.out.println(str);//c +System.out.println(deque);//a b c + +while (deque.size() > 0) { + System.out.println(deque.removeLast());//c b a +} +System.out.println(deque);//null \ No newline at end of file diff --git a/Week 01/id_273/LeetCode_11_273.java b/Week 01/id_273/LeetCode_11_273.java new file mode 100644 index 000000000..16f267f4c --- /dev/null +++ b/Week 01/id_273/LeetCode_11_273.java @@ -0,0 +1,37 @@ +//11. 盛水最多的容器 + +//1. 暴力解法 执行用时击败约30% +//思路:类似两数之和的暴力解法,通过两次遍历获取不同的组合,计算出每个元素组合的容器大小,获取其中的最大值 +//时间复杂度O(n^2) +//空间复杂度O(1) +//总结:重点关注通过两次遍历获取不同组合的遍历方式 +public int maxArea(int[] nums) { + int max = 0; + for (int i = 0; i < nums.length - 1; i++) { + for (int j = i + 1; j < nums.length; j++) { + int volume = Math.min(nums[i], nums[j]) * (j - i); + if (volume > max) { + max = volume; + } + } + } + return max; +} + + +//2. 双指针解法 执行用时击败约96% +//思路:设置左右指针分别指向数组两端,比较两指针的元素,较小的一段前移,计算每一次移动后的容器大小,直到获取最大值 +//时间复杂度O(n) +//空间复杂度O(1) +public int maxArea(int[] nums) { + int max = 0; + int left = 0; + int right = nums.length - 1; + while (left < right) { + int volume = Math.min(nums[left], nums[right]) * (right - left); + max = max < volume ? volume : max; + if (nums[left] < nums[right]) left++; + else right--; + } + return max; +} \ No newline at end of file diff --git a/Week 01/id_273/LeetCode_141_273.java b/Week 01/id_273/LeetCode_141_273.java new file mode 100644 index 000000000..221c5c7c6 --- /dev/null +++ b/Week 01/id_273/LeetCode_141_273.java @@ -0,0 +1,39 @@ +//141. 环形链表 + +//解法1:HashSet +//思路:遍历链表,若当前节点已经存放在map中,则说明有环,若没有则把当前节点放入set +//时间复杂度O(n) +//空间复杂度O(n) +//总结:效率较低 +public static boolean hasCycle2(ListNode head) { + HashSet set = new HashSet<>(); + ListNode curr = head; + while (curr != null) { + if (set.contains(curr)) { + return true; + } else { + set.add(curr); + } + curr = curr.next; + } + return false; +} + +//解法2:快慢指针 +//思路:满指针一次走一步,快指针一次走两步,若有环,那么快指针必定会与满指针相遇 +//时间复杂度O(n) +//空间复杂度O(1) + +public boolean hasCycle(ListNode head) { + if (head == null || head.next == null) { + return false; + } + ListNode slow = head; + ListNode quick = head; + while (quick.next != null && quick.next.next != null) { + slow = slow.next; + quick = quick.next.next; + if (slow == quick) return true; + } + return false; +} \ No newline at end of file diff --git a/Week 01/id_273/LeetCode_142_273.java b/Week 01/id_273/LeetCode_142_273.java new file mode 100644 index 000000000..a8c4cd68e --- /dev/null +++ b/Week 01/id_273/LeetCode_142_273.java @@ -0,0 +1,58 @@ +//142. 环形链表II + +//解法1:HashSet +//思路:参考141-HashSet +//时间复杂度O(n) +//空间复杂度O(n) +//总结:效率低下,不可取 +public ListNode detectCycle(ListNode head) { + HashSet set = new HashSet<>(); + ListNode curr = head; + while (curr != null) { + if (set.contains(curr)) { + return curr; + } else { + set.add(curr); + } + curr = curr.next; + } + return null; +} + +//解法2:双指针 +//思路:参考141-双指针,这里要求的是环的连接点是哪里 +// 除了上述方法还有一种通过数学证明的解法... +//时间复杂度O(n) +//空间复杂度O(1) +//总结:虽然效率是要比起HashMap高,但是我个人更倾向于直观的解法... +public ListNode detectCycle(ListNode head) { + if (head == null || head.next == null) { + return null; + } + ListNode intersect = getIntersect(head); + //no cycle + if (intersect == null) { + return null; + } + ListNode ptr1 = head; + ListNode ptr2 = intersect; + while (ptr1 != ptr2) { + ptr1 = ptr1.next; + ptr2 = ptr2.next; + } + + return ptr1; + +} +private ListNode getIntersect(ListNode head) { + ListNode tortoise = head; + ListNode hare = head; + while (hare != null && hare.next != null) { + tortoise = tortoise.next; + hare = hare.next.next; + if (tortoise == hare) { + return tortoise; + } + } + return null; +} diff --git a/Week 01/id_273/LeetCode_146_273.java b/Week 01/id_273/LeetCode_146_273.java new file mode 100644 index 000000000..42dc5ef27 --- /dev/null +++ b/Week 01/id_273/LeetCode_146_273.java @@ -0,0 +1,42 @@ +//146 LRU缓存 + +//解法1,通过链表作为缓存容器,HashMap作为链表节点位置的映射,使其随机访问时间复杂度为O(1) +class LRUCache { + private HashMap map = new HashMap<>(); + private LinkedList list = new LinkedList<>(); + private int capacity; + + public LRUCache(int capacity) { + this.capacity = capacity; + } + + + public int get(int key) { + //若map存在对应映射 + if(map.containsKey(key)){ + //需要将其放置在list首部 表示最近使用 + list.remove((Integer)key); + list.addFirst(key); + return map.get(key); + } else { + return -1; + } + } + + public void put(int key, int value) { + //若put时 检测到key已经存在 则在链表中移除对应的node + if(map.containsKey(key)){ + list.remove((Integer)key); + //若不存在 + } else { + //若此时缓存已满 则在链表中移除末尾node + if(list.size() == capacity){ + map.remove(list.removeLast()); + } + } + //链表头添加 表示最近使用的 + list.addFirst(key); + //map添加list映射 + map.put(key,value); + } +} \ No newline at end of file diff --git a/Week 01/id_273/LeetCode_155_273.java b/Week 01/id_273/LeetCode_155_273.java new file mode 100644 index 000000000..f8eb73c56 --- /dev/null +++ b/Week 01/id_273/LeetCode_155_273.java @@ -0,0 +1,38 @@ +//155. 最小栈 + + +//解法1:通过创建一个辅助栈存储主栈中的最小值 +//push,pop,top,getmin的时间复杂度都为O(1) +//空间复杂度O(n),考虑到极端情况下dataStack入栈的所有元素相同,那么也要创建一个与dataStack大小相同的helpStack +public class MinStack { + Stack dataStack; + Stack helpStack; + + public MinStack() { + dataStack = new Stack<>(); + helpStack = new Stack<>(); + } + + public void push(int x) { + dataStack.push(x); + if (helpStack.isEmpty() || x <= helpStack.peek()) { + helpStack.push(x); + } + } + + public void pop() { + int x = dataStack.pop(); + if (x == helpStack.peek()) { + helpStack.pop(); + } + } + + public int top() { + return dataStack.peek(); + } + + public int getMin() { + return helpStack.peek(); + } + +} \ No newline at end of file diff --git a/Week 01/id_273/LeetCode_15_273.java b/Week 01/id_273/LeetCode_15_273.java new file mode 100644 index 000000000..46b662c4d --- /dev/null +++ b/Week 01/id_273/LeetCode_15_273.java @@ -0,0 +1,65 @@ +//15. 三数之和 + +//解法1:暴力求解 提交超时 +//思路:类似于两数之和的暴力求解, 都是通过遍历获取不重复的三数组合, 然后观察相加后是否等于0 +//时间复杂度O(n^3) +//空间复杂度O(1) +//总结:加入了一些去重判断尽可能优化了, 虽然调试下来结果应该是正确的, 但提交依然超时, 果然N^3的复杂度还是太高了 +public List> threeSum(int[] nums) { + List> result = new ArrayList<>(); + Arrays.sort(nums); + for (int i = 0; i < nums.length - 2; i++) { + if (nums[i] > 0) break; + if (i > 0 && nums[i] == nums[i - 1]) continue; + for (int j = i + 1; j < nums.length - 1; j++) { + for (int k = j + 1; k < nums.length; k++) { + if (nums[i] + nums[j] + nums[k] == 0) { + if (!result.contains(Arrays.asList(nums[i], nums[j], nums[k]))) { + result.add(Arrays.asList(nums[i], nums[j], nums[k])); + } + } + } + } + } + return result; +} + +//解法2:双指针夹逼法 执行用时击败约98% +//思路:遍历0 ~ length-2, 每次遍历固定一个i指针,然后在i指针之后的数组两端设置左、右指针。 +// 当指针所指向的三个元素相加=0, 获取到结果, 存放至list, left和right继续同时夹逼寻找其他可能的三数组合 +// 当指针所指向的三个元素相加<0, 左指针右移寻找更大的元素 +// 当指针所指向的三个元素相加>0, 右指针左移寻找更小的元素 +// 需要额外注意的是一些去重处理, 若处理得当也可以提高算法性能 +//时间复杂度O(n^2) +//空间复杂度O(1) +//总结:类似11题的最大容器双指针夹逼解法, 区别仅在于外层多套了一次循环和一些去重判断, 核心就是理解双指针的套路 +public List> threeSum(int[] nums) { + List> result = new ArrayList<>(); + Arrays.sort(nums); + for (int i = 0; i < nums.length - 2; i++) { + //排序后, nums[i]为三数之和中最小的数, 若nums[i] > 0, 则不存在三数之和为0 + if (nums[i] > 0) break; + //若nums[i]为之前重复出现过的数, 则跳过当前循环 + if (i > 0 && nums[i] == nums[i - 1]) continue; + int left = i + 1; + int right = nums.length - 1; + while (left < right) { + int sum = nums[i] + nums[left] + nums[right]; + if (sum == 0) { + result.add(Arrays.asList(nums[i], nums[left], nums[right])); + //left去重 + while (left < right && nums[left] == nums[left + 1]) { + left++; + } + left++; + //right去重 + while (left < right && nums[right] == nums[right - 1]) { + right--; + } + right--; + } else if (sum < 0) left++; + else right--; + } + } + return result; +} \ No newline at end of file diff --git a/Week 01/id_273/LeetCode_189_273.java b/Week 01/id_273/LeetCode_189_273.java new file mode 100644 index 000000000..781960855 --- /dev/null +++ b/Week 01/id_273/LeetCode_189_273.java @@ -0,0 +1,56 @@ +//189 数组旋转 + +//1. 暴力解法 执行用时击败约35% +//思路:每次旋转都进行一次arraycopy, 每次旋转后把最后一个元素移动到数组首部 +//时间复杂度为O(n*k) 因为arraycopy方法的时间复杂度是O(n) +//空间复杂度为O(1) +public void rotate(int[] nums, int k) { + k %= nums.length; + for (int j = k; j > 0; j--) { + int temp = nums[nums.length - 1]; + System.arraycopy(nums, 0, nums, 1, nums.length - 1); + nums[0] = temp; + } +} + +//2. 使用反转reverse 执行用时击败约91% +//思路:反转所有元素, 然后再反转前k个元素, 再反转后n-k个元素 +// 初始:1 2 3 4 5 6 +// 反转:6 5 4 3 2 1 +// 反转前2个:5 6 4 3 2 1 +// 反转后6-2=4个:5 6 1 2 3 4 +//时间复杂度O(n) +//空间复杂度O(1) +//总结:三种方法里, 最高效且易理解的题解 +public void rotate(int[] nums, int k) { + k %= nums.length; + reverse(nums, 0, nums.length - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, nums.length - 1); +} + +private void reverse(int[] nums, int start, int end) { + while (start < end) { + int temp = nums[start]; + nums[start] = nums[end]; + nums[end] = temp; + start++; + end--; + } +} + + +//3. 开辟一个新数组, 按正确的位置将值放进去 执行用时击败约91% +//思路:核心在于[(i + k) % nums.length]计算出的即是数组中每一个元素旋转后的位置 +//时间复杂度O(n) +//空间复杂度O(n) +//总结:有些类似数学计算.. 正常情况下, 我应该是考虑不到这种类型的解法 +public void rotate(int[] nums, int k) { + int[] temp = new int[nums.length]; + for (int i = 0; i < nums.length; i++) { + temp[(i + k) % nums.length] = nums[i]; + } + for (int i = 0; i < nums.length; i++) { + nums[i] = temp[i]; + } +} diff --git a/Week 01/id_273/LeetCode_1_273.java b/Week 01/id_273/LeetCode_1_273.java new file mode 100644 index 000000000..2db09a5da --- /dev/null +++ b/Week 01/id_273/LeetCode_1_273.java @@ -0,0 +1,34 @@ +//1. 两数之和 + +//解法1:暴力解法 执行用时击败约53% +//思路:通过两次遍历获取到所有不同的组合,比较组合相加是否等于target +//时间复杂度O(n^2) +//空间复杂度O(1) +//总结:虽然是暴力解法,但是通过两次遍历获取到所有不同的组合的这种遍历方式细节要掌握。 +public int[] twoSum(int[] nums, int target) { + for (int i = 0; i < nums.length - 1; i++) { + for (int j = i + 1; j < nums.length; j++) { + if (nums[i] + nums[j] == target) { + return new int[]{i, j}; + } + } + } + return null; +} + + +//解法2:HashMap 执行用时击败约98% +//思路:遍历nums, 每次遍历判断map中是否存储了target-nums[i]的key, 若已经存在直接取出结果返回, 不存在则将当前nums[i]存入map +//时间复杂度O(n) +//空间复杂度O(n) +//总结:比较典型的空间换时间优化策略,利用了HashMap随机访问高效的特性 +public int[] twoSum(int[] nums, int target) { + HashMap map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + if (map.containsKey(target - nums[i])) { + return new int[]{i, map.get(target - nums[i])}; + } + map.put(nums[i], i); + } + return null; +} \ No newline at end of file diff --git a/Week 01/id_273/LeetCode_206_273.java b/Week 01/id_273/LeetCode_206_273.java new file mode 100644 index 000000000..1e4bbde60 --- /dev/null +++ b/Week 01/id_273/LeetCode_206_273.java @@ -0,0 +1,40 @@ +//206. 反转链表 + +//解法1:迭代法 +//思路:把currentNode的next指针指向上一个元素即可,为此需要创建prev指针用于替换引用 +//时间复杂度O(n) +//空间复杂度O(1) +public ListNode reverseList(ListNode head) { + ListNode prev = null; + ListNode curr = head; + while (curr != null) { + ListNode nextNode = curr.next; + curr.next = prev; + prev = curr; + curr = nextNode; + } + return prev; +} + +//解法2:递归 +//思路:每一次递归,把当前node.next指向上一个node,直到当前node为NULL +//时间复杂度O(n) +//空间复杂度O(n) +//总结:递归思想在于找出重复子问题,这里的重复动作就是将当前节点的next指针指向上一个节点 + public static ListNode reverseList2(ListNode head) { + /* recursive solution */ + return reverseListInt(head, null); +} + +private static ListNode reverseListInt(ListNode head, ListNode newHead) { + //terminate + if (head == null) + return newHead; + + //logical + ListNode next = head.next; + head.next = newHead; + + //recur + return reverseListInt(next, head); +} diff --git a/Week 01/id_273/LeetCode_20_273.java b/Week 01/id_273/LeetCode_20_273.java new file mode 100644 index 000000000..9dcb37967 --- /dev/null +++ b/Week 01/id_273/LeetCode_20_273.java @@ -0,0 +1,29 @@ +//20. 有效括号 + +//解法1:辅助栈 +//时间复杂度O(n) +//空间复杂度O(n) 额外开辟了一个栈,当s字符串全为右括号时,栈大小为s.length() +public void isValid(String s) { + //若长度为奇数 可以直接判定不有效 + if(s.length()%2 != 0) return false; + + HashMap map = new HashMap<>(); + map.put(']','['); + map.put(')','('); + map.put('}','{'); + Stack stack = new Stack<>(); + + for (int i = 0;imax) { + max = arr[i]; + } + } + return max; +} \ No newline at end of file diff --git a/Week 01/id_273/LeetCode_24_273.java b/Week 01/id_273/LeetCode_24_273.java new file mode 100644 index 000000000..2b40a3198 --- /dev/null +++ b/Week 01/id_273/LeetCode_24_273.java @@ -0,0 +1,19 @@ +//24. 两两交换链表中的节点 + +//解法1:递归 +//思路:寻找重复子问题-head与nextTemp交换: +// head.next->下一个交换的nextTemp(因此返回nextTemp), nextTemp.next->head +//时间复杂度:O(n) +//空间复杂度:O(n) +//总结:也算是在这道题里找到了一点递归的感觉... 在写递归的时候,关注的是当前需要重复做的事情,不要去考虑全局 +// 把最后的返回条件确认后,思考当下的逻辑条件即可,因为递归的逻辑都是重复的 +// 这也印证了超哥说的:不要人肉递归,因为没有必要!!! +public ListNode swapPairs(ListNode head) { + if (head == null || head.next == null) { + return head; + } + ListNode nextTemp = head.next; + head.next = swapPairs(nextTemp.next); + nextTemp.next = head; + return nextTemp; +} \ No newline at end of file diff --git a/Week 01/id_273/LeetCode_25_273.java b/Week 01/id_273/LeetCode_25_273.java new file mode 100644 index 000000000..e67b54959 --- /dev/null +++ b/Week 01/id_273/LeetCode_25_273.java @@ -0,0 +1,36 @@ +//25. K个一组翻转链表 + +//解法1:递归 +//思路:结合了206. 反转链表和24. 两两交换链表节点的思想,可以将该题k=2的情况理解为反转链表求解 +// 首先获取k+1 node, +// if (k+1 node exist) 那么可以进行k次节点交换 +// else 说明后面的节点不足k个 直接return head +//时间复杂度O(n) +//空间复杂度O(n), 并不符合题目要求... +//总结:渐渐对递归的掌握越来越熟练,虽然自己写不出来这么优秀的递归,但是已经明白如何正确阅读递归程序了, +// 核心思想就在于找到重复子问题 +public ListNode reverseKGroup(ListNode head, int k) { + //terminate + if (head == null || head.next == null) return head; + int count = 0; + ListNode curr = head; + //find the k+1 node + while (curr != null && count != k) { + curr = curr.next; + count++; + } + //if k+1 node is found ... / else return head + if (count == k) { + curr = reverseKGroup(curr, k); + //完成k次交换 + while (count-- > 0) { + ListNode nextTemp = head.next; + head.next = curr; + curr = head; + head = nextTemp; + } + //最后的curr一定是交换后的头元素 + head = curr; + } + return head; +} \ No newline at end of file diff --git a/Week 01/id_273/LeetCode_26_273.java b/Week 01/id_273/LeetCode_26_273.java new file mode 100644 index 000000000..a1685eb8c --- /dev/null +++ b/Week 01/id_273/LeetCode_26_273.java @@ -0,0 +1,15 @@ +//26. 删除排序数组的重复项 + +//解法1:双指针 执行用时击败约100% +//思路:首先明确题目是排序数组, 定义慢指针i指向不重复序列的末尾, 每当快指针j指向一个与nums[i]不相等的元素就覆盖nums[++i] +//时间复杂度O(n) +//空间复杂度O(1) +class Solution { + public int removeDuplicates(int[] nums) { + int i = 0; + for (int j = 1; j < nums.length; j++) { + if (nums[i] != nums[j]) nums[++i] = nums[j]; + } + return i + 1; + } +} \ No newline at end of file diff --git a/Week 01/id_273/LeetCode_283_273.java b/Week 01/id_273/LeetCode_283_273.java new file mode 100644 index 000000000..7ff173dd0 --- /dev/null +++ b/Week 01/id_273/LeetCode_283_273.java @@ -0,0 +1,35 @@ +//283. 移动0 + +//解法1:双指针 执行用时击败约100% +//思路:定义慢指针k, 遍历数组若nums[i]!=0, 则覆盖指针k所指向的位置, 遍历结束后末尾补0即可 +//时间复杂度为O(n) +//空间复杂度为O(1) +public void moveZeroes(int[] nums) { + int k = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != 0) { + nums[k++] = nums[i]; + } + } + for (int i = k; i < nums.length; i++) { + nums[i] = 0; + } +} + +//解法2:辅助数组 执行用时击败约100% +//思路:遍历数组,将非0元素存储到辅助数组中 +//时间复杂度O(n) +//空间复杂度O(n) +//总结:和解法1的思想类似, 区别就在于一个是操作源数组, 一个开辟了新数组, 考虑到内存消耗, 还是第一种更好一些 +public void moveZeroes(int[] nums) { + int k = 0; + int[] temp = new int[nums.length]; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != 0) { + temp[k++] = nums[i]; + } + } + for (int i = 0; i < temp.length; i++) { + nums[i] = temp[i]; + } +} \ No newline at end of file diff --git a/Week 01/id_273/LeetCode_42_273.java b/Week 01/id_273/LeetCode_42_273.java new file mode 100644 index 000000000..e398531a1 --- /dev/null +++ b/Week 01/id_273/LeetCode_42_273.java @@ -0,0 +1,130 @@ +//42. 接雨水 + +//解法1:暴力解法 提交超时 +//思路:找出数组最大值(最高的柱子),计算每一行的雨水块 +// 先求高度为1行的水,再求高度为2行的水... +// 需要注意的是,最左边和最右边的柱子两侧的空白块不可能会有水 +// 之所以在height[j] >= i时才count += temp, 是因为若当前行只有一个柱子, 不应该存在积水, +// 若该柱子右边不存在第二个柱子, 那么之前添加到temp中的雨水数就不能添加到count +//时间复杂度O(m*n),m为数组最大值 +//空间复杂度O(1) +public int trap(int[] height) { + int max = 0;//最大高度 + for (int i = 0; i < height.length; i++) { + if (max < height[i]) max = height[i]; + } + int count = 0; + for (int i = 1; i <= max; i++) { + int temp = 0; + boolean flag = false; + for (int j = 0; j < height.length; j++) { + if (flag && height[j] < i) temp++; + if (height[j] >= i) { + count += temp; + temp = 0; + flag = true; + } + } + } + return count; +} + +//解法2:按列求 执行用时击败约16% +//思路:遍历除了第一列和最后一列的每一列, 找出它的左边最高块和右边最高块, +// 只有当左边最高列和右边最高列的高度都大于当前列, 才能使当前列积水, 且积水量为min(left_max, right_max) - nums[i] +//时间复杂度为O(n^2) +//空间复杂度为O(1) +public int trap(int[] height) { + int count = 0; + for (int i = 1; i < height.length; i++) { + int max_left = 0; + for (int j = i - 1; j >= 0; j--) { + if (height[j] > max_left) max_left = height[j]; + } + int max_right = 0; + for (int j = i + 1; j < height.length; j++) { + if (height[j] > max_right) max_right = height[j]; + } + int min = Math.min(max_left, max_right); + count += min > height[i] ? min - height[i] : 0; + } + return count; +} + +//解法3:动态规划 执行用时击败100% +//思路:基于题解2按列求的思路, 构建两个数组, max_left[i]代表第i列左边最高的列高度, max_right[i]代表第i列右边最高的列高度 +// 这样就不必每次求max_left和max_right都遍历一次数组了 +//时间复杂度O(n) +//空间复杂度O(n) +public int trap(int[] height) { + int sum = 0; + int[] max_left = new int[height.length]; + int[] max_right = new int[height.length]; + for (int i = 1; i < height.length; i++) { + max_left[i] = Math.max(height[i - 1], max_left[i - 1]); + } + for (int i = height.length - 2; i > 0; i--) { + max_right[i] = Math.max(height[i + 1], max_right[i + 1]); + } + for (int i = 1; i < height.length - 1; i++) { + int min = Math.min(max_left[i], max_right[i]); + sum += min > height[i] ? min - height[i] : 0; + } + return sum; +} + +//解法4:双指针 执行用时击败约75% +//思路:基于解法3动态规划, 可以成功将max_left数组采用变量暂存, 但是对于max_right数组是从右向左遍历的, 因此不能同时把max_right数组用变量替代, 那么就可以通过双指针解决 +// 对于按列求的方法中, 最后都需要寻找左边和右边列最大值中的较小值 : Math.min(left_max, right_max) +// 而对于变量max_left的计算可以通过代码"max_left = Math.min(max_left, height[i - 1])" +// 同理对变量max_right也可以通过代码"max_right = Math.min(max_right, height[i + 1])" +// 由于max_left与max_right最开始都是基于height[left - 1], height[right + 1]计算得出, 那么只要保证height[left - 1] < height[right + 1], max_left就一定小于max_right +// 那么就可以划分为两种情况: +// 1. 如果height[left - 1] < height[right + 1]: 可以确定max_left小于max_right, 那么就可以省略寻找右侧的操作, 只需要比较max_left和当前height[left]的大小并计算雨水量即可 +// 2. 同理可得else的情况 +//时间复杂度O(n) +//空间复杂度O(1) +public int trap(int[] height) { + int count = 0; + int left = 1; + int right = height.length - 2; + int max_left = 0; + int max_right = 0; + for (int i = 1; i < height.length - 1; i++) { + if (height[left - 1] < height[right + 1]) { + max_left = Math.max(max_left, height[left - 1]); + count += max_left > height[left] ? max_left - height[left] : 0; + left++; + } else { + max_right = Math.max(max_right, height[right + 1]); + count += max_right > height[right] ? max_right - height[right] : 0; + right--; + } + } + return count; +} + +//解法5:栈 执行用时击败约32% +//思路:当前高度若小于等于栈顶元素(当前左边界), 入栈(成为新的左边界), 指针后移 +// 若当前高度大于栈顶元素, 说明栈顶元素X与其左右边界存在高度差, 也就是能够接雨水, 计算左右边界之间的水。 +// 直到当前高度不大于栈顶高度或者栈空位置, 然后当前高度作为新的左边界入栈, 指针后移 +public int trap(int[] height) { + int sum = 0; + Stack stack = new Stack<>(); + int curr = 0; + while (curr < height.length) { + while (!stack.isEmpty() && height[curr] > height[stack.peek()]) { + int h = height[stack.pop()]; + if (stack.isEmpty()) break; + int distance = curr - stack.peek() - 1; + int min = Math.min(height[stack.peek()], height[curr]); + sum += min > h ? distance * (min - h) : 0; + } + stack.push(curr); + curr++; + } + return sum; +} + + + diff --git a/Week 01/id_273/LeetCode_641_273.java b/Week 01/id_273/LeetCode_641_273.java new file mode 100644 index 000000000..1cf307615 --- /dev/null +++ b/Week 01/id_273/LeetCode_641_273.java @@ -0,0 +1,97 @@ +//641. 循环双向队列 + +//解法1 双向链表解法 +//自己一开始采用的是数组解法,然后死活通过不了,看了下外国网站的大神用双向链表的实现,十分简单直观.. +public class MyCircularDeque { + int size; + int k; + DoubleListNode head; + DoubleListNode tail; + + public MyCircularDeque(int k) { + this.k = k; + this.size = 0; + head = new DoubleListNode(-1); + tail = new DoubleListNode(-1); + head.prev = tail; + tail.next = head; + } + + public boolean insertFront(int value) { + if (size == k) { + //queue已满 + return false; + } + DoubleListNode node = new DoubleListNode(value); + DoubleListNode prevNode = head.prev; + node.next = head; + node.prev = prevNode; + prevNode.next = node; + head.prev = node; + size++; + return true; + } + + public boolean insertLast(int value) { + if (size == k) { + return false; + } + DoubleListNode node = new DoubleListNode(value); + DoubleListNode nextNode = tail.next; + node.prev = tail; + tail.next = node; + nextNode.prev = node; + node.next = nextNode; + size++; + return true; + } + + public boolean deleteFront() { + if (size == 0) { + return false; + } + DoubleListNode prevNode = head.prev.prev; + prevNode.next = head; + head.prev = prevNode; + size--; + return true; + } + + public boolean deleteLast() { + if (size == 0) { + return false; + } + DoubleListNode nextNode = tail.next.next; + tail.next = nextNode; + nextNode.prev = tail; + size--; + return true; + } + + public int getFront() { + return head.prev.data; + } + + public int getRear() { + return tail.next.data; + } + + public boolean isEmpty() { + return size == 0; + } + + public boolean isFull() { + return size == k; + } + +} + +class DoubleListNode { + DoubleListNode prev; + DoubleListNode next; + int data; + + public DoubleListNode (int data) { + this.data = data; + } +} \ No newline at end of file diff --git a/Week 01/id_273/LeetCode_66_273.java b/Week 01/id_273/LeetCode_66_273.java new file mode 100644 index 000000000..165228ec1 --- /dev/null +++ b/Week 01/id_273/LeetCode_66_273.java @@ -0,0 +1,18 @@ +//66. 加一 + +//解法1:数组遍历 +//思路:从个位的位置开始遍历数组, ++后%10若结果不等于0说明未进位, 直接返回结果即可, +// 若直到最高位取余的结果还等于0, 说明最高位一定进位, 该情况下数组中所有元素为0 +// 返回一个新数组长度为源数组+1, 最高位array[0] = 1即可 +//时间复杂度O(1) +//空间复杂度O(n) +//总结:重点关注需要重复做的事情, 该题的重复操作就是 + 1,然后判断是否需要进位 +public int[] plusOne(int[] digits) { + for(int i = digits.length - 1; i >= 0; i--) { + if (++digits[i] % 10 != 0) return digits; + digits[i] = 0; + } + digits = new int[digits.length + 1]; + digits[0] = 1; + return digits; +} \ No newline at end of file diff --git a/Week 01/id_273/LeetCode_70_283.java b/Week 01/id_273/LeetCode_70_283.java new file mode 100644 index 000000000..2436bfe40 --- /dev/null +++ b/Week 01/id_273/LeetCode_70_283.java @@ -0,0 +1,53 @@ +//70. 爬楼梯 + +//通过规律可知该题的结果为斐波那契数列 + +//解法1:斐波那契数列递归 +//思路:... +//时间复杂度O(2^n) +//总结:提交未通过,该方式也不必考虑了,且面试遇到斐波那契千万不能采用该递归方式 +public static int climbStairs(int n) { + if (n <= 2) { + return n; + } + return climbStairs(n-1) + climbStairs(n-2); +} + +//解法2:动态规划 +//思路:寻找重复子问题,N1=1,N2=2,N3=N1+N2,N4=N3+N2...,可以将计算过程转换为数组迭代 +// 但目前还并不了解什么是动态规划... +//时间复杂度O(n) +//总结:观察重复子问题 +public int climbStairs(int n) { + if (n == 0) return 0; + if (n == 1) return 1; + if (n == 2) return 2; + int[] temp = new int[n]; + temp[0] = 1; + temp[1] = 2; + + for (int i = 2; i 0) return temp[n-k]; + temp[n-k] = climb(k-1, n, temp) + climb(k-2, n, temp); + return temp[n-k]; + } +} diff --git a/Week 01/id_273/LeetCode_84_273.java b/Week 01/id_273/LeetCode_84_273.java new file mode 100644 index 000000000..a593acb8d --- /dev/null +++ b/Week 01/id_273/LeetCode_84_273.java @@ -0,0 +1,86 @@ +//解法1:按列求 执行用时击败约39% +//思路:类似于42. 接雨水, 只要获取到当前柱子左/右边第一个比它矮的柱子,那么就可以确定当前柱子对应的最大矩形 +//时间复杂度:O(n^2) +//空间复杂度:O(1) +public int largestRectangleArea(int[] heights) { + int volume = 0; + for (int i = 0; i < heights.length; i++) { + int tempLeft = -1; + int tempRight = heights.length; + for (int j = i - 1; j >= 0; j--) { + if (heights[j] < heights[i]) { + tempLeft = j; + break; + } + } + for (int j = i + 1; j < heights.length; j++) { + if (heights[j] < heights[i]) { + tempRight = j; + break; + } + } + volume = Math.max((tempRight - tempLeft - 1) * (heights[i]), volume); + } + return volume; +} + +//解法2:动态规划 执行用时击败约99% +//思路:和解法1的思路基本一致, 核心在于将每一个柱子的左右边界分别存储至数组, 最后计算的时候逐个取出 +// 通过利用指针p进行跳跃寻找比当前柱子小的第一个数, 相比起逐个迭代寻找效率要更高 +//时间复杂度:O(n) +//空间复杂度:O(n) +public int largestRectangleArea(int[] height) { + if (height.length == 0 || height == null) return 0; + int volume = 0; + int[] tempLeft = new int[height.length]; + int[] tempRight = new int[height.length]; + tempLeft[0] = -1; + tempRight[height.length - 1] = height.length; + for (int i = 1; i < height.length; i++) { + int p = i - 1; + while (p >= 0 && height[i] <= height[p]) { + p = tempLeft[p]; + } + tempLeft[i] = p; + } + for (int i = height.length - 2; i >= 0; i--) { + int p = i + 1; + while (p < height.length && height[i] <= height[p]) { + p = tempRight[p]; + } + tempRight[i] = p; + } + for (int i = 0; i < height.length; i++) { + int width = tempRight[i] - tempLeft[i] - 1; + int high = height[i]; + volume = Math.max(volume, width * high); + } + return volume; +} + +//解法3:栈 执行用时击败82% +//思路:虽然方法五花八门, 但该题的核心部分就是获取当前柱子左/右边第一个比它矮的柱子 +// 用栈来做维护一个柱状高度单调递增的栈, 使得入栈的柱子高度都保持当前最大 +// 当遍历到一个高度小于栈顶元素的柱子时, 说明找到了当前栈顶元素的右边第一个比它矮的柱子, +// 而左边第一个比它矮的柱子就位于栈顶元素的下面 +// 这样获取一个柱子所对应的的核心条件就具备了, 剩下的就是一些边界条件了 +//时间复杂度:O(n) +//空间复杂度:O(n) +public int largestRectangleArea2(int[] height) { + public int largestRectangleArea(int[] height) { + int volume = 0; + Stack stack = new Stack<>(); + for (int i = 0; i <= height.length; i++) { + int h = (i == height.length ? 0 : height[i]); + if (stack.isEmpty() || height[stack.peek()] <= h) { + stack.push(i); + } else { + int length = height[stack.pop()]; + int width = stack.isEmpty() ? i : (i - 1 - stack.peek()); + volume = Math.max(volume, width * length); + i--; + } + } + return volume; + } +} \ No newline at end of file diff --git a/Week 01/id_273/LeetCode_88_273.java b/Week 01/id_273/LeetCode_88_273.java new file mode 100644 index 000000000..2713ed4cd --- /dev/null +++ b/Week 01/id_273/LeetCode_88_273.java @@ -0,0 +1,26 @@ +//88. 合并有序数组 + +//解法1:arraycopy+sort 执行用时击败约71% +//时间复杂度O((n+m)*logN) +//空间复杂度O(1) +//总结:简单直观,效率一般 +public void merge(int[] nums1, int m, int[] nums2, int n) { + System.arraycopy(nums2, 0,nums1, m, n); + Arrays.sort(nums1); +} + +//解法2:双指针 执行用时:0ms +//思路:可以参考归并排序的merge操作, +// 优化代码:在第一次nums1赋值结束后, 若i指针先指向0, 那么j指针剩下的部分可以直接覆盖掉nums1 +// 若j指针先指向0, 那么i指针剩下的部分就是原本nums1的部分, 不用再操作 +//时间复杂度O(n+m) +//空间复杂度O(1) +public void merge(int[] nums1, int m, int[] nums2, int n) { + int i = m - 1; int j = n - 1; int k = nums1.length - 1; + while (i >= 0 && j >= 0) { + nums1[k--] = (nums1[i] > nums2[j] ? nums1[i--] : nums2[j--]); + } + // while (i >= 0) nums1[k--] = nums1[i--]; + // while (j >= 0) nums1[k--] = nums2[j--]; + System.arraycopy(nums2, 0, nums1, 0, j + 1); +} \ No newline at end of file diff --git a/Week 01/id_273/NOTE.md b/Week 01/id_273/NOTE.md index a6321d6e2..61a5905ae 100644 --- a/Week 01/id_273/NOTE.md +++ b/Week 01/id_273/NOTE.md @@ -1,4 +1,46 @@ # NOTE - +### 第三课 数组、链表、跳表的基本实现和特性 +#### 数组特性: +* 关于ArrayList的总结:https://blog.csdn.net/weixin_43624024/article/details/102542495 + +#### LinkedList特性: +1. 众所周知, LinkedList的增/删时间复杂度为O(1), 但这只针对增加/删除的独立操作。一般我们在使用LinkedList时, 多数情况下可能是删除某个确定位置的节点, 此时就需要通过调用indexOf方法先获取到节点的下标, 再进行删除, 整个过程的时间复杂度是O(n)的。 +2. 针对LinkedList检索的优化:升维(空间换时间)————跳表, 添加跨步索引 +3. LinkedList的工程应用:LRUCache(链表+HashMap ———— LinkedHashMap) +* 关于LinkedList的总结:https://blog.csdn.net/weixin_43624024/article/details/102571442 +* 关于LRU的总结:https://blog.csdn.net/weixin_43624024/article/details/102587005 + +#### LinkedList的升维 ———— 跳表: +1. 在LinkedList的基础上进行升维, 添加了索引指针 +2. 跳表增加的索引级数为logN个 +3. 随机访问的时间复杂度由O(n)降到了O(logn) +4. 实际场景下使用跳表,会因为链表节点的增加/删除导致跳表索引不工整, 在每增加/删除一次节点都会导致索引更新一遍, 因此跳表的维护成本较高, 可使用在增/删操作不那么频繁, 且查询频繁的场景下。 + +### 第四课 栈,队列,优先队列,双端队列 + +#### 栈特性: +1. 先入后出 +2. Java-Stack底层基于Vector实现 +3. 增/删时间复杂度为O(1) +4. 查询时间复杂度O(n) +* 源码分析:https://blog.csdn.net/weixin_43624024/article/details/102635948 + + +#### 队列特性: +1. 先入先出 +2. 增/删时间复杂度为O(1) +3. 查询时间复杂度O(n) +4. Queue不是class而是一个interface, Java中对Queue实现的Class有LinkedList, PriorityQueue等 +* 源码分析:https://blog.csdn.net/weixin_43624024/article/details/102637229 + +#### 双端队列特性: +1. 在工程应用中一般不直接使用queue或stack, 而是采用deque双端队列 +2. 插入删除时间复杂度O(1), 查询时间复杂度O(n) +3. 可以在首部push/pop, 也可以在尾部push/pop, LinkedList也实现了Deque的接口 + +#### 优先队列特性: +1. 插入操作O(1) +2. 增加删除操作:O(logN)-按照元素的优先级取出 +3. 底层具体实现的数据结构较为多样和复杂, 可以用heap/bst/avl/treap diff --git a/Week 01/id_278/Leetcode_1_278.js b/Week 01/id_278/Leetcode_1_278.js new file mode 100644 index 000000000..31265b9a3 --- /dev/null +++ b/Week 01/id_278/Leetcode_1_278.js @@ -0,0 +1,22 @@ +/** + * 1. Two Sum + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = function(nums, target) { + if (!Array.isArray(nums) || nums.length < 2) { + return []; + } + + let map = {}; + + for (let [index, num] of nums.entries()) { + if (map[target - num] != null) { + return [map[target - num], index]; + } + map[num] = index; + } + + return []; +}; diff --git a/Week 01/id_278/Leetcode_283_278.js b/Week 01/id_278/Leetcode_283_278.js new file mode 100644 index 000000000..7f3b8e32d --- /dev/null +++ b/Week 01/id_278/Leetcode_283_278.js @@ -0,0 +1,20 @@ +/** + * 283. Move Zeroes + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ +var moveZeroes = function(nums) { + if (!Array.isArray(nums)) { + return; + } + + let zero = 0; + for (let index = 0; index < nums.length; ++ index) { + if (nums[index] !== 0) { + nums[zero++] = nums[index]; + } + } + while (zero < nums.length) { + nums[zero++] = 0; + } +}; diff --git a/Week 01/id_278/NOTE.md b/Week 01/id_278/NOTE.md index a6321d6e2..ded641ece 100644 --- a/Week 01/id_278/NOTE.md +++ b/Week 01/id_278/NOTE.md @@ -1,4 +1,38 @@ # NOTE - +## 数组、链表、跳表的基本实现和特性 +### Array + +* 高级语言 泛型 +* Inserting: move avg O(n/2) and resize (double size) +* Deleting: move and resize +* prepend - O(1) ? +* append - O(1) +* lookup - O(1) +* insert - **O(n)** +* delete - **O(n)** + +### Linked List (LRU cache) + +* For frequent insertion and deletion +* Java implemetion is doubly linked +* prepend - O(1) +* append - O(1) +* lookup - **O(n)** +* insert - O(1) +* delete - O(1) + +### Skip List (Redis) + +* **increase dimension, space for time** +* multi-level indexing +* O(logn) levels with O(logn) time for lookup +* O(logn) for insert and delete, 'cause need to update index +* in reality, it might not be perfect alignments due to element insertion and deletion +* Space O(n) + +## Leetccode + +* five times +* keep functionally independent util code sections in mind, by repetitive practice diff --git a/Week 01/id_283/LeetCode_1_283.java b/Week 01/id_283/LeetCode_1_283.java new file mode 100644 index 000000000..45d0a3ca3 --- /dev/null +++ b/Week 01/id_283/LeetCode_1_283.java @@ -0,0 +1,23 @@ +/* + * @lc app=leetcode id=1 lang=java + * + * [1] Two Sum + */ + +class Solution { + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(); + if (nums != null && nums.length > 0) { + for (int i = 0; i < nums.length; i++) { + int result = target - nums[i]; + if (map.containsKey(result)) { + return new int[]{map.get(result), i}; + } + map.put(nums[i], i); + } + } + return new int[]{}; + } +} + + diff --git a/Week 01/id_283/LeetCode_26_283.java b/Week 01/id_283/LeetCode_26_283.java new file mode 100644 index 000000000..02916e308 --- /dev/null +++ b/Week 01/id_283/LeetCode_26_283.java @@ -0,0 +1,22 @@ +/* + * @lc app=leetcode id=26 lang=java + * + * [26] Remove Duplicates from Sorted Array + */ + +class Solution { + public int removeDuplicates(int[] nums) { + int len = nums.length; + if(len == 0) + return 0; + int cur = 0; + for(int i = 0; i < len; i++){ + if(nums[i] != nums[cur]){ + cur++; + nums[cur] = nums[i]; + } + } + return cur + 1; + } +} + diff --git a/Week 01/id_298/merge-sorted-array.py b/Week 01/id_298/merge-sorted-array.py new file mode 100644 index 000000000..3982b31a5 --- /dev/null +++ b/Week 01/id_298/merge-sorted-array.py @@ -0,0 +1,32 @@ +class Solution: + def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: + """ + Do not return anything, modify nums1 in-place instead. + """ + temp_m = m + temp_n = n + if m == 0 and n == 1: + nums1[-1] = nums2[-1] + elif m == 1 and n == 1: + if nums1[m - 1] > nums2[n - 1]: + nums1[-1] = nums1[0] + nums1[0] = nums2[0] + else: + nums1[-1] = nums2[0] + else: + for idx in range(len(nums1)): + if n != 0 and m == 0: + for i_n in range(n): + if n == temp_n: + nums1[len(nums1) - 1 - temp_m - i_n] = nums2[n - 1 - i_n] + else: + nums1[(len(nums1) - 1) - temp_m - (len(nums2) - n) - i_n] = nums2[n - 1 - i_n] + elif n != 0 and m != 0: + if nums1[m - 1] >= nums2[n - 1]: + nums1[len(nums1) - 1 - idx] = nums1[m - 1] + m = m - 1 + elif nums1[m - 1] < nums2[n - 1]: + nums1[len(nums1) - 1 - idx] = nums2[n - 1] + n = n - 1 + else: + return diff --git a/Week 01/id_298/rotate-array.py b/Week 01/id_298/rotate-array.py new file mode 100644 index 000000000..97137925c --- /dev/null +++ b/Week 01/id_298/rotate-array.py @@ -0,0 +1,32 @@ +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + if len(nums) < k: + nums.reverse() + k = k - len(nums) + for idx in range(k): + if idx < k // 2: + temp = nums[idx] + nums[idx] = nums[k - 1 - idx] + nums[k - 1 - idx] = temp + + for idx in range(k, len(nums)): + if idx < (len(nums) + k) // 2: + temp = nums[idx] + nums[idx] = nums[(len(nums) - 1) - (idx - k)] + nums[(len(nums) - 1) - (idx - k)] = temp + else: + nums.reverse() + for idx in range(k): + if idx < k // 2: + temp = nums[idx] + nums[idx] = nums[k - 1 - idx] + nums[k - 1 - idx] = temp + + for idx in range(k, len(nums)): + if idx < (len(nums) + k) // 2: + temp = nums[idx] + nums[idx] = nums[(len(nums) - 1) - (idx - k)] + nums[(len(nums) - 1) - (idx - k)] = temp diff --git a/Week 01/id_303/MoveZeros.swift b/Week 01/id_303/MoveZeros.swift new file mode 100644 index 000000000..e1528ed27 --- /dev/null +++ b/Week 01/id_303/MoveZeros.swift @@ -0,0 +1,23 @@ +// +// ReverseLinkedList.swift +// AlgorithmStudy +// +// Created by 张奇 on 2019/10/20. +// Copyright © 2019 张奇. All rights reserved. +// + +// +import Foundation +class MoveZerosSolution { + func moveZeroes(_ nums: inout [Int]) { + let count = nums.count + var j = 0 + for i in 0.. maxValue){ + nums[sum++] = temp; + maxValue = temp; + } + i++; + } + + return sum; +}; \ No newline at end of file diff --git a/Week 01/id_308/LeedCode_641.js b/Week 01/id_308/LeedCode_641.js new file mode 100644 index 000000000..d0766231d --- /dev/null +++ b/Week 01/id_308/LeedCode_641.js @@ -0,0 +1,55 @@ +/** + * 题目: 设计循环双端队列 + * 思路: Javascript中无论使用何种方式创建数组,都会动态分配内存空间,不存在溢出等问题,因此我们通常使用数组来实现栈/各种队列 + */ + +/** + * Initialize your data structure here. Set the size of the deque to be k. + * @param {number} k + */ +class MyCircularDeque{ + constructor(size){ + this.queue = []; + this.size = size; + } + + insertFront(k) { + if(this.isFull()) return false; + this.queue.unshift(k); + return true; + } + + insertLast(k) { + if(this.isFull()) return false; + this.queue.push(k); + return true; + } + + deleteFront() { + if(this.isEmpty()) return false; + this.queue.shift(); + return true; + } + + deleteLast() { + if(this.isEmpty()) return false; + this.queue.pop(); + return true; + } + + getFront() { + return this.isEmpty() ? -1 : this.queue[0]; + } + + getRear() { + return this.isEmpty() ? -1 : this.queue[this.queue.length -1]; + } + + isEmpty() { + return this.queue.length === 0; + } + + isFull() { + return this.queue.length === this.size; + } +} \ No newline at end of file diff --git a/Week 01/id_308/NOTE.md b/Week 01/id_308/NOTE.md index a6321d6e2..17f2e496f 100644 --- a/Week 01/id_308/NOTE.md +++ b/Week 01/id_308/NOTE.md @@ -1,4 +1,63 @@ # NOTE +## 数组、链表、跳表的基本实现和特征 + + +#### 数组 Array + +- 计算机在内存中开辟了一段连续的地址 +- 顺序和链式都可以实现 +- 元素访问的时间复杂度 O(1) +- 插入/删除的时间复杂度 O(n) + +#### 链表 Linked List + +- 由Value和Next组成,Next指针指向下一个元素 +- 每一个元素由class来定义,称作 Node +- 头指针用head,尾指针用tail来表示 +- 链表分类 + * 单链表:只有一个指针,tail指针指向 none + * 双向链表: 比单链表多一个先前3指针prev/previous + * 循环链表: 单链表的tail指针指向head + * 静态链表: 用数组描述的链表叫做静态链表 +- 单链表 + * 不支持随机访问,需要遍历去访问结点 + * 插入和删除只需要移动指针,时间复杂度为O(1) + * 每个结点需要额外的空间存储指针,需要的内存比数组大 + +#### Skip List + +- 跳表工程中主要运用在Redis中 +- 跳表是为了补足链表的设计缺陷而来的 +- 优化思想:升维思想,空间换时间 +- 具体方法:增加多级索引 +- 跳表维护成本较大,需要改动所有索引 +- 查找/增加/删除时间复杂度O(logn) +- 空间复杂度O(n) + + +#### LRU(Least Recently Used) + +- 既按访问时间排序,又能在常数时间内随机访问 -> HashMap + 双向链表 + + + +## 栈、队列、双端队列、优先队列 + +#### 栈和队列的基本实现和特性 + +- Stack & Queue 关键点 + * Stack: 先入后出;添加、删除皆为O(1),查询为O(n) + * Queue: 先入先出;添加、删除皆为O(1),查询为O(n) +- 双端队列 + * 两端都可以出队/入队: 添加、删除皆为O(1),查询为O(n) + +- 优先队列 Priority Queue + * 插入操作:O(1) + * 取出操作:O(logN) 按照元素的优先级取出 + * 底层具体实现的数据结构较为多样和复杂:heap(堆)、bst(binary search tree 平衡二叉搜索树)、treap + + + diff --git a/Week 01/id_313/LeetCode_189_313.go b/Week 01/id_313/LeetCode_189_313.go new file mode 100644 index 000000000..6334827a2 --- /dev/null +++ b/Week 01/id_313/LeetCode_189_313.go @@ -0,0 +1,18 @@ +package id313 + +// 要考虑 k 大于数组长度的情况 切割成两个部分,分别反转 再对整个数组反转 +func rotate(nums []int, k int) { + length := len(nums) + k %= length + reverse(nums, 0, length-k-1) + reverse(nums, length-k, length-1) + reverse(nums, 0, length-1) +} + +func reverse(nums []int, start, end int) { + for start < end { + nums[start], nums[end] = nums[end], nums[start] + start++ + end-- + } +} diff --git a/Week 01/id_313/LeetCode_1_313.go b/Week 01/id_313/LeetCode_1_313.go new file mode 100644 index 000000000..b7bb72155 --- /dev/null +++ b/Week 01/id_313/LeetCode_1_313.go @@ -0,0 +1,17 @@ +package id313 + +// 一遍哈希表通过哈希判断是否重复 +func twoSum(nums []int, target int) []int { + l := len(nums) + m := make(map[int]int, 0) + for i := 0; i < l; i++ { + s := target - nums[i] + if v, ok := m[s]; ok && v != i { + return []int{i, v} + } + m[nums[i]] = i + + } + return []int{0, 0} + +} diff --git a/Week 01/id_313/LeetCode_21_313.go b/Week 01/id_313/LeetCode_21_313.go new file mode 100644 index 000000000..cf0f6747f --- /dev/null +++ b/Week 01/id_313/LeetCode_21_313.go @@ -0,0 +1,38 @@ +package id313 + +// ListNode 结点 +type ListNode struct { + Val int + Next *ListNode +} + +// 弄一个哨兵节点 最后比较容易返回数据 两个链表从前到后 比较大小 +func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode { + l := new(ListNode) + prev := l + for l1 != nil || l2 != nil { + if l1 == nil && l2 != nil { + prev.Next = l2 + l2 = l2.Next + prev = prev.Next + continue + } + + if l2 == nil && l1 != nil { + prev.Next = l1 + l1 = l1.Next + prev = prev.Next + continue + } + if l1.Val > l2.Val { + prev.Next = l2 + l2 = l2.Next + prev = prev.Next + } else { + prev.Next = l1 + l1 = l1.Next + prev = prev.Next + } + } + return l.Next +} diff --git a/Week 01/id_313/LeetCode_26_313.go b/Week 01/id_313/LeetCode_26_313.go new file mode 100644 index 000000000..23b7fb154 --- /dev/null +++ b/Week 01/id_313/LeetCode_26_313.go @@ -0,0 +1,14 @@ +package id313 + +// 重点 排序数组 不需要考虑数组中超出长度后面的元素 快慢指针 +func removeDuplicates(nums []int) int { + length := len(nums) + j := 0 + for i := 1; i < length; i++ { + if nums[i] != nums[j] { + j++ + nums[j] = nums[i] + } + } + return j + 1 +} diff --git a/Week 01/id_313/LeetCode_283_313.go b/Week 01/id_313/LeetCode_283_313.go new file mode 100644 index 000000000..d88fc2da5 --- /dev/null +++ b/Week 01/id_313/LeetCode_283_313.go @@ -0,0 +1,13 @@ +package id313 + +// 快慢指针 +func moveZeroes(nums []int) { + length := len(nums) + j := 0 + for i := 0; i < length; i++ { + if nums[i] != 0 { + nums[j], nums[i] = nums[i], nums[j] + j++ + } + } +} diff --git a/Week 01/id_313/LeetCode_42_313.go b/Week 01/id_313/LeetCode_42_313.go new file mode 100644 index 000000000..dce1f7d8f --- /dev/null +++ b/Week 01/id_313/LeetCode_42_313.go @@ -0,0 +1,30 @@ +package id313 + +// 双指针 两边往中间走 主要是理解lMax 和 rMax +func trap(height []int) int { + lMax := 0 + rMax := 0 + length := len(height) + s := 0 + j := length - 1 + i := 0 + for i < j { + lMax = max(lMax, height[i]) + rMax = max(rMax, height[j]) + if lMax < rMax { + s += lMax - height[i] + i++ + } else { + s += rMax - height[j] + j-- + } + } + return s +} + +func max(m, n int) int { + if m > n { + return m + } + return n +} diff --git a/Week 01/id_313/LeetCode_641_313.go b/Week 01/id_313/LeetCode_641_313.go new file mode 100644 index 000000000..ad9f66fa8 --- /dev/null +++ b/Week 01/id_313/LeetCode_641_313.go @@ -0,0 +1,114 @@ +package id313 + +// 插入和删除都在尾部,查询也在头部和尾部 所以使用双链表实现效率会比使用数组高 +type node struct { + val int + prev *node + next *node +} + +type MyCircularDeque struct { + front *node + rear *node + cap int + size int +} + +// Constructor Initialize your data structure here. Set the size of the deque to be k. +func Constructor(k int) MyCircularDeque { + return MyCircularDeque{front: &node{}, rear: &node{}, size: 0, cap: k} +} + +/** Adds an item at the front of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) InsertFront(value int) bool { + if this.size == this.cap { + return false + } + if 0 == this.size { + this.front = &node{val: value} + this.rear = this.front + } else { + front := this.front + this.front = &node{val: value, next: front} + front.prev = this.front + } + this.size++ + return true +} + +/** Adds an item at the rear of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) InsertLast(value int) bool { + if this.size == this.cap { + return false + } + if 0 == this.size { + this.front = &node{val: value} + this.rear = this.front + } else { + rear := this.rear + this.rear = &node{val: value, prev: rear} + rear.next = this.rear + } + this.size++ + return true +} + +/** Deletes an item from the front of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) DeleteFront() bool { + if 0 == this.size { + return false + } + next := this.front.next + if nil == next { + this.front = nil + this.rear = nil + } else { + this.front = next + this.front.prev = nil + } + this.size-- + return true +} + +/** Deletes an item from the rear of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) DeleteLast() bool { + if 0 == this.size { + return false + } + prev := this.rear.prev + if nil == prev { + this.rear = nil + this.front = nil + } else { + this.rear = prev + this.rear.next = nil + } + this.size-- + return true +} + +/** Get the front item from the deque. */ +func (this *MyCircularDeque) GetFront() int { + if 0 == this.size { + return -1 + } + return this.front.val +} + +/** Get the last item from the deque. */ +func (this *MyCircularDeque) GetRear() int { + if 0 == this.size { + return -1 + } + return this.rear.val +} + +/** Checks whether the circular deque is empty or not. */ +func (this *MyCircularDeque) IsEmpty() bool { + return 0 == this.size +} + +/** Checks whether the circular deque is full or not. */ +func (this *MyCircularDeque) IsFull() bool { + return this.size == this.cap +} diff --git a/Week 01/id_313/LeetCode_66_313.go b/Week 01/id_313/LeetCode_66_313.go new file mode 100644 index 000000000..0fd880ceb --- /dev/null +++ b/Week 01/id_313/LeetCode_66_313.go @@ -0,0 +1,16 @@ +package id313 + +// 高精度加法问题 注意进位 神奇的加1 +func plusOne(digits []int) []int { + length := len(digits) + for i := length - 1; i >= 0; i-- { + digits[i]++ + digits[i] %= 10 + if digits[i] != 0 { + return digits + } + } + digits = make([]int, length+1) + digits[0] = 1 + return digits +} diff --git a/Week 01/id_313/LeetCode_88_313.go b/Week 01/id_313/LeetCode_88_313.go new file mode 100644 index 000000000..61ae789e1 --- /dev/null +++ b/Week 01/id_313/LeetCode_88_313.go @@ -0,0 +1,28 @@ +package id313 + +// 通过 m+n 可以确定数组最终长度,对两个数组从后到前遍历,比较大小 放到相应位置。考虑好边界问题 +func merge(nums1 []int, m int, nums2 []int, n int) { + p1 := m - 1 + p2 := n - 1 + for p := m + n - 1; p >= 0; p-- { + if p1 < 0 { + nums1[p] = nums2[p2] + p2-- + continue + } + + if p2 < 0 { + nums1[p] = nums1[p1] + p1-- + continue + } + if nums2[p2] > nums1[p1] { + nums1[p] = nums2[p2] + p2-- + } else { + nums1[p] = nums1[p1] + nums1[p1] = nums2[p2] + p1-- + } + } +} diff --git a/Week 01/id_318/LeetCode_26_318.py b/Week 01/id_318/LeetCode_26_318.py new file mode 100644 index 000000000..b0c88a926 --- /dev/null +++ b/Week 01/id_318/LeetCode_26_318.py @@ -0,0 +1,20 @@ +# 26. Remove Duplicates from Sorted Array + +class Solution: + def removeDuplicates(self, nums): + if len(nums) == 0: + return 0 + res = 1 + for i in range(1, len(nums)): + if nums[i] != nums[i-1]: + nums[res] = nums[i] + res += 1 + return res + +if __name__ == '__main__': + nums = [1,1,2,3,4,4,4,4,5,5,5,6,7,7,8,8,8,8,8,8,9,9,9] + sol = Solution() + res = sol.removeDuplicates(nums) + print(res) + +# output: 9 diff --git a/Week 01/id_318/LeetCode_283_318.py b/Week 01/id_318/LeetCode_283_318.py new file mode 100644 index 000000000..be12c78d8 --- /dev/null +++ b/Week 01/id_318/LeetCode_283_318.py @@ -0,0 +1,21 @@ +# 283. Move Zeroes + +class Solution(object): + def moveZeroes(self, nums): + """ + :type nums: List[int] + :rtype: None Do not return anything, modify nums in-place instead. + """ + zero_pos = 0 + for i in xrange(len(nums)): + if nums[i] != 0: + nums[zero_pos], nums[i] = nums[i], nums[zero_pos] + zero_pos += 1 + +if __name__ == '__main__': + nums = [0,1,0,3,12] + sol = Solution() + res = sol.moveZeroes(nums) + print(nums) + +# output: [1, 3, 12, 0, 0] diff --git a/Week 01/id_328/MergeTwoLists.java b/Week 01/id_328/MergeTwoLists.java new file mode 100644 index 000000000..3ab80784d --- /dev/null +++ b/Week 01/id_328/MergeTwoLists.java @@ -0,0 +1,34 @@ +public class MergeTwoLists { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + ListNode newHead = null; + ListNode tmp = new ListNode(-1); + boolean firstSetNewHead = true; + while (l1 != null || l2 != null) { + if(l1 == null) { + tmp.next = l2; + l2 = l2.next; + }else if(l2 == null) { + tmp.next = l1; + l1 = l1.next; + }else { + if (l1.val > l2.val) { + tmp.next = l2; + l2 = l2.next; + } else { + tmp.next = l1; + l1 = l1.next; + } + + } + if(firstSetNewHead) { + newHead = tmp; + firstSetNewHead = false; + } + tmp = tmp.next; + } + if(newHead == null) { + return null; + } + return newHead.next; + } +} \ No newline at end of file diff --git a/Week 01/id_328/MergeTwoSortedArray.java b/Week 01/id_328/MergeTwoSortedArray.java new file mode 100644 index 000000000..313f842aa --- /dev/null +++ b/Week 01/id_328/MergeTwoSortedArray.java @@ -0,0 +1,23 @@ +// 方法1 +// 1.头移到尾部 +// 2.处理剩下的元素的移动 +// 3.整个过程执行3次 + + +public class MergeTwoSortedArray { + public void rotate(int[] nums, int k) { + for(int i =0 ;i < k ; i++){ + nums = moveTailToHead(nums); + } + } + public int[] moveTailToHead(int[] nums){ + int tmp = 0; + int lastIndex = nums.length - 1; + int newHead = nums[lastIndex]; + for(int i = lastIndex - 1 ; i > -1; i--){ + nums[i+1] = nums[i]; + } + nums[0] = newHead; + return nums; + } +} \ No newline at end of file diff --git a/Week 01/id_328/RemoveDuplicatesFromSortedArray.java b/Week 01/id_328/RemoveDuplicatesFromSortedArray.java new file mode 100644 index 000000000..bbacea28f --- /dev/null +++ b/Week 01/id_328/RemoveDuplicatesFromSortedArray.java @@ -0,0 +1,12 @@ +public class RemoveDuplicatesFromSortedArray { + public int removeDuplicates(int[] nums) { + int nonRepeatIndex = 0; + for(int i = 0; i< nums.length ;i ++){ + if(nums[nonRepeatIndex] != nums[i]){ + nonRepeatIndex++; + nums[nonRepeatIndex] = nums[i]; + } + } + return nonRepeatIndex + 1; + } +} \ No newline at end of file diff --git a/Week 01/id_328/RotateArray.java b/Week 01/id_328/RotateArray.java new file mode 100644 index 000000000..5180b258d --- /dev/null +++ b/Week 01/id_328/RotateArray.java @@ -0,0 +1,23 @@ +// 方法1 +// 1.头移到尾部 +// 2.处理剩下的元素的移动 +// 3.整个过程执行3次 +// 此方法时间复杂度较高,尚需优化 + +public class RotateArray { + public void rotate(int[] nums, int k) { + for(int i =0 ;i < k ; i++){ + nums = moveTailToHead(nums); + } + } + public int[] moveTailToHead(int[] nums){ + int tmp = 0; + int lastIndex = nums.length - 1; + int newHead = nums[lastIndex]; + for(int i = lastIndex - 1 ; i > -1; i--){ + nums[i+1] = nums[i]; + } + nums[0] = newHead; + return nums; + } +} \ No newline at end of file diff --git a/Week 01/id_333/LeetCode_26_333.java b/Week 01/id_333/LeetCode_26_333.java new file mode 100644 index 000000000..b44c5f97b --- /dev/null +++ b/Week 01/id_333/LeetCode_26_333.java @@ -0,0 +1,27 @@ +public class LeetCode_26_333 { + + public static int removeDuplicates(int[] nums) { + int len = nums.length; + int index = 0; + for (int i = 0;i < len - 1;i ++) { + if (nums[i] == nums[i+1]) { + for (int j = i+1;j < len - 1;j++) { + nums[j] = nums[j+1]; + } + len--; + i--; + } + } + return len; + } + + public static void main(String[] args) { + int[] nums = new int[]{1,1,2,2,2,3,3,3,3,4}; + int len = removeDuplicates(nums); + System.out.println("Length after deleted : " + len); + System.out.println("New array :"); + for (int i = 0;i < len;i++) { + System.out.print("" + nums[i] + " "); + } + } +} diff --git a/Week 01/id_333/LeetCode_641_333.java b/Week 01/id_333/LeetCode_641_333.java new file mode 100644 index 000000000..a860e0b5d --- /dev/null +++ b/Week 01/id_333/LeetCode_641_333.java @@ -0,0 +1,39 @@ +public class LeetCode_641_333 { + + public static int trap(int[] height) { + int max = 0; + int maxIndex = 0; + //寻找最高的柱子 + for (int i = 0; i < height.length; i++) { + if (height[i] > max) { + max = height[i]; + maxIndex = i; + } + } + int maxLeft = 0; + int result = 0; + //计算最高柱子左侧的雨水收集 + for (int i = 0; i < maxIndex; i++) { + if (height[i] > maxLeft) { + maxLeft = height[i]; + } else { + result += maxLeft - height[i]; + } + } + int maxRight = 0; + //计算最高柱子右侧的雨水收集 + for (int i = height.length - 1; i > maxIndex; i--) { + if (height[i] > maxRight) { + maxRight = height[i]; + } else { + result += maxRight - height[i]; + } + } + return result; + } + + public static void main(String[] args) { + int[] con = new int[]{0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1}; + System.out.print("water capacity:" + trap(con)); + } +} diff --git a/Week 01/id_338/LeetCode_1_338.java b/Week 01/id_338/LeetCode_1_338.java new file mode 100644 index 000000000..88748cd6e --- /dev/null +++ b/Week 01/id_338/LeetCode_1_338.java @@ -0,0 +1,23 @@ +/** + * User: liwei + * Date: 2019/10/19 19:46 + * Desc: + */ +public class LeetCode_1_338 { + class Solution { + // 将 a + b = target 问题转化为 b = target - a + // 一次遍历即可解决, O(N) + // 注意: new int[]{} 赋值的时候不能指定大小。map.containsKey + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + if (map.containsKey(nums[i])) { + return new int[]{map.get(nums[i]), i}; + } else { + map.put(target - nums[i], i); // b = target - a + } + } + return new int[]{0, 0}; + } + } +} diff --git a/Week 01/id_338/LeetCode_21_338.java b/Week 01/id_338/LeetCode_21_338.java new file mode 100644 index 000000000..7d071f86e --- /dev/null +++ b/Week 01/id_338/LeetCode_21_338.java @@ -0,0 +1,67 @@ +/** + * User: liwei + * Date: 2019/10/18 12:23 + * Desc: + */ +public class LeetCode_21_338 { + /** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + * + * 标签:链表、递归 + * 这道题可以使用递归实现,新链表也不需要构造新节点,我们下面列举递归三个要素 + * 终止条件:两条链表分别名为 l1 和 l2,当 l1 为空或 l2 为空时结束 + * 返回值:每一层调用都返回排序好的链表头 + * 本级递归内容:如果 l1 的 val 值更小,则将 l1.next 与排序好的链表头相接,l2 同理 + * O(m+n),m 为 l1的长度,n 为 l2 的长度 + * + * 解题关键是找到递归的规律,并且关注好递归的三要素即可,可通过最简单的链表验证,不用脑补所有递归过程 + */ + class Solution { + public ListNode mergeTwoLists(ListNode l1, ListNode 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(l2.next, l1); + return l2; + } + } + } + + /** + * 递归转迭代,需要自己手动实现重复操作部分 + * 时间复杂度:O(n + m)O(n+m) 。因为每次循环迭代中,l1 和 l2 只有一个元素会被放进合并链表中, + * while 循环的次数等于两个链表的总长度。所有其他工作都是常数级别的,所以总的时间复杂度是线性的。 + * 空间复杂度:O(1)O(1) 。迭代的过程只会产生几个指针,所以它所需要的空间是常数级别的。 + * Link: https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/hua-jie-suan-fa-21-he-bing-liang-ge-you-xu-lian-bi/ + */ + class Solution { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + ListNode prevHead = new ListNode(-1); + + ListNode prev = prevHead; //哨兵节点可以减少很多特殊操作 + while (l1 != null && l2 != null) { + if (l1.val < l2.val) { + prev.next = l1; + l1 = l1.next; + } else { + prev.next = l2; + l2 = l2.next; + } + prev = prev.next; //此处容易遗漏 + } + + if (l1 == null) prev.next = l2; + if (l2 == null) prev.next = l1; +// prev.next = l1 == null ? l2 : l1; + + return prevHead.next; //通过哨兵返回合并的表头 + } + } diff --git a/Week 01/id_338/LeetCode_26_338.java b/Week 01/id_338/LeetCode_26_338.java new file mode 100644 index 000000000..6475806a1 --- /dev/null +++ b/Week 01/id_338/LeetCode_26_338.java @@ -0,0 +1,36 @@ +/** + * User: liwei + * Date: 2019/10/19 19:45 + * Desc: + */ +public class LeetCode_26_338 { + /* + * + * 思路:双指针法 + * 数组完成排序后,我们可以放置两个指针 i 和 j,其中 i 是慢指针,而 j 是快指针。 + * 只要 nums[i] = nums[j],我们就增加j以跳过重复项。 + * + * 当我们遇到 nums[j] != nums[i]时,跳过重复项的运行已经结束, + * 因此我们必须把它nums[j]的值复制到 nums[i + 1],然后递增i, + * 接着我们将再次重复相同的过程,直到j到达数组的末尾为止。 + * + * 最重要的是要明确快慢指针的具体作用,然后归纳后做好控制即可 + * + * 复杂度分析 + * 时间复杂度:O(n),假设数组的长度是 n,那么 i 和 j 分别最多遍历 n 步。 + * 空间复杂度:O(1)。 + */ + class Solution { + public int removeDuplicates(int[] nums) { + if (nums == null || nums.length == 0) return 0; + int i = 0; + for (int j = 1; j < nums.length; j++) { + if (nums[j] != nums[i]) { + i++; + nums[i] = nums[j]; + } + } + return i + 1; + } + } +} diff --git a/Week 01/id_338/LeetCode_283_338.java b/Week 01/id_338/LeetCode_283_338.java new file mode 100644 index 000000000..cf0d51c66 --- /dev/null +++ b/Week 01/id_338/LeetCode_283_338.java @@ -0,0 +1,24 @@ +/** + * User: liwei + * Date: 2019/10/19 19:48 + * Desc: + */ +public class LeetCode_283_338 { + class Solution { + public void moveZeroes(int[] nums) { + if (nums == null || nums.length == 0) { + return; + } + + int cur = 0; + + for (int i = 0; i < nums.length; ++i) { + if (nums[i] != 0) { + int temp = nums[cur]; + nums[cur++] = nums[i]; + nums[i] = temp; + } + } + } + } +} diff --git a/Week 01/id_343/LeetCode_26.go b/Week 01/id_343/LeetCode_26.go new file mode 100644 index 000000000..b30c41d66 --- /dev/null +++ b/Week 01/id_343/LeetCode_26.go @@ -0,0 +1,14 @@ +func removeDuplicates(nums []int) int { + if len(nums) <= 1 { + return len(nums) + } + + lens, start := 1, nums[0] + for i := 1; i < len(nums); i++ { + if nums[i] != start { + lens++ + nums[lens-1], start = nums[i], nums[i] + } + } + return lens +} diff --git a/Week 01/id_343/LeetCode_641.go b/Week 01/id_343/LeetCode_641.go new file mode 100644 index 000000000..645eb8cd7 --- /dev/null +++ b/Week 01/id_343/LeetCode_641.go @@ -0,0 +1,80 @@ + +type MyCircularDeque struct { + Size int + Values []int +} + + +/** Initialize your data structure here. Set the size of the deque to be k. */ +func Constructor(k int) MyCircularDeque { + return MyCircularDeque{Size:k} +} + + +/** Adds an item at the front of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) InsertFront(value int) bool { + if (len(this.Values) == this.Size) { + return false + } + this.Values = append([]int{value}, this.Values...) + return true +} + + +/** Adds an item at the rear of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) InsertLast(value int) bool { + if (len(this.Values) == this.Size) { + return false + } + this.Values = append(this.Values, value) + return true +} + + +/** Deletes an item from the front of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) DeleteFront() bool { + if (len(this.Values) == 0) { + return false + } + this.Values = this.Values[1:] + return true +} + + +/** Deletes an item from the rear of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) DeleteLast() bool { + if (len(this.Values) == 0) { + return false + } + this.Values = this.Values[:len(this.Values)-1] + return true +} + +/** Get the front item from the deque. */ +func (this *MyCircularDeque) GetFront() int { + if len(this.Values) == 0 { + return -1 + } + return this.Values[0] +} + + +/** Get the last item from the deque. */ +func (this *MyCircularDeque) GetRear() int { + if len(this.Values) == 0 { + return -1 + } + return this.Values[len(this.Values)-1] +} + + +/** Checks whether the circular deque is empty or not. */ +func (this *MyCircularDeque) IsEmpty() bool { + return len(this.Values) == 0 +} + + +/** Checks whether the circular deque is full or not. */ +func (this *MyCircularDeque) IsFull() bool { + return len(this.Values) == this.Size +} diff --git a/Week 01/id_343/LeetCode_66.go b/Week 01/id_343/LeetCode_66.go new file mode 100644 index 000000000..6e96d7985 --- /dev/null +++ b/Week 01/id_343/LeetCode_66.go @@ -0,0 +1,12 @@ +func plusOne(digits []int) []int { + for i := len(digits) - 1; i >= 0; i-- { + if digits[i] < 9 { + digits[i]++ + return digits + } else { + digits[i] = 0 + } + } + + return append([]int{1}, digits...) +} diff --git a/Week 01/id_348/MyCircularQueue.py b/Week 01/id_348/MyCircularQueue.py new file mode 100644 index 000000000..59b918485 --- /dev/null +++ b/Week 01/id_348/MyCircularQueue.py @@ -0,0 +1,86 @@ +class MyCircularDeque(object): + + def __init__(self, k): + """ + Initialize your data structure here. Set the size of the deque to be k. + :type k: int + """ + self.k, self.q = k, [] + + + def insertFront(self, value): + """ + Adds an item at the front of Deque. Return true if the operation is successful. + :type value: int + :rtype: bool + """ + if len(self.q) < self.k: + self.q.insert(0, value) + return True + return False + + + def insertLast(self, value): + """ + Adds an item at the rear of Deque. Return true if the operation is successful. + :type value: int + :rtype: bool + """ + if len(self.q) < self.k: + self.q.append(value) + return True + return False + + + def deleteFront(self): + """ + Deletes an item from the front of Deque. Return true if the operation is successful. + :rtype: bool + """ + if self.q: + self.q.pop(0) + return True + return False + + + def deleteLast(self): + """ + Deletes an item from the rear of Deque. Return true if the operation is successful. + :rtype: bool + """ + if self.q: + self.q.pop(-1) + return True + return False + + + def getFront(self): + """ + Get the front item from the deque. + :rtype: int + """ + return self.q[0] if self.q else -1 + + + def getRear(self): + """ + Get the last item from the deque. + :rtype: int + """ + return self.q[-1] if self.q else -1 + + + def isEmpty(self): + """ + Checks whether the circular deque is empty or not. + :rtype: bool + """ + return True if len(self.q) == 0 else False + + + def isFull(self): + """ + Checks whether the circular deque is full or not. + :rtype: bool + """ + return True if len(self.q) == self.k else False \ No newline at end of file diff --git a/Week 01/id_348/RemoveDuplicates.py b/Week 01/id_348/RemoveDuplicates.py new file mode 100644 index 000000000..ef25aa199 --- /dev/null +++ b/Week 01/id_348/RemoveDuplicates.py @@ -0,0 +1,15 @@ +class Solution(object): + def removeDuplicates(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + if not nums: + return 0 + + i = 0 + for j in range(1, len(nums)): + if nums[j] != nums[i]: + i += 1 + nums[i] = nums[j] + return i + 1 \ No newline at end of file diff --git a/Week 01/id_353/Leetcode_26_353.cpp b/Week 01/id_353/Leetcode_26_353.cpp new file mode 100644 index 000000000..056a114e4 --- /dev/null +++ b/Week 01/id_353/Leetcode_26_353.cpp @@ -0,0 +1,24 @@ +/* + * @lc app=leetcode.cn id=26 lang=cpp + * + * [26] 删除排序数组中的重复项 + */ + +// @lc code=start +class Solution { +public: + int removeDuplicates(vector& nums) { + if (nums.size() < 2) + return nums.size(); + int i = 0; + for (int j = 1; j < nums.size(); j++) { + if (nums[i] != nums[j]) { + i++; + nums[i] = nums[j]; + } + } + return i + 1; + } +}; +// @lc code=end + diff --git a/Week 01/id_353/Leetcode_641_353.cpp b/Week 01/id_353/Leetcode_641_353.cpp new file mode 100644 index 000000000..893dd08f8 --- /dev/null +++ b/Week 01/id_353/Leetcode_641_353.cpp @@ -0,0 +1,94 @@ +/* + * @lc app=leetcode.cn id=641 lang=cpp + * + * [641] 设计循环双端队列 + */ + +// @lc code=start +class MyCircularDeque { +private: + vector mydeque; + int front, rear, count; + int size; + +public: + /** Initialize your data structure here. Set the size of the deque to be k. */ + MyCircularDeque(int k) { + front = 0; + rear = 0; + count = 0; + mydeque = vector(k); + size = k; + } + + /** Adds an item at the front of Deque. Return true if the operation is successful. */ + bool insertFront(int value) { + if (isFull()) return false; + front = front == 0 ? size - 1 : front - 1; + mydeque[front] = value; + count++; + return true; + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + bool insertLast(int value) { + if (isFull()) return false; + mydeque[rear] = value; + rear = (rear + 1) % size; + count++; + return true; + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + bool deleteFront() { + if (isEmpty()) return false; + front = (front + 1) % size; + count--; + return true; + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + bool deleteLast() { + if (isEmpty()) return false; + rear = rear == 0 ? size - 1 : rear - 1; + count--; + return true; + } + + /** Get the front item from the deque. */ + int getFront() { + if (isEmpty()) return -1; + return mydeque[front]; + } + + /** Get the last item from the deque. */ + int getRear() { + if (isEmpty()) return -1; + return rear == 0 ? mydeque[size - 1] : mydeque[rear - 1]; + } + + /** Checks whether the circular deque is empty or not. */ + bool isEmpty() { + return count == 0; + } + + /** Checks whether the circular deque is full or not. */ + bool isFull() { + return count == size; + } +}; + +/** + * Your MyCircularDeque object will be instantiated and called as such: + * MyCircularDeque* obj = new MyCircularDeque(k); + * bool param_1 = obj->insertFront(value); + * bool param_2 = obj->insertLast(value); + * bool param_3 = obj->deleteFront(); + * bool param_4 = obj->deleteLast(); + * int param_5 = obj->getFront(); + * int param_6 = obj->getRear(); + * bool param_7 = obj->isEmpty(); + * bool param_8 = obj->isFull(); + */ +// @lc code=end + diff --git a/Week 01/id_358/NOTE.md b/Week 01/id_358/NOTE.md index a6321d6e2..f8c9fbbfa 100644 --- a/Week 01/id_358/NOTE.md +++ b/Week 01/id_358/NOTE.md @@ -1,4 +1,72 @@ # NOTE - +## 本周学习总结 +### 第一课学习了基本数据结构数组,链表,跳表的基本实现和特性。 + +1. 数组:访问很快O(1), 增加删除操作需要更多操作 O(n) + +2. 链表:增加删除操作很快 O(1), 访问是O(n)。 + +3. 跳表:为了解决链表访问慢的问题。实现原理是“升维“——空间换时间,增加跨度更大的多级索引。访问的时间复杂度O(logn),空间复杂度O(n)。但增加删除操作需要更多操作,需要更新索引。 + + > 在工程应用上,Redis就是基于跳表实现的。 + +### 第一课相关实战题解析: + +1. 移动零 + + 可以利用一维数组变换元素位置。 + +2. 容纳最多水 + + 可以使用双指针(左右边界向中间收敛),达到优化的目的 + +3. 爬楼梯问题 + + 利用数学归纳法,得到递推公式,其实是斐波那契数列问题。解决方法可以用1:递归+缓存;2:基于数组的动态规划。 + +4. 三数之和 + + 该题是“二数之和”的进阶。可以“暴力法”解决,效率不高。 + + 比较巧妙的思路是基于hashmap,在数组循环时,从map中找 target - nums[i],找不到就添加到map中,key为target - nums[i], value为下标 i。 + +### 第二课学习了栈,队列的基本实现和特性 + +工程中常用的是双端队列deque.即首尾都可以增删节点。查询删除操作O(1),访问O(n)。 + +还有优先队列。底层实现又多种:heap, BST, treap。 + +### 第二课相关实战题解析 + +1. 有效括号问题,最小栈问题 + + 引出问题:什么样的问题可以用栈解决? + + 答:能找最近相关性的问题,类似洋葱模型。 + + 有效括号问题就可以用栈解决 + + 左括号入栈,遇到右括号与栈顶匹配,匹配上就移去栈顶直到栈为空。 + +2. 最大面积问题(Leetc #84) + + 比较巧妙的解法是用栈解决。 + + 从左边开始遍历,以有序的栈保存柱子的高度,遇到比栈顶小的柱子,就弹出处理,计算面积,更新最大面积;遇到较大的就入栈(为了计算面积方便,栈内保留柱子的下标) + +3. 滑动窗口问题 + + 利用双端队列(单调队列,队列)解决。 + + 用一个双端队列保存k个元素的下标;在队尾push新元素,把前面的比新元素小的元素删去。 + +### 总结 + + +跟着老师实战了以一些相关的算法题。 + +学习到了一些解决算法问题的基本思想,比如“找重复性”,“找近似性”;算法优化方面的思路有“空间换时间”等。 +在解决数组,链表一类的问题时,首先想到的是暴力解决的方法,但要进一步想到优化的方法,比如可不可以省略掉一些循环,利用缓存等思路,利用常见的方法:双指针,快慢指针等。 +老师介绍的“五遍刷题法”,很有意思,如果多实践肯定能有效提升算法能力。但老师也指出做算法题,做出来只是完成50%,还需要Feedback,不管是主动的去找其他别人的最优解还是别人review你的解法都可以。 \ No newline at end of file diff --git a/Week 01/id_358/solutions.js b/Week 01/id_358/solutions.js new file mode 100644 index 000000000..db8dcfe72 --- /dev/null +++ b/Week 01/id_358/solutions.js @@ -0,0 +1,131 @@ +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ +/** + * #21. 合并两个有序链表 - 解法一 + * @param {ListNode} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ +function ListNode(val) { + this.val = val; + this.next = null; +} +var mergeTwoLists = function (l1, l2) { + //设置额外的第三条链表,拼接排序后的节点,作为结果返回 + const l3 = new ListNode(-1); + //设置head节点指向l3,随着循环一直指向最后,l3作为链表首节点不变,最后作为结果返回 + const head = l3; + while (l1 !== null && l2 !== null) { + if (l1.val <= l2.val) { + head.next = l1; + l1 = l1.next; + } else { + head.next = l2; + l2 = l2.next; + } + //每轮循环都要向后移动head节点 + head = head.next; + } + //循环结束后拼接剩下的不为空的链表,且一定是排序的。 + head.next = l1 ? l1 : l2; + return l3.next; +}; + +/** + * 21. 合并两个有序链表 - 解法二 + * @param {*} l1 + * @param {*} l2 + */ +var mergeTwoLists = function (l1, l2) { + //递归 + // 终止条件,递归处理的子问题中,链表为空;注意参数顺序;返回不为空的子链表即可; + if (l1 === null) { + return l2; + } + if (l2 === null) { + return l1; + } + // 比较l1和l2的val,较小的节点一定作为头部节点,更新其next节点;把next之后的节点和另一条链表递归处理; + // 注意头部节点需要return; + if (l1.val <= l2.val) { + l1.next = mergeTwoLists(l1.next, l2); + return l1; + } else { + l2.next = mergeTwoLists(l1, l2.next); + return l2; + } +} + +/** + * 88. 合并两个有序数组 + * @param {*} nums1 + * @param {*} m + * @param {*} nums2 + * @param {*} n + */ +var merge = function (nums1, m, nums2, n) { + //双指针,从两个数组有效数字位置往前循环,较大的数字往nums1后面开始填充; + //循环结束后将num2剩余的数字填充到nums1前面;需要自行实现js版本的数组拷贝函数; + let len1 = m - 1; + let len2 = n - 1; + let len = m + n - 1; + while (len1 >= 0 && len2 >= 0) { + if (nums1[len1] >= nums2[len2]) { + nums1[len--] = nums1[len1--]; + } else { + nums1[len--] = nums2[len2--]; + } + } + + function arrayCopy(src, srcIndex, dest, destIndex, length) { + dest.splice(destIndex, length, ...src.slice(srcIndex, srcIndex + length)) + } + arrayCopy(nums2, 0, nums1, 0, len2 + 1); +}; + + +/** + * 1. 两数之和 + * @param {*} nums + * @param {*} target + */ +var twoSum = function (nums, target) { + let hashMap = []; // 这里也可以用map。 + for (let i = 0; i < nums.length; i++) { + let key = target - nums[i] + if (hashMap[key] !== undefined) { // 注意这个地方极容易出错。 + //一般写if判断,按习惯会写成if(hashMap[key]), 但是这里会添加数组下标0, + //导致if(hashMap[key])在匹配时返回0,if不成立,最后得不到正确结果。 + //因此以后遇到向数组和map中添加数组下标并取出判断时注意为0的情况,使用与undefiend判断。 + return [hashMap[key], i] + } else { + hashMap[nums[i]] = i; + } + } +} + +/** + * 66. 加一 + * @param {*} digits + */ +var plusOne = function(digits) { + let len = digits.length; + digits[len-1] +=1; + let i =0; + while(digits[len-1-i] > 9) { + digits[len-1-i] -=10; + i++; + if(digits[len-1-i]) { + digits[len-1-i] +=1 + } else { + digits.unshift(1) + } + + } + return digits; +}; \ No newline at end of file diff --git a/Week 01/id_363/LeetCode_189_363.java b/Week 01/id_363/LeetCode_189_363.java new file mode 100644 index 000000000..9beeb68e3 --- /dev/null +++ b/Week 01/id_363/LeetCode_189_363.java @@ -0,0 +1,88 @@ +package com.test.leetcode; + +import org.junit.Test; + +import java.util.List; + + +public class SolutionRotateArray189 { + + + @Test + public void test1() { + int[] arr = new int[]{1,2,3,4,5,6}; +// rotate( arr, 10); + rotate3( arr, 2); + System.out.println(arr); + } + + /** + * 旋转数组 + * 空间复杂度是O(k) + * 1,2,3,4,5,6,7 k = 3 + * 5,6,7,1,2,3,4 + * @param nums + * @param k + */ + public void rotate1(int[] nums, int k) { + if (nums == null || nums.length <= 1 || k == 0) return; + k = k % nums.length; + int[] temp = new int[k]; + System.arraycopy(nums, nums.length - k, temp, 0, k); + System.arraycopy(nums, 0, nums, k, nums.length - k); + System.arraycopy(temp, 0, nums, 0, k); + } + + + /** + * 循环替换 + * @param nums + * @param k + */ + public void rotate2(int[] nums, int k) { + if (nums == null || nums.length <= 1 || k == 0) return; + int len = nums.length; + k = k % nums.length; + int count = 0; + for(int start = 0; count < len ; start ++) { + int curIndex = start; + int curValue = nums[start]; + do { + int nextIndex = (curIndex + k) % len; + int nextValue = nums[nextIndex]; + nums[nextIndex] = curValue; + curIndex = nextIndex; + curValue = nextValue; + count ++; + } while (start != curIndex); + } + } + + + /** + * 翻转 + * @param nums + * @param k + */ + public void rotate3_best(int[] nums, int k) { + if (nums == null || nums.length <= 1 || k == 0) return; + k = k % nums.length; + reverse(nums, 0, nums.length -1); + reverse(nums, 0, k - 1); + reverse(nums, k, nums.length - 1); + + } + + private void reverse(int[] nums, int start, int end) { + if (nums == null || nums.length <= 1 || end <= start) return; + while (start < end) { + int temp = nums[end]; + nums[end] = nums[start]; + nums[start] = temp; + start ++; + end --; + } + } + + +} diff --git a/Week 01/id_363/LeetCode_1_363.java b/Week 01/id_363/LeetCode_1_363.java new file mode 100644 index 000000000..6036e6be7 --- /dev/null +++ b/Week 01/id_363/LeetCode_1_363.java @@ -0,0 +1,67 @@ +package com.test.leetcode; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +public class SolutionTwoNum1 { + + + @Test + public void test1() { + System.out.println(Arrays.toString(twoSum_20191017_1(new int[]{3,3,4,7}, 10))); + System.out.println(Arrays.toString(twoSum_20191017_2(new int[]{3,3,4,7}, 10))); + System.out.println(Arrays.toString(twoSum_20191017_3_best(new int[]{3,3,4,7}, 10))); + } + + + /** + * 1.暴力方法:两层for循环 + * 2.两遍map + * 3.一遍map + * @param nums + * @param target + * @return + */ + public int[] twoSum_20191017_1(int[] nums, int target) { + for (int i = 0; i < nums.length - 1; i ++) { + for (int j = i + 1; j < nums.length; j ++) { + if (nums[i] + nums[j] == target) { + return new int[]{i, j}; + } + } + } + return new int[2]; + } + + public int[] twoSum_20191017_2(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; i ++) { + map.put(nums[i], i); + } + for (int i = 0; i < nums.length; i ++) { + if (map.containsKey(target - nums[i])) { + return new int[]{i, map.get(target - nums[i])}; + } + } + return new int[2]; + } + + public int[] twoSum_20191017_3_best(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; i ++) { + int result = target - nums[i]; + if (map.containsKey(result)) { + return new int[]{i, map.get(result)}; + } else { + map.put(nums[i], i); + } + } + return new int[2]; + } + +} diff --git a/Week 01/id_363/LeetCode_21_363.java b/Week 01/id_363/LeetCode_21_363.java new file mode 100644 index 000000000..299c91f91 --- /dev/null +++ b/Week 01/id_363/LeetCode_21_363.java @@ -0,0 +1,130 @@ +package com.test.leetcode; + +import org.junit.Test; + +import java.util.List; + + +public class SolutionMergeTwoList21 { + + + @Test + public void test1() { + ListNode node1 = new ListNode(1); + ListNode node2 = new ListNode(2); + ListNode node3 = new ListNode(4); + + ListNode node4 = new ListNode(1); + ListNode node5 = new ListNode(3); + ListNode node6 = new ListNode(4); + + node1.next = node2; + node2.next = node3; + node4.next = node5; + node5.next = node6; + ListNode result = mergeTwoLists(node1, node4); + System.out.println(result); + } + + + /** + * 时间复杂度是O(n + m) + * 空间复杂度也是O(n + m) + * @param l1 + * @param l2 + * @return + */ + public ListNode mergeTwoLists1(ListNode l1, ListNode l2) { + if (l1 == null && l2 == null) return null; + if (l1 == null && l2 != null) return l2; + if (l2 == null && l1 != null) return l1; + ListNode result = new ListNode(0); + ListNode nowNode = result; + while (l1 != null && l2 != null) { + int min; + while (l1 != null && l2 != null && l1.val < l2.val) { + min = l1.val; + l1 = l1.next; + result.next = new ListNode(min); + result = result.next; + } + while (l1 != null && l2 != null && l1.val > l2.val) { + min = l2.val; + l2 = l2.next; + result.next = new ListNode(min); + result = result.next; + } + while (l1 != null && l2 != null && l1.val == l2.val) { + result.next = new ListNode(l1.val); + result = result.next; + result.next = new ListNode(l2.val); + result = result.next; + l1 = l1.next; + l2 = l2.next; + } + } + + while (l1 != null) { + result.next = new ListNode(l1.val); + result = result.next; + l1 = l1.next; + } + + while (l2 != null) { + result.next = new ListNode(l2.val); + result = result.next; + l2 = l2.next; + } + return nowNode.next; + } + + /** + * 递归解法: + * 1.终止条件 l1 = null || l2 = null + * 2.递归方程: 找两个聊表头的最小值,把最小值拼接到结果上, + * + * @param l1 + * @param l2 + * @return + */ + public ListNode mergeTwoLists(ListNode l1, ListNode 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; + } + } + + + /** + * 链表题目: 1.遍历 2. 哨兵 3.原有节点尽可能的重复使用 + * 链表两种解题思路: 1. 循环 2. 递归 + * @param l1 + * @param l2 + * @return + */ + public ListNode mergeTwoLists_xunhuan_best(ListNode l1, ListNode l2) { + if (l1 == null && l2 != null) return l2; + if (l1 != null && l2 == null) return l1; + ListNode preHead = new ListNode(-1); + ListNode pre = preHead; + while (l1 != null && l2 != null) { + if (l1.val > l2.val) { + pre.next = l2; + l2 = l2.next; + } else { + pre.next = l1; + l1 = l1.next; + } + pre = pre.next; + } + pre.next = l1 == null ? l2 : l1; + return preHead.next; + } + + +} diff --git a/Week 01/id_363/LeetCode_26_363.java b/Week 01/id_363/LeetCode_26_363.java new file mode 100644 index 000000000..775c71eaf --- /dev/null +++ b/Week 01/id_363/LeetCode_26_363.java @@ -0,0 +1,73 @@ +package com.test.leetcode; + +import org.junit.Test; + +import java.util.List; + + +public class SolutionRemoveDuplicate26 { + + + @Test + public void test1() { + System.out.println(removeDuplicates(new int[]{0,0,1,1,1,2,2,3,3,4})); + } + + + /** + * 删除数组中的重复元素,返回新数组的长度 + * 1.索引:记录不是重复数组的索引位置 + * @param nums + * @return + */ + public int removeDuplicates_best(int[] nums) { + if (nums == null || nums.length == 0) return 0; + int lastNonDuplicateIndex = 1; + for (int i = 1; i < nums.length; i ++) { + if (nums[i] != nums[i - 1] && i > lastNonDuplicateIndex) { + nums[lastNonDuplicateIndex ++] = nums[i]; + } + } + return lastNonDuplicateIndex ; + } + + + /** + * 2. 前面有多少重复的值移动多少位 + * @param nums + * @return + */ + public int removeDuplicates2(int[] nums) { + if (nums == null || nums.length == 0) return 0; + int duplicateCount = 0; + for (int i = 1; i < nums.length; i ++) { + if (nums[i] == nums[i - 1]) { + duplicateCount ++; + } else { + if (duplicateCount != 0) { + nums[i - duplicateCount] = nums[i]; + } + } + } + return nums.length - duplicateCount ; + } + + + /** + * 双指针 和 1.索引:记录不是重复数组的索引位置 思路一致 + * @param nums + * @return + */ + public int removeDuplicates3(int[] nums) { + if (nums == null) return 0; + int i = 0, j = i + 1; + while (j < nums.length) { + if (nums[j] != nums[i] && j - i > 1) { + nums[++ i] = nums[j]; + } + j ++; + } + return i + 1; + } + +} diff --git a/Week 01/id_363/LeetCode_283_363.java b/Week 01/id_363/LeetCode_283_363.java new file mode 100644 index 000000000..3f06f7a98 --- /dev/null +++ b/Week 01/id_363/LeetCode_283_363.java @@ -0,0 +1,93 @@ +package com.test.leetcode; + +import org.junit.Test; + +import java.util.Arrays; + +/** + * done 20191015 + * done 20191016 + * todo 20191017 看leetcode most-votes 前面三个 + * todo 20191022 看leetcode most-votes 前面三个 + * @return + */ +public class SolutionMoveZero283 { + + + + @Test + public void test1() { + moveZeroes_20191016_1(new int[]{0,1,0,3,12}); + moveZeroes_20191016_1(new int[]{0,0,1}); + moveZeroes_20191016_1(new int[]{1}); + System.out.println("-----------------"); + moveZeroes_20191016_2(new int[]{0,1,0,3,12}); + moveZeroes_20191016_2(new int[]{0,0,1}); + moveZeroes_20191016_2(new int[]{1}); + System.out.println("---------------------"); + moveZeroes_20191016_3(new int[]{0,1,0,3,12}); + moveZeroes_20191016_3(new int[]{0,0,1}); + moveZeroes_20191016_3(new int[]{1}); + } + + + /** + * 暴力:遍历数组时遇到0,则把零和下一下不是0的数字进行交换 + * 循环:先把不是0的数挪到数组的最前面,后面补 0 + * 循环:每个数前面有多少0就往前挪几位 + * @param nums + */ + public void moveZeroes_20191016_1(int[] nums) { + System.out.println(Arrays.toString(nums)); + int len = nums.length; + for (int i = 0; i < len ; i ++) { + if (nums[i] == 0) { + int nowIndex = i; + while (nowIndex < len && nums[nowIndex] == 0) { + nowIndex += 1; + } + if (nowIndex == len) { + break; + } + nums[i] = nums[nowIndex]; + nums[nowIndex] = 0; + } + } + System.out.println(Arrays.toString(nums)); + } + + public void moveZeroes_20191016_2(int[] nums) { + System.out.println(Arrays.toString(nums)); + int lastNonZeorIndex = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != 0) { + nums[lastNonZeorIndex ++] = nums[i]; + } + } + for (int i = lastNonZeorIndex; i < nums.length; i ++) { + nums[i] = 0; + } + System.out.println(Arrays.toString(nums)); + } + + /** + * 最优 best + * @param nums + */ + public void moveZeroes_20191016_3(int[] nums) { + System.out.println(Arrays.toString(nums)); + int zeroCount = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != 0) { + if (zeroCount > 0) { + nums[i - zeroCount] = nums[i]; + nums[i] = 0; + } + } else { + zeroCount += 1; + } + } + + System.out.println(Arrays.toString(nums)); + } +} diff --git a/Week 01/id_363/LeetCode_66_363.java b/Week 01/id_363/LeetCode_66_363.java new file mode 100644 index 000000000..672e29df7 --- /dev/null +++ b/Week 01/id_363/LeetCode_66_363.java @@ -0,0 +1,99 @@ +package com.test.leetcode; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + + +public class SolutionPlusOne66 { + + + @Test + public void test1() { + System.out.println(Arrays.toString(plusOne3(new int[]{9,9,9}))); + System.out.println(Arrays.toString(plusOne3(new int[]{1,9,9}))); + System.out.println(Arrays.toString(plusOne3(new int[]{1,2,3}))); + } + + + public void addOne(int[] digits) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < digits.length; i++) { + sb.append(digits[i]); + } +// String resultNum = String.valueOf(Integer.valueOf(sb.toString()) + 1); + String resultNum = String.valueOf(new BigInteger((sb.toString()) + 1)); + digits = new int[resultNum.length()]; + for (int i = 0; i < resultNum.length(); i++) { + digits[i] = Integer.valueOf(resultNum.substring(i, i + 1)); + } + } + + + public int[] plusOne(int[] digits) { + if (digits == null || digits.length == 0) return null; + int len = digits.length; + int lastValue = digits[ len - 1]; + if (9 != lastValue) { + digits[len - 1] = lastValue + 1; + return digits; + } + + Boolean allNine = true; + for (int i = 0;i < len ; i ++) { + if (digits[i] != 9) { + allNine = false; + break; + } + } + if (allNine) { + int[] newDigits = new int[len + 1]; + newDigits[0] = 0; + System.arraycopy(digits, 0, newDigits, 1, len); + digits = newDigits; + len = len + 1; + } + + for (int j = len - 1; j >= 0; j --) { + if (digits[j] == 9) { + digits[j] = 0; + continue; + } else { + digits[j] = digits[j] + 1; + break; + } + } + return digits; + } + + public int[] plusOne3(int[] digits) { + if (digits == null || digits.length == 0) return null; + for (int i = digits.length - 1; i >= 0; i --) { + if (digits[i] < 9) { + digits[i] ++; + return digits; + } + digits[i] = 0; + } + digits = new int[digits.length + 1]; + digits[0] = 1; + return digits; + } + + public int[] plusOne2(int[] digits) { + if (digits == null || digits.length == 0) return null; + for (int i = digits.length - 1; i >= 0; i --) { + digits[i] ++; + digits[i] = digits[i] % 10; + if (digits[i] != 0) return digits; + } + digits = new int[digits.length + 1]; + digits[0] = 1; + return digits; + } + + + + +} diff --git a/Week 01/id_363/LeetCode_88_363.java b/Week 01/id_363/LeetCode_88_363.java new file mode 100644 index 000000000..40836b6ad --- /dev/null +++ b/Week 01/id_363/LeetCode_88_363.java @@ -0,0 +1,110 @@ +package com.test.leetcode; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + + +public class SolutionMergeArray88 { + + + @Test + public void test1() { + int[] nums1 = new int[]{1,2,3,0,0,0}; + int m = 3; + int[] nums2 = new int[]{2,5,6}; + int n = 3; + merge1(nums1, m, nums2, n); + } + + + /** + * 自己写 + * @param nums1 + * @param m + * @param nums2 + * @param n + */ + public void merge(int[] nums1, int m, int[] nums2, int n) { + if ( nums2 == null) return; + // 1. 吧nums1 中的数据像后移动m位 + System.arraycopy(nums1, 0, nums1, n , m); + // 2. 循环nums1 和 num2 向nums1 中加数据 + int mi = n , ni = 0,i = 0; + while (mi < m + n && ni < n) { + while(nums1[mi] < nums2[ni]) { + nums1[i ++] = nums1[mi ++]; + if (mi == m + n ) { + break; + } + } + while(mi < m + n && ni < n && nums1[mi] >= nums2[ni]) { + nums1[i ++] = nums2[ni ++]; + if (ni == n) { + break; + } + } + } + while(mi < m + n) { + nums1[i ++] = nums1[mi ++]; + } + while(ni < n) { + nums1[i ++] = nums2[ni ++]; + } + System.out.println(Arrays.toString(nums1)); + } + + /** + * 合并+排序 + * 双指针:从前往后 复制一份新的nums1 + * 双指针:从后往前 + * @param nums1 + * @param m + * @param nums2 + * @param n + */ + public void merge1(int[] nums1, int m, int[] nums2, int n) { + if ( nums2 == null) return; + System.arraycopy(nums2, 0, nums1, m, n); + Arrays.sort(nums1); + System.out.println(Arrays.toString(nums1)); + } + + public void merge2(int[] nums1, int m, int[] nums2, int n) { + if ( nums2 == null) return; + int[] nums1Copy = nums1.clone(); + int i1 = 0, i2 = 0, i = 0; + while (i1 < m && i2 < n) { + nums1[i ++] = nums1Copy[i1] < nums2[i2] ? nums1Copy[i1 ++] : nums2[i2 ++]; + } + if (i1 < m) { + System.arraycopy(nums1Copy, i1, nums1, n + i1 , m - i1); + } + if (i2 < n) { + System.arraycopy(nums2, i2, nums1, m + i2 , n - i2); + } + System.out.println(Arrays.toString(nums1)); + } + + + /** + * 最佳 best + * @param nums1 + * @param m + * @param nums2 + * @param n + */ + public void merge3(int[] nums1, int m, int[] nums2, int n) { + if ( nums2 == null) return; + int i1 = m - 1, i2 = n - 1, i = m + n -1; + while (i1 >= 0 && i2 >= 0) { + nums1[i --] = nums1[i1] > nums2[i2] ? nums1[i1 --] : nums2[i2 --]; + } + if (i2 >= 0) { + System.arraycopy(nums2, 0, nums1, 0, i2 + 1); + } + + System.out.println(Arrays.toString(nums1)); + } +} diff --git a/Week 01/id_363/NOTE.md b/Week 01/id_363/NOTE.md index a6321d6e2..64b1127bc 100644 --- a/Week 01/id_363/NOTE.md +++ b/Week 01/id_363/NOTE.md @@ -1,4 +1,179 @@ -# NOTE +### 学习总结 +- 1.刷题方法:自己写/leetcode-cn.com 中看题解/leetcode.com中看discuss --> 至少五遍哟 +- 2.第一次分析了源码(表示好开心),发现PriorityQueue的底层实现使用的就是堆。 +- 3.最近重复子问题 +- 4.重要思想:升维,时间换空间 - +### 分析源码 +#### Queue : +先进先出, 继承了Collection接口,Queue中元素的顺序可以是FIFO,也可以按照comparator来排序(priorityQueue),提供了两种格式的接口:抛异常和返回特殊值(比如null) +抛异常: add element remove +返回特殊值: offer peek poll +网上查找为什么有两种格式的接口:有些Queue的实现,比如LinkedList,允许null值的存在,可能有时候需要通过是否抛异常来判断队列中没有值还是值是null。 +#### AbstractQueue +抽象类,实现了Queue的接口,包含了一些基础实现 +add: 调用offer,如果offer方法的返回值是false,抛出异常 +remove:调用poll,如果返回值是null,抛出异常 +element:调用peek方法,如果返回null,抛出异常 +addAll: 循环调用add方法 +clear:循环调用poll方法 +#### PriorityQueue +无界队列,是基于优先级堆来实现的,优先级队列中不允许null元素,同时优先级队列中的元素必须实现Comparator接口。 +PriorityQueue 不是线程安全的。 +时间复杂度分析: 入队和出队:O(logn) 删除和查看是否包含(remove/contains):O(n) 获取堆顶元素(peek)是O(1) + +属性分析: +``` +private static final int DEFAULT_INITIAL_CAPACITY = 11; +// 底层使用堆来实现 +transient Object[] queue; +private int size = 0; +private final Comparator comparator; +transient int modCount = 0; +``` +方法分析: +add: 底层调用offer方法 +offer: +``` +public boolean offer(E e) { + if (e == null) throw new NullPointerException(); + modCount++; + int i = size; + // 扩容 + if (i >= queue.length) + grow(i + 1); + size = i + 1; + // 添加元素 + if (i == 0) + queue[0] = e; + else + siftUp(i, e); + return true; + } +private void grow(int minCapacity) { + int oldCapacity = queue.length; + // 如果oldCapacity < 64 增加一倍, 否则增加 50% + int newCapacity = oldCapacity + ((oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1)); + // overflow-conscious code + if (newCapacity - MAX_ARRAY_SIZE > 0) + newCapacity = hugeCapacity(minCapacity); + queue = Arrays.copyOf(queue, newCapacity); + } +// 从下向上的堆化:默认使用小顶堆 +private void siftUpComparable(int k, E x) { + Comparable key = (Comparable) x; + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = queue[parent]; + if (key.compareTo((E) e) >= 0) + break; + queue[k] = e; + k = parent; + } + queue[k] = key; + } +``` +poll: +``` +// 把数据末尾放到堆顶,从上向下堆化 +public E poll() { + if (size == 0) + return null; + int s = --size; + modCount++; + E result = (E) queue[0]; + E x = (E) queue[s]; // 获取数组末尾元素 + queue[s] = null; + if (s != 0) + siftDown(0, x); + return result; + } +// 从上向下堆化 +private void siftDownComparable(int k, E x) { + Comparable key = (Comparable)x; + int half = size >>> 1; // 只循环非叶子节点 + while (k < half) { + int child = (k << 1) + 1; // child是子节点最小值的下标,最开始假设left是做节点 + Object c = queue[child]; // 叶子节点中的最小值 + int right = child + 1; + // 如果右节点比做节点小,那么child = right , c= queue[right] + if (right < size && + ((Comparable) c).compareTo((E) queue[right]) > 0) + c = queue[child = right]; + // 如果当前节点的值比最小值小,那么满足小顶堆的条件,直接退出循环 + // 否则,把当前节点和最小值节点替换,继续一个循环的堆化 + if (key.compareTo((E) c) <= 0) + break; + queue[k] = c; + k = child; + } + queue[k] = key; + } +``` +removeAt: +``` +// 还不太懂 +private E removeAt(int i) { + // assert i >= 0 && i < size; + modCount++; + int s = --size; + if (s == i) // removed last element + queue[i] = null; + else { + E moved = (E) queue[s]; + queue[s] = null; + siftDown(i, moved); + if (queue[i] == moved) { + siftUp(i, moved); + if (queue[i] != moved) + return moved; + } + } + return null; + } +``` +### 改写Deque代码 +#### 用add first 或 add last 新的api 改写Deque的代码 +修改前代码: +``` +public static void srcDemo(){ + + Deque deque = new LinkedList<>(); + deque.push("a"); + deque.push("b"); + deque.push("c"); + + System.out.println(deque); + + String str = deque.peek(); + System.out.println(str); + System.out.println(deque); + + while (deque.size() > 0) { + System.out.println(deque.pop()); + } + + System.out.println(deque); + } +``` + +修改后代码 +``` +Deque deque = new LinkedList<>(); + deque.addFirst("a"); + deque.addFirst("b"); + deque.addFirst("c"); + + System.out.println(deque); + + String str = deque.peekFirst(); + System.out.println(str); + System.out.println(deque); + + while (deque.size() > 0) { + System.out.println(deque.removeFirst()); + } + + System.out.println(deque); +``` \ No newline at end of file diff --git a/Week 01/id_368/LeetCode_189_368.java b/Week 01/id_368/LeetCode_189_368.java new file mode 100644 index 000000000..00be0ce91 --- /dev/null +++ b/Week 01/id_368/LeetCode_189_368.java @@ -0,0 +1,60 @@ +import java.util.Arrays; + +/** + * @author: liuyanhui@daojia-inc.com + * @date: 2019/10/22 + */ +public class LeetCode_189_368 { + + /*给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数 + https://leetcode-cn.com/problems/rotate-array/*/ + + public static void main(String[] args) { + int[] nums = {1,2,3,4,5,6,7}; + int k = 3; + rotate02(nums, k); + Arrays.stream(nums).forEach(i -> System.out.print(i + " ")); + } + + public static void rotate01(int[] nums, int k) { + //暴力法:向右移动k个位置相当于是将k个最末尾的元素移动到头部 + if (nums == null || nums.length == 0) { + return; + } + k = k % nums.length; + if (k == nums.length) { + return; + } + int temp; + while (k > 0) { + for (int i = nums.length - 1; i > 0; i--) { + temp = nums[i]; + nums[i] = nums[i-1]; + nums[i-1] = temp; + } + k--; + } + } + + public static void rotate02(int[] nums, int k) { + // 反转 + k = k % nums.length; + if (k == nums.length) { + return; + } + reverse(nums, 0, nums.length - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, nums.length - 1); + } + + public static void reverse (int[] nums, int start, int end) { + int temp; + while (start < end) { + temp = nums[start]; + nums[start] = nums[end]; + nums[end] = temp; + start++; + end--; + } + } +} diff --git a/Week 01/id_368/LeetCode_1_368.java b/Week 01/id_368/LeetCode_1_368.java new file mode 100644 index 000000000..a8eeff4e8 --- /dev/null +++ b/Week 01/id_368/LeetCode_1_368.java @@ -0,0 +1,29 @@ +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class LeetCode_1_368 { + + /* 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 + https://leetcode-cn.com/problems/two-sum/ */ + + public static void main(String[] args) { + int[] nums = {2, 7, 11, 15}; + int target = 9; + Arrays.stream(twoSum(nums, target)).forEach(num -> { + System.out.println(num); + }); + } + + public static int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + int num = target - nums[i]; + if (map.containsKey(num)) { + return new int[]{map.get(num), i}; + } + map.put(nums[i], i); + } + return null; + } +} diff --git a/Week 01/id_368/LeetCode_21_368.java b/Week 01/id_368/LeetCode_21_368.java new file mode 100644 index 000000000..ef598ae17 --- /dev/null +++ b/Week 01/id_368/LeetCode_21_368.java @@ -0,0 +1,64 @@ +import practice.ListNode; + +/** + * @author: liuyanhui@daojia-inc.com + * @date: 2019/10/22 + */ +public class LeetCode_21_368 { + /*将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 + https://leetcode-cn.com/problems/merge-two-sorted-lists/*/ + + public static void main(String[] args) { + ListNode l1 = new ListNode(1); + ListNode l12 = new ListNode(2); + ListNode l13 = new ListNode(4); + l1.next = l12; + l12.next = l13; + l13.next = null; + ListNode l2 = new ListNode(1); + ListNode l22 = new ListNode(3); + ListNode l23 = new ListNode(5); + l2.next = l22; + l22.next = l23; + l23.next = null; + + ListNode pre = mergeTwoListsRecursion(l1, l2); + while (pre != null) { + System.out.println(pre.val); + pre = pre.next; + } + } + + public static ListNode mergeTwoLists(ListNode l1, ListNode l2) { + ListNode preHead = new ListNode(-1); + ListNode pre = preHead; + while (l1 != null && l2 != null) { + if (l1.val <= l2.val) { + pre.next = l1; + l1 = l1.next; + } else { + pre.next = l2; + l2 = l2.next; + } + pre = pre.next; + } + pre.next = l1 != null ? l1 : l2; + return preHead.next; + } + + public static ListNode mergeTwoListsRecursion(ListNode l1, ListNode l2) { + if (l1 == null) { + return l2; + } + if (l2 == null) { + return l1; + } + if (l1.val < l2.val) { + l1.next = mergeTwoListsRecursion(l1.next, l2); + return l1; + } else { + l2.next = mergeTwoListsRecursion(l1, l2.next); + return l2; + } + } +} \ No newline at end of file diff --git a/Week 01/id_368/LeetCode_26_368.java b/Week 01/id_368/LeetCode_26_368.java new file mode 100644 index 000000000..1d54d885c --- /dev/null +++ b/Week 01/id_368/LeetCode_26_368.java @@ -0,0 +1,32 @@ +/** + * @author: liuyanhui@daojia-inc.com + * @date: 2019/10/22 + */ +public class LeetCode_26_368 { + + /*给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 + 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 + 链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array*/ + + public static void main(String[] args) { + int[] nums = {1, 1, 2}; + System.out.println(removeDuplicates(nums)); + } + + public static int removeDuplicates(int[] nums) { + // 因为数组已经排序,所以采用双指针从头至尾分别记录已去重位置和遍历位置 + if (nums == null || nums.length <= 0 ) { + return 0; + } + int j = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != nums[j]) { + j++; + if (i != j) { + nums[j] = nums[i]; + } + } + } + return j+1; + } +} diff --git a/Week 01/id_368/LeetCode_283_368.java b/Week 01/id_368/LeetCode_283_368.java new file mode 100644 index 000000000..bfb22b57f --- /dev/null +++ b/Week 01/id_368/LeetCode_283_368.java @@ -0,0 +1,33 @@ +import java.util.Arrays; + +public class LeetCode_283_368 { + + /* 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 + https://leetcode-cn.com/problems/move-zeroes/ */ + + public static void main(String[] args) { + int[] nums = {-1, 0, 3, 1, 0 ,0 ,100}; + Arrays.stream(moveZeros(nums)).forEach(i -> { + System.out.println(i); + }); + } + + public static int[] moveZeros (int[] nums) { + if (nums == null || nums.length == 0 || nums.length == 1) { + return nums; + } + // 设置下一个非零元素的下标 + int j = 0; + for (int i = 0 ; i < nums.length; i++) { + if (nums[i] != 0) { + // 非零元素移动到的下标位置 + if (i != j) { + nums[j] = nums[i]; + nums[i] = 0; + } + j++; + } + } + return nums; + } +} diff --git a/Week 01/id_368/LeetCode_42_368.java b/Week 01/id_368/LeetCode_42_368.java new file mode 100644 index 000000000..f779753ca --- /dev/null +++ b/Week 01/id_368/LeetCode_42_368.java @@ -0,0 +1,64 @@ +/** + * @author: liuyanhui@daojia-inc.com + * @date: 2019/10/25 + */ +public class LeetCode_42_368 { + + /*给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 + https://leetcode-cn.com/problems/trapping-rain-water/*/ + + public static void main(String[] args) { + int[] height = {0,1,0,2,1,0,1,3,2,1,2,1}; + System.out.println(trap(height)); + } + + public static int trap(int[] height) { + // 按照列来求面积 + // 当前列的面积:当前列高度、左侧最高的高度、右侧最高高度 + // 只有左右最高高度中的较低的高度 > 当前列高度:才可以积水,面积为高度差 + int sum = 0; + if (height == null || height.length == 0) { + return sum; + } + for (int i = 1; i < height.length - 1; i++) { + int max_left = 0; + for (int j = i - 1; j >= 0; j--) { + if (height[j] > max_left) { + max_left = height[j]; + } + } + + int max_right = 0; + for (int k = i + 1; k < height.length; k++) { + if (height[k] > max_right) { + max_right = height[k]; + } + } + + int min = Math.min(max_left, max_right); + if (height[i] < min) { + sum += min - height[i]; + } + } + return sum; + } + + public static int trap01(int[] height) { + int sum = 0; + int[] max_left = new int[height.length]; + int[] max_right = new int[height.length]; + for (int i = 1; i < height.length; i++) { + max_left[i] = Math.max(max_left[i-1], height[i-1]); + } + for (int i = height.length - 2; i >= 0; i--) { + max_right[i] = Math.max(max_right[i+1], height[i+1]); + } + for (int i = 0; i < height.length; i++) { + int min = Math.min(max_left[i], max_right[i]); + if (height[i] < min) { + sum += min - height[i]; + } + } + return sum; + } +} diff --git a/Week 01/id_368/LeetCode_66_368.java b/Week 01/id_368/LeetCode_66_368.java new file mode 100644 index 000000000..8d4080653 --- /dev/null +++ b/Week 01/id_368/LeetCode_66_368.java @@ -0,0 +1,28 @@ +import java.util.Arrays; + +public class LeetCode_66_368 { + + public static void main(String[] args) { + + /* 给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。 + 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 + 你可以假设除了整数 0 之外,这个整数不会以零开头。 + 链接:https://leetcode-cn.com/problems/plus-one */ + + int[] digits = {9, 9, 9, 9, 9}; + Arrays.stream(plusOne(digits)).forEach(i -> System.out.print(i)); + } + + public static int[] plusOne(int[] digits) { + for (int i = digits.length - 1; i >= 0; i--) { + digits[i]++; + digits[i] %= 10; + if (digits[i] != 0) { + return digits; + } + } + digits = new int[digits.length + 1]; + digits[0] = 1; + return digits; + } +} diff --git a/Week 01/id_368/LeetCode_88_368.java b/Week 01/id_368/LeetCode_88_368.java new file mode 100644 index 000000000..793d78b43 --- /dev/null +++ b/Week 01/id_368/LeetCode_88_368.java @@ -0,0 +1,40 @@ +import java.util.Arrays; + +/** + * @author: liuyanhui@daojia-inc.com + * @date: 2019/10/22 + */ +public class LeetCode_88_368 { + + /*给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。 + 初始化 nums1 和 nums2 的元素数量分别为 m 和 n。 + 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。 + 链接:https://leetcode-cn.com/problems/merge-sorted-array*/ + + public static void main(String[] args) { + int[] nums1 = {1,2,3,0,0,0}; + int m = 3; + int[] nums2 = {2,5,6}; + int n = 3; + merge(nums1, m, nums2, n); + Arrays.stream(nums1).forEach(i -> System.out.print(i + " ")); + } + + public static void merge(int[] nums1, int m, int[] nums2, int n) { + // 采用双指针的形式,因为数组本身有序,所以从后往前遍历操作,直接将合适的数据放到该位置即可 + int i = m - 1; + int j = n - 1; + int k = m + n - 1; + while (i >= 0 && j >= 0) { + if (nums1[i] <= nums2[j]) { + nums1[k--] = nums2[j--]; + } else { + nums1[k--] = nums1[i--]; + } + } + // nums2数组未遍历完需要单独处理 + while (j >= 0) { + nums1[k--] = nums2[j--]; + } + } +} diff --git a/Week 01/id_368/NOTE.md b/Week 01/id_368/NOTE.md index a6321d6e2..45187edb6 100644 --- a/Week 01/id_368/NOTE.md +++ b/Week 01/id_368/NOTE.md @@ -1,4 +1,29 @@ # NOTE +Stack +先进后出 +添加、删除皆为0(1) +Queue +先入先出 +添加、删除皆为O(1) +Priority Queue +插入操作:O(1) +取出操作:O(logN) 按照元素的优先级取出 +底层具体实现的数据结构较为多样和复杂:heap、bst、treap + +思想:升维&空间换时间 +Array:内存中一段连续的地址 +时间复杂度: +插入、删除:O(n) 会有大量的System.arraycopy()操作 +随机查找:O(1) 直接通过内存管理器找到地址 +LinkedList +时间复杂度: +插入、删除:O(1) +随机访问:O(n) +Skip List +定义:在原始链表的基础上,增加多级索引 + +时间复杂度:O(logn) +空间复杂度:O(n) diff --git a/Week 01/id_368/practice/LeecCode_11_368.java b/Week 01/id_368/practice/LeecCode_11_368.java new file mode 100644 index 000000000..3c8ca0eea --- /dev/null +++ b/Week 01/id_368/practice/LeecCode_11_368.java @@ -0,0 +1,28 @@ +package practice; + +public class LeecCode_11_368 { + /* 给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。 + 在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。 + 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 + 链接:https://leetcode-cn.com/problems/container-with-most-water */ + + public static void main(String[] args) { + int[] height = {1,8,6,2,5,4,8,3,7}; + System.out.println(maxArea(height)); + } + + public static int maxArea (int[] height) { + // 左右指针向中间收敛 + // O(n) 时间复杂度 + int max = 0; + for (int i = 0, j = height.length - 1; i < j; ) { + max = Math.max(max, (j - i) * Math.min(height[i], height[j])); + if (height[i] < height[j]) { + i++; + } else { + j--; + } + } + return max; + } +} diff --git a/Week 01/id_368/practice/LeetCode_15_368.java b/Week 01/id_368/practice/LeetCode_15_368.java new file mode 100644 index 000000000..dc6ba0291 --- /dev/null +++ b/Week 01/id_368/practice/LeetCode_15_368.java @@ -0,0 +1,53 @@ +package practice; + +import java.util.*; + +public class LeetCode_15_368 { + + /* 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 + 找出所有满足条件且不重复的三元组。 + 注意:答案中不可以包含重复的三元组。 + 链接:https://leetcode-cn.com/problems/3sum */ + + public static void main(String[] args) { + int[] nums = {-1, 0, 1, 2, -1, -4}; + List> res = threeSum(nums); + res.stream().forEach(list -> { + list.stream().forEach(integer -> { + System.out.print(integer + " "); + }); + System.out.println(); + }); + } + + public static List> threeSum(int[] nums) { + List> res = new ArrayList<>(); + if (nums.length < 3) { + return res; + } + Arrays.sort(nums); + for (int i = 0; i < nums.length - 2; i++) { + if (nums[i] > 0) { + break; + } + if (i > 0 && nums[i] == nums[i-1]) { + continue; + } + int j = i + 1; + int k = nums.length - 1; + while (j < k) { + int sum = nums[i] + nums[j] + nums[k]; + if (((j > i + 1) && nums[j] == nums[j-1]) || sum < 0) { + j++; + } else if (((k < nums.length - 1) && nums[k] == nums[k+1]) || sum > 0) { + k--; + } else { + res.add(Arrays.asList(nums[i], nums[j], nums[k])); + j++; + k--; + } + } + } + return res; + } +} diff --git a/Week 01/id_368/practice/LeetCode_20_368.java b/Week 01/id_368/practice/LeetCode_20_368.java new file mode 100644 index 000000000..f8b3d67b2 --- /dev/null +++ b/Week 01/id_368/practice/LeetCode_20_368.java @@ -0,0 +1,66 @@ +package practice; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +/** + * @author: liuyanhui@daojia-inc.com + * @date: 2019/10/22 + */ +public class LeetCode_20_368 { + + /*给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。 + 有效字符串需满足: + 左括号必须用相同类型的右括号闭合。 + 左括号必须以正确的顺序闭合。 + 注意空字符串可被认为是有效字符串。 + 链接:https://leetcode-cn.com/problems/valid-parentheses*/ + + public static void main(String[] args) { + String s = "({[]}){}()"; + System.out.println(isValid02(s)); + } + + private static final Map map = new HashMap<>(); + static { + map.put('(', ')'); + map.put('[', ']'); + map.put('{', '}'); + } + + public static boolean isValid(String s) { + if (s == "") { + return true; + } + Stack stack = new Stack<>(); + for (char c : s.toCharArray()) { + if (map.containsKey(c)) { + stack.push(c); + } else { + if (stack.isEmpty() || !map.get(stack.peek()).equals(c)) { + return false; + } else { + stack.pop(); + } + } + } + return stack.isEmpty(); + } + + public static boolean isValid02(String s) { + while (s.contains("()") || s.contains("[]") || s.contains("{}")) { + if (s.contains("()")) { + s = s.replace("()", ""); + } + if (s.contains("[]")) { + s = s.replace("[]", ""); + } + if (s.contains("{}")) { + s = s.replace("{}", ""); + } + } + return s.isEmpty(); + } + +} diff --git a/Week 01/id_368/practice/LeetCode_239_368.java b/Week 01/id_368/practice/LeetCode_239_368.java new file mode 100644 index 000000000..3aba7434f --- /dev/null +++ b/Week 01/id_368/practice/LeetCode_239_368.java @@ -0,0 +1,62 @@ +package practice; + +import java.util.LinkedList; + +/** + * @author: liuyanhui@daojia-inc.com + * @date: 2019/10/23 + */ +public class LeetCode_239_368 { + + /* 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。 + 你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 + 返回滑动窗口中的最大值。 + 链接:https://leetcode-cn.com/problems/sliding-window-maximum */ + + public static void main(String[] args) { + + } + + public static int[] maxSlidingWindow(int[] nums, int k) { + // 暴力法 + if (nums == null || nums.length == 0) { + return nums; + } + int[] res = new int[nums.length - k + 1]; + for (int i = 0; i <= nums.length - k; i++) { + int max = Integer.MIN_VALUE; + for (int j = i; j < i + k; j++) { + if (nums[j] > max) { + max = nums[j]; + } + } + res[i] = max; + } + return res; + } + + public static int[] maxSlidingWindow01(int[] nums, int k) { + if (nums == null || nums.length == 0) { + return new int[0]; + } + + int[] res = new int[nums.length - k + 1]; + LinkedList deque = new LinkedList<>(); + for (int i = 0; i < nums.length; i++) { + // 排序 + while (!deque.isEmpty() && nums[deque.getLast()] < nums[i]) { + deque.removeLast(); + } + deque.addLast(i); + // 校验队首是否在窗口内 + if (deque.getFirst() < i - k + 1) { + deque.removeFirst(); + } + // 保存最大值 + if (i >= k - 1) { + res[i - k + 1] = nums[deque.getFirst()]; + } + } + return res; + } +} diff --git a/Week 01/id_368/practice/LeetCode_70_368.java b/Week 01/id_368/practice/LeetCode_70_368.java new file mode 100644 index 000000000..563da00ba --- /dev/null +++ b/Week 01/id_368/practice/LeetCode_70_368.java @@ -0,0 +1,27 @@ +package practice; + +public class LeetCode_70_368 { + + /* 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 + 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? + https://leetcode-cn.com/problems/climbing-stairs/ */ + + public static void main(String[] args) { + int n = 4 ; + System.out.println(climbStairs(n)); + } + + public static int climbStairs (int n) { + // 找最近重复 + if (n <= 2) { + return n; + } + int f3 = 0, f1 = 1, f2 = 2; + for (int i = 3; i <= n; i++) { + f3 = f1 + f2; + f1 = f2; + f2 = f3; + } + return f3; + } +} diff --git a/Week 01/id_368/practice/LeetCode_84_368.java b/Week 01/id_368/practice/LeetCode_84_368.java new file mode 100644 index 000000000..dea5d2448 --- /dev/null +++ b/Week 01/id_368/practice/LeetCode_84_368.java @@ -0,0 +1,126 @@ +package practice; + +import jdk.nashorn.internal.ir.WhileNode; + +import java.util.LinkedList; +import java.util.Stack; + +/** + * @author: liuyanhui@daojia-inc.com + * @date: 2019/10/22 + */ +public class LeetCode_84_368 { + /*给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 + 求在该柱状图中,能够勾勒出来的矩形的最大面积。 + https://leetcode-cn.com/problems/largest-rectangle-in-histogram/*/ + + public static void main(String[] args) { + int[] heights = {2, 1, 5, 6, 2, 3}; + System.out.println(largestRectangleArea01Better(heights)); + } + + public static int largestRectangleArea(int[] heights) { + // 暴力法: 分别遍历每个元素,求出该元素对应的最低高度,之后求出每个元素的最大面积。 + // 时间复杂度: O(n²) + int max = 0; + for (int i = 0; i < heights.length; i++) { + int minHeight = Integer.MAX_VALUE; + for (int j = i; j < heights.length; j++) { + minHeight = Math.min(minHeight, heights[j]); + max = Math.max(max, (j - i + 1) * minHeight); + } + } + return max; + } + + public static int largestRectangleArea01Better(int[] heights) { + // 左右可达指针法:遍历每个元素,求出该元素可达的最左距离(高度小于当前元素)和最右距离(高度小于当前元素), + // 即为以该元素为高度的最大面积。 height[i] * (right - left - 1) + // 记录每个元素的最左距离 + // 时间复杂度:O(n) + if (heights == null || heights.length == 0) { + return 0; + } + int[] left = new int[heights.length]; + left[0] = -1; + // 优化:在计算当前元素的最左距离时,可以借助之前已经计算好的元素的最左距离进行跳跃 + for (int i = 1; i < heights.length; i++) { + int j = i - 1; + while (j >= 0 && heights[j] >= heights[i]) { + j = left[j]; + } + left[i] = j; + } + // 记录每个元素的最右距离 + int[] right = new int[heights.length]; + right[heights.length-1] = heights.length; + for (int i = heights.length - 2; i >= 0 ; i++) { + int j = i + 1; + while (j < heights.length && heights[j] >= heights[i]) { + j = right[j]; + } + right[i] = j; + } + // 计算最大面积 + int max = 0; + for (int i = 0; i < heights.length; i++) { + max = Math.max(max, heights[i] * (right[i] - left[i] - 1)); + } + return max; + } + + public static int largestRectangleArea01(int[] heights) { + // 左右可达指针法:遍历每个元素,求出该元素可达的最左距离(高度小于当前元素)和最右距离(高度小于当前元素), + // 即为以该元素为高度的最大面积。 height[i] * (right - left - 1) + // 记录每个元素的最左距离 + // 时间复杂度:O(n²) + if (heights == null || heights.length == 0) { + return 0; + } + int[] left = new int[heights.length]; + left[0] = -1; + for (int i = 1; i < heights.length; i++) { + int j = i - 1; + while (j >= 0 && heights[j] >= heights[i]) { + j--; + } + left[i] = j; + } + // 记录每个元素的最右距离 + int[] right = new int[heights.length]; + right[heights.length-1] = heights.length; + for (int i = 0; i < heights.length - 1; i++) { + int j = i + 1; + while (j < heights.length && heights[j] >= heights[i]) { + j++; + } + right[i] = j; + } + // 计算最大面积 + int max = 0; + for (int i = 0; i < heights.length; i++) { + max = Math.max(max, heights[i] * (right[i] - left[i] - 1)); + } + return max; + } + + public static int largestRectangleArea02(int[] heights) { + if (heights == null || heights.length == 0) { + return 0; + } + LinkedList stack = new LinkedList<>(); + int max = 0; + for (int i = 0; i <= heights.length; i++) { + int height = i == heights.length ? -1 : heights[i]; + if (stack.isEmpty() || height >= heights[stack.peek()]) { + stack.push(i); + } else { + int top = stack.pop(); + int left = stack.isEmpty() ? -1 : stack.peek(); + max = Math.max(max, heights[top] * (i - left - 1)); + i--; + } + } + return max; + } +} diff --git a/Week 01/id_368/practice/ListNode.java b/Week 01/id_368/practice/ListNode.java new file mode 100644 index 000000000..a607f2637 --- /dev/null +++ b/Week 01/id_368/practice/ListNode.java @@ -0,0 +1,11 @@ +package practice; + +/** + * @author: liuyanhui@daojia-inc.com + * @date: 2019/10/22 + */ +public class ListNode { + public int val; + public ListNode next; + public ListNode(int x) { val = x; } +} diff --git a/Week 01/id_373/373-Week 01 b/Week 01/id_373/373-Week 01 new file mode 100644 index 000000000..09071df6a --- /dev/null +++ b/Week 01/id_373/373-Week 01 @@ -0,0 +1,148 @@ +//1 +class Solution { +public: + int removeDuplicates(vector& nums) { + if(0 == nums.size()) + return 0; + + int i = 0; + for(int j = 0; j < nums.size(); ++j) + { + if(nums[j] != nums[i]) + { + i++; + nums[i] = nums[j]; + } + } + + return i + 1; + + } +}; + +//2 +class Solution { +public: + void rotate(vector& nums, int k) { + k = k % nums.size(); + int count = 0; + for(int i = 0; count < nums.size(); i++) + { + int current = i; + int prev = nums[i]; + do + { + int next = (current + k) % nums.size(); + int temp = nums[next]; + nums[next] = prev; + prev = temp; + current = next; + count++; + } while(i != current); + } + + } +}; + +//3 +class Solution { +public: + ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { + ListNode *prehead = new ListNode(-1); + ListNode *prev = prehead; + + while (l1 != NULL && l2 != NULL) { + if (l1->val <= l2->val) { + prev->next = l1; + l1 = l1->next; + } else { + prev->next = l2; + l2 = l2->next; + } + prev = prev->next; + } + + prev->next = l1 == NULL ? l2 : l1; + + return prehead->next; + } + +}; + +//4 +class Solution { +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + for (int i = m - 1, j = n - 1, k = m + n - 1; k >= 0; --k) + { + if (j < 0 || i >= 0 && nums1[i] > nums2[j]) + nums1[k] = nums1[i--]; + else + nums1[k] = nums2[j--]; + } + + } +}; + +//5 +class Solution { +public: + vector twoSum(vector& nums, int target) { + int i,j; + for(i=0;i& nums) { + int lastNonZero = 0; + + for (int i = 0; i < nums.size(); i++) + { + if (nums[i] != 0) + { + nums[lastNonZero++] = nums[i]; + } + } + + for (int i = lastNonZero; i < nums.size(); i++) + { + nums[i] = 0; + } +} + +//7 +class Solution { +public: + vector plusOne(vector& digits) { + for (int i = (int)digits.size() - 1; i >= 0; i--) + { + if (digits[i] == 9) + { + digits[i] = 0; + } + else + { + digits[i]++; + break; + } + } + if (digits[0] == 0) + { + digits.push_back(0); + digits[0] = 1; + } + return digits; + } +}; + diff --git a/Week 01/id_373/week01_373_homework/LeetCode_189_373.cpp b/Week 01/id_373/week01_373_homework/LeetCode_189_373.cpp new file mode 100644 index 000000000..e8b125b54 --- /dev/null +++ b/Week 01/id_373/week01_373_homework/LeetCode_189_373.cpp @@ -0,0 +1,22 @@ +class Solution { +public: + void rotate(vector& nums, int k) { + k = k % nums.size(); + int count = 0; + for(int i = 0; count < nums.size(); i++) + { + int current = i; + int prev = nums[i]; + do + { + int next = (current + k) % nums.size(); + int temp = nums[next]; + nums[next] = prev; + prev = temp; + current = next; + count++; + } while(i != current); + } + + } +}; \ No newline at end of file diff --git a/Week 01/id_373/week01_373_homework/LeetCode_1_373.cpp b/Week 01/id_373/week01_373_homework/LeetCode_1_373.cpp new file mode 100644 index 000000000..39ba071e4 --- /dev/null +++ b/Week 01/id_373/week01_373_homework/LeetCode_1_373.cpp @@ -0,0 +1,18 @@ +//5 +class Solution { +public: + vector twoSum(vector& nums, int target) { + int i,j; + for(i=0;ival <= l2->val) { + prev->next = l1; + l1 = l1->next; + } else { + prev->next = l2; + l2 = l2->next; + } + prev = prev->next; + } + + prev->next = l1 == NULL ? l2 : l1; + + return prehead->next; + } + +}; \ No newline at end of file diff --git a/Week 01/id_373/week01_373_homework/LeetCode_26_373.cpp b/Week 01/id_373/week01_373_homework/LeetCode_26_373.cpp new file mode 100644 index 000000000..55e58123a --- /dev/null +++ b/Week 01/id_373/week01_373_homework/LeetCode_26_373.cpp @@ -0,0 +1,21 @@ +//1 +class Solution { +public: + int removeDuplicates(vector& nums) { + if(0 == nums.size()) + return 0; + + int i = 0; + for(int j = 0; j < nums.size(); ++j) + { + if(nums[j] != nums[i]) + { + i++; + nums[i] = nums[j]; + } + } + + return i + 1; + + } +}; \ No newline at end of file diff --git a/Week 01/id_373/week01_373_homework/LeetCode_283_373.cpp b/Week 01/id_373/week01_373_homework/LeetCode_283_373.cpp new file mode 100644 index 000000000..7f71f7db8 --- /dev/null +++ b/Week 01/id_373/week01_373_homework/LeetCode_283_373.cpp @@ -0,0 +1,17 @@ +//6 +void moveZeroes(vector& nums) { + int lastNonZero = 0; + + for (int i = 0; i < nums.size(); i++) + { + if (nums[i] != 0) + { + nums[lastNonZero++] = nums[i]; + } + } + + for (int i = lastNonZero; i < nums.size(); i++) + { + nums[i] = 0; + } +} \ No newline at end of file diff --git a/Week 01/id_373/week01_373_homework/LeetCode_66_373.cpp b/Week 01/id_373/week01_373_homework/LeetCode_66_373.cpp new file mode 100644 index 000000000..0cfb635c7 --- /dev/null +++ b/Week 01/id_373/week01_373_homework/LeetCode_66_373.cpp @@ -0,0 +1,24 @@ +//7 +class Solution { +public: + vector plusOne(vector& digits) { + for (int i = (int)digits.size() - 1; i >= 0; i--) + { + if (digits[i] == 9) + { + digits[i] = 0; + } + else + { + digits[i]++; + break; + } + } + if (digits[0] == 0) + { + digits.push_back(0); + digits[0] = 1; + } + return digits; + } +}; \ No newline at end of file diff --git a/Week 01/id_373/week01_373_homework/LeetCode_88_373.cpp b/Week 01/id_373/week01_373_homework/LeetCode_88_373.cpp new file mode 100644 index 000000000..ede28f164 --- /dev/null +++ b/Week 01/id_373/week01_373_homework/LeetCode_88_373.cpp @@ -0,0 +1,14 @@ +//4 +class Solution { +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + for (int i = m - 1, j = n - 1, k = m + n - 1; k >= 0; --k) + { + if (j < 0 || i >= 0 && nums1[i] > nums2[j]) + nums1[k] = nums1[i--]; + else + nums1[k] = nums2[j--]; + } + + } +}; \ No newline at end of file diff --git a/Week 01/id_383/LeetCode_189_383.java b/Week 01/id_383/LeetCode_189_383.java new file mode 100644 index 000000000..8938405c6 --- /dev/null +++ b/Week 01/id_383/LeetCode_189_383.java @@ -0,0 +1,59 @@ +public class LeetCode_189_383 { + + /** + * 暴力解法: 每次向右移动1个位置,循环k次 + * 时间复杂度:O(n) + * 空间复杂度:O(1) + */ + public void rotate1(int[] nums, int k) { + int len = nums.length; + k %= len; + for (int j = 0; j < k; j++) { + int temp = nums[len-1]; + for (int i = len - 2; i > -1; i--) { + nums[i+1] = nums[i]; + } + nums[0] = temp; + } + } + + /** + * new一个相同长度的数组,将数据挪动到正确的位置后,再赋值给原来的数组 + * 时间复杂度:O(n) + * 空间复杂度:O(n) + */ + public void rotate2(int[] nums, int k) { + int len = nums.length; + int[] newArray = new int[len]; + for (int i = 0; i < len; i++) { + newArray[(i+k)%len] = nums[i]; + } + for (int i = 0; i < len; i++) { + nums[i] = newArray[i]; + } + } + + /** + * 循环替换 + * 时间复杂度:O(n) + * 空间复杂度:O(1) + */ + public static void rotate3(int[] nums, int k) { + int len = nums.length; + k %= len; + int count = 0; + for (int i = 0; count < len; i++) { + int currentIndex = i; + int currentValue = nums[currentIndex]; + do { + int nextIndex = (currentIndex + k) % len; + int temp = nums[nextIndex]; + nums[nextIndex] = currentValue; + currentIndex = nextIndex; + currentValue = temp; + count++; + } while (currentIndex != i); + } + } + +} \ No newline at end of file diff --git a/Week 01/id_383/LeetCode_26_383.java b/Week 01/id_383/LeetCode_26_383.java new file mode 100644 index 000000000..f5f98ed53 --- /dev/null +++ b/Week 01/id_383/LeetCode_26_383.java @@ -0,0 +1,18 @@ +public class LeetCode_26_383 { + + /** + * 时间复杂度:O(n) + * 空间复杂度:O(1) + */ + public int removeDuplicates(int[] nums) { + int i = 0; + for (int j = 1, k = nums.length; j < k; j++) { + if (nums[i] != nums[j]) { + i++; + nums[i] = nums[j]; + } + } + return i + 1; + } + +} \ No newline at end of file diff --git a/Week 01/id_388/LeeCode_42_388.java b/Week 01/id_388/LeeCode_42_388.java new file mode 100644 index 000000000..133a75fa7 --- /dev/null +++ b/Week 01/id_388/LeeCode_42_388.java @@ -0,0 +1,34 @@ +/** +* 接雨水问题 +/ +class Solution { + + private Stack stack = new Stack(); + + public int trap(int[] height) { + + if (height.length == 0) { + return 0; + } + int area = 0; + int current = 0; + while (current < height.length) { + + while (!stack.isEmpty() && height[current] > height[stack.peek()]) { + int top = stack.pop(); + if (stack.isEmpty()){ + break; + } + + int distance = current - stack.peek() - 1; + int h = Math.min(height[current],height[stack.peek()]) - height[top]; + area += distance * h; + } + + stack.push(current++); + } + + return area; + } + +} \ No newline at end of file diff --git a/Week 01/id_388/LeetCode_641_388.java b/Week 01/id_388/LeetCode_641_388.java new file mode 100644 index 000000000..e2983b257 --- /dev/null +++ b/Week 01/id_388/LeetCode_641_388.java @@ -0,0 +1,107 @@ +/** + * 双端队列 + */ +class MyCircularDeque { + + private int[] queue; + private int maxSize; + private int tail; + private int head; + private int size; + + /** + * Initialize your data structure here. Set the size of the deque to be k. + */ + public MyCircularDeque(int k) { + maxSize = k; + queue = new int[k]; + size = 0; + tail = 0; + head = k - 1; + + } + + /** + * Adds an item at the front of Deque. Return true if the operation is successful. + */ + public boolean insertFront(int value) { + if (size == maxSize) { + return false; + } + queue[head] = value; + head = (head - 1 + maxSize) % maxSize; + size++; + return true; + } + + /** + * Adds an item at the rear of Deque. Return true if the operation is successful. + */ + public boolean insertLast(int value) { + if (size == maxSize) { + return false; + } + queue[tail] = value; + tail = (tail + 1) % maxSize; + size++; + return true; + } + + /** + * Deletes an item from the front of Deque. Return true if the operation is successful. + */ + public boolean deleteFront() { + if (size == 0) { + return false; + } + head = (head + 1) % maxSize; + size--; + return true; + } + + /** + * Deletes an item from the rear of Deque. Return true if the operation is successful. + */ + public boolean deleteLast() { + if (size == 0) { + return false; + } + tail = (tail - 1 + maxSize) % maxSize; + size--; + return true; + } + + /** + * Get the front item from the deque. + */ + public int getFront() { + if (size == 0) { + return -1; + } + return queue[(head + 1) % maxSize]; + } + + /** + * Get the last item from the deque. + */ + public int getRear() { + if (size == 0) { + return -1; + } + return queue[(tail - 1 + maxSize) % maxSize]; + } + + /** + * Checks whether the circular deque is empty or not. + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * Checks whether the circular deque is full or not. + */ + public boolean isFull() { + return size == maxSize; + } +} \ No newline at end of file diff --git a/Week 01/id_388/NOTE.md b/Week 01/id_388/NOTE.md index a6321d6e2..5eb5d1069 100644 --- a/Week 01/id_388/NOTE.md +++ b/Week 01/id_388/NOTE.md @@ -1,4 +1,21 @@ # NOTE - +### 第一周总结 + 这周主要讲了两大块,第一块是数组链表和跳表,第二块是栈和队列。 + +#### 1、数组、链表和跳表 + 数组的读取操作时间复杂度是1,但是插入和删除操作时间复杂度较高。然后分析了java的arrayList。 + 为了解决删除和插入的问题,引入了linklist。但是linklist查询效率不高,因此引入了调表,时间复杂度是logn + + 实战中 + +#### 2、栈和队列 + 栈的特点是先入后出,队列是先进先出。 + + java源码查找方式:google输入java queue source code download.这样可以查找到源码,或者下载到idea查看。 + + java的队列和栈有多种实现方式。这次学习优先级队列,默认最小的在队首,可以自定义比较函数。 + + 课后作业接雨水问题有种方案用到了栈这个数据结构。 + 用栈记录左边界,直到新入的高度大于栈顶元素,则通过距离和高度差计算面积,直到栈为空。 diff --git "a/Week 01/id_388/Queue\345\222\214priorityQueue\346\272\220\347\240\201\345\210\206\346\236\220" "b/Week 01/id_388/Queue\345\222\214priorityQueue\346\272\220\347\240\201\345\210\206\346\236\220" new file mode 100644 index 000000000..fb7897ee2 --- /dev/null +++ "b/Week 01/id_388/Queue\345\222\214priorityQueue\346\272\220\347\240\201\345\210\206\346\236\220" @@ -0,0 +1,26 @@ +### queue源码解析 +[源码地址](http://fuseyism.com/classpath/doc/java/util/Queue-source.html) + +### queue是一个队列的接口定义,按照先入先出的方式操作元素。 + +#### 添加操作 +add 和 offser。两者都会把元素插入队列,但是如果空间不足,add会抛出异常,offer只返回false + +#### 删除操作 +remove 和 poll。都是移除并返回队首的元素。remove失败会抛出异常,poll会返回null + +#### 检查操作 +element 和 peek。两者都是返回队首的元素,但是队列为空 peek会返回nil,element会抛出空队列异常。 + +### PriorityQueue 源码解析 +PriorityQueue实现了queue接口的方法。有Object存储元素,size表示元素个数,modCount表示修改次数,comparator比较先后元素的方法。 + +#### 添加操作 +add 和 offer。add直接调用的offer,因此只看offer就可以。如果添加的元素为空会抛异常。如果空间不足会调用grow方法扩容。小于64扩容两倍,否则扩容50%。 +扩容采用的是复制数组的方法。插入的时候如果新元素大于队首的元素则直接插入,否则大于他的元素迁移,新元素往后放,直到大于剩下的元素。修改次数和元素个数同时加一 + +#### 删除 +remove 和 poll。remove调用的poll接口。删除操作会将队首元素删除其他元素往按照大小填充队首。修改次数和元素个数减一。 + +#### 检查 +peek和element。返回数组0位置的元素。空的时候element会抛异常 \ No newline at end of file diff --git "a/Week 01/id_388/\347\224\250 add first \346\210\226 add last \350\277\231\345\245\227\346\226\260\347\232\204 API \346\224\271\345\206\231 Deque \347\232\204\344\273\243\347\240\201" "b/Week 01/id_388/\347\224\250 add first \346\210\226 add last \350\277\231\345\245\227\346\226\260\347\232\204 API \346\224\271\345\206\231 Deque \347\232\204\344\273\243\347\240\201" new file mode 100644 index 000000000..6874425c2 --- /dev/null +++ "b/Week 01/id_388/\347\224\250 add first \346\210\226 add last \350\277\231\345\245\227\346\226\260\347\232\204 API \346\224\271\345\206\231 Deque \347\232\204\344\273\243\347\240\201" @@ -0,0 +1,21 @@ +public class Main { + + public static void main(String[] args) { + + //改写deque + Deque deque = new LinkedList<>(); + deque.addFirst("a"); + deque.addFirst("b"); + deque.addFirst("c"); + System.out.println(deque); + + String str = deque.peek(); + System.out.println(str); + System.out.println(deque); + + while (deque.size() > 0) { + System.out.println(deque.removeFirst()); + } + System.out.println(deque); + } +} diff --git a/Week 01/id_393/LeetCode_1_393.java b/Week 01/id_393/LeetCode_1_393.java new file mode 100644 index 000000000..0aa5c012d --- /dev/null +++ b/Week 01/id_393/LeetCode_1_393.java @@ -0,0 +1,51 @@ +//给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 +// +// 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 +// +// 示例: +// +// 给定 nums = [2, 7, 11, 15], target = 9 +// +//因为 nums[0] + nums[1] = 2 + 7 = 9 +//所以返回 [0, 1] +// +// Related Topics 数组 哈希表 + +package leetcode.editor.cn; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertArrayEquals; + +/** + * @author boyiren + */ +public class TwoSum { + public static void main(String[] args) { + Solution solution = new TwoSum().new Solution(); + int[] nums = {2, 7, 11, 15}; + int target = 9; + int[] answer = {0, 1}; + int[] result = solution.twoSum(nums, target); + assertArrayEquals(result, answer); + } + + + //leetcode submit region begin(Prohibit modification and deletion) + class Solution { + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(16); + for (int i = 0; i < nums.length; i++) { + int complement = target - nums[i]; + if (map.containsKey(complement)) { + return new int[] {map.get(complement), i}; + } + map.put(nums[i], i); + } + throw new IllegalArgumentException("No two sum solution"); + } + } +//leetcode submit region end(Prohibit modification and deletion) + +} \ No newline at end of file diff --git a/Week 01/id_393/LeetCode_21_393.java b/Week 01/id_393/LeetCode_21_393.java new file mode 100644 index 000000000..832154e3d --- /dev/null +++ b/Week 01/id_393/LeetCode_21_393.java @@ -0,0 +1,66 @@ +//将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 +// +// 示例: +// +// 输入:1->2->4, 1->3->4 +//输出:1->1->2->3->4->4 +// +// Related Topics 链表 + +package leetcode.editor.cn; + +import static org.junit.Assert.assertEquals; + +public class MergeTwoSortedLists { + public static void main(String[] args) { + Solution solution = new MergeTwoSortedLists().new Solution(); + ListNode l1 = new ListNode(1); + l1.next = new ListNode(2); + l1.next.next = new ListNode(4); + ListNode l2 = new ListNode(1); + l2.next = new ListNode(3); + l2.next.next = new ListNode(4); + ListNode result = solution.mergeTwoLists(l1, l2); + assertEquals(result.val, 1); + assertEquals(result.next.val, 1); + assertEquals(result.next.next.val, 2); + assertEquals(result.next.next.next.next.next.val, 4); + } + + static class ListNode { + int val; + ListNode next; + + ListNode(int x) { + val = x; + } + } + +//leetcode submit region begin(Prohibit modification and deletion) + + /** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ + class Solution { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + if (l1 == null) { + return l2; + } else if (l2 == null) { + return l1; + } else if (l1.val < l2.val) { + l1.next = mergeTwoLists(l1.next, l2); + return l1; + } else { + l2.next = mergeTwoLists(l1, l2.next); + return l2; + } + } + } +//leetcode submit region end(Prohibit modification and deletion) + +} \ No newline at end of file diff --git a/Week 01/id_408/LeetCode_189_408.go b/Week 01/id_408/LeetCode_189_408.go new file mode 100644 index 000000000..3ff050bc9 --- /dev/null +++ b/Week 01/id_408/LeetCode_189_408.go @@ -0,0 +1,25 @@ +package main + +import "fmt" + +func rotate(nums []int, k int) { + + kk := k % len(nums) + reverse(nums, 0, len(nums)-1) + slice1 := nums[:kk] + slice2 := nums[kk:] + reverse(slice1, 0, kk-1) + reverse(slice2, 0, len(nums)-kk-1) +} + +func reverse(nums []int, start int, end int) { + for start < end { + + tmp := nums[start] + nums[start] = nums[end] + nums[end] = tmp + start++ + end-- + + } +} diff --git a/Week 01/id_408/LeetCode_26_408.go b/Week 01/id_408/LeetCode_26_408.go new file mode 100644 index 000000000..bd37a3cd3 --- /dev/null +++ b/Week 01/id_408/LeetCode_26_408.go @@ -0,0 +1,33 @@ +func removeDuplicates(nums []int) int { + var j int = 0 + var repCnt = 0 + + for i, v := range nums { + rel := nums[:i] + inStatus := isItemInSlice(rel, v) + + if inStatus == true { + repCnt++ + if repCnt == 1 { + j = i + } + } else { + if repCnt > 0 { + nums[j] = nums[i] + j++ + } + } + } + + return (len(nums) - repCnt) +} + +func isItemInSlice(nums []int, item int) bool { + for _, v := range nums { + if v == item { + return true + } + } + + return false +} diff --git a/Week 01/id_408/NOTE.md b/Week 01/id_408/NOTE.md index a6321d6e2..f1896a191 100644 --- a/Week 01/id_408/NOTE.md +++ b/Week 01/id_408/NOTE.md @@ -1,4 +1,11 @@ # NOTE +通过本周的学习我主要获得了以下几点的提升及新的认知: +1.对计算时间复杂度有了新的认知,不再恐惧。 +2.对算法的分类及各自的时间复杂度掌握更加深入。 +3.体会到了升维和用空间时间的重要性。 +4.在写算法和分析算法的过程中体会到用简单例子来分析的重要性。 +5.知道了刷题的误区是只刷一遍题及提高算法能力的新方法。 + diff --git "a/Week 01/id_413/1\345\221\250.xmind" "b/Week 01/id_413/1\345\221\250.xmind" new file mode 100644 index 000000000..e5c2b42bd Binary files /dev/null and "b/Week 01/id_413/1\345\221\250.xmind" differ diff --git a/Week 01/id_413/LeetCode_1_189.py b/Week 01/id_413/LeetCode_1_189.py new file mode 100644 index 000000000..083fdb026 --- /dev/null +++ b/Week 01/id_413/LeetCode_1_189.py @@ -0,0 +1,51 @@ +# 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。 +# +# 示例 1: +# +# 输入: [1,2,3,4,5,6,7] 和 k = 3 +# 输出: [5,6,7,1,2,3,4] +# 解释: +# 向右旋转 1 步: [7,1,2,3,4,5,6] +# 向右旋转 2 步: [6,7,1,2,3,4,5] +# 向右旋转 3 步: [5,6,7,1,2,3,4] +# +# +# 示例 2: +# +# 输入: [-1,-100,3,99] 和 k = 2 +# 输出: [3,99,-1,-100] +# 解释: +# 向右旋转 1 步: [99,-1,-100,3] +# 向右旋转 2 步: [3,99,-1,-100] +# +# 说明: +# +# +# 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。 +# 要求使用空间复杂度为 O(1) 的 原地 算法。 +# +# Related Topics 数组 + + +from typing import List + + +# leetcode submit region begin(Prohibit modification and deletion) +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + k %= len(nums) + # 头部,顾头不顾尾 + nums[:] = nums[-k:] + nums[:-k] + return nums + + +# leetcode submit region end(Prohibit modification and deletion) + + +nums = [1, 2, 3, 4, 5, 6, 7] +k = 3 +res = Solution().rotate(nums, k) +print(res) diff --git a/Week 01/id_413/LeetCode_1_26.py b/Week 01/id_413/LeetCode_1_26.py new file mode 100644 index 000000000..f0c340d90 --- /dev/null +++ b/Week 01/id_413/LeetCode_1_26.py @@ -0,0 +1,63 @@ +# 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 +# +# 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 +# +# 示例 1: +# +# 给定数组 nums = [1,1,2], +# +# 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 +# +# 你不需要考虑数组中超出新长度后面的元素。 +# +# 示例 2: +# +# 给定 nums = [0,0,1,1,1,2,2,3,3,4], +# +# 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 +# +# 你不需要考虑数组中超出新长度后面的元素。 +# +# +# 说明: +# +# 为什么返回数值是整数,但输出的答案是数组呢? +# +# 请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。 +# +# 你可以想象内部操作如下: +# +# // nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 +# int len = removeDuplicates(nums); +# +# // 在函数里修改输入数组对于调用者是可见的。 +# // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。 +# for (int i = 0; i < len; i++) { +#     print(nums[i]); +# } +# +# Related Topics 数组 双指针 + + +from typing import List + + +# leetcode submit region begin(Prohibit modification and deletion) +class Solution: + def removeDuplicates(self, nums: List) -> int: + if not len(nums): + return 0 + + j = 1 + for i in range(1, len(nums)): + if nums[i - 1] != nums[i]: + nums[j] = nums[i] + j += 1 + + return j + + +nums = [0, 0, 1, 1, 1, 2, 2, 3, 3, 4] +res = Solution().removeDuplicates(nums) +# leetcode submit region end(Prohibit modification and deletion) +print(res) diff --git "a/Week 01/id_413/arithmetic\345\237\272\347\241\200.xmind" "b/Week 01/id_413/arithmetic\345\237\272\347\241\200.xmind" new file mode 100644 index 000000000..b1e39b31e Binary files /dev/null and "b/Week 01/id_413/arithmetic\345\237\272\347\241\200.xmind" differ diff --git a/Week 01/id_413/big-o-cheatsheet.pdf b/Week 01/id_413/big-o-cheatsheet.pdf new file mode 100644 index 000000000..1bacafef4 Binary files /dev/null and b/Week 01/id_413/big-o-cheatsheet.pdf differ diff --git "a/Week 01/id_413/\347\256\227\346\263\225\345\255\246\344\271\240\346\226\271\346\263\225.xmind" "b/Week 01/id_413/\347\256\227\346\263\225\345\255\246\344\271\240\346\226\271\346\263\225.xmind" new file mode 100644 index 000000000..1cf6ca5af Binary files /dev/null and "b/Week 01/id_413/\347\256\227\346\263\225\345\255\246\344\271\240\346\226\271\346\263\225.xmind" differ diff --git a/Week 01/id_418/LeetCode_42_418.java b/Week 01/id_418/LeetCode_42_418.java new file mode 100644 index 000000000..ca0b4daf9 --- /dev/null +++ b/Week 01/id_418/LeetCode_42_418.java @@ -0,0 +1,45 @@ +package com.ljg.leetcode.a_009.trapping_rain_water; + +/** + * TrappingRainWater + * + * 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水 + * + * 示例: + * + * 输入: [0,1,0,2,1,0,1,3,2,1,2,1] + * + * 输出: 6 + * + * https://leetcode.com/problems/trapping-rain-water/ + * + */ +public class TrappingRainWater { + + public static void main(String[] args) { + int[] height = new int[] { 0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1 }; + // int[] height = new int[] { 5, 4, 1, 2 }; + int rainWater = getRainWater_03(height); + System.out.println("rainWater=" + rainWater); + } + + private static int getRainWater_03(int[] height) { + int rainWater = 0; + + int len = height.length; + for (int i = 1; i < len - 1; i++) { + int leftMax = height[i]; + int rightMax = height[i]; + + for (int j = i - 1; j >= 0; j--) { + leftMax = Math.max(leftMax, height[j]); + } + for (int j = i + 1; j < len; j++) { + rightMax = Math.max(rightMax, height[j]); + } + + rainWater += (Math.min(leftMax, rightMax) - height[i]); + } + return rainWater; + } +} \ No newline at end of file diff --git a/Week 01/id_418/LeetCode_641_418.java b/Week 01/id_418/LeetCode_641_418.java new file mode 100644 index 000000000..33f4c45cb --- /dev/null +++ b/Week 01/id_418/LeetCode_641_418.java @@ -0,0 +1,311 @@ +package com.ljg.leetcode.a_008.deque; + +/** + * MyCircularDeque 设计实现双端队列。 你的实现需要支持以下操作: + * + * MyCircularDeque(k):构造函数,双端队列的大小为k。 + * + * insertFront():将一个元素添加到双端队列头部。 如果操作成功返回true。 + * + * insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。 + * + * deleteFront():从双端队列头部删除一个元素。如果操作成功返回 true。 + * + * deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。 + * + * getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。 + * + * getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回-1。 + * + * isEmpty():检查双端队列是否为空。 + * + * isFull():检查双端队列是否满了。 + * + * 示例: + * + * MyCircularDeque circularDeque = new MycircularDeque(3); // 设置容量大小为3 + * + * circularDeque.insertLast(1); // 返回 true + * + * circularDeque.insertLast(2); // 返回true + * + * circularDeque.insertFront(3); // 返回 true + * + * circularDeque.insertFront(4); //已经满了,返回 false + * + * circularDeque.getRear(); // 返回 2 + * + * circularDeque.isFull(); // 返回 true + * + * circularDeque.deleteLast(); // 返回 true + * + * circularDeque.insertFront(4); //返回 true + * + * circularDeque.getFront(); // 返回 4 + * + * 提示: + * + * 所有值的范围为 [1, 1000] + * + * 操作次数的范围为 [1, 1000] + * + * 请不要使用内置的双端队列库 + * + * 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/design-circular-deque + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class MyCircularDeque { + + private int modCount; + + private int size; + + private int elementCount; + + private Node first; + + private Node last; + + /** Initialize your data structure here. Set the size of the deque to be k. */ + public MyCircularDeque(int k) { + modCount = 0; + size = k; + elementCount = 0; + + first = null; + last = null; + } + + /** + * Adds an item at the front of Deque. Return true if the operation is + * successful. + */ + public boolean insertFront(int value) { + + try { + validModCount(); + + validRange(value); + } catch (Exception e) { + return false; + } + + + if (elementCount >= size) { + return false; + } + + return linkFirst(value); + } + + private boolean linkFirst(int value) { + Node temp = first; + Node node = new Node(null, temp, value); + first = node; + if (temp == null) { + last = node; + } else { + temp.pre = node; + } + elementCount++; + modCount++; + + return true; + } + + private boolean linkLast(int value) { + Node temp = last; + Node node = new Node(temp, null, value); + last = node; + if (temp == null) { + first = node; + } else { + temp.next = node; + } + elementCount++; + modCount++; + return true; + } + + private boolean unLinkFirst() { + if (first == null) { + return false; + } + + Node temp = first.next; + if (temp == null) { + last = null; + } else { + temp.pre = null; + } + first = temp; + + modCount++; + elementCount--; + + return true; + } + + private boolean unLinkLast() { + if (last == null) { + return false; + } + + Node temp = last.pre; + if (temp == null) { + first = null; + } else { + temp.next = null; + } + last = temp; + + modCount++; + elementCount--; + + return true; + } + + /** + * Adds an item at the rear of Deque. Return true if the operation is + * successful. + */ + public boolean insertLast(int value) { + + try { + validModCount(); + + validRange(value); + } catch (Exception e) { + return false; + } + + if (elementCount >= size) { + return false; + } + + return linkLast(value); + + } + + /** + * Deletes an item from the front of Deque. Return true if the operation is + * successful. + */ + public boolean deleteFront() { + try { + validModCount(); + + } catch (Exception e) { + return false; + } + + return unLinkFirst(); + } + + /** + * Deletes an item from the rear of Deque. Return true if the operation is + * successful. + */ + public boolean deleteLast() { + try { + validModCount(); + + } catch (Exception e) { + return false; + } + + return unLinkLast(); + } + + /** Get the front item from the deque. */ + public int getFront() { + if (elementCount == 0) { + return -1; + } + + return first.value; + } + + /** Get the last item from the deque. */ + public int getRear() { + if (elementCount == 0) { + return -1; + } + + return last.value; + } + + /** Checks whether the circular deque is empty or not. */ + public boolean isEmpty() { + return elementCount == 0; + } + + /** Checks whether the circular deque is full or not. */ + public boolean isFull() { + return elementCount == size; + } + + private void validRange(int value) { + // if (value < 1 || value > 1000) { + // throw new RuntimeException("value must not greater than and lesser than 1"); + // } + } + + private void validModCount() { + if (modCount < 0 || modCount > 1000) { + throw new RuntimeException("modCount must not greater than and lesser than 0"); + } + } + + private static class Node { + private Node pre; + private Node next; + private int value; + + Node(Node pre, Node next, int value) { + this.pre = pre; + this.next = next; + this.value = value; + } + } + + public static void main(String[] args) { + + // Your MyCircularDeque object will be instantiated and called as such: + // MyCircularDeque circularDeque = new MyCircularDeque(3); // 设置容量大小为3 + // System.out.println(); + // System.out.println(circularDeque.insertLast(1)); // 返回 true + // System.out.println(circularDeque.insertLast(2)); // 返回 true + // System.out.println(circularDeque.insertFront(3)); // 返回 true + // System.out.println(circularDeque.insertFront(4)); // 已经满了,返回 false + // System.out.println(circularDeque.getRear()); // 返回 2 + // System.out.println(circularDeque.isFull()); // 返回 true + // System.out.println(circularDeque.deleteLast()); // 返回 true + // System.out.println(circularDeque.insertFront(4)); // 返回 true + // System.out.println(circularDeque.getFront()); // 返回 4 + // System.out.println(circularDeque.getRear()); + // System.out.println(circularDeque.deleteLast()); + // System.out.println(circularDeque.insertLast(0)); + // System.out.println(circularDeque.getRear()); + + MyCircularDeque circularDeque = new MyCircularDeque(5); // 设置容量大小为3 + System.out.println(); + System.out.println(circularDeque.insertFront(7)); + System.out.println(circularDeque.insertLast(0)); + System.out.println(circularDeque.getFront()); + System.out.println(circularDeque.insertLast(3)); + System.out.println(circularDeque.getFront()); + System.out.println(circularDeque.insertFront(9)); + System.out.println(circularDeque.getRear()); + System.out.println(circularDeque.getFront()); + System.out.println(circularDeque.getFront()); + System.out.println(circularDeque.deleteLast()); + System.out.println(circularDeque.getRear()); + +// ["MyCircularDeque","insertFront","insertLast","getFront","insertLast","getFront","insertFront","getRear","getFront","getFront","deleteLast","getRear"] +// [[5],[7],[0],[],[3],[],[9],[],[],[],[],[]] + +// [null,true,false,7,true,7,true,3,9,9,true,7] +// [null,true,true,7,true,7,true,3,9,9,true,0] + } +} \ No newline at end of file diff --git a/Week 01/id_418/NOTE.md b/Week 01/id_418/NOTE.md index a6321d6e2..f40d1ad37 100644 --- a/Week 01/id_418/NOTE.md +++ b/Week 01/id_418/NOTE.md @@ -1,4 +1,5 @@ -# NOTE - - - +# 双端队列 +* 结点Node是pre、next双向指针 +* 队列有first和last头尾两个指标 +* 可以分别从头或尾入或出元素 +* 既实现了队列的功能,也实现了栈的功能。 diff --git a/Week 01/id_423/LeetCode_189_423.java b/Week 01/id_423/LeetCode_189_423.java new file mode 100644 index 000000000..0e72a4543 --- /dev/null +++ b/Week 01/id_423/LeetCode_189_423.java @@ -0,0 +1,52 @@ +class Solution { + public void rotate(int[] nums, int k) { + int[] tmp = new int[nums.length]; + for (int i = 0; i < nums.length; i++) + tmp[(i + k) % nums.length] = nums[i]; + for (int i = 0; i < nums.length; i++) + nums[i] = tmp[i]; + } + + public void rotate_brute_force(int[] nums, int k) { + int previous, tmp; + for (int i = 0; i < k; i++) { + previous = nums[nums.length - 1]; + for (int j = 0; j < nums.length; j++) { + tmp = nums[j]; + nums[j] = previous; + previous = tmp; + } + } + } + + public void rotate_cyclic_replacement(int[] nums, int k) { + k %= nums.length; + int count = 0; + for (int start = 0; count < nums.length; start++) { + int current = start; + int previous = nums[start]; + do { + int next = (start + k) % nums.length; + int tmp = nums[next]; + nums[next] = previous; + previous = tmp; + current = next; + count++; + } while (start != current); + } + } + + public void rotate_reverse(int[] nums, int k) { + k %= nums.length; + reverse(nums, 0, nums.length-1); + reverse(nums, 0, k-1); + reverse(nums, k, nums.length-1); + } + private void reverse(int[] nums, int start, int end) { + while (start < end) { + int tmp = nums[start]; + nums[start++] = nums[end]; + nums[end--] = tmp; + } + } +} \ No newline at end of file diff --git a/Week 01/id_423/LeetCode_1_423.java b/Week 01/id_423/LeetCode_1_423.java new file mode 100644 index 000000000..3da2c39d2 --- /dev/null +++ b/Week 01/id_423/LeetCode_1_423.java @@ -0,0 +1,39 @@ +class Solution { + + public int[] twoSum(int[] nums, int target) { + for (int i = 0; i < nums.length; i++) { + for (int j = i + 1; j < nums.length; j++) { + if (nums[j] == target - nums[i]) { + return new int[] { i, j }; + } + } + } + throw new IllegalArgumentException("No two sum solution"); + } + + public int[] twoSum_two_pass_hash_table(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + map.put(nums[i], i); + } + for (int i = 0; i < nums.length; i++) { + int complement = target - nums[i]; + if (map.containsKey(complement) && map.get(complement) != i) { + return new int[] { i, map.get(complement) }; + } + } + throw new IllegalArgumentException("No two sum solution"); + } + + public int[] twoSum_one_pass_hash_table(int[] nums, int target) { + Map map = new HashMap<>; + for (int i = 0; i < nums.length; i++) { + int complement = target = nums[i]; + if (map.containsKey(complement)) { + return new int[map.get(complement), i]; + } + map.put(nums[i], i); + } + throw new IllegalArgumentException("No two sum solution"); + } +} \ No newline at end of file diff --git a/Week 01/id_423/LeetCode_21_423.java b/Week 01/id_423/LeetCode_21_423.java new file mode 100644 index 000000000..5b3a575ff --- /dev/null +++ b/Week 01/id_423/LeetCode_21_423.java @@ -0,0 +1,35 @@ +class Solution { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + if (null == l1) return l2; + if (null == l2) return l1; + ListNode origin = new ListNode(-1); + ListNode prev = origin; + while (null != l1 && null != l2) { + if (l1.val <= l2.val) { + prev.next = l1; + l1 = l1.next; + } else { + prev.next = l2; + l2 = l2.next; + } + prev = prev.next; + } + // exactly one of l1 and l2 can be non-null at this point, so connect + // the non-null list to the end of the merged list. + prev.next = null == l1 ? l2 : l1; + return origin.next; + } + + public ListNode mergeTwoLists_recursive(ListNode l1, ListNode l2) { + if (null == l1) return l2; + if (null == l2) return l1; + if (l1.val <= l2.val) { + l1.next = mergeTwoLists_recursive(l1.next, l2); + return l1; + } else { + l2.next = mergeTwoLists_recursive(l2.next, l1); + return l2; + } + } + } +} \ No newline at end of file diff --git a/Week 01/id_423/LeetCode_26_423.java b/Week 01/id_423/LeetCode_26_423.java new file mode 100644 index 000000000..98da48be0 --- /dev/null +++ b/Week 01/id_423/LeetCode_26_423.java @@ -0,0 +1,18 @@ + +class Solution { + public int removeDuplicates(int[] nums) { + if (0 == nums.length) return 0; + int i = 0, j = 1; + for (; j array = new ArrayList<>(nums.length); + for (int i = 0; i < nums.length; i++) + if (0!=nums[i]) array.add(nums[i]); + while(0 < count_zero--) + array.add(0); + for (int i = 0; i < nums.length; i++) + nums[i] = array.get(i); + } + + public void moveZeroes(int[] nums) { + int p = 0; + for (int i = 0; i < nums.length; i++) + if (0 != nums[i]) nums[p++] = nums[i]; + for (int i = p; i < nums.length; i++) + nums[i] = 0; + } + + public void moveZeroes(int[] nums) { + for (int p = 0,i = 0; i < nums.length; i++){ + if (0 != nums[i]) { + int tmp = nums[p]; + nums[p++] = nums[i]; + nums[i] = tmp; + } + } + } +} \ No newline at end of file diff --git a/Week 01/id_423/LeetCode_42_423.java b/Week 01/id_423/LeetCode_42_423.java new file mode 100644 index 000000000..7d35d8536 --- /dev/null +++ b/Week 01/id_423/LeetCode_42_423.java @@ -0,0 +1,73 @@ +class Solution { + public int trap_brute_force(int[] height) { + if (0 == height.length) return 0; + int ans = 0; + int[] left_max = new int[height.length]; + int[] right_max = new int[height.length]; + for (int i = 0; i < height.length; i++){ + int max_left = 0, max_right = 0; + for (int j = i; j >= 0; j--) max_left = Math.max(max_left, height[j]); + for (int j = i; j < height.length; j++) max_right = Math.max(max_right, height[j]); + ans += Math.min(max_left, max_right) - height[i]; + } + return ans; + } + + public int trap_dp(int[] height) { + if (0 == height.length) return 0; + int ans = 0; + int[] left_max = new int[height.length]; + int[] right_max = new int[height.length]; + + left_max[0] = height[0]; + right_max[height.length - 1] = height[height.length - 1]; + + for (int i = 1; i < height.length; i++) left_max[i] = Math.max(height[i], left_max[i - 1]); + for (int i = height.length - 2; i >= 0; i--) right_max[i] = Math.max(height[i],right_max[i + 1]); + + for (int i = 0; i < height.length; i++) ans += Math.min(left_max[i], right_max[i]) - height[i]; + + return ans; + } + + public int trap_stack(int[] height) { + if (0 == height.length) return 0; + int ans = 0, current = 0; + + Stack st = new Stack<>(); + while (current < height.length) { + while (!st.empty() && height[st.peek()] < height[current]) { + int top = st.pop(); + if (st.empty()) break; + ans += (current - st.peek() - 1) * (Math.min(height[current], height[st.peek()]) - height[top]); + } + st.push(current++); + } + return ans; + } + + public int trap_double_pointer(int[] height) { + if (0 == height.length) retrun 0; + + int ans = 0; + int max_left = 0; + int max_right = 0; + int left = 0; + int right = height.length - 2; + + for (int i = 1; i < height.length - 1 ; i++) { + if (height[left-1] < height[right+1]) { + max_left = Math.max(height[left-1], max_left); + if (max_left > height[left]) + ans += max_left - height[left]; + left++; + } else { + max_right = Math.max(height[right+1], max_right); + if (max_right > height[right]) + ans += max_right - height[right]; + right++; + } + } + return ans; + } +} \ No newline at end of file diff --git a/Week 01/id_423/LeetCode_641_423.java b/Week 01/id_423/LeetCode_641_423.java new file mode 100644 index 000000000..40d68e0ec --- /dev/null +++ b/Week 01/id_423/LeetCode_641_423.java @@ -0,0 +1,69 @@ +class MyCircularDeque { + + private int[] data; + private int head = 0, tail = 0, count = 0; + + /** Initialize your data structure here. Set the size of the deque to be k. */ + public MyCircularDeque(int k) { + data = new int[k]; + } + + /** Adds an item at the front of Deque. Return true if the operation is successful. */ + public boolean insertFront(int value) { + if (count >= data.length) return false; + head = 0 == head?data.length - 1:head--; + data[head] = value; + count++; + return true; + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + public boolean insertLast(int value) { + if (count >= data.length) return false; + data[tail++] = value; + tail %= data.length; + count++; + return true; + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + public boolean deleteFront() { + if (0 == count) return false; + head++; + head %= data.length; + count--; + return true; + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + public boolean deleteLast() { + if (0 == count) return false; + tail = 0 == tail?data.length - 1:tail--; + tail %= data.length; + count--; + return true; + } + + /** Get the front item from the deque. */ + public int getFront() { + if (0 == count) return -1; + return data[head]; + + } + + /** Get the last item from the deque. */ + public int getRear() { + if (0 == count) return -1; + return 0 == tail?data[data.length - 1]:data[tail - 1]; + } + + /** Checks whether the circular deque is empty or not. */ + public boolean isEmpty() { + return 0 == count; + } + + /** Checks whether the circular deque is full or not. */ + public boolean isFull() { + return count == data.length; + } +} \ No newline at end of file diff --git a/Week 01/id_423/LeetCode_66_423.java b/Week 01/id_423/LeetCode_66_423.java new file mode 100644 index 000000000..dba254fc8 --- /dev/null +++ b/Week 01/id_423/LeetCode_66_423.java @@ -0,0 +1,16 @@ + +class Solution { + public int[] plusOne(int[] digits) { + for (int i = digits.length-1;i >= 0; i--) { + if (9 > digits[i]) { + digits[i]++; + return digits; + } + digits[i] = 0; + } + + int[] newNumber = new int[digits.length+1]; + newNumber[0] = 1; + return newNumber; + } +} \ No newline at end of file diff --git a/Week 01/id_423/LeetCode_88_423.java b/Week 01/id_423/LeetCode_88_423.java new file mode 100644 index 000000000..6ae234ac0 --- /dev/null +++ b/Week 01/id_423/LeetCode_88_423.java @@ -0,0 +1,54 @@ +class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + int index = 0; + //traverse nums2 + for (int i = 0; i < n; i++) { + //traverse nums1 from last modified index++ to last index of current nums1 avaliable index + for (int j = index; j <= m + i; j++) { + //if at last index, no element in nums1 is greater than the value in nums2, just replace it + if (j == m + i) { + } else if (nums1[j] > nums2[i]) { + for (int k = m + i - 1; k >= j; k--) { + nums1[k + 1] = nums1[k]; + } + } else { + continue; + } + nums1[j] = nums2[i]; + index = j + 1; + break; + } + } + } + + public void merge(int[] nums1, int m, int[] nums2, int n) { + System.arraycopy(num2, 0, mums1, nums1, m, n); + Arrays.sort(nums1); + } + + //双指针, 从前往后, 额外空间 O(m)空间复杂度 O(n+m)时间复杂度 + public void merge(int[] nums1, int m, int[] nums2, int n) { + int[] nums1_copy = new int[m]; + System.arraycopy(nums1, 0,nums1_copy,0,m); + + int p = 0, p1 = 0, p2 = 0; + while ( p1 < m && p2 < n) + nums1[p++] = nums1_copy[p1] > nums2[p2]?nums2[p2++]:nums1_copy[p1++]; + + if (p1 < m) + System.arraycopy(nums1_copy, p1, nums1, p, m-p1); + if (p2 < n) + System.arraycopy(nums2, p2, nums1, p, n-p2); + } + //双指针, 从后往前, 额外空间 O(m)空间复杂度 O(n+m)时间复杂度 + public void merge(int[] nums1, int m, int[] nums2, int n) { + int p = m + n - 1, p1 = m - 1, p2 = n - 1; + while ( -1 < p1 && -1 < p2) + nums1[p--] = nums1[p1] < nums2[p2]?nums2[p2--]:nums1[p1--]; + + if (-1 < p2) + System.arraycopy(nums2, 0, nums1, 0, p2 + 1); +// while (-1 < p2) +// nums1[p--] = nums2[p2--]; + } +} \ No newline at end of file diff --git a/Week 01/id_428/LeeCode_283_428.java b/Week 01/id_428/LeeCode_283_428.java new file mode 100644 index 000000000..c4ded75b6 --- /dev/null +++ b/Week 01/id_428/LeeCode_283_428.java @@ -0,0 +1,17 @@ +class Solution { + public void moveZeroes(int[] nums) { + if (nums.length <= 0) { + return; + } + int j = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != 0) { + nums[j] = nums[i]; + if (i != j) { + nums[i] = 0; + } + j++; + } + } + } +} diff --git a/Week 01/id_428/LeeCode_66_428.java b/Week 01/id_428/LeeCode_66_428.java new file mode 100644 index 000000000..f3c90cf3d --- /dev/null +++ b/Week 01/id_428/LeeCode_66_428.java @@ -0,0 +1,17 @@ +class Solution { + public int[] plusOne(int[] digits) { + int currentRemainder = 0, lastRemainder = 1; + for (int i = digits.length -1 ; i >= 0; i--) { + currentRemainder = (digits[i] + lastRemainder) / 10; + digits[i] = (digits[i] + lastRemainder) % 10; + lastRemainder = currentRemainder; + } + if (lastRemainder > 0) { + int[] result = new int[digits.length + 1] ; + result[0] = lastRemainder; + System.arraycopy(digits, 0, result, 1, digits.length); + return result; + } + return digits; + } +} \ No newline at end of file diff --git a/Week 01/id_428/NOTE.md b/Week 01/id_428/NOTE.md index a6321d6e2..a3a0e8813 100644 --- a/Week 01/id_428/NOTE.md +++ b/Week 01/id_428/NOTE.md @@ -1,4 +1,172 @@ -# NOTE +# Week_01_学习总结 - +## 1、学习过程 +​ 1)虽然很多内容平时工作也在使用,但更多的为了完整工作内容而用,更多的时候忽略了底层实现和更优解。 + +​ 2)老师的五毒神掌非常好,但目前只使用了三掌,争取掌握节奏更好的应用到平时的工作中。 + +​ 3)学习一周下来其实还是有不少想细看,但积攒下来的内容,先备注下,争取加快些速度。 + +​ 4)目前第一遍整体看一遍视频,第二遍逐个做例题和刷老师推荐的文档(未完成...) + +​ 5)大龄程序员多些热爱和兴趣,找到了新的学习乐趣,感谢!加油! + +## 2、本周学习内容(待细化) + +​ 1)数组 + +​ 2)链表 + +​ 3)跳表 + +​ 4)栈 + +​ 5)队列 + +​ 6)双端队列 + +​ 7)优先队列 + +## 3、优先队列使用 + +1)常用方法 + +``` +peek()//返回队首元素 +poll()//返回队首元素,队首元素出队列 +add()//添加元素 +size()//返回队列元素个数 +isEmpty()//判断队列是否为空,为空返回true,不空返回false +``` + +2)优先队列的使用 + +1.队列保存的是基本数据类型的包装类 + +``` +//自定义比较器,降序排列 +static Comparator cmp = new Comparator() { + public int compare(Integer e1, Integer e2) { + return e2 - e1; + } + }; +public static void main(String[] args) { + //不用比较器,默认升序排列 + Queue q = new PriorityQueue<>(); + q.add(3); + q.add(2); + q.add(4); + while(!q.isEmpty()) + { + System.out.print(q.poll()+" "); + } + /** + * 输出结果 + * 2 3 4 + */ + //使用自定义比较器,降序排列 + Queue qq = new PriorityQueue<>(cmp); + qq.add(3); + qq.add(2); + qq.add(4); + while(!qq.isEmpty()) + { + System.out.print(qq.poll()+" "); + } + /** + * 输出结果 + * 4 3 2 + */ +} +``` + +2.队列保存的是自定义类 + +``` +//矩形类 +class Node{ + public Node(int chang,int kuan) + { + this.chang=chang; + this.kuan=kuan; + } + int chang; + int kuan; +} + +public class Test { +    //自定义比较类,先比较长,长升序排列,若长相等再比较宽,宽降序 + static Comparator cNode=new Comparator() { + public int compare(Node o1, Node o2) { + if(o1.chang!=o2.chang) + return o1.chang-o2.chang; + else + return o2.kuan-o1.kuan; + } + + }; + public static void main(String[] args) { + Queue q=new PriorityQueue<>(cNode); + Node n1=new Node(1, 2); + Node n2=new Node(2, 5); + Node n3=new Node(2, 3); + Node n4=new Node(1, 2); + q.add(n1); + q.add(n2); + q.add(n3); + Node n; + while(!q.isEmpty()) + { + n=q.poll(); + System.out.println("长: "+n.chang+" 宽:" +n.kuan); + } +     /** +      * 输出结果 +      * 长: 1 宽:2 +      * 长: 2 宽:5 +      * 长: 2 宽:3 +      */ + } +} +``` + + 3.优先队列遍历 + +  PriorityQueue的iterator()不保证以任何特定顺序遍历队列元素。 + +  若想按特定顺序遍历,先将队列转成数组,然后排序遍历 + +示例 + +``` +Queue q = new PriorityQueue<>(cmp); + int[] nums= {2,5,3,4,1,6}; + for(int i:nums) + { + q.add(i); + } + Object[] nn=q.toArray(); + Arrays.sort(nn); + for(int i=nn.length-1;i>=0;i--) + System.out.print((int)nn[i]+" "); + /** + * 输出结果 + * 6 5 4 3 2 1 + */ +``` + +4.比较器生降序说明 + +``` +Comparator cmp = new Comparator() { + public int compare(Object o1, Object o2) { + //升序 + return o1-o2; + //降序 + return o2-o1; + } + }; +``` + +## 4、源码分析(未完成) \ No newline at end of file diff --git a/Week 01/id_433/LeetCode_1_433.txt b/Week 01/id_433/LeetCode_1_433.txt new file mode 100644 index 000000000..cdabf58c6 --- /dev/null +++ b/Week 01/id_433/LeetCode_1_433.txt @@ -0,0 +1,7 @@ +class Solution: + def twoSum(self, nums: List[int], target: int) -> List[int]: + cache = {} + for i in range(len(nums)): + if nums[i] in cache: + return [cache[nums[i]], i] + cache[target - nums[i]] = i \ No newline at end of file diff --git a/Week 01/id_433/LeetCode_26_433.py b/Week 01/id_433/LeetCode_26_433.py new file mode 100644 index 000000000..eb045eff5 --- /dev/null +++ b/Week 01/id_433/LeetCode_26_433.py @@ -0,0 +1,8 @@ +class Solution: + def removeDuplicates(self, nums: List[int]) -> int: + i = 0 + for j in range(1, len(nums)): + if nums[j] != nums[i]: + i += 1 + nums[i] = nums[j] + return i+1 \ No newline at end of file diff --git a/Week 01/id_443/LeetCode_189_443.java b/Week 01/id_443/LeetCode_189_443.java new file mode 100644 index 000000000..202b7f028 --- /dev/null +++ b/Week 01/id_443/LeetCode_189_443.java @@ -0,0 +1,83 @@ +//给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。 +// +// 示例 1: +// +// 输入: [1,2,3,4,5,6,7] 和 k = 3 +//输出: [5,6,7,1,2,3,4] +//解释: +//向右旋转 1 步: [7,1,2,3,4,5,6] +//向右旋转 2 步: [6,7,1,2,3,4,5] +//向右旋转 3 步: [5,6,7,1,2,3,4] +// +// +// 示例 2: +// +// 输入: [-1,-100,3,99] 和 k = 2 +//输出: [3,99,-1,-100] +//解释: +//向右旋转 1 步: [99,-1,-100,3] +//向右旋转 2 步: [3,99,-1,-100] +// +// 说明: +// +// +// 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。 +// 要求使用空间复杂度为 O(1) 的 原地 算法。 +// +// Related Topics 数组 + +package com.modds.alltest.leetcode.editor.cn; + +public class LeetCode_189_443_RotateArray { + public static void main(String[] args) { + Solution solution = new LeetCode_189_443_RotateArray().new Solution(); + } + + + //leetcode submit region begin(Prohibit modification and deletion) + class Solution { + public void rotate(int[] nums, int k) { + if (nums.length <= 1) return; + k %= nums.length; + reverse(nums, 0, nums.length - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, nums.length - 1); + } + + private void reverse(int[] nums, int start, int end) { + while (start < end) { + int tmp = nums[start]; + nums[start] = nums[end]; + nums[end] = tmp; + + start++; + end--; + } + } + + + public void rotate2(int[] nums, int k) { + int[] a = new int[nums.length]; + for (int i = 0; i < nums.length; i++) { + a[(i + k) % nums.length] = nums[i]; + } + System.arraycopy(a, 0, nums, 0, nums.length); + } + + public void rotate1(int[] nums, int k) { + + if (nums.length <= 1) return; + for (int j = 0; j < k; j++) { + int tmp = nums[0]; + for (int i = 0; i < nums.length; i++) { + int ni = (i + 1) % nums.length; + int tmp2 = nums[ni]; + nums[ni] = tmp; + tmp = tmp2; + } + } + } + } +//leetcode submit region end(Prohibit modification and deletion) + +} \ No newline at end of file diff --git a/Week 01/id_443/LeetCode_21_443.java b/Week 01/id_443/LeetCode_21_443.java new file mode 100644 index 000000000..7d0cee5f2 --- /dev/null +++ b/Week 01/id_443/LeetCode_21_443.java @@ -0,0 +1,62 @@ +//将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 +// +// 示例: +// +// 输入:1->2->4, 1->3->4 +//输出:1->1->2->3->4->4 +// +// Related Topics 链表 + +package com.modds.alltest.leetcode.editor.cn; + +public class LeetCode_21_443_MergeTwoSortedLists { + public static void main(String[] args) { + Solution solution = new LeetCode_21_443_MergeTwoSortedLists().new Solution(); + } + + +//leetcode submit region begin(Prohibit modification and deletion) + + /** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ + class Solution { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + ListNode head = new ListNode(-1); + ListNode node = head; + while (l1 != null && l2 != null) { + if (l1.val <= l2.val) { + node.next = l1; + l1 = l1.next; + } else { + node.next = l2; + l2 = l2.next; + } + node = node.next; + } + if (l1 != null) { + node.next = l1; + } + if (l2 != null) { + node.next = l2; + } + return head.next; + } + } + + //leetcode submit region end(Prohibit modification and deletion) + public class ListNode { + int val; + ListNode next; + + ListNode(int x) { + val = x; + } + } + +} \ No newline at end of file diff --git a/Week 01/id_443/LeetCode_26_443.java b/Week 01/id_443/LeetCode_26_443.java new file mode 100644 index 000000000..15a77fb4c --- /dev/null +++ b/Week 01/id_443/LeetCode_26_443.java @@ -0,0 +1,64 @@ +//给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 +// +// 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 +// +// 示例 1: +// +// 给定数组 nums = [1,1,2], +// +//函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 +// +//你不需要考虑数组中超出新长度后面的元素。 +// +// 示例 2: +// +// 给定 nums = [0,0,1,1,1,2,2,3,3,4], +// +//函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 +// +//你不需要考虑数组中超出新长度后面的元素。 +// +// +// 说明: +// +// 为什么返回数值是整数,但输出的答案是数组呢? +// +// 请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。 +// +// 你可以想象内部操作如下: +// +// // nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 +//int len = removeDuplicates(nums); +// +//// 在函数里修改输入数组对于调用者是可见的。 +//// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。 +//for (int i = 0; i < len; i++) { +//    print(nums[i]); +//} +// +// Related Topics 数组 双指针 + +package com.modds.alltest.leetcode.editor.cn; + +public class Leetcode_26_443_Week_1_RemoveDuplicatesFromSortedArray { + public static void main(String[] args) { + Solution solution = new Leetcode_26_443_Week_1_RemoveDuplicatesFromSortedArray().new Solution(); + } + + + //leetcode submit region begin(Prohibit modification and deletion) + class Solution { + public int removeDuplicates(int[] nums) { + if (nums.length == 0) return 0; + int i = 0; + for (int j = 0; j < nums.length; j++) { + if (nums[i] != nums[j]) { + nums[++i] = nums[j]; + } + } + return i + 1; + } + } +//leetcode submit region end(Prohibit modification and deletion) + +} \ No newline at end of file diff --git "a/Week 01/id_448/\357\274\222\357\274\226\357\274\277" "b/Week 01/id_448/\357\274\222\357\274\226\357\274\277" new file mode 100644 index 000000000..37bdd571a --- /dev/null +++ "b/Week 01/id_448/\357\274\222\357\274\226\357\274\277" @@ -0,0 +1,16 @@ +class Solution { + public int removeDuplicates(int[] nums) { + if (nums.length == 0) { + return 0; + } + + int i = 0; + for (int j = 1; j < nums.length; j++){ + if (nums[j] != nums[i]){ + i++; + nums[i] = nums[j]; + } + } + return i + 1; + } +} diff --git a/Week 01/id_458/leetcode_026_458.cs b/Week 01/id_458/leetcode_026_458.cs new file mode 100644 index 000000000..8829e2722 --- /dev/null +++ b/Week 01/id_458/leetcode_026_458.cs @@ -0,0 +1,16 @@ +public class Solution { + public int RemoveDuplicates(int[] nums) { + if (nums.Length == 0) + return 0; + var i = 0; + for (var j = 1; j < nums.Length; j++) + { + if (nums[i] != nums[j]) + { + i++; + nums[i] = nums[j]; + } + } + return i + 1; + } +} \ No newline at end of file diff --git a/Week 01/id_458/leetcode_189_458.cs b/Week 01/id_458/leetcode_189_458.cs new file mode 100644 index 000000000..9ab1994b1 --- /dev/null +++ b/Week 01/id_458/leetcode_189_458.cs @@ -0,0 +1,28 @@ +public class Solution { + public void Rotate(int[] nums, int k) { + if (k > nums.Length) + k = k % nums.Length; + + for (var i = 0; i < nums.Length / 2; i++) + { + swap(nums, i, nums.Length - 1 - i); + } + + for (var i = 0; i < k / 2; i++) + { + swap(nums, i, k - 1 - i); + } + + for (var i = 0; i < (nums.Length - k) / 2; i++) + { + swap(nums, i + k, nums.Length - 1 - i); + } + } + + private void swap(int[] nums, int i, int j) + { + var temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } +} \ No newline at end of file diff --git a/Week 01/id_468/Leetcode155_1_468.java b/Week 01/id_468/Leetcode155_1_468.java new file mode 100644 index 000000000..01e18329b --- /dev/null +++ b/Week 01/id_468/Leetcode155_1_468.java @@ -0,0 +1,55 @@ +import javax.jws.WebParam; + +/** + * @program: leetcode + * @description: minStack + * @author: 王瑞全 + * @create: 2019-10-1820:00 + **/ + + +public class Leetcode155_1_468 { + class MinStack { + private Node node; + + public MinStack() { + } + + public void push(int x) { + if(node==null){ + node=new Node(x,x); + }else { + node=new Node(x,Math.min(x,node.min),node); + } + } + + public void pop() { + node=node.next; + } + + public int top() { + return node.val; + } + + public int getMin() { + return node.min; + } + private class Node{ + int val; + int min; + Node next; + + public Node(int val, int min) { + this.val = val; + this.min = min; + } + + public Node(int val, int min, Node next) { + this.val = val; + this.min = min; + this.next = next; + } + } + } + +} diff --git a/Week 01/id_468/leetcode11_1_468.java b/Week 01/id_468/leetcode11_1_468.java new file mode 100644 index 000000000..41e00363a --- /dev/null +++ b/Week 01/id_468/leetcode11_1_468.java @@ -0,0 +1,33 @@ +/** + * @program: leetcode + * @description: Container With Most Water + * @author: 王瑞全 + * @create: 2019-10-1818:49 + **/ + + +public class leetcode11_1_468 { + public int maxArea(int[] height){ + int left=0,right=height.length-1; + int maxArea=0; + while(left> threeSum(int[] nums) { + if (nums.length==0){ + return new ArrayList<>(); + } + Arrays.sort(nums); + Set> result=new HashSet<>(); + for(int i=0;i+2 0) { + k--; + } + } + } + return new ArrayList<>(result); + } +} diff --git a/Week 01/id_468/leetcode189_1_468.java b/Week 01/id_468/leetcode189_1_468.java new file mode 100644 index 000000000..2df499498 --- /dev/null +++ b/Week 01/id_468/leetcode189_1_468.java @@ -0,0 +1,23 @@ +/** + * @program: leetcode + * @description: Rotate Array + * @author: 王瑞全 + * @create: 2019-10-2019:36 + **/ + + +public class leetcode189_1_468 { + public void rotate(int[] nums, int k) { + k%=nums.length; + reserve(nums, 0, nums.length - 1); + reserve(nums, 0, k - 1); + reserve(nums, k, nums.length - 1); } + + private void reserve(int[] nums, int begin, int end) { + if (begin < end) { + int node = nums[begin]; + nums[begin++]=nums[end]; + nums[end--]=node; + } + } +} diff --git a/Week 01/id_468/leetcode1_1_468.java b/Week 01/id_468/leetcode1_1_468.java new file mode 100644 index 000000000..fadb2bfdc --- /dev/null +++ b/Week 01/id_468/leetcode1_1_468.java @@ -0,0 +1,28 @@ +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +/** + * @program: leetcode + * @description: leetcode1 + * @author: 王瑞全 + * @create: 2019-10-1623:24 + **/ + + +public class leetcode1_1_468 { + private int[] twoSum(int[] nums, int end , int target) { + Map store=new HashMap(); + int[] result=new int[2]; + for(int i=0;i<=end;i++) { + if (store.get(target - nums[i]) != null) { + result[0] = store.get(target - nums[i]); + result[1] = i; + } + store.put(nums[i], i); + } + return result; + + } +} diff --git a/Week 01/id_468/leetcode206_1_468.java b/Week 01/id_468/leetcode206_1_468.java new file mode 100644 index 000000000..786895f3b --- /dev/null +++ b/Week 01/id_468/leetcode206_1_468.java @@ -0,0 +1,27 @@ +/** + * @program: leetcode + * @description: Reverse Linked List + * @author: 王瑞全 + * @create: 2019-10-2016:30 + **/ + + +public class leetcode206_1_468 { + + public class ListNode { + int val; + ListNode next; + ListNode(int x) { val = x; } + } + + public ListNode reverseList(ListNode head) { + if(head==null||head.next==null){ + return head; + } + ListNode newHead=head.next; + ListNode newNode=reverseList(newHead); + newHead.next=head; + head.next=null; + return newNode; + } +} diff --git a/Week 01/id_468/leetcode20_1_468.java b/Week 01/id_468/leetcode20_1_468.java new file mode 100644 index 000000000..a76d68446 --- /dev/null +++ b/Week 01/id_468/leetcode20_1_468.java @@ -0,0 +1,34 @@ +import java.util.Stack; + +/** + * @program: leetcode + * @description: Vaild Parentheses + * @author: 王瑞全 + * @create: 2019-10-1818:47 + **/ + + +public class leetcode20_1_468 { + public boolean isValid(String s) { + Stack test=new Stack<>(); + char[] chars=s.toCharArray(); + for(int i=0;i deque=new ArrayDeque<>(); + int[] result=new int[nums.length-k+1]; + int r=0; + for(int i=0;i=k-1){ + result[r++]=nums[deque.peek()]; + } + } + return result; + } + + public static int[] maxSlidingWindow(int[] nums, int k) { + if(nums.length==0){ + return new int[]{}; + } + int kth=k; + + if(nums.length0&&tail!=null){ + count--; + tail=tail.next; + } + if (tail==null) break;//Has reached the end + + + head=prev.next;//for next cycle + while(prev.next!=tail){ + temp=prev.next;//Assign + prev.next=temp.next;//Delete + + temp.next=tail.next; + tail.next=temp;//Insert + + } + + tail=head; + prev=head; + + } + return dummy.next; + } +} diff --git a/Week 01/id_468/leetcode26_1_468.java b/Week 01/id_468/leetcode26_1_468.java new file mode 100644 index 000000000..1f9af4ff3 --- /dev/null +++ b/Week 01/id_468/leetcode26_1_468.java @@ -0,0 +1,32 @@ +import java.util.HashSet; +import java.util.Set; + +/** + * @program: leetcode + * @description: Remove Duplicates from Sorted Array + * @author: 王瑞全 + * @create: 2019-10-2019:54 + **/ + + +public class leetcode26_1_468 { + public int removeDuplicates1(int[] nums) { + Set set=new HashSet<>(); + for(int num:nums){ + set.add(num); + } + return set.size(); + } + public int removeDuplicates(int[] nums) { + if(nums.length==0){ + return 0; + } + int index=0; + for(int j=1;j= 0; i--) { + if (digits[i] != 9) { + digits[i]++; + break; + } + else { + digits[i]=0; + } + } + if(digits[0]==0){ + digits=new int[digits.length+1]; + digits[0]=1; + } + return digits; + } +} diff --git a/Week 01/id_468/leetcode84_1_468.java b/Week 01/id_468/leetcode84_1_468.java new file mode 100644 index 000000000..9af69c522 --- /dev/null +++ b/Week 01/id_468/leetcode84_1_468.java @@ -0,0 +1,61 @@ +import java.util.Stack; + +/** + * @program: leetcode + * @description: Largest Rectangle in Histogram + * @author: 王瑞全 + * @create: 2019-10-1914:56 + **/ + + +public class leetcode84_1_468 { + //while的性能是for的3倍 + public int largestRectangleArea(int[] heights) { + int n=heights.length; + int maxArea=0; + Stack stack=new Stack<>(); + for (int i=0;i=0&&heights[left]>=heights[i]){ + left--; + } + while(right=heights[i]){ + right++; + } + maxArea=Math.max(maxArea,findTheMinHeight(heights,left+1,right-1)*(right-left-1)); + } + return maxArea; + } + public int largestRectangleArea1(int[] heights) { + if(heights.length==0){ + return 0; + } + int maxArea=heights[0]; + for(int i=0;i=0&&two>=0){ + nums1[length--]=nums1[one]>nums2[two]?nums1[one--]:nums2[two--]; + } + while(two>=0){ + nums1[length--]=nums2[two--]; + } + } +} diff --git a/Week 01/id_473/LeetCode_1.java b/Week 01/id_473/LeetCode_1.java new file mode 100644 index 000000000..04d2a277a --- /dev/null +++ b/Week 01/id_473/LeetCode_1.java @@ -0,0 +1,42 @@ +import java.util.HashMap; +import java.util.Map; + +/** + * 1. 两数之和 + * @Author CJ + * @create 2019/10/20 + */ + +class LeetCode_1 { + public static void main(String[] args) { + int[] ints = twoSum(new int[]{3,2,4}, 6); + + for (int i : ints){ + System.out.println(i); + } + } + //暴力法 + /* public static int[] twoSum(int[] nums, int target) { + for ( int i = 0; i < nums.length-1; i++ ) { + for ( int j = i+1; j < nums.length; j++ ) { + if( nums[i] + nums[j] == target ) { + return new int[]{ i, j }; + } + } + } + throw new IllegalArgumentException("在输入的数组中没有符合相加之和为 " + target + " 的两个数。"); + }*/ + + //一遍哈希表 + public static int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(); + for ( int i = 0; i < nums.length; i++ ) { + int resultNum = target - nums[i]; + if( map.containsKey( resultNum ) ) { + return new int[] { map.get( resultNum ), i }; + } + map.put( nums[i] , i ); + } + throw new IllegalArgumentException("在输入的数组中没有符合相加之和为 " + target + " 的两个数。"); + } +} \ No newline at end of file diff --git a/Week 01/id_473/LeetCode_26.java b/Week 01/id_473/LeetCode_26.java new file mode 100644 index 000000000..e30272d9f --- /dev/null +++ b/Week 01/id_473/LeetCode_26.java @@ -0,0 +1,36 @@ + +/** + * 26. 删除排序数组中的重复项 + * @Author CJ + * @create 2019/10/20 + */ + +public class LeetCode_26 { + public static void main(String[] args) { + int[] nums = new int[]{1,1,1,3,3,3,4,5,6,7,7,88}; + // nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 + int len = removeDuplicates(nums); + + // 在函数里修改输入数组对于调用者是可见的。 + // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。 + for (int i = 0; i < len; i++) { + System.out.println(nums[i]); + } + } + + //快慢指针 + public static int removeDuplicates(int[] nums) { + if (nums.length == 0){ + return 0; + } + int i = 0; + for(int j = 1; j < nums.length; j++){ + if ( nums[i] != nums[j] ){ + i++; + nums[i] = nums[j]; + } + } + return i+1; + } + +} \ No newline at end of file diff --git a/Week 01/id_473/NOTE.md b/Week 01/id_473/NOTE.md index a6321d6e2..4139b9343 100644 --- a/Week 01/id_473/NOTE.md +++ b/Week 01/id_473/NOTE.md @@ -1,4 +1,82 @@ -# NOTE +# 学习总结 +### Queue 和 PriorityQueue 的源码分析 +##### Queue(队列),是一种继承自Conlection接口先进先出的数据结构 ++ boolean add(E e): 增加一个元素,如果队列已经满了,则会抛出IllegalStateException异常。 + ++ E element(): 返回队列头部的元素。 此方法与peek的不同之处仅在于,如果队列为空,将抛出NoSuchElementException异常。 + ++ boolean offer(E e): 添加一个元素并在添加成功时返回true,添加失败则返回false。此方法比add方法不同之处在于,add方法只能通过抛异常的方式相应添加元素失败。 + ++ E peek(): 返回队列头部的元素,如果此队列为空,不抛出异常返回null。 + ++ E poll(): 返回并删除队列头部的元素,如果队列为空,则返回null。 + ++ E remove(): 返回并删除队列头部的元素。 此方法与poll的不同之处仅在于,如果队列为空,将抛出NoSuchElementException异常。 +#### PriorityQueue(优先队列)继承了AbstractQueue(继承了AbstractCollection接口并实现了Queue接口) ++ 优先队列不允许为空值。 ++ 不支持不可比较的对象。 ++ 优先队列的大小没有限制,可以指定初始值大小,在添加元素的时候,队列的大小会自动增加。 ++ 优先队列是非线程安全的,因此java提供了PriorityBlockingQueue用于多线程环境。 + +###### 还有好多关于堆的没弄明白。 + +### Deque源代码: +``` +Deque deque = new LinkedList(); +deque.push("a"); +deque.push("b"); +deque.push("c"); +System.out.println(deque); + +String str = deque.peek(); +System.out.println(str); +System.out.println(deque); + +while (deque.size() > 0) { + System.out.println(deque.pop()); +} +System.out.println(deque); +``` +### Deque.addFirst()改写: +``` +Deque deque = new LinkedList(); +deque.addFirst("a"); +deque.addFirst("b"); +deque.addFirst("c"); +System.out.println(deque); + +String str = deque.peek(); +System.out.println(str); +System.out.println(deque); + +while (deque.size() > 0) { + System.out.println(deque.pop()); +} +System.out.println(deque); +``` +### Deque.addLast()改写: +``` +Deque deque = new LinkedList(); +deque.addLast("a"); +deque.addLast("b"); +deque.addLast("c"); +System.out.println(deque); + +String str = deque.getLast(); +System.out.println(str); +System.out.println(deque); + +while (deque.size() > 0) { + System.out.println(deque.removeLast()); +} +System.out.println(deque); +``` +--- +### 第一周学习总结 + +1. 重新认识了数组、链表、跳表、栈、队列等数据结构,学习了他们的基本实现和特性。 +2. 针对性的实践了这些数据结构的算法实现。 +3. 逐渐熟练并掌握“五毒神掌”等“看家”本领。 diff --git a/Week 01/id_488/LeetCode_283_488.js b/Week 01/id_488/LeetCode_283_488.js new file mode 100644 index 000000000..869a3e47e --- /dev/null +++ b/Week 01/id_488/LeetCode_283_488.js @@ -0,0 +1,70 @@ +//https://leetcode-cn.com/problems/move-zeroes/ +// 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾, +// 同时保持非零元素的相对顺序。 + +/** + * 解题思路、方法分析: + * 1. 遍历数组,将值为0的项从数组删除,最后数组末尾补0(使用js提供的数组方法splice,filter) + * 2. 遍历数组,累计0的个数m,非零项向前移动m位 + * 3. 遍历数组,维护无0数组的索引 +*/ +// 方法一 +var moveZeroes = function(nums) { + for (let i=0,len=nums.length; i< len;i++) { + if (nums[i] === 0) { + nums.push(nums.splice(i--,1));//注意 i-- + len--; + } + } +}; + +// 方法二 : loop ,无0项数组索引(只关注非0),补0 +var moveZeroes = function(nums) { + let j=0; + let len=nums.length; + for (let i=0; i< len;i++) { + if (nums[i] !== 0) { + nums[j] = nums[i]; + j++ + } + } + + while(j< len) { + nums[j] = 0; + j++ + } +}; +// 方法二优化 +var moveZeroes = function(nums) { + let j = 0; + for (let i=0; i 0) { + nums[len - j] = 0; + j--; + } +}; diff --git "a/Week 01/id_493/189.\346\227\213\350\275\254\346\225\260\347\273\204.js" "b/Week 01/id_493/189.\346\227\213\350\275\254\346\225\260\347\273\204.js" new file mode 100644 index 000000000..d388ed4e6 --- /dev/null +++ "b/Week 01/id_493/189.\346\227\213\350\275\254\346\225\260\347\273\204.js" @@ -0,0 +1,31 @@ +/* + * @lc app=leetcode.cn id=189 lang=javascript + * + * [189] 旋转数组 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ + +var rotate = function(nums, k) { + k = k % nums.length; + function reverse(start, end) { + while(start < end) { + let temp = nums[start]; + nums[start] = nums[end]; + nums[end] = temp; + start++; + end--; + } + } + reverse(0, nums.length - 1); + reverse(0, k - 1); + reverse(k, nums.length - 1); +}; + +// @lc code=end + diff --git "a/Week 01/id_493/26.\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271.js" "b/Week 01/id_493/26.\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271.js" new file mode 100644 index 000000000..531e0ae10 --- /dev/null +++ "b/Week 01/id_493/26.\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271.js" @@ -0,0 +1,26 @@ +/* + * @lc app=leetcode.cn id=26 lang=javascript + * + * [26] 删除排序数组中的重复项 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var removeDuplicates = function(nums) { + if (!nums || nums.length === 0) { + return 0; + } + let index = 0; + for (let i = 1; i < nums.length; i++) { + if (nums[i] !== nums[index]) { + index++; + nums[index] = nums[i]; + } + } + return index + 1; +}; +// @lc code=end + diff --git a/Week 01/id_503/NOTE.md b/Week 01/id_503/NOTE.md index a6321d6e2..911b48b8e 100644 --- a/Week 01/id_503/NOTE.md +++ b/Week 01/id_503/NOTE.md @@ -1,4 +1,56 @@ -# NOTE +## 数组 Array +* 是一种**线性**数据结构 +* 用一组**连续**的内存空间,来存储一组具有相同类型的数据 +* 最大的特点就是支持**随机访问** +* 时间复杂度 + * prepend:O(1) + * append:O(1) + * lookup:O(1) + * **insert:O(n)** + * **delete:O(n)** - +## 链表 Linked List +* 是一种**线性**数据结构 +* **不需要**一块**连续的**内存空间 +* 不支持**随机访问** +* 常见的链表有:单链表,双向链表,循环链表 +* 和数组相比,链表**更适合插入、删除操作频繁**的场景 +* 时间复杂度 + * prepend:O(1) + * append:O(1) + * **lookup:O(n)** + * insert:O(1) + * delete:O(1) +## 跳表 Skip List +* 可以理解为加了"索引"的链表 +* 为了解决链表查询慢的缺点 +* 通过构建多级索引来提高查询的效率,实现了基于链表的“二分查找" +* 工程应用:Redis 中的有序集合(Sorted Set) +* 空间复杂度是 O(n) +* 时间复杂度 + * prepend:O(1) + * append:O(1) + * **lookup:O(logn)** + * **insert:O(logn)** + * **delete:O(logn)** + +## 栈 stack +* 栈是一种“操作受限”的线性表,只允许**在一端**插入和删除数据 +* 特性:后进先出(LIFO) +* 既可以用数组来实现(顺序栈),也可以用链表来实现(链式栈) +* 应用:括号匹配、浏览器前进后退、函数调用 +* 时间复杂度 + * **lookup:O(n)** + * 入栈(插入):O(1) + * 出栈(删除):O(1) + +## 队列 Queue +* 队列是一种“操作受限”的线性表,只允许**在一端**插入和删除数据 +* 特性:先进先出(FIFO) +* 既可以用数组来实现(顺序队列),也可以用链表来实现(链式队列) +* 常用队列:双端队列 Deque(Double End queue), 优先队列:Priority Queue +* 应用:括号匹配、浏览器前进后退、函数调用 +* 时间复杂度 + * 入队(插入):O(1) + * 出队(删除):O(1);优先队列:**取出操作:O(logN)** - 按照元素的优先级取出 \ No newline at end of file diff --git a/Week 01/id_503/leetcode_189_503.go b/Week 01/id_503/leetcode_189_503.go new file mode 100644 index 000000000..762b20a65 --- /dev/null +++ b/Week 01/id_503/leetcode_189_503.go @@ -0,0 +1,30 @@ +package week01 + +// 循环替换法 +func rotate(nums []int, k int) { + n := len(nums) + if k >= n { + k = k % n + if k == 0 { // 表示数组右移动了 x * n (x > 0) 次,相当于不移动 + return + } + } + + for i, count := 0, 0; count < n; i++ { + curr := i + prev := nums[curr] + for { + next := (curr + k) % n + temp := nums[next] + nums[next] = prev + prev = temp + + curr = next + count++ + + if curr == i { + break + } + } + } +} diff --git a/Week 01/id_503/leetcode_189_503.js b/Week 01/id_503/leetcode_189_503.js new file mode 100644 index 000000000..063cc2e05 --- /dev/null +++ b/Week 01/id_503/leetcode_189_503.js @@ -0,0 +1,33 @@ +/** + * 反转法 + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ +var rotate = function (nums, k) { + if (k >= nums.length) { + k = k % nums.length; + if (k === 0) { // 表示数组右移动了 x * n (x > 0) 次,相当于不移动 + return; + } + } + + reverse(nums, 0, nums.length - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, nums.length - 1) +} + +/** + * 反转数组 + * @param {number[]} nums + * @param {number} start + * @param {number} end + * @return {void} + */ +function reverse(nums, start, end) { + for (; start < end; start++ , end--) { + let tmp = nums[start]; + nums[start] = nums[end] + nums[end] = tmp; + } +} \ No newline at end of file diff --git a/Week 01/id_503/leetcode_1_503.go b/Week 01/id_503/leetcode_1_503.go new file mode 100644 index 000000000..64f013ac7 --- /dev/null +++ b/Week 01/id_503/leetcode_1_503.go @@ -0,0 +1,18 @@ +package week01 + +// 引入 map, 空间换时间 +func twoSum(nums []int, target int) []int { + + subToIndex := map[int]int{} + + for i, e := range nums { + sub := target - e + if index, ok := subToIndex[sub]; ok { + return []int{index, i} + } + + subToIndex[e] = i + } + + return nil +} diff --git a/Week 01/id_503/leetcode_1_503.js b/Week 01/id_503/leetcode_1_503.js new file mode 100644 index 000000000..bc996308a --- /dev/null +++ b/Week 01/id_503/leetcode_1_503.js @@ -0,0 +1,21 @@ +/** + * 引入 map, 空间换时间 + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = function (nums, target) { + const map = new Map(); + + for (let i = 0; i < nums.length; i++) { + const key = target - nums[i]; + const value = map.get(key); + if (value !== undefined) { + return [value, i]; + } + + map.set(nums[i], i); + } + + return [] +}; \ No newline at end of file diff --git a/Week 01/id_503/leetcode_21.503.go b/Week 01/id_503/leetcode_21.503.go new file mode 100644 index 000000000..8c748c838 --- /dev/null +++ b/Week 01/id_503/leetcode_21.503.go @@ -0,0 +1,34 @@ +package week01 + +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode { + + head := &ListNode{} + prev := head + + for l1 != nil && l2 != nil { + if l1.Val < l2.Val { + prev.Next = l1 + l1 = l1.Next + } else { + prev.Next = l2 + l2 = l2.Next + } + + prev = prev.Next + } + + if l1 == nil { + prev.Next = l2 + } else { + prev.Next = l1 + } + + return head.Next +} diff --git a/Week 01/id_503/leetcode_21.503.js b/Week 01/id_503/leetcode_21.503.js new file mode 100644 index 000000000..1a258d2f8 --- /dev/null +++ b/Week 01/id_503/leetcode_21.503.js @@ -0,0 +1,28 @@ +/** + * 递归解法 + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ +/** + * @param {ListNode} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ +var mergeTwoLists = function (l1, l2) { + if (l1 == null) { + return l2; + } else 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; + } +}; \ No newline at end of file diff --git a/Week 01/id_503/leetcode_26_503.go b/Week 01/id_503/leetcode_26_503.go new file mode 100644 index 000000000..57a1d1d0a --- /dev/null +++ b/Week 01/id_503/leetcode_26_503.go @@ -0,0 +1,14 @@ +package week01 + +// 双指针法 +func removeDuplicates(nums []int) int { + snow, fast := 0, 1 + for ; fast < len(nums); fast++ { + if nums[fast] != nums[fast-1] { + snow++ + nums[snow] = nums[fast] + } + } + + return snow + 1 +} diff --git a/Week 01/id_503/leetcode_26_503.js b/Week 01/id_503/leetcode_26_503.js new file mode 100644 index 000000000..7759e1f5d --- /dev/null +++ b/Week 01/id_503/leetcode_26_503.js @@ -0,0 +1,18 @@ +/** + * 统计重复数量法 + * @param {number[]} nums + * @return {number} + */ +var removeDuplicates = function (nums) { + let count = 0; + for (let i = 0; i < nums.length; i++) { + if (nums[i] !== nums[i - 1]) { + nums[i - count] = nums[i]; + continue; + } + + count++; + } + + return nums.length - count; +}; \ No newline at end of file diff --git a/Week 01/id_503/leetcode_283_503.go b/Week 01/id_503/leetcode_283_503.go new file mode 100644 index 000000000..607a96824 --- /dev/null +++ b/Week 01/id_503/leetcode_283_503.go @@ -0,0 +1,13 @@ +package week01 + +// 双指针法 +func moveZeroes(nums []int) { + + nonZeroIndex := 0 + for i := range nums { + if nums[i] != 0 { + nums[nonZeroIndex], nums[i] = nums[i], nums[nonZeroIndex] + nonZeroIndex++ + } + } +} diff --git a/Week 01/id_503/leetcode_283_503.js b/Week 01/id_503/leetcode_283_503.js new file mode 100644 index 000000000..f8d7795d4 --- /dev/null +++ b/Week 01/id_503/leetcode_283_503.js @@ -0,0 +1,16 @@ +/** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ +var moveZeroes = function (nums) { + let nonZeroIndex = 0; + for (let i = 0; i < nums.length; i++) { + if (nums[i] != 0) { + const temp = nums[nonZeroIndex]; + nums[nonZeroIndex] = nums[i]; + nums[i] = temp; + + nonZeroIndex++ + } + } +}; \ No newline at end of file diff --git a/Week 01/id_503/leetcode_42_503.go b/Week 01/id_503/leetcode_42_503.go new file mode 100644 index 000000000..a3db1a04e --- /dev/null +++ b/Week 01/id_503/leetcode_42_503.go @@ -0,0 +1,36 @@ +package week01 + +// 双指针解法 +func trap(height []int) int { + left, right := 0, len(height)-1 + leftMax, rightMax := 0, 0 + r := 0 + + for left < right { + + if height[left] < height[right] { + + cur := height[left] + if cur < leftMax { + r += leftMax - cur + } else { + leftMax = cur + } + + left++ + + } else { + + cur := height[right] + if cur < rightMax { + r += rightMax - cur + } else { + rightMax = cur + } + + right-- + } + } + + return r +} diff --git a/Week 01/id_503/leetcode_42_503.js b/Week 01/id_503/leetcode_42_503.js new file mode 100644 index 000000000..7cca3978c --- /dev/null +++ b/Week 01/id_503/leetcode_42_503.js @@ -0,0 +1,34 @@ +/** + * @param {number[]} height + * @return {number} + */ +var trap = function (height) { + let left = 0; + let right = height.length - 1; + let leftMax = rightMax = r = 0; + + while (left < right) { + if (height[left] < height[right]) { + + const cur = height[left]; + if (cur < leftMax) { + r += leftMax - cur; + } else { + leftMax = cur; + } + + left++; + } else { + const cur = height[right]; + if (cur < rightMax) { + r += rightMax - cur; + } else { + rightMax = cur; + } + + right--; + } + } + + return r; +}; \ No newline at end of file diff --git a/Week 01/id_503/leetcode_66_503.go b/Week 01/id_503/leetcode_66_503.go new file mode 100644 index 000000000..2507b8eb1 --- /dev/null +++ b/Week 01/id_503/leetcode_66_503.go @@ -0,0 +1,17 @@ +package week01 + +func plusOne(digits []int) []int { + + for i := len(digits) - 1; i >= 0; i-- { + digits[i]++ + digits[i] %= 10 + if digits[i] != 0 { + return digits + } + } + + newDigits := make([]int, len(digits)+1) + newDigits[0] = 1 + + return newDigits +} diff --git a/Week 01/id_503/leetcode_66_503.js b/Week 01/id_503/leetcode_66_503.js new file mode 100644 index 000000000..6c6004bff --- /dev/null +++ b/Week 01/id_503/leetcode_66_503.js @@ -0,0 +1,16 @@ +/** + * @param {number[]} digits + * @return {number[]} + */ +var plusOne = function (digits) { + for (let i = digits.length - 1; i >= 0; i--) { + + digits[i] = ++digits[i] % 10; + + if (digits[i] !== 0) { + return digits; + } + } + + return [1, ...digits]; +}; \ No newline at end of file diff --git a/Week 01/id_503/leetcode_88_503.go b/Week 01/id_503/leetcode_88_503.go new file mode 100644 index 000000000..2e75d05ac --- /dev/null +++ b/Week 01/id_503/leetcode_88_503.go @@ -0,0 +1,25 @@ +package week01 + +func merge(nums1 []int, m int, nums2 []int, n int) { + + p1, p2, p := m-1, n-1, m+n-1 + + for p1 >= 0 && p2 >= 0 { + + if nums1[p1] < nums2[p2] { + nums1[p] = nums2[p2] + p2-- + } else { + nums1[p] = nums1[p1] + p1-- + } + + p-- + } + + for p2 >= 0 { + nums1[p] = nums2[p2] + p-- + p2-- + } +} diff --git a/Week 01/id_503/leetcode_88_503.js b/Week 01/id_503/leetcode_88_503.js new file mode 100644 index 000000000..94e5d4500 --- /dev/null +++ b/Week 01/id_503/leetcode_88_503.js @@ -0,0 +1,19 @@ +/** + * @param {number[]} nums1 + * @param {number} m + * @param {number[]} nums2 + * @param {number} n + * @return {void} Do not return anything, modify nums1 in-place instead. + */ +var merge = function (nums1, m, nums2, n) { + let p1 = m - 1; + let p2 = n - 1; + let p = m + n - 1; + while (p1 >= 0 && p2 >= 0) { + nums1[p--] = nums1[p1] < nums2[p2] ? nums2[p2--] : nums1[p1--]; + } + + while (p2 >= 0) { + nums1[p--] = nums2[p2--] + } +}; \ No newline at end of file diff --git a/Week 01/id_503/week01.go b/Week 01/id_503/week01.go new file mode 100644 index 000000000..628f85c07 --- /dev/null +++ b/Week 01/id_503/week01.go @@ -0,0 +1,7 @@ +package week01 + +// ListNode represents singly-linked list +type ListNode struct { + Val int + Next *ListNode +} diff --git a/Week 01/id_508/LeetCode_001_508.cpp b/Week 01/id_508/LeetCode_001_508.cpp new file mode 100644 index 000000000..7a952380d --- /dev/null +++ b/Week 01/id_508/LeetCode_001_508.cpp @@ -0,0 +1,18 @@ +class Solution { +public: + vector twoSum(vector& nums, int target) { + unordered_map hash_map; + int n = nums.size(); + vector result; + for( int i = 0;inext = l1; + while(cur) { + if(cur->next&&left&&cur->next->val<=left->val) { + cur = cur->next; + } else if(cur->next&&left) { + ListNode *swap = cur->next; + cur->next = left; + cur = left; + left = swap; + } else if(cur->next) { + cur = cur->next; + } else { + cur->next = left; + left = nullptr; + cur = cur->next; + } + } + return Head.next; + + } +}; diff --git a/Week 01/id_508/LeetCode_026_508.cpp b/Week 01/id_508/LeetCode_026_508.cpp new file mode 100644 index 000000000..2325bba5c --- /dev/null +++ b/Week 01/id_508/LeetCode_026_508.cpp @@ -0,0 +1,14 @@ +class Solution { +public: + int removeDuplicates(vector& nums) { + int cache=INT_MAX,left_valid=0,right=0; + int n = nums.size(); + while(right& height) { + deque s; + int res = 0; + for(int current=0;currentheight[s.back()]) { + int top = s.back(); + s.pop_back(); + if(s.empty()) break; + int dis = current-s.back() - 1; + res += dis*(min(height[current],height[s.back()])-height[top]); + } + s.push_back(current); + } + return res; + } +}; diff --git a/Week 01/id_508/LeetCode_066_508.cpp b/Week 01/id_508/LeetCode_066_508.cpp new file mode 100644 index 000000000..d9f70a49e --- /dev/null +++ b/Week 01/id_508/LeetCode_066_508.cpp @@ -0,0 +1,15 @@ +class Solution { +public: + vector plusOne(vector& digits) { + int n = digits.size()-1; + while(digits[n]==9&&n>0){ + digits[n--] = 0; + } + if(digits[n]!=9) digits[n] += 1; + else { + digits[0] = 1; + digits.push_back(0); + } + return digits; + } +}; diff --git a/Week 01/id_508/LeetCode_088_508.cpp b/Week 01/id_508/LeetCode_088_508.cpp new file mode 100644 index 000000000..5eb74024f --- /dev/null +++ b/Week 01/id_508/LeetCode_088_508.cpp @@ -0,0 +1,13 @@ +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + int cur = m+n-1; + int up = m - 1; + int dn = n - 1; + while(cur>-1) { + if(dn<0) break; + if(up>-1&&nums1[up]>nums2[dn]) { + nums1[cur--] = nums1[up--]; + } else nums1[cur--] = nums2[dn--]; + } + } +}; diff --git a/Week 01/id_508/LeetCode_155_508.cpp b/Week 01/id_508/LeetCode_155_508.cpp new file mode 100644 index 000000000..1e2774c74 --- /dev/null +++ b/Week 01/id_508/LeetCode_155_508.cpp @@ -0,0 +1,44 @@ +class MinStack { +public: + /** initialize your data structure here. */ + MinStack() { + + } + + void push(int x) { + st.push_back(x); + if(min.empty()||x<=min.back()) + min.push_back(x); + } + + void pop() { + if(!st.empty()&&!min.empty()&&st.back()==min.back()){ + st.pop_back(); + min.pop_back(); + } else { + st.pop_back(); + } + } + + int top() { + if(!st.empty()) + return st.back(); + else return -1; + } + + int getMin() { + if(!min.empty()) return min.back(); + else return -1; + } + deque st; + deque min; +}; + +/** + * Your MinStack object will be instantiated and called as such: + * MinStack* obj = new MinStack(); + * obj->push(x); + * obj->pop(); + * int param_3 = obj->top(); + * int param_4 = obj->getMin(); + */ diff --git a/Week 01/id_508/LeetCode_189_508.cpp b/Week 01/id_508/LeetCode_189_508.cpp new file mode 100644 index 000000000..6b5087ea5 --- /dev/null +++ b/Week 01/id_508/LeetCode_189_508.cpp @@ -0,0 +1,28 @@ +class Solution { +public: + void rotate(vector& nums, int k) { + int n = nums.size(); + int cache; + int level = 0; + int next = 0; + int start = 0; + while(level& nums) { + int slow = 0; + for(auto i:nums) { + if(i!=0) { + nums[slow++] = i; + } + } + while(slow0) { + front++; + front=front%cap; + siz--; + return true; + } else return false; + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + bool deleteLast() { + if(siz>0) { + siz--; + + return true; + } else return false; + } + + /** Get the front item from the deque. */ + int getFront() { + if(siz>0) return array[(front+1)%cap]; + else return -1; + } + + /** Get the last item from the deque. */ + int getRear() { + if(siz>0) return array[(front+siz)%cap]; + else return -1; + } + + /** Checks whether the circular deque is empty or not. */ + bool isEmpty() { + return siz?false:true; + } + + /** Checks whether the circular deque is full or not. */ + bool isFull() { + + return siz==cap?true:false; + } +}; + +/** + * Your MyCircularDeque object will be instantiated and called as such: + * MyCircularDeque* obj = new MyCircularDeque(k); + * bool param_1 = obj->insertFront(value); + * bool param_2 = obj->insertLast(value); + * bool param_3 = obj->deleteFront(); + * bool param_4 = obj->deleteLast(); + * int param_5 = obj->getFront(); + * int param_6 = obj->getRear(); + * bool param_7 = obj->isEmpty(); + * bool param_8 = obj->isFull(); + */ diff --git a/Week 01/id_513/LeetCode_1_513.java b/Week 01/id_513/LeetCode_1_513.java new file mode 100644 index 000000000..cf2f87f82 --- /dev/null +++ b/Week 01/id_513/LeetCode_1_513.java @@ -0,0 +1,13 @@ +class Solution { + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(); + + for (int i = 0; i < nums.length; i++) { + if (map.containsKey(target - nums[i])) { + return new int[] {i,map.get(target-nums[i])}; + } + map.put(nums[i],i); + } + return new int[0]; + } +} \ No newline at end of file diff --git a/Week 01/id_513/LeetCode_283_513.java b/Week 01/id_513/LeetCode_283_513.java new file mode 100644 index 000000000..249415566 --- /dev/null +++ b/Week 01/id_513/LeetCode_283_513.java @@ -0,0 +1,13 @@ +class Solution { + public void moveZeroes(int[] nums) { + int j = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != 0) { + nums[j++] = nums[i]; + } + if (i != j-1) { + nums[i] = 0; + } + } + } +} \ No newline at end of file diff --git a/Week 01/id_513/LeetCode_42_513.java b/Week 01/id_513/LeetCode_42_513.java new file mode 100644 index 000000000..69cb53ad6 --- /dev/null +++ b/Week 01/id_513/LeetCode_42_513.java @@ -0,0 +1,22 @@ +class Solution { + public int trap(int[] height) { + int sum = 0; + Stack stack = new Stack<>(); + int current = 0; + + while (current < height.length) { + while (!stack.empty() && height[current] > height[stack.peek()]) { + int h = height[stack.pop()]; + if (stack.empty()) { + break; + } + int distance = current - stack.peek() - 1; + int min = Math.min(height[current],height[stack.peek()]); + sum = sum + distance * (min - h); + } + stack.push(current); + current++; + } + return sum; + } +} \ No newline at end of file diff --git a/Week 01/id_513/LeetCode_641_513.java b/Week 01/id_513/LeetCode_641_513.java new file mode 100644 index 000000000..9e2de82da --- /dev/null +++ b/Week 01/id_513/LeetCode_641_513.java @@ -0,0 +1,112 @@ +class MyCircularDeque { + + class DoubleListNode { + DoubleListNode pre; + DoubleListNode next; + int val; + public DoubleListNode(int val) { + this.val = val; + } + } + + DoubleListNode head; + DoubleListNode tail; + int k; + int size; + + /** Initialize your data structure here. Set the size of the deque to be k. */ + public MyCircularDeque(int k) { + head = new DoubleListNode(-1); + tail = new DoubleListNode(-1); + head.pre = tail; + tail.next = head; + this.k = k; + this.size = 0; + } + + /** Adds an item at the front of Deque. Return true if the operation is successful. */ + public boolean insertFront(int value) { + if (size == k) { + return false; + } + + DoubleListNode node = new DoubleListNode(value); + head.pre.next = node; + node.pre = head.pre; + head.pre = node; + node.next = head; + size++; + return true; + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + public boolean insertLast(int value) { + if (size == k) { + return false; + } + + DoubleListNode node = new DoubleListNode(value); + tail.next.pre = node; + node.next = tail.next; + tail.next = node; + node.pre = tail; + size++; + return true; + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + public boolean deleteFront() { + if (size == 0) { + return false; + } + head.pre.pre.next = head; + head.pre = head.pre.pre; + size--; + return true; + + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + public boolean deleteLast() { + if (size == 0) { + return false; + } + tail.next.next.pre = tail; + tail.next = tail.next.next; + size--; + return true; + } + + /** Get the front item from the deque. */ + public int getFront() { + return head.pre.val; + } + + /** Get the last item from the deque. */ + public int getRear() { + return tail.next.val; + } + + /** Checks whether the circular deque is empty or not. */ + public boolean isEmpty() { + return size == 0; + } + + /** Checks whether the circular deque is full or not. */ + public boolean isFull() { + return size == k; + } +} + +/** + * Your MyCircularDeque object will be instantiated and called as such: + * MyCircularDeque obj = new MyCircularDeque(k); + * boolean param_1 = obj.insertFront(value); + * boolean param_2 = obj.insertLast(value); + * boolean param_3 = obj.deleteFront(); + * boolean param_4 = obj.deleteLast(); + * int param_5 = obj.getFront(); + * int param_6 = obj.getRear(); + * boolean param_7 = obj.isEmpty(); + * boolean param_8 = obj.isFull(); + */ \ No newline at end of file diff --git a/Week 01/id_513/NOTE.md b/Week 01/id_513/NOTE.md index a6321d6e2..32827b056 100644 --- a/Week 01/id_513/NOTE.md +++ b/Week 01/id_513/NOTE.md @@ -1,4 +1,4 @@ # NOTE - + 21214 diff --git a/Week 01/id_518/LeetCode_001_518.c b/Week 01/id_518/LeetCode_001_518.c new file mode 100644 index 000000000..1ddf3efc4 --- /dev/null +++ b/Week 01/id_518/LeetCode_001_518.c @@ -0,0 +1,35 @@ +#include +#include + +void printArray(int *nums, int numSize) { + for (int i = 0; i < numSize; i++) { + printf("%d ", nums[i]); + } + printf("\n"); +} + +int *twoSum(int* nums, int numsSize, int target, int *returnSize) { + int *arr = malloc(sizeof(int) * 2); + *returnSize = 0; + for (int i = 0; i < numsSize - 1; i++) { + for (int j = i + 1; j < numsSize; j++) { + if (target == nums[i] + nums[j]) { + arr[0] = i; + arr[1] = j; + *returnSize += 2; + return arr; + } + } + } + return NULL; +} + +int main(void) { + int nums[] = { 3, 7, 11, 15 }; + int numsSize = sizeof(nums) / sizeof(nums[0]); + int target = 9; + int returnSize; + int *arr = twoSum(nums, numsSize, target, &returnSize); + printArray(nums, numsSize); + printArray(arr, returnSize); +} \ No newline at end of file diff --git a/Week 01/id_518/LeetCode_283_518.c b/Week 01/id_518/LeetCode_283_518.c new file mode 100644 index 000000000..4c1d7711c --- /dev/null +++ b/Week 01/id_518/LeetCode_283_518.c @@ -0,0 +1,36 @@ +#include + +void printArray(int *nums, int numSize) { + for (int i = 0; i < numSize; i++) { + printf("%d ", nums[i]); + } + printf("\n"); +} + + +void moveZeroes(int *nums, int numsSize) { + int j = 0; + for (int i = 0; i < numsSize; i++) { + if (nums[i] != 0) { + nums[j] = nums[i]; + if (i != j) { // i must be larger or equal to j + nums[i] = 0; + } + j++; + } + } + printArray(nums, numsSize); +} + +/** + * input [0,1,0,3,12] + * output [1,3,12,0,0] + **/ + +int main(void) { + int input[] = { 0, 1, 0, 3, 12 }; + int length = sizeof(input)/sizeof(input[0]); + printArray(input, length); + moveZeroes(input, length); +} + diff --git a/Week 01/id_523/LeetCode_189_523.cs b/Week 01/id_523/LeetCode_189_523.cs new file mode 100644 index 000000000..05f63a27d --- /dev/null +++ b/Week 01/id_523/LeetCode_189_523.cs @@ -0,0 +1,24 @@ +public class Solution +{ + public void Rotate(int[] nums, int k) + { + var length = nums.Length; + k = k % length; + Reverse(nums, 0, length - 1); + Reverse(nums, 0, k - 1); + Reverse(nums, k, length - 1); + + } + + private void Reverse(int[] nums, int start, int end) + { + while (start < end) + { + int t = nums[start]; + nums[start] = nums[end]; + nums[end] = t; + start++; + end--; + } + } +} \ No newline at end of file diff --git a/Week 01/id_523/LeetCode_1_523.cs b/Week 01/id_523/LeetCode_1_523.cs new file mode 100644 index 000000000..d89f0ff16 --- /dev/null +++ b/Week 01/id_523/LeetCode_1_523.cs @@ -0,0 +1,18 @@ +public class Solution +{ + public int[] TwoSum(int[] nums, int target) + { + Hashtable hash = new Hashtable(); + for (int i = 0; i < nums.Length; i++) + { + if (hash.Contains(target - nums[i])) + { + return new int[] { i, (int)hash[target - nums[i]] }; + } + if (!hash.Contains(nums[i])) + hash.Add(nums[i], i); + } + + return new int[] { }; + } +} \ No newline at end of file diff --git a/Week 01/id_523/LeetCode_21_523.cs b/Week 01/id_523/LeetCode_21_523.cs new file mode 100644 index 000000000..a27a091bd --- /dev/null +++ b/Week 01/id_523/LeetCode_21_523.cs @@ -0,0 +1,28 @@ +public class Solution +{ + public ListNode MergeTwoLists(ListNode l1, ListNode l2) + { + ListNode head = new ListNode(0); + var current = head; + + while (l1 != null && l2 != null) + { + if (l1.val < l2.val) + { + current.next = l1; + l1 = l1.next; + } + else + { + current.next = l2; + l2 = l2.next; + } + + current = current.next; + } + + current.next = l1 ?? l2; + + return head.next; + } +} \ No newline at end of file diff --git a/Week 01/id_523/LeetCode_26_523.cs b/Week 01/id_523/LeetCode_26_523.cs new file mode 100644 index 000000000..4b9d55abf --- /dev/null +++ b/Week 01/id_523/LeetCode_26_523.cs @@ -0,0 +1,21 @@ +public class Solution +{ + public int RemoveDuplicates(int[] nums) + { + if (nums.Length == 0) return 0; + + int i = 0; + for (int j = 1; j < nums.Length; j++) + { + if (nums[j] == nums[i]) + { + continue; + } + else + { + nums[++i] = nums[j]; + } + } + return i + 1; + } +} \ No newline at end of file diff --git a/Week 01/id_523/LeetCode_283_523.cs b/Week 01/id_523/LeetCode_283_523.cs new file mode 100644 index 000000000..36b694056 --- /dev/null +++ b/Week 01/id_523/LeetCode_283_523.cs @@ -0,0 +1,27 @@ +public class Solution +{ + public void MoveZeroes(int[] nums) + { + int zeroIndex = MoveToZero(nums, 0); + + for (int i = 0; i < nums.Length && zeroIndex < nums.Length; i++) + { + if (nums[i] != 0 && i > zeroIndex) + { + nums[zeroIndex] = nums[i]; + nums[i] = 0; + zeroIndex = MoveToZero(nums, zeroIndex); + } + } + } + + private int MoveToZero(int[] nums, int currentIndex) + { + while (currentIndex < nums.Length && nums[currentIndex] != 0) + { + currentIndex++; + } + + return currentIndex; + } +} \ No newline at end of file diff --git a/Week 01/id_523/LeetCode_66_523.cs b/Week 01/id_523/LeetCode_66_523.cs new file mode 100644 index 000000000..c7671a1da --- /dev/null +++ b/Week 01/id_523/LeetCode_66_523.cs @@ -0,0 +1,18 @@ +public class Solution +{ + public int[] PlusOne(int[] digits) + { + var len = digits.Length; + for (int i = len - 1; i >= 0; i--) + { + digits[i]++; + digits[i] = digits[i] % 10; + if (digits[i] != 0) return digits; + } + + digits = new int[len + 1]; + digits[0] = 1; + return digits; + + } +} \ No newline at end of file diff --git a/Week 01/id_523/LeetCode_88_523.cs b/Week 01/id_523/LeetCode_88_523.cs new file mode 100644 index 000000000..15d11ad1b --- /dev/null +++ b/Week 01/id_523/LeetCode_88_523.cs @@ -0,0 +1,14 @@ +public class Solution { + public void Merge(int[] nums1, int m, int[] nums2, int n) { + int i = m -1; + int j = n-1; + int k = m+n -1; + while(i>-1 && j>-1){ + nums1[k--] = nums1[i] > nums2[j]?nums1[i--]:nums2[j--]; + } + + while(j>-1){ + nums1[k--] = nums2[j--]; + } + } +} \ No newline at end of file diff --git a/Week 01/id_533/LeetCode_1_533.js b/Week 01/id_533/LeetCode_1_533.js new file mode 100644 index 000000000..cca15b5d8 --- /dev/null +++ b/Week 01/id_533/LeetCode_1_533.js @@ -0,0 +1,53 @@ +// 题目 https://leetcode-cn.com/problems/two-sum/ + +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +// 暴力破解 时间复杂度O(n^2) 空间复杂度O(1) +var twoSum = function(nums, target) { + var length = nums.length; + for (var i = 0; i < length - 1; i++) { + for (var j = 0; j < length; j++) { + if(nums[i] + nums[j] === target) return [i, j] + } + } +}; + +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +// 使用对象快速查找 时间复杂度O(n) 空间复杂度O(n) +var twoSum = function(nums, target) { + var length = nums.length, + exist = {}; + for (var i = 0; i < length; i++) { + exist[nums[i]] = i; + } + for(var j = 0; j < length; j++) { + var complement = target - nums[j]; + if(exist[complement] != undefined) return [j, exist[complement]] + } +}; + +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +// 使用对象实现快速查找 时间复杂度O(n) 空间复杂度O(n) --> 代码优化 +var twoSum = function(nums, target) { + var length = nums.length, + exist = {}; + for (var i = 0; i < length; i++) { + var complement = target - nums[i]; + if(exist[complement] != undefined) return i < exist[complement] ? [i, exist[complement]] : [exist[complement], i]; + exist[nums[i]] = i; + } +}; + +var res = twoSum([2, 7, 11, 15], 13) +console.log(res) \ No newline at end of file diff --git a/Week 01/id_533/LeetCode_42_533.js b/Week 01/id_533/LeetCode_42_533.js new file mode 100644 index 000000000..f80b62c4c --- /dev/null +++ b/Week 01/id_533/LeetCode_42_533.js @@ -0,0 +1,79 @@ +// https://leetcode-cn.com/problems/trapping-rain-water/ + +/** + * @param {number[]} height + * @return {number} + */ +// 暴力破解 时间复杂度O(n^2) 空间复杂度O(1) +var trap = function(height) { + var length = height.length, + ans = 0; + for (var i = 1; i < length - 1; i++) { + var maxLeft = 0, + maxRight = 0; + for (var j = i; j >= 0; j--) { + maxLeft = Math.max(maxLeft, height[j]) + } + for (var j = i; j < length; j++) { + maxRight = Math.max(maxRight, height[j]) + } + ans += Math.min(maxLeft, maxRight) - height[i] + } + return ans; +}; + +/** + * @param {number[]} height + * @return {number} + */ +// 遍历存储每个格子的左边最高墙和右边最高强 时间复杂度O(n) 空间复杂度O(n) +var trap = function(height) { + var length = height.length, + ans = 0, + maxLeft = [], + maxRight = []; + maxLeft[0] = height[0]; + for (var i = 1; i < length; i++) { + maxLeft[i] = Math.max(height[i], maxLeft[i - 1]) + } + maxRight[length - 1] = height[length - 1] + for (var i = length - 2; i >= 0; i--) { + maxRight[i] = Math.max(height[i], maxRight[i + 1]) + } + for (var i = 1; i < length - 1; i++) { + ans += Math.min(maxLeft[i], maxRight[i]) - height[i] + } + return ans; +}; + +/** + * @param {number[]} height + * @return {number} + */ +// 使用栈 +var trap = function(height) { + var length = height.length, + ans = 0, + current = 0, + stack = []; + while (current < height.length) { + while (stack.length > 0 && (height[current] > height[stack.top()])) { + var top = stack.top(); + stack.pop(); + if(stack.length <= 0) break; + var instance = current - stack.top() - 1; + var boundedHeight = Math.min(height[current], height[stack.top()]) - height[top]; + ans += instance * boundedHeight; + } + stack.push(current++) + } + return ans; +}; + +Array.prototype.top = function () { + return this[this.length - 1] +} + +var arr = [0,1,0,2,1,0,1,3,2,1,2,1]; +var result = trap(arr); +console.log(result) \ No newline at end of file diff --git a/Week 01/id_543/LeetCode_189_543.java b/Week 01/id_543/LeetCode_189_543.java new file mode 100644 index 000000000..eaaa90bc9 --- /dev/null +++ b/Week 01/id_543/LeetCode_189_543.java @@ -0,0 +1,51 @@ +//给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。 +// +// 示例 1: +// +// 输入: [1,2,3,4,5,6,7] 和 k = 3 +//输出: [5,6,7,1,2,3,4] +//解释: +//向右旋转 1 步: [7,1,2,3,4,5,6] +//向右旋转 2 步: [6,7,1,2,3,4,5] +//向右旋转 3 步: [5,6,7,1,2,3,4] +// +// +// 示例 2: +// +// 输入: [-1,-100,3,99] 和 k = 2 +//输出: [3,99,-1,-100] +//解释: +//向右旋转 1 步: [99,-1,-100,3] +//向右旋转 2 步: [3,99,-1,-100] +// +// 说明: +// +// +// 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。 +// 要求使用空间复杂度为 O(1) 的 原地 算法。 +// +// Related Topics 数组 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public void rotate(int[] nums, int k) { + public void rotate(int[] nums, int k) { + k %= nums.length; + reverse(nums, 0, nums.length - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, nums.length - 1); + } + public void reverse(int[] nums, int start, int end) { + while (start < end) { + int temp = nums[start]; + nums[start] = nums[end]; + nums[end] = temp; + start++; + end--; + } + } + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 01/id_543/LeetCode_26_543.java b/Week 01/id_543/LeetCode_26_543.java new file mode 100644 index 000000000..27410dcb2 --- /dev/null +++ b/Week 01/id_543/LeetCode_26_543.java @@ -0,0 +1,58 @@ +//给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 +// +// 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 +// +// 示例 1: +// +// 给定数组 nums = [1,1,2], +// +//函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 +// +//你不需要考虑数组中超出新长度后面的元素。 +// +// 示例 2: +// +// 给定 nums = [0,0,1,1,1,2,2,3,3,4], +// +//函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 +// +//你不需要考虑数组中超出新长度后面的元素。 +// +// +// 说明: +// +// 为什么返回数值是整数,但输出的答案是数组呢? +// +// 请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。 +// +// 你可以想象内部操作如下: +// +// // nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 +//int len = removeDuplicates(nums); +// +//// 在函数里修改输入数组对于调用者是可见的。 +//// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。 +//for (int i = 0; i < len; i++) { +//    print(nums[i]); +//} +// +// Related Topics 数组 双指针 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int removeDuplicates(int[] nums) { + public int removeDuplicates(int[] nums) { + int j = 0; + for(int i = 0;i < nums.length;i++){ + if(nums[i] != nums[j]){ + ++j; + nums[j] = nums[i]; + } + } + return ++j; + } + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 01/id_548/LeetCode_189_548/Rotate.java b/Week 01/id_548/LeetCode_189_548/Rotate.java new file mode 100644 index 000000000..b0c655c9a --- /dev/null +++ b/Week 01/id_548/LeetCode_189_548/Rotate.java @@ -0,0 +1,43 @@ +public class Rotate { + + + public void rotate(int[] nums, int k) { + if(k > nums.length){ + k = k - nums.length; + } + + if(k >= nums.length/2){ + int[] nums2 = new int[nums.length - k]; + int j = 0; + for(int i = 0; i < nums.length - k;i++){ + nums2[i] = nums[i]; + } + for(int i = nums.length - k ;i < nums.length;i++){ + nums[j] = nums[i]; + j++; + } + j = 0; + for(int i = k; i < nums.length;i++){ + nums[i] = nums2[j]; + j++; + } + } + if(k < nums.length/2){ + int[] nums2 = new int[k]; + int j = 0; + for(int i = nums.length - k; i < nums.length;i++){ + nums2[j] = nums[i]; + j++; + } + j = nums.length - 1; + for(int i = nums.length - k - 1 ; i >=0;i--){ + nums[j] = nums[i]; + j--; + } + for(int i = 0;i < nums2.length;i++){ + nums[i] = nums2[i]; + } + } + } + +} diff --git a/Week 01/id_548/LeetCode_26_548/RemoveDuplicates.java b/Week 01/id_548/LeetCode_26_548/RemoveDuplicates.java new file mode 100644 index 000000000..2d8415bbf --- /dev/null +++ b/Week 01/id_548/LeetCode_26_548/RemoveDuplicates.java @@ -0,0 +1,26 @@ +public class RemoveDuplicates { + + public int removeDuplicates(int[] nums) { + if (nums.length <= 1) { + return nums.length; + } + + int count = 1; + + for (int i = 1; i < nums.length; i++) { + int previous = nums[i - 1]; + int current = nums[i]; + + if (previous != current) { + count++; + nums[count - 1] = current; + } + } + + return count; + } + +} + + + diff --git a/Week 01/id_548/LeetCode_283_548/MoveZeroes.java b/Week 01/id_548/LeetCode_283_548/MoveZeroes.java new file mode 100644 index 000000000..3c6539c53 --- /dev/null +++ b/Week 01/id_548/LeetCode_283_548/MoveZeroes.java @@ -0,0 +1,24 @@ +public class MoveZeroes { + + + + + public void moveZeroes(int[] nums) { + if (nums == null || nums.length <= 1) { + return; + } + int index = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != 0) { + nums[index] = nums[i]; + index++; + } + } + + for (int i = index; i < nums.length; i++) { + nums[i] = 0; + } + } + + +} diff --git a/Week 01/id_558/LeetCode_189_558.java b/Week 01/id_558/LeetCode_189_558.java new file mode 100644 index 000000000..5112624d8 --- /dev/null +++ b/Week 01/id_558/LeetCode_189_558.java @@ -0,0 +1,51 @@ +/** + * @see https://leetcode-cn.com/problems/rotate-array/ + * 189. 旋转数组 + * 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。 + */ +public class LeetCode_189_558 { + /** + * 步骤: + * 1、处理边界条件 + * 2、最后k个元素拷贝到一个临时数组 + * 3、原数组向整体向右移动k个位置 + * 4、把临时数组拷贝到原数组前k个位置 + *

+ * 时间复杂度:O(n) + * 空间复杂度:O(n) + */ + public void rotate(int[] nums, int k) { + k = k % nums.length; + int arr[] = new int[k]; + for (int i = nums.length - k; i < nums.length; i++) { + arr[i - (nums.length - k)] = nums[i]; + } + System.arraycopy(nums, 0, nums, k, nums.length - k); + System.arraycopy(arr, 0, nums, 0, k); + } + + /** + * 优化解法: + * 1、整体旋转数组 + * 2、旋转数组前k个元素 + * 3、旋转数组后len - k个元素 + * + * 时间复杂度:O(n) + * 空间复杂度:O(1) + */ + + public void optRotate(int[] nums, int k) { + k = k % nums.length; + reverseArr(nums, 0, nums.length); + reverseArr(nums, 0, k); + reverseArr(nums, k, nums.length); + } + + public void reverseArr(int[] nums, int start, int end) { + for (int i = start; i < start + (end - start + 1) / 2; i++) { + int temp = nums[i]; + nums[i] = nums[end - 1 - i + start]; + nums[end - 1 - i + start] = temp; + } + } +} diff --git a/Week 01/id_558/LeetCode_42_558.java b/Week 01/id_558/LeetCode_42_558.java new file mode 100644 index 000000000..5a92a2008 --- /dev/null +++ b/Week 01/id_558/LeetCode_42_558.java @@ -0,0 +1,33 @@ +/** + * @see https://leetcode-cn.com/problems/trapping-rain-water/ + * 42. 接雨水 + * 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 + */ +public class LeetCode_42_558 { + /** + * 暴力法: + * 思路:Math.min(leftMax,rightMax) - height[currentIndex]; + * 步骤: + * 1、从下标[1,len-1)遍历整个数组(最左侧和最右侧排除) + * 2、获取当前元素左侧最大值 + * 3、获取当前元素右侧最大值 + * 4、累计求和:total += Math.min(leftMax, rightMax) - height[i]; + */ + + public int trap(int[] height) { + int len = height.length; + int total = 0; + for (int i = 1; i < len - 1; i++) { + int leftMax = 0; + int rightMax = 0; + for (int j = i; j >= 0; j--) { + leftMax = Math.max(leftMax, height[j]); + } + for (int k = i; k < len; k++) { + rightMax = Math.max(rightMax, height[k]); + } + total = total + Math.min(leftMax, rightMax) - height[i]; + } + return total; + } +} diff --git a/Week 01/id_558/NOTE.md b/Week 01/id_558/NOTE.md index a6321d6e2..775f39d25 100644 --- a/Week 01/id_558/NOTE.md +++ b/Week 01/id_558/NOTE.md @@ -1,4 +1,41 @@ -# NOTE +##第一周学习总结 - +#### 知识回顾 +- 数组、链表、跳表 +- 栈、队列 +- 思想方法 + - 过遍数:第一遍 + - 5分钟:读题 + 思考 + - 直接看解法:注意!多解法,比较解法优劣 + - 背诵、默写好解法 + - 解题套路: + - 升维、空间换时间 + - 找核心重复单元(顺序、分支、循环) + - 左右指针:左右边界向中间收敛、快慢指针(判断是否有环等) + - 最近相关性问题:考虑用栈来解决 + +#### 处理输出 + +- 练习题目 + + > 1. https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/ + > 2. https://leetcode-cn.com/problems/rotate-array/ + > 3. https://leetcode-cn.com/problems/merge-two-sorted-lists/ + > 4. https://leetcode-cn.com/problems/merge-sorted-array/ + > 5. https://leetcode-cn.com/problems/two-sum/ + > 6. https://leetcode-cn.com/problems/move-zeroes/ + > 7. https://leetcode-cn.com/problems/plus-one/ + > 8. https://leetcode.com/problems/design-circular-deque + > 9. https://leetcode.com/problems/trapping-rain-water/ + +#### 疑难点 + +- 跳表实现 +- PriorityQueue实现原理 + +#### 总结改进 + +- 意识到效率问题,严格按照五步刷题法去解题 +- 每日刷1-2题(一题多解) +- 冰冻三尺非一日之寒,重视分解积累的力量 \ No newline at end of file diff --git a/Week 01/id_558/NewApiDeque.java b/Week 01/id_558/NewApiDeque.java new file mode 100644 index 000000000..85c8d5a96 --- /dev/null +++ b/Week 01/id_558/NewApiDeque.java @@ -0,0 +1,31 @@ +import java.util.Deque; +import java.util.LinkedList; + +/** + * 用add first或add last这套新的API改写Deque的代码 + */ +public class NewApiDeque { + public static void testNewApi() { + Deque deque = new LinkedList(); +// deque.push("a"); +// deque.push("b"); +// deque.push("c"); + deque.addFirst("a"); + deque.addFirst("b"); + deque.addFirst("c"); + System.out.println(deque); +// String str = deque.peek(); + String str = deque.peekFirst(); + System.out.println(str); + System.out.println(deque); + while (deque.size() > 0) { +// System.out.println(deque.pop()); + System.out.println(deque.pollFirst()); + } + System.out.println(deque); + } + + public static void main(String[] args) { + testNewApi(); + } +} diff --git "a/Week 01/id_558/\346\272\220\347\240\201\345\210\206\346\236\220.txt" "b/Week 01/id_558/\346\272\220\347\240\201\345\210\206\346\236\220.txt" new file mode 100644 index 000000000..c228ea668 --- /dev/null +++ "b/Week 01/id_558/\346\272\220\347\240\201\345\210\206\346\236\220.txt" @@ -0,0 +1,124 @@ +Queue是一个接口继承自Collection接口。 + +package java.util; +public interface Queue extends Collection { + boolean add(E e); //添加元素,超出容量时抛异常 + boolean offer(E e); // 添加元素,超出容量时返回false + E remove();//删除元素,队列为空时抛异常 + E poll();//删除元素,队列为空时返回null + E element();////查看队头元素,不删除,队列为空时抛异常 + E peek();//查看队头元素,不删除,队列为空时返回null +} + +---------------------------------------------------------------------------------------------------------------- +PriorityQueue:优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的Comparator进行排序,具体取决于所使用的构造方法。 +transient Object[] queue; //采用数组来存储元素 + +入队: + +public boolean offer(E e) { + if (e == null) + throw new NullPointerException(); + modCount++; + int i = size; + if (i >= queue.length) + grow(i + 1); //当队满的时候,动态扩容 + size = i + 1; + if (i == 0) + queue[0] = e; + else + siftUp(i, e); + return true; +} + +private void siftUp(int k, E x) { + if (comparator != null) + siftUpUsingComparator(k, x); //使用构造参数里指定的比较器 + else + siftUpComparable(k, x); //使用默认自然比较器 +} + +//么有看懂。。。(repeat) +private void siftUpComparable(int k, E x) { + Comparable key = (Comparable) x; + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = queue[parent]; + if (key.compareTo((E) e) >= 0) + break; + queue[k] = e; + k = parent; + } + queue[k] = key; +} + +private void siftUpUsingComparator(int k, E x) { + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = queue[parent]; + if (comparator.compare(x, (E) e) >= 0) + break; + queue[k] = e; + k = parent; + } + queue[k] = x; +} + +---------------------------------------------------------------------------------------------------------------- +出队:当第一个元素出列之后,对剩下的元素进行再排序,挑选出最小的元素排在数组第一个位置,从而保证出队顺序 + +public E poll() { + if (size == 0) + return null; + int s = --size; + modCount++; + E result = (E) queue[0]; + E x = (E) queue[s]; + queue[s] = null; + if (s != 0) + siftDown(0, x); + return result; +} + +private void siftDown(int k, E x) { + if (comparator != null) + siftDownUsingComparator(k, x); //指定比较器 + else + siftDownComparable(k, x); //默认比较器 +} + +private void siftDownComparable(int k, E x) { + Comparable key = (Comparable)x; + int half = size >>> 1; // loop while a non-leaf + while (k < half) { + int child = (k << 1) + 1; // assume left child is least + Object c = queue[child]; + int right = child + 1; + if (right < size && + ((Comparable) c).compareTo((E) queue[right]) > 0) + c = queue[child = right]; + if (key.compareTo((E) c) <= 0) + break; + queue[k] = c; + k = child; + } + queue[k] = key; +} + + +private void siftDownUsingComparator(int k, E x) { + int half = size >>> 1; + while (k < half) { + int child = (k << 1) + 1; + Object c = queue[child]; + int right = child + 1; + if (right < size && + comparator.compare((E) c, (E) queue[right]) > 0) + c = queue[child = right]; + if (comparator.compare(x, (E) c) <= 0) + break; + queue[k] = c; + k = child; + } + queue[k] = x; +} diff --git a/Week 01/id_563/Leecode_26_563.go b/Week 01/id_563/Leecode_26_563.go new file mode 100644 index 000000000..4c4d1292f --- /dev/null +++ b/Week 01/id_563/Leecode_26_563.go @@ -0,0 +1,15 @@ +func removeDuplicates(nums []int) int { + l := len(nums) + if l < 2 { + return l + } + i := 1 + for _, v := range nums[1:] { + if v == nums[i-1] { + continue + } + nums[i] = v + i++ + } + return i +} diff --git a/Week 01/id_563/Leecode_43_563.go b/Week 01/id_563/Leecode_43_563.go new file mode 100644 index 000000000..13b72cd21 --- /dev/null +++ b/Week 01/id_563/Leecode_43_563.go @@ -0,0 +1,21 @@ +func trap(height []int) int { + left, right, sum, maxLeft, maxRight := 0, len(height)-1, 0, 0, 0 + for left <= right { + if height[left] <= height[right] { + if height[left] >= maxLeft { + maxLeft = height[left] + } else { + sum += maxLeft - height[left] + } + left++ + } else { + if height[right] >= maxRight { + maxRight = height[right] + } else { + sum += maxRight - height[right] + } + right-- + } + } + return sum +} diff --git a/Week 01/id_568/LeetCode_189_568.java b/Week 01/id_568/LeetCode_189_568.java new file mode 100644 index 000000000..e975bc91e --- /dev/null +++ b/Week 01/id_568/LeetCode_189_568.java @@ -0,0 +1,55 @@ +package com.leetcode.kelvin; + +/** + * @author kelvin + * @date 2019/10/19 7:18 PM + */ +public class RetateArray { + public void rotateReverse(int[] nums, int k) { + k %= nums.length; + reverse(nums, 0, nums.length - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, nums.length - 1); + } + + private void reverse(int[] nums, int start, int end) { + while (start < end) { + int temp = nums[start]; + nums[start] = nums[end]; + nums[end] = temp; + start++; + end--; + } + } + + public void rotateCircle(int[] nums, int k) { + k = k % nums.length; + int count = 0; + for (int i = 0; count < nums.length; i++) { + int current = i; + int previous = nums[i]; + do { + int next = (current + k) % nums.length; + int temp = nums[next]; + nums[next] = previous; + previous = temp; + current = next; + count++; + } while (i != current); + + } + } + + public void rotateVoilate(int[] nums, int k) { + //暴力 + for (int i = 0; i < k; i++) { + int previousValue = nums[nums.length - 1]; + for (int j = 0; j < nums.length; j++) { + int temp = nums[j]; + nums[j] = previousValue; + previousValue = temp; + } + + } + } +} diff --git a/Week 01/id_568/LeetCode_26_568.java b/Week 01/id_568/LeetCode_26_568.java new file mode 100644 index 000000000..85da42c2d --- /dev/null +++ b/Week 01/id_568/LeetCode_26_568.java @@ -0,0 +1,41 @@ +package com.leetcode.kelvin; + +import java.util.PriorityQueue; +import java.util.Stack; + +/** + * @author kelvin + * @date 2019/10/16 3:10 PM + */ +public class RemoveDuplicates { + public static int removeDuplicates(int[] nums) { + if (nums.length == 0) { + return 0; + } + int slowIndex = 0; + //相同的索引直接跳过 + for (int quickIndex = 1; quickIndex < nums.length; quickIndex++) { + //不同的索引位置,慢指针增加,两个指针重合复制 + if (nums[quickIndex] != nums[slowIndex]) { + slowIndex++; + nums[slowIndex] = nums[quickIndex]; + } + } + return slowIndex + 1; + } + + + + + public static void main(String[] args) { + int[] nums = {1, 1, 2}; + removeDuplicates(nums); + printArray(nums); + } + + private static void printArray(int[] nums) { + for (int i = 0; i < nums.length; i++) { + System.out.println(nums[i]); + } + } +} diff --git a/Week 01/id_568/LeetCode_42_568.java b/Week 01/id_568/LeetCode_42_568.java new file mode 100644 index 000000000..edc5979ab --- /dev/null +++ b/Week 01/id_568/LeetCode_42_568.java @@ -0,0 +1,33 @@ +package com.leetcode.kelvin; + +import java.util.Deque; +import java.util.LinkedList; + +/** + * @author kelvin + * @date 2019/10/20 11:17 PM + */ +public class Trap { + public int trap(int[] height) { + int sum = 0; + Deque stack = new LinkedList<>(); + int current = 0; + while (current < height.length) { + + while (!stack.isEmpty() && height[current] > height[stack.peek()]) { + int h = height[stack.peek()]; + stack.removeFirst(); + if (stack.isEmpty()) { + break; + } + int distance = current - stack.peek() - 1; + int min = Math.min(height[stack.peek()], height[current]); + sum = sum + distance * (min - h); + } + stack.addFirst(current); + current++; + } + return sum; + + } +} diff --git a/Week 01/id_568/LeetCode_641_568.java b/Week 01/id_568/LeetCode_641_568.java new file mode 100644 index 000000000..f7b6b1771 --- /dev/null +++ b/Week 01/id_568/LeetCode_641_568.java @@ -0,0 +1,115 @@ +package com.leetcode.kelvin; + +/** + * @author kelvin + * @date 2019/10/20 7:44 PM + */ +public class MyCircularDeque { + private int size; + private int k; + private Node head; + private Node tail; + + public MyCircularDeque(int k) { + head = new Node(-1); + tail = new Node(-1); + head.pre = tail; + tail.next = head; + this.k = k; + this.size = 0; + } + + public boolean insertFront(int value) { + if (this.isFull()) { + return false; + } + Node node = new Node(value); + node.next = head; + node.pre = head.pre; + head.pre.next = node; + head.pre = node; + size++; + return true; + } + + /** + * Adds an item at the rear of Deque. Return true if the operation is successful. + */ + public boolean insertLast(int value) { + if (this.isFull()) { + return false; + } + Node node = new Node(value); + node.next = tail.next; + tail.next.pre = node; + tail.next = node; + node.pre = tail; + size++; + return true; + } + + /** + * Deletes an item from the front of Deque. Return true if the operation is successful. + */ + public boolean deleteFront() { + if (isEmpty()) { + return false; + } + head.pre.pre.next = head; + head.pre = head.pre.pre; + size--; + return true; + + } + + /** + * Deletes an item from the rear of Deque. Return true if the operation is successful. + */ + public boolean deleteLast() { + if (isEmpty()) { + return false; + } + tail.next.next.pre = tail; + tail.next = tail.next.next; + size--; + return true; + } + + /** + * Get the front item from the deque. + */ + public int getFront() { + return head.pre.val; + } + + /** + * Get the last item from the deque. + */ + public int getRear() { + return tail.next.val; + } + + /** + * Checks whether the circular deque is empty or not. + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * Checks whether the circular deque is full or not. + */ + public boolean isFull() { + return size == k; + } + + class Node { + Node pre; + Node next; + int val; + + public Node(int val) { + this.val = val; + } + } +} diff --git a/Week 01/id_573/NOTE.md b/Week 01/id_573/NOTE.md index a6321d6e2..9871c9335 100644 --- a/Week 01/id_573/NOTE.md +++ b/Week 01/id_573/NOTE.md @@ -1,4 +1,25 @@ # NOTE +### Array + +a、线性表:属于逻辑结构中的线性结构,是最基本、最简单、也是最常用的一种数据结构。线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。线性表的逻辑结构简单,便于实现和操作。因此,线性表这种数据结构在实际应用中是广泛采用的一种数据结构,它包括顺序表和链表。 + + 顺序表:线性表中的一种,它是用数组来实现的一种线性表,所以它的存储结构(物理结构)是连续的。 + 链表:线性表中的一种,它的存储结构是用任意一组存储单元来存储数据元素。所以它的存储结构可以是连续的,也可以不是连续的。一般我们说的链表都是不连续的。有一种用数组来表示的链表,叫做静态链表,它的存储结构就是连续的。 + +b、数组:一种物理结构,它的存储单元是连续的,是由类型名、标识符和维数组成的符合数据类型,类型名规定了存放在数组中的元素的类型,而维数则指数组中包含的元素个数。 + +区别: + + 数组长度固定,线性表长度可变 + 线性表是一种概念,数组是一种类型。 + 线性表可以删除元素,数组只能整体删除 + +### Queue + + a、栈又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。 + b、队列是一种特殊的线性表,特殊之处在于它只允许在表的前端进行删除操作,而在表的后端进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。 + +总结一点:这一周大部分时间是在看理论知道。学习方针是:拆分知识点,刻意练习,查找重复性,五遍刷题法,理解其中的思维方式。本周学习的内容:1、用Hashmap 来解决 匹配性 问题,2、用双指针来解决数组去重问题, diff --git a/Week 01/id_573/leetcode_189_573.java b/Week 01/id_573/leetcode_189_573.java new file mode 100644 index 000000000..40fd12fa5 --- /dev/null +++ b/Week 01/id_573/leetcode_189_573.java @@ -0,0 +1 @@ +class Solution { /*** * 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。 * @param nums * @param k */ public int[] rotate(int[] nums, int k) { int len = nums.length; if (k == len) { return nums; } if (k > len) { k = k % len; } //暴力 int[] num = new int[len]; for (int i = 0; i < k; i++) { num[i] = nums[len - k + i]; } for (int i = 0; i < len - k; i++) { num[k + i] = nums[i]; } //方案二 swop(nums, 0, len - k - 1); swop(nums, len - k, len - 1); swop(nums, 0, len - 1); return num; } private void swop(int[] nums, int start, int end) { while (start < end) { int tmp = nums[start]; nums[start] = nums[end]; nums[end] = tmp; start++; end--; } } public static void main(String[] args) { Solution solution = new Solution(); //输入: [1,2,3,4,5,6,7] 和 k = 3 int[] nss = new int[]{1, 2, 3, 4, 5, 6, 7}; nss = solution.rotate(nss, 3); for (int i : nss) { System.out.println(i + " "); } } } \ No newline at end of file diff --git a/Week 01/id_573/leetcode_1_573.java b/Week 01/id_573/leetcode_1_573.java new file mode 100644 index 000000000..e1f850d87 --- /dev/null +++ b/Week 01/id_573/leetcode_1_573.java @@ -0,0 +1 @@ +class Solution { public int[] twoSum(int[] nums, int target) { Map map = new HashMap<>(); for (int i = 0; i < nums.length; i++) { int complement = (target - nums[i]); System.out.println("complement:" + complement + " i:" + i + " nums:" + nums[i]); if (map.containsKey(complement)) { return new int[]{map.get(complement), i}; } //如果条件是最先的俩个数累加需加上该判断,否则不需要 if (map.containsKey(nums[i])) { continue; } map.put(nums[i], i); } throw new IllegalArgumentException("No two sum solution"); } public static void main(String[] args) { Solution solution = new Solution(); //给定 nums = [2, 7, 11, 15], target = 9 //因为 nums[0] + nums[1] = 2 + 7 = 9 //所以返回 [0, 1] int[] nums = new int[]{10, 3, 3, 7, 6, 11, 15}; int target = 9; int[] ns = new int[2]; ns = solution.twoSum(nums, target); for (int i : ns) { System.out.println(i); } } } \ No newline at end of file diff --git a/Week 01/id_573/leetcode_88_573.java b/Week 01/id_573/leetcode_88_573.java new file mode 100644 index 000000000..59a9a30f0 --- /dev/null +++ b/Week 01/id_573/leetcode_88_573.java @@ -0,0 +1 @@ +class Solution { public void merge(int[] nums1, int m, int[] nums2, int n) { int p1 = m - 1; int p2 = n - 1; int p = m + n - 1; while ((p1 >= 0) && (p2 >= 0)) { nums1[p--] = nums1[p1] > nums2[p2] ? nums1[p1--] ? nums2[p2--]; } //拷贝数组内容 System.arraycopy(nums2, 0, nums1, 0, p2 + 1); return nums1; } public static void main(String[] args) { Solution solution = new Solution(); int[] nums1 = new int[1, 2, 3, 8, 0, 0, 0]; int[] nums2 = new int[2, 5, 6]; int m = 4, n = 3; nums1 = solution.merge(nums1, m, nums2, n); for (int i : nums1) { System.out.print(i + " "); } } } \ No newline at end of file diff --git a/Week 01/id_578/LeetCode_189_578.java b/Week 01/id_578/LeetCode_189_578.java new file mode 100644 index 000000000..067ba48c8 --- /dev/null +++ b/Week 01/id_578/LeetCode_189_578.java @@ -0,0 +1,26 @@ +package com.hand.week1.class3; + +/** + * @description:LeetCode189. 旋转数组 + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/16 + */ +public class LeetCode189 { + public void rotate(int[] nums, int k) { + k %= nums.length; + reverse(nums, 0, nums.length - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, nums.length - 1); + } + + public void reverse(int[] nums, int start, int end) { + while (start < end) { + int tmp = nums[start]; + nums[start] = nums[end]; + nums[end] = tmp; + start++; + end--; + } + } +} diff --git a/Week 01/id_578/LeetCode_1_578.java b/Week 01/id_578/LeetCode_1_578.java new file mode 100644 index 000000000..aeadd878d --- /dev/null +++ b/Week 01/id_578/LeetCode_1_578.java @@ -0,0 +1,25 @@ +package com.hand.week1.class3; + +import java.util.HashMap; +import java.util.Map; + +/** + * @description:LeetCode1. 两数之和 + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/16 + */ +public class LeetCode1 { + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; ++i) { + //target=nums[i]+difference + int difference = target - nums[i]; + if (map.containsKey(difference)) { + return new int[]{i, map.get(difference)}; + } + map.put(nums[i], i); + } + return new int[]{}; + } +} diff --git a/Week 01/id_578/LeetCode_21_578.java b/Week 01/id_578/LeetCode_21_578.java new file mode 100644 index 000000000..4bfb1a9b0 --- /dev/null +++ b/Week 01/id_578/LeetCode_21_578.java @@ -0,0 +1,39 @@ +package com.hand.week1.class3; + +/** + * @description:LeetCode21. 合并两个有序链表 + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/16 + */ +public class LeetCode21 { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + ListNode listNode = new ListNode(0); + ListNode current = listNode; + while (l1 != null && l2 != null) { + if (l1.val > l2.val) { + current.next = l2; + l2 = l2.next; + } else { + current.next = l1; + l1 = l1.next; + } + current = current.next; + } + if (l1 != null) { + current.next = l1; + } else { + current.next = l2; + } + return listNode.next; + } + + class ListNode { + int val; + ListNode next; + + ListNode(int x) { + val = x; + } + } +} diff --git a/Week 01/id_578/LeetCode_26_578.java b/Week 01/id_578/LeetCode_26_578.java new file mode 100644 index 000000000..d5c52bdfa --- /dev/null +++ b/Week 01/id_578/LeetCode_26_578.java @@ -0,0 +1,21 @@ +package com.hand.week1.class3; + +/** + * @description:LeetCode26. 删除排序数组中的重复项 + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/16 + */ +public class LeetCode26 { + public int removeDuplicates(int[] nums) { + if (nums.length == 0) return 0; + int i = 0; + for (int j = 1; j < nums.length; ++j) { + if (nums[i] != nums[j]) { + i++; + nums[i] = nums[j]; + } + } + return i + 1; + } +} diff --git a/Week 01/id_578/LeetCode_283_578.java b/Week 01/id_578/LeetCode_283_578.java new file mode 100644 index 000000000..be2d8338f --- /dev/null +++ b/Week 01/id_578/LeetCode_283_578.java @@ -0,0 +1,21 @@ +package com.hand.week1.class3; + +/** + * @description:LeetCode283. 移动零 + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/16 + */ +public class LeetCode283 { + public void moveZeroes(int[] nums) { + int j = 0; + for (int i = 0; i < nums.length; ++i) { + if (nums[i] != 0) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + j++; + } + } + } +} diff --git a/Week 01/id_578/LeetCode_66_578.java b/Week 01/id_578/LeetCode_66_578.java new file mode 100644 index 000000000..4ab1e651a --- /dev/null +++ b/Week 01/id_578/LeetCode_66_578.java @@ -0,0 +1,20 @@ +package com.hand.week1.class3; + +/** + * @description:LeetCode66. 加一 + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/16 + */ +public class LeetCode66 { + public int[] plusOne(int[] digits) { + for (int i = digits.length - 1; i >= 0; --i) { + digits[i] = (digits[i] + 1) % 10; + if (digits[i] != 0) return digits; + + } + digits = new int[digits.length + 1]; + digits[0] = 1; + return digits; + } +} diff --git a/Week 01/id_578/LeetCode_88_578.java b/Week 01/id_578/LeetCode_88_578.java new file mode 100644 index 000000000..1ca81d039 --- /dev/null +++ b/Week 01/id_578/LeetCode_88_578.java @@ -0,0 +1,20 @@ +package com.hand.week1.class3; + +/** + * @description:LeetCode88. 合并两个有序数组 + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/16 + */ +public class LeetCode88 { + public void merge(int[] nums1, int m, int[] nums2, int n) { + int p1=m-1; + int p2=n-1; + int p=m+n-1; + while(p1>=0&&p2>=0){ + nums1[p--]=(nums1[p1]>nums2[p2])?nums1[p1--]:nums2[p2--]; + } + System.arraycopy(nums2,0,nums1,0,p2+1); + } + +} diff --git a/Week 01/id_583/LeetCode_1_583.php b/Week 01/id_583/LeetCode_1_583.php new file mode 100644 index 000000000..febbc0400 --- /dev/null +++ b/Week 01/id_583/LeetCode_1_583.php @@ -0,0 +1,30 @@ + $num) { + $diff = $target - $num; + + if (!isset($result[$diff])) { + $result[$num] = $key; + continue; + } + + return [$result[$diff], $key]; + } + } +} diff --git a/Week 01/id_583/LeetCode_26_583.php b/Week 01/id_583/LeetCode_26_583.php new file mode 100644 index 000000000..0ea69dd87 --- /dev/null +++ b/Week 01/id_583/LeetCode_26_583.php @@ -0,0 +1,27 @@ += 0; j --) { + maxLeft = Math.max(maxLeft, a[j]); + } + // 找到右边最大高度 + for (int j = i; j < a.length; j ++) { + maxRight = Math.max(maxRight, a[j]); + } + ret = ret + Math.min(maxLeft, maxRight) - a[i]; + } + + return ret; + } + + public static void main(String[] args) { + LeetCode_42_588 solution = new LeetCode_42_588(); + System.out.println(solution.trappingRain(new int[]{0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1})); + } +} diff --git a/Week 01/id_588/LeetCode_641_588.java b/Week 01/id_588/LeetCode_641_588.java new file mode 100644 index 000000000..9bbfe1c53 --- /dev/null +++ b/Week 01/id_588/LeetCode_641_588.java @@ -0,0 +1,125 @@ +import java.util.Arrays; + +/** + * 设计循环双端队列 + * https://leetcode.com/problems/design-circular-deque/ + */ +public class LeetCode_641_588 { + + private int[] elements; + private int front; + private int rear; + private int size; + private int capacity; + + /** Initialize your data structure here. Set the size of the deque to be k. */ + public LeetCode_641_588(int k) { + elements = new int[k]; + front = 0; + rear = 0; + size = 0; + capacity = k; + } + + /** Adds an item at the front of Deque. Return true if the operation is successful. */ + public boolean insertFront(int value) { + if (size == capacity) { + return false; + } + + front = (front - 1 + capacity) % capacity; + elements[front] = value; + size ++; + return true; + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + public boolean insertLast(int value) { + if (size == capacity) { + return false; + } + + elements[rear] = value; + rear = (rear + 1) % capacity; + size ++; + return true; + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + public boolean deleteFront() { + if (0 == size) { + return false; + } + + front = (front + 1) % capacity; + size --; + return true; + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + public boolean deleteLast() { + if (0 == size) { + return false; + } + + rear = (rear - 1 + capacity) % capacity; + size --; + return true; + } + + /** Get the front item from the deque. */ + public int getFront() { + if (0 == size) { + return -1; + } + return elements[front]; + } + + /** Get the last item from the deque. */ + public int getRear() { + if (0 == size) { + return -1; + } + return elements[(rear - 1 + capacity) % capacity]; + } + + /** Checks whether the circular deque is empty or not. */ + public boolean isEmpty() { + return 0 == size; + } + + /** Checks whether the circular deque is full or not. */ + public boolean isFull() { + return size == capacity; + } + + public String toString() { + return Arrays.toString(elements); + } + + public static void main(String[] args) { + LeetCode_641_588 deque = new LeetCode_641_588(8); + deque.insertFront(5); + System.out.println(deque.toString()); + deque.insertLast(4); + System.out.println(deque.toString()); + deque.insertLast(1); + System.out.println(deque.toString()); + deque.insertLast(3); + System.out.println(deque.toString()); + deque.insertFront(9); + System.out.println(deque.toString()); + deque.deleteFront(); + System.out.println(deque.toString()); + deque.deleteLast(); + System.out.println(deque.toString()); + int frontElement = deque.getFront(); + System.out.println(frontElement); + int rearElement = deque.getRear(); + System.out.println(rearElement); + boolean isEmpty = deque.isEmpty(); + System.out.println(isEmpty); + boolean isFull = deque.isFull(); + System.out.println(isFull); + } +} diff --git a/Week 01/id_588/LeetCode_66_588.java b/Week 01/id_588/LeetCode_66_588.java new file mode 100644 index 000000000..7065f680d --- /dev/null +++ b/Week 01/id_588/LeetCode_66_588.java @@ -0,0 +1,29 @@ +import java.util.Arrays; + +/** + * 加一 + * https://leetcode-cn.com/problems/plus-one/ + */ +public class LeetCode_66_588 { + + public int[] plusOne(int[] a) { + int i = a.length - 1; + while (i >= 0 && 9 == a[i]) { + a[i] = 0; + i --; + if(i < 0) { + a = new int[a.length + 1]; + i = 0; + } + } + a[i] = a[i] + 1; + return a; + } + + public static void main(String[] args) { + LeetCode_66_588 solution = new LeetCode_66_588(); + System.out.println(Arrays.toString(solution.plusOne(new int[]{9, 9, 9}))); + System.out.println(Arrays.toString(solution.plusOne(new int[]{1, 9, 9}))); + System.out.println(Arrays.toString(solution.plusOne(new int[]{1, 2, 3}))); + } +} diff --git a/Week 01/id_588/NOTE.md b/Week 01/id_588/NOTE.md index a6321d6e2..b3e581444 100644 --- a/Week 01/id_588/NOTE.md +++ b/Week 01/id_588/NOTE.md @@ -1,4 +1,25 @@ -# NOTE +# 第一周学习总结 + + +在做题过程中发现自己遇到以前做过的一些题目又不会解了,实践下来觉得“五毒神掌刷题法”是一个挺好的学习方法。在大多数题目开始做题的时候,基本上只能想到暴力解题法,以前是觉得能解出题目就行,也不太在意空间和时间复杂度。进行了第一周的学习之后,发现有那么多优秀解题方法,决定还是要多多练习。 +## deque示例代码改写 + +``` + Deque deque = new LinkedList(); + deque.addFirst("a"); + deque.addFirst("b"); + deque.addFirst("c"); + System.out.println(deque); + + String str = deque.getFirst(); + System.out.println(str); + System.out.println(deque); + + while (deque.size() > 0) { + System.out.println(deque.removeFirst()); + } + System.out.println(deque); +``` diff --git a/Week 01/id_593/LeetCode_1_593.java b/Week 01/id_593/LeetCode_1_593.java new file mode 100644 index 000000000..fd55ec413 --- /dev/null +++ b/Week 01/id_593/LeetCode_1_593.java @@ -0,0 +1,61 @@ +/** + * 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 + *

+ * 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 + *

+ * 示例:s + *

+ * 给定 nums = [2, 7, 11, 15], target = 9 + *

+ * 因为 nums[0] + nums[1] = 2 + 7 = 9 + * 所以返回 [0, 1] + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/two-sum + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + */ +public class LeetCode_1_593 { + + /** + * 暴力解法: + * 两层for循环,遍历所有结果 + * + * @param nums 原数组 + * @param target 目标值 + * @return + */ + public int[] violentTwoSum(int[] nums, int target) { + for (int i = 0; i < nums.length; i++) { + for (int j = i + 1; j < nums.length; j++) { + if (nums[i] + nums[j] == target) { + return new int[]{i, j}; + } + } + } + return new int[]{}; + } + + /** + * hash 解决办法 + * + * @param nums 入参 + * @param target 目标值 + * @return 符合目标值的两个下标 + */ + public int[] hashTwoSum(int[] nums, int target) { + int length = nums.length; + Map map = new HashMap<>(length); + for (int i = 0; i < nums.length; i++) { + int complement = target - nums[i]; + if (map.containsKey(complement)) { + return new int[]{map.get(complement), i}; + } + map.put(nums[i], i); + } + return new int[]{}; + } + + +} diff --git a/Week 01/id_593/LeetCode_26_593.java b/Week 01/id_593/LeetCode_26_593.java new file mode 100644 index 000000000..a2341266d --- /dev/null +++ b/Week 01/id_593/LeetCode_26_593.java @@ -0,0 +1,44 @@ +/** + * + * 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 + *

+ * 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 + *

+ * 示例 1: + *

+ * 给定数组 nums = [1,1,2], + *

+ * 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 + *

+ * 你不需要考虑数组中超出新长度后面的元素。 + * 示例 2: + *

+ * 给定 nums = [0,0,1,1,1,2,2,3,3,4], + *

+ * 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 + *

+ * 你不需要考虑数组中超出新长度后面的元素。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * 优先回答: + * https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/solution/26-shan-chu-pai-xu-shu-zu-zhong-de-zhong-fu-xian-6/ + * @author jaryoung + */ +public class LeetCode_26_593 { + + public int removeDuplicates(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int slow = 0; + for (int quick = 0; quick < nums.length; quick++) { + if (nums[quick] != nums[slow]) { + slow++; + nums[slow] = nums[quick]; + } + } + return slow + 1; + } +} \ No newline at end of file diff --git a/Week 01/id_593/LeetCode_283_593.java b/Week 01/id_593/LeetCode_283_593.java new file mode 100644 index 000000000..fc88eaba9 --- /dev/null +++ b/Week 01/id_593/LeetCode_283_593.java @@ -0,0 +1,65 @@ +/** + * 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 + *

+ * 示例: + *

+ * 输入: [0,1,0,3,12] + * 输出: [1,3,12,0,0] + * 说明: + *

+ * 必须在原数组上操作,不能拷贝额外的数组。 + * 尽量减少操作次数。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/move-zeroes + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author wusonghui@bubi.cn + * @since 1.0.0 2019/10/17 22:50 + */ +public class LeetCode_283_593 { + /** + * 解法一:找出非零的数字,跟零的位置的数字不断交换位置。 + * 定义两个指针,定义两个 i j 两个指针,i 遍历使用,j 作为记录零的位置。当然,i == j 的时候,不需要交换位置。 + * [0,1,0,3,12] i = 0,j = 0; i == j,不进行交换,i++ + * [0,1,0,3,12] i = 1,j = 0; [i] != 0, [j] = [i] [i] = 0 ,j++,i++ + * [1,0,0,3,12] i = 2,j = 1; [i] == 0,i++ + * [1,0,0,3,12] i = 3,j = 1; [i] != 0, [j] = [i] [i] = 0 ,j++,i++ + * [1,3,0,0,12] i = 4,j = 2; [i] != 0, [j] = [i] [i] = 0 ,j++,i++ + * [1,3,12,0,0] 遍历完成 + * + * @param nums + */ + public void moveZeroesOne(int[] nums) { + for (int i = 0, j = 0; i < nums.length; i++) { + if (nums[i] != 0) { + if (i != j) { + nums[j] = nums[i]; + nums[i] = 0; + } + j++; + } + } + } + + /** + * 解法二:先找出非零的数字,后面补零 + */ + public void moveZeroesTwo(int[] nums) { + int count = 0; + int length = nums.length; + for (int i = 0; i < length; i++) { + if (nums[i] == 0) { + count++; + } else if (count > 0) { + // 前面几个零 + nums[i - count] = nums[i]; + nums[i] = 0; + } + } + for (int i = length - count; i < length; i++) { + nums[i] = 0; + } + } + +} \ No newline at end of file diff --git a/Week 01/id_593/LeetCode_42_593.java b/Week 01/id_593/LeetCode_42_593.java new file mode 100644 index 000000000..1967d97fe --- /dev/null +++ b/Week 01/id_593/LeetCode_42_593.java @@ -0,0 +1,98 @@ +import java.util.Stack; + +/** + * 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 + *

+ * https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2018/10/22/rainwatertrap.png + * 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。  + *

+ * 示例: + *

+ * 输入: [0,1,0,2,1,0,1,3,2,1,2,1] + * 输出: 6 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/trapping-rain-water + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + *

+ * 优秀回答: + * https://leetcode-cn.com/problems/trapping-rain-water/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-8/ + */ +public class LeetCode_42_593 { + + /** + * 遍历每一行; + * 开始计数前,左边必须有墙,height[j] >= i,累计总水滴, 清空temp + * 计数时,必须当前有坑,就是:height[j] < i + * + * @param height 墙的高度 + * @return 总水滴 + */ + public int rowTrap(int[] height) { + if (height.length <= 1) { + return 0; + } + int max = getMax(height); + int sum = 0; + for (int i = 1; i <= max; i++) { + int temp = 0; + boolean start = false; + for (int j = 0; j < height.length; j++) { + if (start && height[j] < i) { + temp++; + } + if (height[j] >= i) { + start = true; + sum = sum + temp; + temp = 0; + } + } + } + return sum; + } + + private int getMax(int[] height) { + int max = 0; + for (int i = 0; i < height.length; i++) { + if (max < height[i]) { + max = height[i]; + } + } + return max; + } + + + /** + * 1. 如果栈不空并且当前指向的高度大于栈顶高度,代表栈内元素和当前的墙可能存在雨水; + * 2. 如果栈为空,将当前指针入栈,当前指针向后移动 + * + * @param height + * @return + */ + public int stackTrap(int[] height) { + if (height.length <= 1) { + return 0; + } + Stack stack = new Stack<>(); + int sum = 0; + int current = 0; + while (current < height.length) { + while (!stack.isEmpty() && height[stack.peek()] < height[current]) { + int h = height[stack.peek()]; + stack.pop(); + if (stack.isEmpty()) { + break; + } + int distance = current - stack.peek() - 1; + int min = Math.min(height[stack.pop()], height[current]); + sum = sum + distance * (min - h); + } + stack.push(current); + current++; + } + return sum; + } + +} \ No newline at end of file diff --git a/Week 01/id_593/NOTE.md b/Week 01/id_593/NOTE.md index a6321d6e2..0452b4c04 100644 --- a/Week 01/id_593/NOTE.md +++ b/Week 01/id_593/NOTE.md @@ -1,4 +1,232 @@ -# NOTE +# NOTE +# 学习总结 + +## 学习感想 + +直接上个脑图吧: + +![KMIWwQ.png](https://s2.ax1x.com/2019/10/20/KMIWwQ.png) + + + + + +## 改写代码 + + ```java +/** + * @author jaryoung + */ +public class DequeDemo { + public static void main(String[] args) { + Deque deque = new LinkedList<>(); + // This push method is equivalent to {@link #addFirst}. + deque.addFirst("a"); + deque.addFirst("b"); + deque.addFirst("c"); + System.out.println(deque); + // This peek method is equivalent to {@link #peekFirst()}. + String str = deque.peekFirst(); + System.out.println(str); + System.out.println(deque); + while (deque.size() > 0) { + // This pop method is equivalent to {@link #removeFirst()}. + System.out.println(deque.removeFirst()); + } + System.out.println(deque); + } +} + ``` + + + +## 分析源码 + +### Queue + +> 定义一个队列的操作的接口,也就是定义一组规范。 + +```java +public interface Queue extends Collection { + + // 插入操作,插入成功返回true,插入失败就抛出异常 + boolean add(E e); + + // 插入操作,插入成功返回true,插入失败返回false + boolean offer(E e); + + // 检索并移除,删除成功返回true,删除失败抛出异常 + E remove(); + + // 检索并移除,删除成功返回被删除的元素,如果队列为空返回null + E poll(); + + // 返回队头元素,存在则返回,不存在则抛出异常 + E element(); + // 返回队头元素,存在则返回,如果队列为空返回null + E peek(); +} +``` + + + +### Priority Queue + +>

The head of this queue is the least element +> with respect to the specified ordering. If multiple elements are +> tied for least value, the head is one of those elements -- ties are +> broken arbitrarily. The queue retrieval operations {@code poll}, +> {@code remove}, {@code peek}, and {@code element} access the +> element at the head of the queue. + +大体意思,就是优先级越低越排前面,底层数据结构是一个小顶堆。 + +先来一张类图: + +[![KnB0TH.md.png](https://s2.ax1x.com/2019/10/19/KnB0TH.md.png)](https://imgchr.com/i/KnB0TH) + +从上面,我们可以看出PriorityQueue的继承关系。 + +再看看抽象的AbstractQueue如何根据Queue接口,定义了规范的使用,子类只要继承抽象的父类就能很方便去实现队列 **规范**。 + +```java + public boolean add(E e) { + if (offer(e))// 由子类自定义实现 + return true; + else + throw new IllegalStateException("Queue full"); + } + + public E remove() { + E x = poll(); // 由子类自定义实现 + if (x != null) + return x; + else + throw new NoSuchElementException(); + } + + public E element() { + E x = peek(); // 由子类自定义实现 + if (x != null) + return x; + else + throw new NoSuchElementException(); + } + + public void clear() { + while (poll() != null) + ; + } + + public boolean addAll(Collection c) { + if (c == null) + throw new NullPointerException(); + if (c == this) + throw new IllegalArgumentException(); + boolean modified = false; + for (E e : c) + if (add(e)) + modified = true; + return modified; + } +``` + +### + +### 分析 offer 方法 + +```java + public boolean offer(E e) { + int i = size; + ... + if (i == 0)// 如果队列为0,直接放到队列头部 + queue[0] = e; + else + siftUp(i, e);// 根据规则规则防止合适的位置 + return true; + } + + private void siftUp(int size, E e) { + if (comparator != null) // 如果有自定义比较器,使用自定义比较器,否则使用默认 + siftUpUsingComparator(size, e); + else + siftUpComparable(size, e); + } + + private void siftUpUsingComparator(int k, E x) {// k 位之前size的值,也是最后的位置,过来看看 + while (k > 0) { + int parent = (k - 1) >>> 1; // 不断无符号右移1位 + Object e = queue[parent]; + if (comparator.compare(x, (E) e) >= 0)// 通过自定义比较器比较,>=0,表示x大于或者等于e,优先级越低,元素越靠前 + break; + queue[k] = e; + k = parent; // 当前位置设置为父亲的位置,不断往上走 + } + queue[k] = x;// 优先级非常低,上面的while循环会将k右移到0,当为0的时候,说明这个人优先级最低,能坐第一的位置。 + } +``` + +### 分析poll方法 + +```java + + public E poll() { + int s = --size; + ... + E result = (E) queue[0]; // 从队列头部取元素 + E x = (E) queue[s]; + queue[s] = null; + if (s != 0) + siftDown(0, x); + return result; + } + + private void siftDown(int k, E x) { + if (comparator != null) + siftDownUsingComparator(k, x); + else + siftDownComparable(k, x); + } + + // 根据规则重新排序 + private void siftDownUsingComparator(int k, E x) { + int half = size >>> 1; + while (k < half) { + int child = (k << 1) + 1; + Object c = queue[child]; + int right = child + 1; + if (right < size && + comparator.compare((E) c, (E) queue[right]) > 0) + c = queue[child = right]; + if (comparator.compare(x, (E) c) <= 0)// 优先级越低,越是往前排 + break; + queue[k] = c; + k = child; + } + queue[k] = x; + } +``` + +### debug 是个厉害角色 + +```java +/** + * @author jaryoung + */ +public class PriorityQueueDemo { + public static void main(String[] args) { + PriorityQueue priorityQueue = new PriorityQueue<>(4, + Comparator.comparing(item -> !item.equalsIgnoreCase("苹果"))); + priorityQueue.offer("华为P30"); + priorityQueue.offer("小米3"); + priorityQueue.offer("苹果"); + priorityQueue.offer("小米8"); + priorityQueue.offer("vivo"); + System.out.println("我真的不要 " + priorityQueue.poll()); + } +} +// 我真的不要 苹果 +``` diff --git a/Week 01/id_598/LeetCode_1_598.java b/Week 01/id_598/LeetCode_1_598.java new file mode 100644 index 000000000..909c46b88 --- /dev/null +++ b/Week 01/id_598/LeetCode_1_598.java @@ -0,0 +1,56 @@ +import java.util.HashMap; +import java.util.Map; + +/** + * LeetCode第一题 + * + * @author northleaf + * @create 2019年10月17日 + */ +public class LeetCode_1_598 { + + /** + * 暴力解法 + * 双重循环暴力求解 + * 时间复杂度:O(n²) + * @param nums 给定的数组 + * @param target 要计算的目标值 + * @return + */ + public int[] twoSum1(int[] nums, int target) { + for(int i = 0 ;i map = new HashMap(); + for(int i = 0 ;i< nums.length ;i++){ + map.put(nums[i],i); + } + //再次扫描数组 + for(int i = 0;i deque = new LinkedList(); + deque.addFirst("a"); + deque.addFirst("b"); + deque.addFirst("c"); + System.out.println(deque); + + String str = deque.peekFirst(); + System.out.println(str); + System.out.println(deque); + while (deque.size()>0){ + System.out.println(deque.removeFirst()); + } + System.out.println(deque); + } +} diff --git a/Week 01/id_608/NOTE.md b/Week 01/id_608/NOTE.md index a6321d6e2..59976d6c5 100644 --- a/Week 01/id_608/NOTE.md +++ b/Week 01/id_608/NOTE.md @@ -1,4 +1,36 @@ -# NOTE +# 学习总结 +### 对算法和数据结构的认识 +#### 数据结构 +虽然我们语言中用到的数据机构已经被封装好了, +全是开箱即用的,但是如果我们了解了每一个方法的底层用的数据结构, +并且要把他们的时间和空间复杂度训练成我们的一种条件反射, +就可以对自己写的程序更加的了解,也能更好的使我们去选择合适的方法, +从而使用程序的性能发挥到最好。 - +我最近1~2年内体会最深的就是在我们使用的Mysql、Redis、ES等软件,虽然我们不清楚底层的具体实现, +但是我们知道底层用的什么数据结构,什么算法,就可以更好的对SQL等进行更好优化,而不是单纯的只是加索引等。 +例如我在面试中经常问的一个SQL就是: +```sql +select * from user where age>50 order by id desc(age是普通索引) +``` + +问用id排序会不会用到索引,结果就连工作十几年的,都回答会用到索引,因为id是主键,假如比较熟练的掌握索引是怎么存储的, +那么这个问题就迎刃而解了。说这个问题的目的就是说明数据结构与我们的日常开发息息相关,并不是冷冰冰的理论或者用来装X的资本。 + +所以我们一定要充分意识到数据结构和算法的重要性,接下来的7周以及在我们程序员职业生涯中都要绷紧这根弦,最终把它变成我们的本能性的东西。 + +#### 算法 +在这一周里,虽然工作很忙,经常加班,但是也抽时间去认真体会覃超大神的课程并且抽时间去亲自实现算法、刷题, +通过实际去刷题,才意识到以前对算法的认知只是了解而已。 + +刷题的过程中体会到了更多写代码的巧妙之处,特别要将覃超大神反反复复强调的:空间换时间、降维的原则应用到刷题以及我们日常的开发当中。 +长时间写代码养成的习惯就是拿来即用,不深入思考。例如:覃超大神带大家读Java的源码,才了解我们日常用的现成的方法的时间和空间复杂度。 +虽然我不是搞Java的,但是思想是相通的。我们要对自己用到的每一个方法都了如指掌,在能对自己写出的代码洞若观火。 + +还有就是通过刷题,来培养我们写代码的思维,更加严谨,更多的去考虑程序的性能,而非实现即可。 + +#### 总结 +以上只是我对数据结构和算法的个人认识,在职业生涯中,反复的去强化这部分知识。最近一直在加班, +没有对某个知识点进行深入的研究,所以没能产出一篇技术文章,只是对数据结构和算法总结了一下自己到的认识。 +在之后的日子里争取更深入的思考并产出技术文章和大家分享,一起进步。 \ No newline at end of file diff --git a/Week 01/id_608/merge_two_sorted_lists.go b/Week 01/id_608/merge_two_sorted_lists.go new file mode 100644 index 000000000..576e3342a --- /dev/null +++ b/Week 01/id_608/merge_two_sorted_lists.go @@ -0,0 +1,38 @@ +package main + +//将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 +// +// 示例: +// +// 输入:1->2->4, 1->3->4 +//输出:1->1->2->3->4->4 +// +// Related Topics 链表 + +//leetcode submit region begin(Prohibit modification and deletion) +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode { + head := &ListNode{} + newList := head + + for l1 != nil || l2 != nil { + if l1 == nil || (l2 != nil && l1.Val > l2.Val) { + newList.Next = l2 + newList = l2 + l2 = l2.Next + } else { + newList.Next = l1 + newList = l1 + l1 = l1.Next + } + } + return head.Next +} + +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 01/id_608/move_zeroes.go b/Week 01/id_608/move_zeroes.go new file mode 100644 index 000000000..4f3f3b26c --- /dev/null +++ b/Week 01/id_608/move_zeroes.go @@ -0,0 +1,34 @@ +package main + +//给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 +// +// 示例: +// +// 输入: [0,1,0,3,12] +//输出: [1,3,12,0,0] +// +// 说明: +// +// +// 必须在原数组上操作,不能拷贝额外的数组。 +// 尽量减少操作次数。 +// +// Related Topics 数组 双指针 + +//leetcode submit region begin(Prohibit modification and deletion) +func moveZeroes(nums []int) []int { + count := len(nums) + j := 0 + for i := 0; i < count; i++ { + if nums[i] != 0 { + nums[j] = nums[i] + if i != j { + nums[i] = 0 + } + j++ + } + } + return nums +} + +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 01/id_608/two_sum.go b/Week 01/id_608/two_sum.go new file mode 100644 index 000000000..6fcf7655f --- /dev/null +++ b/Week 01/id_608/two_sum.go @@ -0,0 +1,33 @@ +package main + +//给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 +// +// 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 +// +// 示例: +// +// 给定 nums = [2, 7, 11, 15], target = 9 +// +//因为 nums[0] + nums[1] = 2 + 7 = 9 +//所以返回 [0, 1] +// +// Related Topics 数组 哈希表 + +//leetcode submit region begin(Prohibit modification and deletion) +func twoSum(nums []int, target int) []int { + lenth := len(nums) + hash := make(map[int]int) + var result []int + for i := 0; i < lenth; i++ { + sub := target - nums[i] + if _, ok := hash[nums[i]]; ok { + result = append(result, hash[nums[i]], i) + break + } else { + hash[sub] = i + } + } + return result +} + +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 01/id_613/NOTE.md b/Week 01/id_613/NOTE.md index a6321d6e2..40d24b2e5 100644 --- a/Week 01/id_613/NOTE.md +++ b/Week 01/id_613/NOTE.md @@ -1,4 +1,39 @@ # NOTE +# 学习总结 + +# 掌握任何技能的普遍方法 +1、拆碎知识点 chunk it up +2、刻意练习 Deliberate exercise +3、反馈 feedback + +# 刻意练习的方法:刷题5步法(五毒神掌) +第一遍: + - 5 - 10分钟:读题 + 思考;没思路直接看解法,有思路先做和写 + - 直接看解法:注意!多解法,比较解法优劣 + - 背诵、默写好的解法 + +第二遍: + - 马上自己写 --> LeetCode提交 + - 多种解法比较、体会 --> 优化! + +第三遍: + - 过了一天后,再重复做题 + - 不同解法的熟练程度 --> 对不熟的题,做专项练习 + +第四遍: + - 过了一周后:反复回来练习相同题目 + +第五遍: + - 面试前一周恢复性训练 + +# 反馈的方法 +1、主动反馈:看高手代码(github, leetcode) +2、被动反馈:高手给你指点(code review) + +# 解题的思路 +1、升维 +2、空间换时间 + diff --git a/Week 01/id_613/java/src/main/Leetcode26SolutionOne.java b/Week 01/id_613/java/src/main/Leetcode26SolutionOne.java new file mode 100644 index 000000000..df745cf08 --- /dev/null +++ b/Week 01/id_613/java/src/main/Leetcode26SolutionOne.java @@ -0,0 +1,29 @@ +/** + * 发现重复元素,把后面的元素向前挪动,覆盖重复元素 + * 平均时间复杂度O(n^2),空间复杂度O(1) + * + * 未看题解 + * + * 执行用时 : * 15 ms * , 在所有 java 提交中击败了 * 11.84% * 的用户 * + * 内存消耗 : * 40.5 MB * , 在所有 java 提交中击败了 * 94.42% * 的用户 * + */ +class Leetcode26SolutionOne { + public int removeDuplicates(int[] nums) { + + if (nums.length <= 1) { + return nums.length; + } + + int len = nums.length; + for (int i = 0; i < len - 1; ) { + if (nums[i + 1] == nums[i]) { + System.arraycopy(nums, i + 1, nums, i, len - i - 1); + len--; + } else { + i++; + } + } + + return len; + } +} \ No newline at end of file diff --git a/Week 01/id_613/java/src/main/Leetcode26SolutionTwo.java b/Week 01/id_613/java/src/main/Leetcode26SolutionTwo.java new file mode 100644 index 000000000..4ca38cc26 --- /dev/null +++ b/Week 01/id_613/java/src/main/Leetcode26SolutionTwo.java @@ -0,0 +1,36 @@ +/** + * 左右指针,左指针指向新数组 + * 平均时间复杂度为O(n),空间复杂度O(1) + * + * 未看题解 + * + * 执行用时 : * 1 ms * , 在所有 java 提交中击败了 * 100.00% * 的用户 + * 内存消耗 : * 38.2 MB * , 在所有 java 提交中击败了 * 98.87% * 的用户 + */ +class Leetcode26SolutionTwo { + public int removeDuplicates(int[] nums) { + int leftIdx = 0; + int rightIdx = 0; + for (; rightIdx < nums.length; ) { + if (nums[leftIdx] == nums[rightIdx]) { + rightIdx++; + } else { + nums[leftIdx + 1] = (leftIdx + 1 == rightIdx ? nums[leftIdx + 1] : nums[rightIdx]); + rightIdx++; + leftIdx++; + } + } + + return leftIdx + 1; + } + + public static void main(String[] args) { + int[] nums = new int[]{0, 0, 1, 1, 1, 2, 2, 3, 3, 4}; + Leetcode26SolutionTwo sl = new Leetcode26SolutionTwo(); + int len = sl.removeDuplicates(nums); + for (int i = 0; i < len; i++) { + System.out.print(nums[i]); + } + System.out.println(); + } +} diff --git a/Week 01/id_613/java/src/main/Leetcode42SolutionOne.java b/Week 01/id_613/java/src/main/Leetcode42SolutionOne.java new file mode 100644 index 000000000..ec966c231 --- /dev/null +++ b/Week 01/id_613/java/src/main/Leetcode42SolutionOne.java @@ -0,0 +1,34 @@ +/** + * 思路是计算每个柱子位置上盛雨水的量,然后所有柱子加起来就是总量; + * 暴力法:从每根柱子出发,向左和向右遍历,分别找到左右两边的最高柱子,两者的较 + * 小者即为雨水能达到的最大高度,再减去柱子本身的高度,即得到,这根柱子位置上 + * 盛雨水的量 + * + * 时间复杂度O(n^2),空间复杂度O(1) + * + * + * 先看题解 + * + * 执行用时 : * 118 ms * , 在所有 java 提交中击败了 * 8.06% * 的用户 * + * 内存消耗 : * 38 MB * , 在所有 java 提交中击败了 * 80.21% * 的用户 + */ +public class Leetcode42SolutionOne { + public int trap(int[] height) { + int area = 0; + + for (int i = 1; i < height.length; ++i) { + int leftMaxHeight = 0; + int rightMaxHeight = 0; + for (int j = i; j >= 0; j--) { + leftMaxHeight = Math.max(height[j], leftMaxHeight); + } + for (int j = i; j < height.length; j++) { + rightMaxHeight = Math.max(height[j], rightMaxHeight); + } + + area += Math.min(leftMaxHeight, rightMaxHeight) - height[i]; + } + + return area; + } +} diff --git a/Week 01/id_613/java/src/main/Leetcode42SolutionTwo.java b/Week 01/id_613/java/src/main/Leetcode42SolutionTwo.java new file mode 100644 index 000000000..41685d1ec --- /dev/null +++ b/Week 01/id_613/java/src/main/Leetcode42SolutionTwo.java @@ -0,0 +1,40 @@ +/** + * 暴力法加速,思路是减少暴力法中找leftMax和rightMax中不必要的重复遍历,把中间结果记录下来 + * 用两次遍历得到每个柱子的leftMaxHeight和rightMaxHeight + * 然后用一次遍历得到结果 + * + * 先看题解 + * + * 执行用时 : * 1 ms * , 在所有 java 提交中击败了 * 100.00% * 的用户 + * 内存消耗 : * 37.5 MB * , 在所有 java 提交中击败了 * 83.52% * 的用户 + */ +public class Leetcode42SolutionTwo { + public int trap(int[] height) { + int area = 0; + + if (height.length == 0) { + return area; + } + + int[] leftMax = new int[height.length]; + int[] rightMax = new int[height.length]; + + // 填充leftMax + leftMax[0] = height[0]; + for (int i = 1; i < leftMax.length; i++) { + leftMax[i] = Math.max(height[i], leftMax[i - 1]); + } + + // 填充rightMax + rightMax[rightMax.length - 1] = height[height.length - 1]; + for (int i = rightMax.length - 2; i >= 0; i--) { + rightMax[i] = Math.max(height[i], rightMax[i + 1]); + } + + for (int i = 1; i < height.length - 1; i++) { + area += Math.min(leftMax[i], rightMax[i]) - height[i]; + } + + return area; + } +} diff --git a/Week 01/id_613/java/src/main/Leetcode66SolutionOne.java b/Week 01/id_613/java/src/main/Leetcode66SolutionOne.java new file mode 100644 index 000000000..6a60200ca --- /dev/null +++ b/Week 01/id_613/java/src/main/Leetcode66SolutionOne.java @@ -0,0 +1,66 @@ +/** + * 从数组最低位逐项拷贝,有进位的话设置标记,并在遍历过程中传递标记 + * 时间复杂度为O(n),空间复杂度O(1) + * + * 未看题解 + * + * 执行用时 : * 0 ms * , 在所有 java 提交中击败了 * 100.00% * 的用户 + * 内存消耗 : * 35.1 MB * , 在所有 java 提交中击败了 * 38.13% * 的用户 + */ +public class Leetcode66SolutionOne { + public int[] plusOne(int[] digits) { + boolean allNine = true; + + // 确定数组长度 + for (int i = 0; i < digits.length; ++i) { + if (digits[i] != 9) { + allNine = false; + } + } + + int[] result; + if (allNine) { + result = new int[digits.length + 1]; + } else { + result = new int[digits.length]; + } + + int resultIdx = result.length - 1; + int digitIdx = digits.length - 1; + boolean flag = false; + + // 最低位加1,有进位 + result[resultIdx] = digits[digitIdx]; + result[resultIdx]++; + if (result[resultIdx] > 9) { + result[resultIdx] = 0; + flag = true; + } + digitIdx--; + resultIdx--; + + // 从最低位逐个拷贝 + while (digitIdx >= 0) { + result[resultIdx] = digits[digitIdx]; + if (flag) { + if (result[resultIdx] + 1 > 9) { + result[resultIdx] = 0; + flag = true; + } else { + result[resultIdx]++; + flag = false; + } + } + + digitIdx--; + resultIdx--; + } + + // 最高位 + if (resultIdx >= 0) { + result[resultIdx] = 1; + } + + return result; + } +} diff --git a/Week 01/id_613/java/src/test/Leetcode26Test.java b/Week 01/id_613/java/src/test/Leetcode26Test.java new file mode 100644 index 000000000..ee8159b7c --- /dev/null +++ b/Week 01/id_613/java/src/test/Leetcode26Test.java @@ -0,0 +1,52 @@ +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +/** + * + */ +public class Leetcode26Test { + @Test + public void testSolutionOne1() { + int nums[] = new int[]{1, 1, 2}; + Leetcode26SolutionOne solution1 = new Leetcode26SolutionOne(); + int len = solution1.removeDuplicates(nums); + + int nums2[] = new int[len]; + System.arraycopy(nums, 0, nums2, 0, len); + assertArrayEquals(new int[]{1, 2}, nums2); + } + + @Test + public void testSolutionOne2() { + int nums[] = new int[]{0, 0, 1, 1, 1, 2, 2, 3, 3, 4}; + Leetcode26SolutionOne solution1 = new Leetcode26SolutionOne(); + int len = solution1.removeDuplicates(nums); + + int nums2[] = new int[len]; + System.arraycopy(nums, 0, nums2, 0, len); + assertArrayEquals(new int[]{0, 1, 2, 3, 4}, nums2); + } + + @Test + public void testSolutionTwo1() { + int nums[] = new int[]{1, 1, 2}; + Leetcode26SolutionTwo solution2 = new Leetcode26SolutionTwo(); + int len = solution2.removeDuplicates(nums); + + int nums2[] = new int[len]; + System.arraycopy(nums, 0, nums2, 0, len); + assertArrayEquals(new int[]{1, 2}, nums2); + } + + @Test + public void testSolutionTwo2() { + int nums[] = new int[]{0, 0, 1, 1, 1, 2, 2, 3, 3, 4}; + Leetcode26SolutionTwo solution2 = new Leetcode26SolutionTwo(); + int len = solution2.removeDuplicates(nums); + + int nums2[] = new int[len]; + System.arraycopy(nums, 0, nums2, 0, len); + assertArrayEquals(new int[]{0, 1, 2, 3, 4}, nums2); + } +} diff --git a/Week 01/id_613/java/src/test/Leetcode42Test.java b/Week 01/id_613/java/src/test/Leetcode42Test.java new file mode 100644 index 000000000..7c95a812a --- /dev/null +++ b/Week 01/id_613/java/src/test/Leetcode42Test.java @@ -0,0 +1,26 @@ +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * + */ +public class Leetcode42Test { + @Test + // [0,1,0,2,1,0,1,3,2,1,2,1] + public void testSolutionOne1() { + int height[] = new int[]{0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1}; + Leetcode42SolutionOne solution = new Leetcode42SolutionOne(); + int result = solution.trap(height); + assertEquals(6, result); + } + + @Test + // [0,1,0,2,1,0,1,3,2,1,2,1] + public void testSolutionTwo1() { + int height[] = new int[]{0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1}; + Leetcode42SolutionTwo solution = new Leetcode42SolutionTwo(); + int result = solution.trap(height); + assertEquals(6, result); + } +} diff --git a/Week 01/id_613/java/src/test/Leetcode66Test.java b/Week 01/id_613/java/src/test/Leetcode66Test.java new file mode 100644 index 000000000..265ff926c --- /dev/null +++ b/Week 01/id_613/java/src/test/Leetcode66Test.java @@ -0,0 +1,60 @@ +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +/** + * + */ +public class Leetcode66Test { + @Test + // [1,1,2] + public void testSolution1() { + int nums[] = new int[]{1, 1, 2}; + Leetcode66SolutionOne solution = new Leetcode66SolutionOne(); + int[] result = solution.plusOne(nums); + + assertArrayEquals(new int[]{1, 1, 3}, result); + } + + @Test + // [0] + public void testSolution2() { + int nums[] = new int[]{0}; + Leetcode66SolutionOne solution = new Leetcode66SolutionOne(); + int[] result = solution.plusOne(nums); + + assertArrayEquals(new int[]{1}, result); + } + + @Test + // [9,9,9] + public void testSolution3() { + int nums[] = new int[]{9, 9, 9}; + Leetcode66SolutionOne solution = new Leetcode66SolutionOne(); + int[] result = solution.plusOne(nums); + + assertArrayEquals(new int[]{1, 0, 0, 0}, result); + } + + @Test + // [9,8,7,6,5,4,3,2,1,0] + public void testSolution4() { + int nums[] = new int[]{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; + Leetcode66SolutionOne solution = new Leetcode66SolutionOne(); + int[] result = solution.plusOne(nums); + + assertArrayEquals(new int[]{9, 8, 7, 6, 5, 4, 3, 2, 1, 1}, result); + } + + @Test + // [7,2,8,5,0,9,1,2,9,5,3,6,6,7,3,2,8,4,3,7,9,5,7,7,4,7,4,9,4,7,0,1,1,1,7,4,0,0,6] + public void testSolution5() { + int nums[] = new int[]{7, 2, 8, 5, 0, 9, 1, 2, 9, 5, 3, 6, 6, 7, 3, 2, 8, + 4, 3, 7, 9, 5, 7, 7, 4, 7, 4, 9, 4, 7, 0, 1, 1, 1, 7, 4, 0, 0, 6}; + Leetcode66SolutionOne solution = new Leetcode66SolutionOne(); + int[] result = solution.plusOne(nums); + + assertArrayEquals(new int[]{7, 2, 8, 5, 0, 9, 1, 2, 9, 5, 3, 6, 6, 7, 3, 2, 8, + 4, 3, 7, 9, 5, 7, 7, 4, 7, 4, 9, 4, 7, 0, 1, 1, 1, 7, 4, 0, 0, 7}, result); + } +} diff --git a/Week 01/id_618/LeetCode_189_618.java b/Week 01/id_618/LeetCode_189_618.java new file mode 100644 index 000000000..966810573 --- /dev/null +++ b/Week 01/id_618/LeetCode_189_618.java @@ -0,0 +1,45 @@ + class Solution { + public void rotate(int[] nums, int k) { + int len = nums.length; + if (k >= len) { + k = k % len; + } + + if (k == 0) { + return; + } + + // 移位计数 + int count = 0; + // 每一轮起始 + int start = 0; + + // 当前位置 + int current = start; + // 当前位置的值 + int currentVal = nums[current]; + + do { + // 计算移位目标 + int target = (current + k) % len; + + // 将currentVal放入目标位置,目标原值存入temp + int temp = nums[target]; + nums[target] = currentVal; + + // 移位计数+1 + count++; + + current = target; + currentVal = temp; + + // 如果回到起点,则start+1,开始下一轮 + if (current == start) { + start++; + current = start; + currentVal = nums[current]; + } + + } while (count < len);// 所有元素都移动后终止 + } + } \ No newline at end of file diff --git a/Week 01/id_618/LeetCode_1_618.java b/Week 01/id_618/LeetCode_1_618.java new file mode 100644 index 000000000..e853df405 --- /dev/null +++ b/Week 01/id_618/LeetCode_1_618.java @@ -0,0 +1,21 @@ + class Solution { + public int[] twoSum(int[] nums, int target) { + int len = nums.length; + Map map = new HashMap<>(len, 1); + + for (int i = 0; i < nums.length; i++) { + int num = nums[i]; + int goal = target - num; + + Integer idx = map.get(goal); + if (idx != null) { + return new int[] { idx, i }; + } + + map.put(num, i); + + } + + return null; + } + } \ No newline at end of file diff --git a/Week 01/id_618/LeetCode_21_618.java b/Week 01/id_618/LeetCode_21_618.java new file mode 100644 index 000000000..81f648248 --- /dev/null +++ b/Week 01/id_618/LeetCode_21_618.java @@ -0,0 +1,50 @@ + class Solution { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + if (l1 == null) { + return l2; + } + + if (l2 == null) { + return l1; + } + + // 头节点,最后的返回值 + ListNode head = null; + // 标记当前节点 + ListNode current = null; + // 与当前节点比较的目标节点 + ListNode target = null; + + // 比如l1和l2的头,初始化head current和target + if (l1.val < l2.val) { + head = l1; + target = l2; + } else { + head = l2; + target = l1; + } + + current = head; + + while (target != null) { + // 退出判断:已经是最后一个,追加target后退出循环 + if (current.next == null) { + current.next = target; + break; + } + + if (current.next.val > target.val) { + // current.next和target引用交换,继续比较 + ListNode temp = target; + target = current.next; + current.next = temp; + } else { + //移到后一个节点,继续比较 + current = current.next; + } + } + + return head; + + } + } \ No newline at end of file diff --git a/Week 01/id_618/LeetCode_26_618.java b/Week 01/id_618/LeetCode_26_618.java new file mode 100644 index 000000000..687e7f575 --- /dev/null +++ b/Week 01/id_618/LeetCode_26_618.java @@ -0,0 +1,24 @@ + class Solution { + public int removeDuplicates(int[] nums) { + // 处理特殊情况 + if (nums.length <= 1) { + return nums.length; + } + + // 表示每当出现不同数字的计数,也就是最后的返回值 + int count = 1; + + for (int i = 1; i < nums.length; i++) { + int previous = nums[i - 1]; + int current = nums[i]; + + // 如果不一样,不同数字计数+1,并将当前值存放到其应该存在的位置,即count-1的值,例如第2个不同值,其位置就是1 + if (previous != current) { + count++; + nums[count - 1] = current; + } + } + + return count; + } + } \ No newline at end of file diff --git a/Week 01/id_618/LeetCode_283_618 .java b/Week 01/id_618/LeetCode_283_618 .java new file mode 100644 index 000000000..5685fb99d --- /dev/null +++ b/Week 01/id_618/LeetCode_283_618 .java @@ -0,0 +1,23 @@ + class Solution { + public void moveZeroes(int[] nums) { + // 当前0的位置 + int zeroPos = -1; + + for (int i = 0; i < nums.length; i++) { + int num = nums[i]; + + if (num == 0) { + // 记录索引位置 + zeroPos = zeroPos == -1 ? i : zeroPos; + } else if (num != 0 && zeroPos > -1) { + // 非0数字放到当前第一个0的位置 + nums[zeroPos] = num; + nums[i] = 0; + + // 重置0的位置,如果原先zeroPos后面不是0,那么现在一定为0,如果是0,那么正好后移一位 + zeroPos++; + } + } + + } + } \ No newline at end of file diff --git a/Week 01/id_618/LeetCode_42_618.java b/Week 01/id_618/LeetCode_42_618.java new file mode 100644 index 000000000..453cf2803 --- /dev/null +++ b/Week 01/id_618/LeetCode_42_618.java @@ -0,0 +1,55 @@ + class Solution { + public int trap(int[] height) { + int area = 0; + Stack stack = new Stack<>(); + int current = 0; + + while (current < height.length) { + int currentHeight = height[current]; + + while (!stack.isEmpty()) { + int h = height[stack.peek()]; + + // 比之前高度小或者相等,跳出循环 + if (currentHeight <= h) { + break; + } + + // 比之前的高度大,开始计算面积, + stack.pop(); + + // 已空,没有左边界,跳出循环 + if (stack.isEmpty()) { + break; + } + + // 左右两边的位置和高度 + int left = stack.peek(); + int leftHeight = height[left]; + + int right = current; + int rightHeight = currentHeight; + + int bottomHeight = h; + + // 没有高度差,继续出栈,因为栈里的高度是由低到高出栈的,相邻是平的,因此bottomHeight所在的位置不是低洼,不会有积水 + if (leftHeight == bottomHeight) { + continue; + } + + int distance = right - left - 1; + int heightDiff = Math.min(leftHeight, rightHeight) - bottomHeight; + + area += distance * heightDiff; + + } + + // 将当前高度加入栈 + stack.push(current++); + + } + + return area; + + } + } \ No newline at end of file diff --git a/Week 01/id_618/LeetCode_641_618.java b/Week 01/id_618/LeetCode_641_618.java new file mode 100644 index 000000000..eafab4552 --- /dev/null +++ b/Week 01/id_618/LeetCode_641_618.java @@ -0,0 +1,124 @@ + class MyCircularDeque { + + private int[] data; + + private int capacity; + private int size = 0; + private int head = -1; + private int tail = -1; + + public MyCircularDeque(int capacity) { + this.capacity = capacity; + this.data = new int[capacity]; + } + + public boolean deleteFront() { + if (this.isEmpty()) { + return false; + } + + this.data[head] = 0; + size--; + head++; + + if (head == capacity) { + head = 0; + } + + if (this.isEmpty()) { + head = -1; + tail = -1; + } + + return true; + } + + public boolean deleteLast() { + if (this.isEmpty()) { + return false; + } + + this.data[tail] = 0; + size--; + tail--; + + if (tail < 0) { + tail = capacity - 1; + } + + if (this.isEmpty()) { + head = -1; + tail = -1; + } + + return true; + } + + public int getFront() { + if (head != -1) { + return this.data[head]; + } + + return -1; + } + + public int getRear() { + if (tail != -1) { + return this.data[tail]; + } + + return -1; + } + + public boolean insertFront(int value) { + if (this.isFull()) { + return false; + } + + if (head == -1) { + head = 0; + tail = head; + } else { + head--; + + if (head < 0) { + head = capacity - 1; + } + } + + this.data[head] = value; + size++; + + return true; + } + + public boolean insertLast(int value) { + if (this.isFull()) { + return false; + } + + if (tail == -1) { + tail = 0; + head = tail; + } else { + tail++; + + if (tail == capacity) { + tail = 0; + } + } + + this.data[tail] = value; + size++; + + return true; + } + + public boolean isEmpty() { + return size == 0; + } + + public boolean isFull() { + return size == this.capacity; + } + } diff --git a/Week 01/id_618/LeetCode_66_618.java b/Week 01/id_618/LeetCode_66_618.java new file mode 100644 index 000000000..76d9cc9f3 --- /dev/null +++ b/Week 01/id_618/LeetCode_66_618.java @@ -0,0 +1,26 @@ + class Solution { + public int[] plusOne(int[] digits) { + int i = digits.length - 1; + + while (true) { + int digit = digits[i]; + digit = (digit + 1) % 10; + digits[i] = digit; + + if (digit != 0) { + return digits; + } + + i--; + + if (i < 0) { + break; + } + } + + int[] ret = new int[digits.length + 1]; + ret[0] = 1; + + return ret; + } + } \ No newline at end of file diff --git a/Week 01/id_618/LeetCode_88_618.java b/Week 01/id_618/LeetCode_88_618.java new file mode 100644 index 000000000..733802947 --- /dev/null +++ b/Week 01/id_618/LeetCode_88_618.java @@ -0,0 +1,51 @@ + class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + if (nums2 == null || n == 0) { + return; + } + + // 遍历nums2,从最大值开始 + for (int i = n - 1; i > -1; i--) { + int num2 = nums2[i]; + if (m == 0) { + // 特殊情况,直接填充 + nums1[i] = num2; + continue; + } + + // nums1中的最大值 + int num1Max = nums1[m - 1]; + if (num2 >= num1Max) { + // 比num1最大值大,直接填充 + nums1[m + i] = num2; + } else { + // 将num1Max填充,用num2顶替其位置 + nums1[m + i] = num1Max; + nums1[m - 1] = num2; + + // 如有必要,将m-1位置的数字前移 + move(nums1, m); + } + } + + } + + /** + * 尝试将m-1位置的数字移动,直到nums1数组有序 + * + * @param nums1 + * @param m + */ + private void move(int[] nums1, int m) { + int last = nums1[m - 1]; + + for (int i = m - 2; i > -1; i--) { + if (last > nums1[i]) { + break; + } + + nums1[i + 1] = nums1[i]; + nums1[i] = last; + } + } + } \ No newline at end of file diff --git a/Week 01/id_618/NOTE.md b/Week 01/id_618/NOTE.md index a6321d6e2..ad5a1ecb6 100644 --- a/Week 01/id_618/NOTE.md +++ b/Week 01/id_618/NOTE.md @@ -1,4 +1,247 @@ -# NOTE +### 第三课课后习题 - +#### 删除排序数组中的重复项 + +直观的想法非常简单,遇到相邻重复的元素,则后续元素都往前移动一位,简单暴力,但是马上就能意识到这个肯定是个比较糟糕的解法,时间复杂度为O(n²)。 + +``` + class Solution { + public int removeDuplicates(int[] nums) { + // 处理特殊情况 + if (nums.length <= 1) { + return nums.length; + } + + // 有重复则减去一个,最后返回len + int len = nums.length; + + for (int i = 1; i < len; i++) { + int previous = nums[i - 1]; + int current = nums[i]; + + // 如果相邻两个数一样,则后续的都往前移动一位 + if (previous == current) { + for (int j = i + 1; j < len; j++) { + nums[j - 1] = nums[j]; + } + + // 移动后索引需要往前一格,len也往前一格,避免尾部不需要的判断 + i--; + len--; + } + } + + return len; + } + } +``` + +要改善时间复杂度,关键在于如何减少迭代,开始的确饶了弯路,后来还是看了官方题解,有这么一点体会,上述解法本质在于"修复",而与之相反的思路是“构建”,即从“如果有重复则。。。”换为“如果有不一致则。。。”,不难发现,后者一次迭代就能构建我们所需要的数组,可能开始被题目的"原地"所干扰了,所以脑袋一开始没有“构建”念头,但后来发现,构建过程完全可以在传入的数组上进行。 + +``` + class Solution { + public int removeDuplicates(int[] nums) { + // 处理特殊情况 + if (nums.length <= 1) { + return nums.length; + } + + // 表示每当出现不同数字的计数,也就是最后的返回值 + int count = 1; + + for (int i = 1; i < nums.length; i++) { + int previous = nums[i - 1]; + int current = nums[i]; + + // 如果不一样,不同数字计数+1,并将当前值存放到其应该存在的位置,即count-1的值,例如第2个不同值,其位置就是1 + if (previous != current) { + count++; + nums[count - 1] = current; + } + } + + return count; + } + } +``` + +#### 旋转数组 + +1. 数组每次右移一格,执行k次,恩,当然知道这个很挫,时间复杂度O(n*k)。 + +``` + class Solution { + public void rotate(int[] nums, int k) { + int len = nums.length; + + if (k >= len) { + k = k % len; + } + + while (k > 0) { + int temp = nums[len - 1]; + + //所有元素往右移位 + for (int i = len - 2; i > -1; i--) { + nums[i + 1] = nums[i]; + } + + nums[0] = temp; + k--; + } + } + } +``` + +2. 利用临时数组,一次性完成k位右移,时间复杂度O(n),但是利用了一个长度为k的数组,最坏情况下空间复杂度为O(n) + +``` + class Solution { + public void rotate(int[] nums, int k) { + int len = nums.length; + if (k >= len) { + k = k % len; + } + + if (k == 0) { + return; + } + + int[] temp = new int[k]; + + //后k个元素放入temp + for (int i = 0; i < k; i++) { + temp[i] = nums[len - k + i]; + } + + //前len-k个元素往后移k位 + for (int i = len - k - 1; i > -1; i--) { + nums[i + k] = nums[i]; + } + + //temp中放入数组头 + for (int i = 0; i < k; i++) { + nums[i] = temp[i]; + } + + } + } + +``` +写完后发现,如果k>len/2,可以将问题变为左移len-k位,这样临时数组空间可以小一点。 + +3. 如果不希望依赖临时数组,那么思路就要转换为原地+直接到位的排序方式,例如把n需要移到n+k,原先n+k的数字放入temp,然后n=n+k,循环这个步骤,直到所有格子都移动过,其中最大的问题是如何解决n移动k位多次后回到原点后,不能保证所有节点都移动过,所以这个步骤需要执行多次,一开始 0-->k,每次移位计数器+1,当k回到0时,此时再从1开始,即1->k,k回到0结束,直到计数等于数据长度为止,这样就保证所有元素都完成移动,本质上,这个方法是逐一右移的进阶,只不过步长从1变为n,算法的关键是确保所有元素都移动过且只移动一次。后来发现评论区有说,这个轮次是k和n的最大公约数,开始我也想计算出这个轮次,但是发现用count计数更简单,所以就没有往这条路走了。 + +``` + class Solution { + public void rotate(int[] nums, int k) { + int len = nums.length; + if (k >= len) { + k = k % len; + } + + if (k == 0) { + return; + } + + // 移位计数 + int count = 0; + // 每一轮起始 + int start = 0; + + // 当前位置 + int current = start; + // 当前位置的值 + int currentVal = nums[current]; + + do { + // 计算移位目标 + int target = (current + k) % len; + + // 将preVal放入目标位置,目标原值存入temp + int temp = nums[target]; + nums[target] = currentVal; + + // 移位计数+1 + count++; + + current = target; + currentVal = temp; + + // 如果回到起点,则start+1,开始下一轮 + if (current == start) { + start++; + current = start; + currentVal = nums[current]; + } + + } while (count < len);// 所有元素都移动后终止 + } + } + +``` + + + +4. 看了码友的思路,大呼叫绝,我觉得文字的描述是苍白的,代码已经很直观地表达出思路了。我自己的确没有想到这一层,所以作业还是用上一个方法为准。 + +``` + class Solution { + public void rotate(int[] nums, int k) { + int len = nums.length; + if (k >= len) { + k = k % len; + } + + if (k == 0) { + return; + } + + swap(nums, 0, len - k - 1); + swap(nums, len - k, len - 1); + swap(nums, 0, len - 1); + } + + private void swap(int[] nums, int start, int end) { + + while (start < end) { + int temp = nums[start]; + nums[start] = nums[end]; + nums[end] = temp; + + start++; + end--; + } + } + } +``` + +#### 合并两个有序链表 +闷想一会后,并没有想到很好的办法,于是拿起纸笔开始用一个例子推导过程,马上就有思路,大体上和题解中的迭代法一致,只是我的写法略微啰嗦了点,但是顺着我的思路,可读性略强一些(个人觉得......),至于递归解法,想了很久,发现本质和迭代是一样的,即拿小的节点的后一个与另一个节点对比,直到没有目标或者自己是结尾。 + +#### 合并两个有序数组 +与链表不同,数组可以随机访问,而且原始已经有序且目标数组就是nums1,通过上述条件不难发现,减少次数的思路就是:用nums2的最大值和nums1的最大值对比,大的移到nums1最后,小的占据nums1的m-1的位置,如果m-1位置的数字是nums2过来的,则与前m-2位对比,必要则移位,重复此过程直到nums2都插入完成。这个思路与题解中的解法3类似,只不过它用了双指针逐个比较,大的落位并指针迁移,也许是思路关系,尽管代码比我更精简,但是表达力不是很直观。 + + +#### 两数之和 +忽略暴力解法,思路聚焦于一次迭代得到结果,很容易想到用一个Map存储之前记录,与当前数字之和匹配即返回索引。 + +#### 移动零 +不借助额外容器,就要相伴法在原地移动,还是一样的技巧,拿起笔纸书面推导一个例子,思路很快就出来了,本质就是在一次迭代中,找准“0位置”,将非零的放到这个位置,并更新“0位置”。 + +#### 加一 +这个比较简单,处理好9+1的进位即可。 + +### 第四课课后练习 + +#### 设计循环双端队列 +用2个stack的解法比较容易,我很快就完成了,但是想尝试一下数组实现。 +java源码中,Deque的代表实现是LinkedList和ArrayDeque,前者比较简单,以前我也尝试写过,这次就想试试用数组实现。 + +既然队列大小是初始固定的,那么就初始化一个足够大的数组就可以了,即2k-1,初始添加的元素在正中间(不管是添加头还是添加尾),后续添加的元素就往其两边放,分别用head和tail指针指向头尾索引即可,这样的结构,添、删、查的时间复杂度都是O(1),缺点就是空间比较浪费。 + +后来仔细看了ArrayDeque的源码分析,受到启发:k长度的数组也足够了,head和tail指针可以环状移动,这样空间就节省了。 + +#### 接雨水 +撇开暴力法 开始并没有好的思路,后看了题解,理解了动态规划的解法,但是本节主要学习栈,所以还是用栈的思路完成了,看显然这个算法不算好。 diff --git a/Week 01/id_628/LeetCode_141_628.java b/Week 01/id_628/LeetCode_141_628.java new file mode 100644 index 000000000..c4626b638 --- /dev/null +++ b/Week 01/id_628/LeetCode_141_628.java @@ -0,0 +1,121 @@ +import java.util.HashSet; +import java.util.Set; + +import com.leetCode.exercise.struct.ListNode; + +//һжǷл +// +// Ϊ˱ʾеĻʹ pos ʾβӵеλã 0 ʼ pos -1ڸûл +// +// +// +// ʾ 1 +// +// 룺head = [3,2,0,-4], pos = 1 +//true +//ͣһβӵڶڵ㡣 +// +// +// +// +// ʾ 2 +// +// 룺head = [1,2], pos = 0 +//true +//ͣһβӵһڵ㡣 +// +// +// +// +// ʾ 3 +// +// 룺head = [1], pos = -1 +//false +//ͣûл +// +// +// +// +// +// +// ף +// +// O(1)ڴ +// Related Topics ˫ָ + + + +//leetcode submit region begin(Prohibit modification and deletion) +/** + * Definition for singly-linked list. + * class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class LeetCode_141_628 { + public boolean hasCycle(ListNode head) { + hasCycle1(head); + } + /** + * ˼·ָ롣 + * ÿָָͬл߻Խ + * + * ʹÿָʶȻοָ㻷ڽڵ + * ָʱ + * ָ߹ p=a+x+n*(x+y) + * ָ߹2p=a+x+m*(x+y) + * ʽϲɵã + * a=(m-2n)(x+y)-x + * a=(m-2n-1)(x+y)+y + * x+yǻijȣΪֵ + * ҲָaĻ൱m-2n-1ȦԼyʱָ뽫صA + * ʶΪָߵIJa + * οָ-ָ룬ʹ㿪ʼָB㿪ʼͬʱÿһڵ㣬 + * ָa󵽴A㣬ָaҲA + * ָ뽫ͬʱߵA㣬A,ʱָָڵ㼴Ϊڵ㡣 + * + * + * ʱ临ӶȡO(n+k) + * ռ临ӶȡO(1) + * @param head ԭʼͷ + * @return true-ڻ false-ڻ + */ + public static boolean hasCycle1(ListNode head) { + if (null == head || head.next == null) + return false; + ListNode fastNode = head.next.next;// ָ + ListNode slowNode = head.next;// ָ + while (fastNode != slowNode) { + if (fastNode == null || null == fastNode.next) + return false; + fastNode = fastNode.next.next; + slowNode = slowNode.next; + } + return true; + } + + /** + * ˼·¼ + * һÿ뻺setУжǷ˽ڵȷǷл + * ʱ临ӶȡO(n) + * ռ临ӶȡO(n) + * @param head ԭʼͷ + * @return true-ڻ false-ڻ + */ + public static boolean hasCycle2(ListNode head) { + Set cacheSet = new HashSet(); + while (!cacheSet.contains(head)) { + if (head == null) + return false; + cacheSet.add(head); + head = head.next; + } + return true; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 01/id_628/LeetCode_142_628.java b/Week 01/id_628/LeetCode_142_628.java new file mode 100644 index 000000000..b2cba188e --- /dev/null +++ b/Week 01/id_628/LeetCode_142_628.java @@ -0,0 +1,124 @@ +import java.util.HashSet; +import java.util.Set; + +import com.leetCode.exercise.struct.ListNode; + +//һʼ뻷ĵһڵ㡣 ޻򷵻 null +// +//Ϊ˱ʾеĻʹ pos ʾβӵеλã 0 ʼ pos -1ڸûл +// +//˵޸ĸ +// +// +// +//ʾ 1 +// +//룺head = [3,2,0,-4], pos = 1 +//tail connects to node index 1 +//ͣһβӵڶڵ㡣 +// +// +// +// +//ʾ 2 +// +//룺head = [1,2], pos = 0 +//tail connects to node index 0 +//ͣһβӵһڵ㡣 +// +// +// +// +//ʾ 3 +// +//룺head = [1], pos = -1 +//no cycle +//ͣûл +// +// +// +// +// +// +//ף +//ǷԲöռ⣿ +//Related Topics ˫ָ + + + +//leetcode submit region begin(Prohibit modification and deletion) +/** +* Definition for singly-linked list. +* class ListNode { +* int val; +* ListNode next; +* ListNode(int x) { +* val = x; +* next = null; +* } +* } +*/ +public class LeetCode_141_628 { + public ListNode detectCycle(ListNode head) { + detectCycle1(head); + } + /** + * ˼·ָ뷨 + * ʹÿָʶȻοָ㻷ڽڵ + * ָʱ + * ָ߹ p=a+x+n*(x+y) + * ָ߹2p=a+x+m*(x+y) + * ʽϲɵã + * a=(m-2n)(x+y)-x + * a=(m-2n-1)(x+y)+y + * x+yǻijȣΪֵ + * ҲָaĻ൱m-2n-1ȦԼyʱָ뽫صA + * ʶΪָߵIJa + * οָ-ָ룬ʹ㿪ʼָB㿪ʼͬʱÿһڵ㣬 + * ָa󵽴A㣬ָaҲA + * ָ뽫ͬʱߵA㣬A,ʱָָڵ㼴Ϊڵ㡣 + * ʱ临ӶȡO(n+k) + * ռ临ӶȡO(1) + * @param head ԭʼͷ + * @return 뻷ڵnull + */ + public ListNode detectCycle1(ListNode head) { + if (null == head || head.next == null) + return null; + ListNode fastNode = head.next.next;// ָ + ListNode slowNode = head.next;// ָ + while (fastNode != slowNode) { + if (fastNode == null || null == fastNode.next) + return null; + fastNode = fastNode.next.next; + slowNode = slowNode.next; + } + //ʱ˵лȻ󹹽ָ룬ָͬߣʱΪ뻷λá + ListNode zeroNode = head;// ָ + while(zeroNode != slowNode) { + zeroNode = zeroNode.next; + slowNode = slowNode.next; + } + return zeroNode; + } + + /** + * ˼·¼ + * һÿ뻺setУжǷ˽ڵȷǷллش˽ڵ㣬޻null + * ʱ临ӶȡO(n) + * ռ临ӶȡO(n) + * @param head ԭʼͷ + * @return 뻷ڵnull + */ + public ListNode detectCycle2(ListNode head) { + Set cacheSet = new HashSet(); + while (!cacheSet.contains(head)) { + if (head == null) + return null; + cacheSet.add(head); + head = head.next; + } + return head; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 01/id_628/LeetCode_189_628.java b/Week 01/id_628/LeetCode_189_628.java new file mode 100644 index 000000000..1e911dbce --- /dev/null +++ b/Week 01/id_628/LeetCode_189_628.java @@ -0,0 +1,59 @@ +//һ飬еԪƶ k λã k ǷǸ +// +// ʾ 1: +// +// : [1,2,3,4,5,6,7] k = 3 +//: [5,6,7,1,2,3,4] +//: +//ת 1 : [7,1,2,3,4,5,6] +//ת 2 : [6,7,1,2,3,4,5] +//ת 3 : [5,6,7,1,2,3,4] +// +// +// ʾ 2: +// +// : [-1,-100,3,99] k = 2 +//: [3,99,-1,-100] +//: +//ת 1 : [99,-1,-100,3] +//ת 2 : [3,99,-1,-100] +// +// ˵: +// +// +// ĽֲͬķԽ⡣ +// Ҫʹÿռ临ӶΪ O(1) ԭ 㷨 +// +// Related Topics + + + +//leetcode submit region begin(Prohibit modification and deletion) +class LeetCode_189_628 { + public void rotate(int[] nums, int k) { + if(nums == null || nums.length == 0)return; + int m = k % nums.length; + reverse(nums , 0 , nums.length-1 ); + reverse(nums , 0 , m-1 ); + reverse(nums , m , nums.length - 1); + } + + public void reverse(int[] nums, int start, int end){ + while(start < end) { + int tmp = nums[start]; + nums[start] = nums[end]; + nums[end] = tmp; + start ++ ; + end --; + } + } +} +//leetcode submit region end(Prohibit modification and deletion) +/* + 1 + ˫ѭһλһλŲƣһ滻λʵ + 2 + չ󣬶kĵһͬ಻ظŲƣҪһ + 3ת + 鷴תٷתǰk0~k-1kĩβk~len-1ʵ + */ diff --git a/Week 01/id_628/LeetCode_1_628.java b/Week 01/id_628/LeetCode_1_628.java new file mode 100644 index 000000000..6d9fc143c --- /dev/null +++ b/Week 01/id_628/LeetCode_1_628.java @@ -0,0 +1,34 @@ +//һ nums һĿֵ targetڸҳΪĿֵ ǵ±ꡣ +// +// ԼÿֻӦһ𰸡ǣ㲻ظͬԪء +// +// ʾ: +// +// nums = [2, 7, 11, 15], target = 9 +// +//Ϊ nums[0] + nums[1] = 2 + 7 = 9 +//Է [0, 1] +// +// Related Topics ϣ + + + +//leetcode submit region begin(Prohibit modification and deletion) +class LeetCode_1_628 { + public int[] twoSum(int[] nums, int target) { + Map cache=new HashMap(); + for (int i = 0; i < nums.length; i++) { + if(cache.containsKey(target-nums[i])){ + return new int[] {cache.get(target-nums[i]),i}; + } + cache.put(nums[i], i); + } + return new int[] {}; + } +} +//leetcode submit region end(Prohibit modification and deletion) +/**** + * 1. + * 2.˫ָ + * 3. + */ diff --git a/Week 01/id_628/LeetCode_21_628.java b/Week 01/id_628/LeetCode_21_628.java new file mode 100644 index 000000000..ba84317f3 --- /dev/null +++ b/Week 01/id_628/LeetCode_21_628.java @@ -0,0 +1,48 @@ +//ϲΪһµءͨƴӸнڵɵġ +// +//ʾ +// +//룺1->2->4, 1->3->4 +//1->1->2->3->4->4 +// +//Related Topics + + + +//leetcode submit region begin(Prohibit modification and deletion) +/** +* Definition for singly-linked list. +* public class ListNode { +* int val; +* ListNode next; +* ListNode(int x) { val = x; } +* } +*/ +class LeetCode_21_628 { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + if(l1 == null) return l2; + if(l2 == null) return l1; + ListNode res = new ListNode(-1) ; + ListNode cur = res ; + while (l1 != null || l2 != null){ + if(l1 == null){ + cur.next = l2 ; + l2 = l2.next; + }else if(l2 == null || l1.val <= l2.val){ + cur.next = l1 ; + l1 = l1.next; + }else{ + cur.next = l2 ; + l2 = l2.next; + } + cur = cur.next; + } + return res.next ; + + } +} +//leetcode submit region end(Prohibit modification and deletion) + +//ִ㡿 +//1δǵl1Ϊյl2ΪȺ˳ +//2δǵl1l2ͷΪʼĻᵼѭָ֡ diff --git a/Week 01/id_628/LeetCode_641_628.java b/Week 01/id_628/LeetCode_641_628.java new file mode 100644 index 000000000..b06805644 --- /dev/null +++ b/Week 01/id_628/LeetCode_641_628.java @@ -0,0 +1,150 @@ +//设计实现双端队列。 +//你的实现需要支持以下操作: +// +// +// MyCircularDeque(k):构造函数,双端队列的大小为k。 +// insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true。 +// insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。 +// deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true。 +// deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。 +// getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。 +// getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。 +// isEmpty():检查双端队列是否为空。 +// isFull():检查双端队列是否满了。 +// +// +// 示例: +// +// MyCircularDeque circularDeque = new MycircularDeque(3); // 设置容量大小为3 +//circularDeque.insertLast(1); // 返回 true +//circularDeque.insertLast(2); // 返回 true +//circularDeque.insertFront(3); // 返回 true +//circularDeque.insertFront(4); // 已经满了,返回 false +//circularDeque.getRear(); // 返回 2 +//circularDeque.isFull(); // 返回 true +//circularDeque.deleteLast(); // 返回 true +//circularDeque.insertFront(4); // 返回 true +//circularDeque.getFront(); // 返回 4 +//  +// +// +// +// 提示: +// +// +// 所有值的范围为 [1, 1000] +// 操作次数的范围为 [1, 1000] +// 请不要使用内置的双端队列库。 +// +// Related Topics 设计 队列 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class LeetCode_641_628 { + //记录队列最大存储多大 + int k; + //记录队列当前放入的元素 + int size; + Node head; + Node tail; + + + /** Initialize your data structure here. Set the size of the deque to be k. */ + public MyCircularDeque(int k) { + //初始化头尾节点 + head = new Node(-1); + tail = new Node(-1); + head.pre = tail ; + tail.next = head ; + this.k = k ; + this.size = 0 ; + } + + /** Adds an item at the front of Deque. Return true if the operation is successful. */ + public boolean insertFront(int value) { + if(size >= k) return false ; + Node node = new Node(value); + node.next = head ; + node.pre = head.pre ; + head.pre.next = node; + head.pre = node ; + size++; + return true; + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + public boolean insertLast(int value) { + if(size >= k) return false ; + Node node = new Node(value); + node.next = tail.next; + node.pre = tail ; + tail.next.pre = node; + tail.next = node ; + size++; + return true; + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + public boolean deleteFront() { + if(size <= 0 )return false; + head.pre.pre.next = head; + head.pre = head.pre.pre; + size--; + return true; + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + public boolean deleteLast() { + if(size <= 0 )return false; + tail.next.next.pre = tail; + tail.next = tail.next.next; + size--; + return true; + } + + /** Get the front item from the deque. */ + public int getFront() { + return head.pre.value; + } + + /** Get the last item from the deque. */ + public int getRear() { + return tail.next.value; + } + + /** Checks whether the circular deque is empty or not. */ + public boolean isEmpty() { + return size == 0; + } + + /** Checks whether the circular deque is full or not. */ + public boolean isFull() { + return size == k ; + } + + class Node{ + int value; + Node pre; + Node next; + public Node(int value){ + this.value = value ; + } + } +} + + + +/** + * Your MyCircularDeque object will be instantiated and called as such: + * MyCircularDeque obj = new MyCircularDeque(k); + * boolean param_1 = obj.insertFront(value); + * boolean param_2 = obj.insertLast(value); + * boolean param_3 = obj.deleteFront(); + * boolean param_4 = obj.deleteLast(); + * int param_5 = obj.getFront(); + * int param_6 = obj.getRear(); + * boolean param_7 = obj.isEmpty(); + * boolean param_8 = obj.isFull(); + */ +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 01/id_628/NOTE.md b/Week 01/id_628/NOTE.md index a6321d6e2..6f0ebce26 100644 --- a/Week 01/id_628/NOTE.md +++ b/Week 01/id_628/NOTE.md @@ -1,4 +1,369 @@ # NOTE +# NOTE +# 【学习总结】 + +​ 第一周已经过去了,这周内容里面数据结构占大多比重,熟悉源码了解数据结构的基础实现成了这周最耗时间的地方,重新去认识了一遍底层的实现,并去细致的揣摩其内部的原因,预习周的感觉是转变,那这周的感觉就是踏实,很多以前看源码时候没注意到的问题,在群里小伙伴们的讨论中关注到了一些细节,收益匪浅。这周主要内容如下: + +【视频方面】 + +​ 超哥主要围绕线性表进行的讲解,对于计算机而言,本质上就是存储与计算的过程,而存储无非就是顺序存储和离散存储,顺序存储的底层就是数组,可以随机访问,而离散存储的本质就是链表可以充分利用碎片化的空间。各有优劣的同时也成为了数据结构的基石,所以第三节主要围绕这两种存储的特性以及相关的高频题目进行了讲解。 + +​ 在数组和链表熟悉的基础上,讲解了栈和队列,跳表这些数据结构,然后分享了去查阅资料,看源码的方法,布置源码习题也旨在了解这些看似独立的数据结构,在底层大多是依托于数组或链表进行的封装。 + +【刷题总结】 + +由于时间原因,题目并没刷太多,在完成低保的情况下,发现了自己一些问题,记录出来以共勉。 + +1、对边界条件的忽视。 + +2、对问题场景未动笔动纸,只是脑子里面演练了过程,毕竟好记性不如烂笔头,何况记性也不好。 + +# 【作业地址】 + +第一周作业的地址,如下: + + + +# 【源码分析】 + +### 【1】Queue + +在java中,Queue 属于JAVA自身容器体系的一种,以接口的形式存在,其除了继承基础的Collection接口方法外,自身定义了一些方法。规定了java内的队列具体应该实现什么方法 + +```java + /****Collection 中主要定义的方法****/ + int size(); + boolean isEmpty(); + boolean contains(Object o); + Iterator iterator(); + Object[] toArray(); + T[] toArray(T[] a); + boolean add(E e); + boolean remove(Object o); + boolean containsAll(Collection c); + boolean addAll(Collection c); + boolean removeAll(Collection c); + boolean retainAll(Collection c); + void clear(); + boolean equals(Object o); + int hashCode(); + + /********Queue 中主要定义的方法**************/ + boolean add(E e); //将元素插入队列,如果插入出错则返回异常 + boolean offer(E e); //将元素插入队列,与add相比,在容量受限时应该使用这个 + E remove(); //将队首的元素删除,队列为空则抛出异常 + E poll(); //将队首的元素删除,队列为空则返回null + E element(); //获取队首元素,但不移除,队列为空则抛出异常 + E peek(); //获取队首元素,但不移除,队列为空则返回null +``` + + + +### 【2】AbstractQueue + +在Queue定义了这些方法后,为了方便使用和扩充,采用AbstractQueue作为其抽象的公共基类,实现了接口中的公共方法。其除了继承AbstractCollection实现的Collection接口中的公共方法外,AbstractQueue还基于offer、poll、peek实现了add、remove、element三个通用方法以及重写了 clear与addAll方法 ,而offer、poll、peek留给其具体实现子类进行实现,充分加强了扩展性。而上层调用者只需关注方法,无需关注其具体实现。 + +以下是AbstractQueue中利用peek实现element的方法。 + +```java + public E element() { + E x = peek(); + if (x != null) + return x; + else + throw new NoSuchElementException(); + } +``` + + + +### 【3】PriorityQueue + + + +相比于Queue而言,PriorityQueue则是一个它的具体实现类,其公共方法使用继承的方式,继承了AbstractQueue(队列的公共基类)实现,而其内部通过维护了一个堆实现优先级队列,具体如下: + +#### 【3.1】属性源码 + +```java + + //序列化与反序列化时使用的ID + private static final long serialVersionUID = -7720805057305804111L; + //默认的容量 + private static final int DEFAULT_INITIAL_CAPACITY = 11; + //队列中保存元素的数组(ps:堆在内存里面可以采用数组的方式存储) + transient Object[] queue; // non-private to simplify nested class access + //队列的大小 + private int size = 0; + //比较器,通过比较器结果判断元素之间的权重大小关系 + private final Comparator comparator; + //队列被修改过的次数 + transient int modCount = 0; // non-private to simplify nested class access +``` + +#### 【3.2】方法源码 + +##### 3.2.1 构造方法 + + 核心采用如下构造方法,参数initialCapacity为设定队列容量,comparator指定队列内元素间比较使用的比较器 + +```java + public PriorityQueue(int initialCapacity, + Comparator comparator) { + // Note: This restriction of at least one is not actually needed, + // but continues for 1.5 compatibility + if (initialCapacity < 1) + throw new IllegalArgumentException(); + this.queue = new Object[initialCapacity]; + this.comparator = comparator; + } +``` + + + +##### 3.2.2 扩容方法 + +```java + private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + + private void grow(int minCapacity) { + //获取到原队列长度 + int oldCapacity = queue.length; + // Double size if small; else grow by 50% + //计算新队列长度,如果低于64则直接翻倍,如果元队列高于64,则扩展为原来1.5倍 + int newCapacity = oldCapacity + ((oldCapacity < 64) ? + (oldCapacity + 2) : + (oldCapacity >> 1)); + // overflow-conscious code + //计算新队列长度是否大于数组最大值最大值 + if (newCapacity - MAX_ARRAY_SIZE > 0) + newCapacity = hugeCapacity(minCapacity); + //新分配数组,并将原数组copy至新数组 + queue = Arrays.copyOf(queue, newCapacity); + } + + private static int hugeCapacity(int minCapacity) { + if (minCapacity < 0) // overflow + throw new OutOfMemoryError(); + return (minCapacity > MAX_ARRAY_SIZE) ? + Integer.MAX_VALUE : + MAX_ARRAY_SIZE; + } +``` + +##### 3.2.3 入堆操作 + +在位置k插入元素x,通过将x提升到树的上方直到其大于或等于其父级或成为根,从而保持堆不变。 + +```java + private void siftUp(int k, E x) { + if (comparator != null) + //如果没有配置比较器,则使用默认入堆方法 + siftUpUsingComparator(k, x); + else + siftUpComparable(k, x); + } + + @SuppressWarnings("unchecked") + private void siftUpComparable(int k, E x) { + Comparable key = (Comparable) x; + while (k > 0) { + //获取k的父节点索引 + int parent = (k - 1) >>> 1; + //获取k的父节点 + Object e = queue[parent]; + //判断当前节点是否大于父节点 + if (key.compareTo((E) e) >= 0) + break; + //和父节点交换 + queue[k] = e; + k = parent; + } + queue[k] = key; + } + + @SuppressWarnings("unchecked") + private void siftUpUsingComparator(int k, E x) { + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = queue[parent]; + if (comparator.compare(x, (E) e) >= 0) + break; + queue[k] = e; + k = parent; + } + queue[k] = x; + } + +``` + + + +##### 3.2.4 入队方法 + +```java +public boolean offer(E e) { + + //队列中不能插入空元素 + if (e == null) + throw new NullPointerException(); + + //记录队列修改操作数 + modCount++; + int i = size; + + //数组大小不够,进行扩容 + if (i >= queue.length) + grow(i + 1); + size = i + 1; + + //第一个元素,放在堆顶 + if (i == 0) + queue[0] = e; + else + + //否则调用siftUp函数从下往上调整堆 + siftUp(i, e); + return true; + +} +``` + + + +##### 3.2.5 堆中移除元素方法 + + + +```java + private void siftDownUsingComparator(int k, E x) { + //获取遍历最后的节点索引 + int half = size >>> 1; + while (k < half) { + //获取左子节点 + int child = (k << 1) + 1; + Object c = queue[child]; + //获取右子节点 + int right = child + 1; + if (right < size && + comparator.compare((E) c, (E) queue[right]) > 0)//获取最小子节点 + c = queue[child = right]; + + //比较x与子节点大小 + if (comparator.compare(x, (E) c) <= 0) + break; + //讲子节点与父节点交换 + queue[k] = c; + k = child; + } + queue[k] = x; + } +``` + + + +##### 3.2.6 出队方法 + +```java +public E poll() { + if (size == 0) + return null; + + //获取最后一个节点索引 + int s = --size; + + //记录操作数 + modCount++; + + //获取头节点 + E result = (E) queue[0]; + + //获取尾节点 + E x = (E) queue[s]; + + //置空尾节点 + queue[s] = null; + + //堆中不止一个元素 + if (s != 0) + siftDown(0, x); + return result; + +} +``` + + + + + +# 【代码改写】 + + 用 add first 或 add last 这套新的 API 改写 Deque 的代码 + +```java + /******************************* + * + * 【改写之前代码】 + * + *******************************/ + public static void srcDemo(){ + Deque deque = new LinkedList<>(); + + deque.push("a"); + deque.push("b"); + deque.push("c"); + + System.out.println(deque); + + String str = deque.peek(); + System.out.println(str); + System.out.println(deque); + + while (deque.size() > 0) { + System.out.println(deque.pop()); + } + + System.out.println(deque); + } + + + /******************************* + * + * 【改写之后代码】 + * 内容: + * push->addFirst + * peek->peekFirst + * pop->removeFirst + * + *******************************/ + public static void targetDemo(){ + Deque deque = new LinkedList<>(); + + deque.addFirst("a"); + deque.addFirst("b"); + deque.addFirst("c"); + + System.out.println(deque); + + String str = deque.peekFirst(); + System.out.println(str); + System.out.println(deque); + + while (deque.size() > 0) { + System.out.println(deque.removeFirst()); + } + + System.out.println(deque); + } +``` + + + + + + + + diff --git a/Week 01/id_633/LeetCode_26_633.java b/Week 01/id_633/LeetCode_26_633.java new file mode 100644 index 000000000..a59caee20 --- /dev/null +++ b/Week 01/id_633/LeetCode_26_633.java @@ -0,0 +1,19 @@ +package lesson3; + +public class LeetCode_26_633 { + + public int removeDuplicates(int[] nums) { + int i = 0; // 不变指针指向的元素 + // 有序, j = i + 1 + for (int j = 1; j < nums.length; j++) { + // 与i指针指向的元素不同 则更新i后面的元素 + if (nums[i] != nums[j]) { + nums[++i] = nums[j]; + } + // 与nums[i]重复, i指针不动,跳过,继续遍历下一个元素 + } + // for 循环结束后 i指针指向了数组的最后一个元素 + return i + 1; + } + +} diff --git a/Week 01/id_633/LeetCode_42_633.java b/Week 01/id_633/LeetCode_42_633.java new file mode 100644 index 000000000..6ffc32a5a --- /dev/null +++ b/Week 01/id_633/LeetCode_42_633.java @@ -0,0 +1,25 @@ +package lesson3; + +public class LeetCode_42_633 { + // 1. 暴力法 + public int trap(int[] height) { + int s = 0; // 面积 + // 枚举i + for (int i = 0; i < height.length - 1; i++) { + int maxLeft = 0, maxRight = 0; + // 计算左边最高高度 + for (int j = i; j >= 0; --j) { + maxLeft = Math.max(maxLeft, height[j]); + } + // 计算右边最高高度 + for (int j = i; j < height.length; ++j) { + maxRight = Math.max(maxRight, height[j]); + } + // 计算围成的凹槽中 遍历到i对应这个条的面积 + // 注意,一次计算,不是 maxRight 与 maxLeft 及 height[i]围成的的整个面积 + // 仅仅是凹槽中下标为i这个竖条在凹槽中的面积 + s += Math.min(maxLeft, maxRight) - height[i]; + } + return s; + } +} diff --git a/Week 01/id_638/Leetcode_189_638.java b/Week 01/id_638/Leetcode_189_638.java new file mode 100644 index 000000000..1fad011ea --- /dev/null +++ b/Week 01/id_638/Leetcode_189_638.java @@ -0,0 +1,31 @@ +package test1; + +import java.util.Arrays; + +/** + * Created by Administrator on 2019/10/20. + */ +public class Leetcode_189_638 { + + public static void rotate(int[] nums, int k) { + int len = nums.length; + k = k % len; + if(k == 0){//移动次数长度整数倍,不需要处理 + return; + } + for(int i=0;i map = new HashMap<>(); + int[] res = new int[2]; + for (int i = 0; i < nums.length; i++) { + int dif = target - nums[i]; + if (map.get(dif) != null) { + res[0] = map.get(dif); + res[1] = i; + return res; + } + map.put(nums[i],i); + } + return res; + } +} + diff --git a/Week 01/id_643/LeetCode_283_643.java b/Week 01/id_643/LeetCode_283_643.java new file mode 100644 index 000000000..659586773 --- /dev/null +++ b/Week 01/id_643/LeetCode_283_643.java @@ -0,0 +1,72 @@ + + + +一维度数数组坐标变化使用,i j 两个坐标 +to do 前面两种方法是用两个下标从左到右的进行移动,遇到部位零的数字就是放到最左边。 + + +public void moveZeroes(int[] nums) { + + if(nums == null || nums.length == 0 ) return; + + int inserPos = 0; + + for(int num : nums) { + if(num != 0) nums[inserPos++] = num; + } + + while(inserPos < nums.length) + nums[inserPos++] = 0; + + + int j = 0; + for(int i = 0; i < nums.length; i++) { + + if(nums[i] != 0) { + + int temp = nums[j]; + + nums[j] = nums[i]; + + nums[i] = temp; + + j++; + } + } +//后面一种是滚雪球的方法 + +int snowBallSize = 0; +for(int i = 0 ; i < nums.length; i++){ + + if(nums[i] == 0) snowBallSize++; + + else if(snowBallSize > 0){ + + int t = num[i]; + + num[i]=0; + + num[i-snowBallSize] = t; + + } + +} + + + + + + + + + + + + + +} + + + + + diff --git a/Week 01/id_648/LeetCode_01_648.java b/Week 01/id_648/LeetCode_01_648.java new file mode 100644 index 000000000..19b18777b --- /dev/null +++ b/Week 01/id_648/LeetCode_01_648.java @@ -0,0 +1,36 @@ +import java.util.HashMap; +import java.util.Map; + +public class LeetCode_01_648 { + public int[] twoSum(int[] nums, int target) { + for(int i=0;i map = new HashMap<>(); + for(int i=0;i list = new ArrayList<>(); + for(int i=0;i end = new Stack(); + private Stack top = new Stack(); + private int MAX = 1000; + private int currentSize = 0; + private int initSize = 0; + + /** + * Initialize your data structure here. Set the size of the deque to be k. + */ + public MyCircularDeque(int k) { + if (k > 1000) { + throw new OutOfMemoryError("exception , out of maxsize"); + } + + initSize = k; + } + + /** + * Adds an item at the front of Deque. Return true if the operation is successful. + */ + public synchronized boolean insertFront(int value) { + + if (isFull()) { + return false; + } + + top.push(value); + + return true; + } + + /** + * Adds an item at the rear of Deque. Return true if the operation is successful. + */ + public synchronized boolean insertLast(int value) { + if (isFull()) { + return false; + } + end.push(value); + return true; + } + + /** + * Deletes an item from the front of Deque. Return true if the operation is successful. + */ + public synchronized boolean deleteFront() { + if(isEmpty()) { + return false; + } + try { + if (top.size()>0) { + top.pop(); + return true; + } + + if (end.size()>0) { + end.removeElementAt(0); + return true; + } + + } catch (Exception e) { + return false; + } + return true; + } + + /** + * Deletes an item from the rear of Deque. Return true if the operation is successful. + */ + public synchronized boolean deleteLast() { + if(isEmpty()) { + return false; + } + try { + if (end.size()>0) { + end.pop(); + return true; + } + + if (top.size()>0) { + top.removeElementAt(0); + return true; + } + } catch (Exception e) { + return false; + } + return true; + } + + /** + * Get the front item from the deque. + */ + public int getFront() { + if(top.size()>0) { + return top.peek(); + } + + if (end.size()>0) { + return end.get(0); + } + return -1; + } + + /** + * Get the last item from the deque. + */ + public int getRear() { + if (end.size()>0) { + return end.peek(); + } + + if(top.size()>0) { + return top.get(0); + } + return -1; + } + + /** + * Checks whether the circular deque is empty or not. + */ + public boolean isEmpty() { + if (top.isEmpty()&&end.isEmpty()) { + return true; + } + + return false; + } + + /** + * Checks whether the circular deque is full or not. + */ + public boolean isFull() { + if (top.size()+end.size()==initSize) { + return true; + } + + return false; + } +} + +/** + * Your MyCircularDeque object will be instantiated and called as such: + * MyCircularDeque obj = new MyCircularDeque(k); + * boolean param_1 = obj.insertFront(value); + * boolean param_2 = obj.insertLast(value); + * boolean param_3 = obj.deleteFront(); + * boolean param_4 = obj.deleteLast(); + * int param_5 = obj.getFront(); + * int param_6 = obj.getRear(); + * boolean param_7 = obj.isEmpty(); + * boolean param_8 = obj.isFull(); + */ \ No newline at end of file diff --git a/Week 01/id_658/LeetCode_189_658.java b/Week 01/id_658/LeetCode_189_658.java new file mode 100644 index 000000000..e0e5fe8e2 --- /dev/null +++ b/Week 01/id_658/LeetCode_189_658.java @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode.cn id=189 lang=java + * + * [189] 旋转数组 + */ + +// @lc code=start +class Solution { + // 建立一个等长度的数组接收原数组 + public void rotate(int[] nums, int k) { + // 考虑不够周全 + if (k == 0 || k == nums.length) { + return; + } + int[] tempNums = nums.clone(); + if (k > tempNums.length) { + k -= tempNums.length; + } + for (int i = 0; i < tempNums.length; i++) { + if (i < k) { + nums[i] = tempNums[tempNums.length - k + i]; + } else { + nums[i] = tempNums[i - k]; + } + } + } +} diff --git a/Week 01/id_658/LeetCode_1_658.java b/Week 01/id_658/LeetCode_1_658.java new file mode 100644 index 000000000..b81cbbaa0 --- /dev/null +++ b/Week 01/id_658/LeetCode_1_658.java @@ -0,0 +1,55 @@ +import java.util.Map; + +/* + * @lc app=leetcode.cn id=1 lang=java + * + * [1] 两数之和 + * + * https://leetcode-cn.com/problems/two-sum/description/ + * + * algorithms + * Easy (45.73%) + * Total Accepted: 332.4K + * Total Submissions: 727K + * Testcase Example: '[2,7,11,15]\n9' + * + * 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 + * + * 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 + * + * 示例: + * + * 给定 nums = [2, 7, 11, 15], target = 9 + * + * 因为 nums[0] + nums[1] = 2 + 7 = 9 + * 所以返回 [0, 1] + * + * + */ +class Solution { + // 暴力解法双重for循环 + // public int[] twoSum(int[] nums, int target) { + // for(int i = 0; i < nums.length; i++) { + // for(int j = i + 1; j < nums.length; j++) { + // if(nums[i] == target - nums[j]) { + // return new int[] {i, j}; + // } + // } + // } + // return new int[2]; + // } + + // 空间换时间 用hash表存 + // 可以一边遍历一边存 + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap(); + for (int i = 0; i < nums.length; i++) { + int temp = target - nums[i]; + if (map.containsKey(temp)) { + return new int[] { map.get(temp), i }; + } + map.put(nums[i], i); + } + return new int[2]; + } +} diff --git a/Week 01/id_658/LeetCode_21_658.java b/Week 01/id_658/LeetCode_21_658.java new file mode 100644 index 000000000..d50d6e45f --- /dev/null +++ b/Week 01/id_658/LeetCode_21_658.java @@ -0,0 +1,56 @@ +/* + * @lc app=leetcode.cn id=21 lang=java + * + * [21] 合并两个有序链表 + */ + +// @lc code=start +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +class Solution { + // public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + // ListNode head = new ListNode(0); + // ListNode cur = head; + // while (l1 != null && l2 != null) { + // if (l1.val < l2.val) { + // cur.next = l1; + // cur = cur.next; + // l1 = l1.next; + // } else { + // cur.next = l2; + // cur = cur.next; + // l2 = l2.next; + // } + // } + // if (l1 == null) { + // cur.next = l2; + // } + // if (l2 == null) { + // cur.next = l1; + // } + // return head.next; + // } + + // 递归 + public ListNode mergeTwoLists(ListNode l1, ListNode 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(l2.next, l1); + return l2; + } + } +} diff --git a/Week 01/id_658/LeetCode_26_658.java b/Week 01/id_658/LeetCode_26_658.java new file mode 100644 index 000000000..05bc22df6 --- /dev/null +++ b/Week 01/id_658/LeetCode_26_658.java @@ -0,0 +1,69 @@ +/* + * @lc app=leetcode.cn id=26 lang=java + * + * [26] 删除排序数组中的重复项 + * + * https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/description/ + * + * algorithms + * Easy (43.75%) + * Total Accepted: 106K + * Total Submissions: 242.3K + * Testcase Example: '[1,1,2]' + * + * 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 + * + * 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 + * + * 示例 1: + * + * 给定数组 nums = [1,1,2], + * + * 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 + * + * 你不需要考虑数组中超出新长度后面的元素。 + * + * 示例 2: + * + * 给定 nums = [0,0,1,1,1,2,2,3,3,4], + * + * 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 + * + * 你不需要考虑数组中超出新长度后面的元素。 + * + * + * 说明: + * + * 为什么返回数值是整数,但输出的答案是数组呢? + * + * 请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。 + * + * 你可以想象内部操作如下: + * + * // nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 + * int len = removeDuplicates(nums); + * + * // 在函数里修改输入数组对于调用者是可见的。 + * // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。 + * for (int i = 0; i < len; i++) { + * print(nums[i]); + * } + * + * + */ +class Solution { + public int removeDuplicates(int[] nums) { + if (nums.length == 0) { + return 0; + } + // 额外使用一个指针来跟着走 + int a = 0; + for (int i = 1; i <= nums.length - 1; i++) { + if (nums[a] != nums[i]) { + a++; + nums[a] = nums[i]; + } + } + return a + 1; + } +} diff --git a/Week 01/id_658/LeetCode_283_658.java b/Week 01/id_658/LeetCode_283_658.java new file mode 100644 index 000000000..326ce78cc --- /dev/null +++ b/Week 01/id_658/LeetCode_283_658.java @@ -0,0 +1,23 @@ +/* + * @lc app=leetcode.cn id=283 lang=java + * + * [283] 移动零 + */ + +// @lc code=start +class Solution { + public void moveZeroes(int[] nums) { + // 双指针 往前移动非零元素 + int p = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != 0) { + // 出现了元素为0的情况 + if (i > p) { + nums[p] = nums[i]; + nums[i] = 0; + } + p++; + } + } + } +} diff --git a/Week 01/id_658/LeetCode_641_658.java b/Week 01/id_658/LeetCode_641_658.java new file mode 100644 index 000000000..404a65be9 --- /dev/null +++ b/Week 01/id_658/LeetCode_641_658.java @@ -0,0 +1,117 @@ +/* + * @lc app=leetcode.cn id=641 lang=java + * + * [641] 设计循环双端队列 + */ + +// @lc code=start +class MyCircularDeque { + + private int[] data; + private int head; + private int tail; + private int length; + private int capacity; + + /** Initialize your data structure here. Set the size of the deque to be k. */ + public MyCircularDeque(int k) { + capacity = k; + head = 0; + tail = 0; + length = 0; + data = new int[k]; + } + + /** Adds an item at the front of Deque. Return true if the operation is successful. */ + public boolean insertFront(int value) { + if (isFull()) { + return false; + } + if (head - 1 >= 0) { + head--; + } else { + head = capacity - 1; + } + data[head] = value; + length++; + return true; + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + public boolean insertLast(int value) { + if (isFull()) { + return false; + } + data[tail] = value; + tail = (tail + 1) % capacity; + length++; + return true; + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + public boolean deleteFront() { + if (isEmpty()) { + return false; + } + head = (head + 1) % capacity; + length--; + return true; + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + public boolean deleteLast() { + if (isEmpty()) { + return false; + } + if (tail - 1 >= 0) { + tail--; + } else { + tail = capacity - 1; + } + length--; + return true; + } + + /** Get the front item from the deque. */ + public int getFront() { + if (isEmpty()) { + return -1; + } + return data[head]; + } + + /** Get the last item from the deque. */ + public int getRear() { + if (isEmpty()) { + return -1; + } + if (tail - 1 >= 0) { + return data[tail - 1]; + } + return data[capacity - 1]; + } + + /** Checks whether the circular deque is empty or not. */ + public boolean isEmpty() { + return length == 0; + } + + /** Checks whether the circular deque is full or not. */ + public boolean isFull() { + return length == capacity; + } + +} + +/** + * Your MyCircularDeque object will be instantiated and called as such: + * MyCircularDeque obj = new MyCircularDeque(k); + * boolean param_1 = obj.insertFront(value); + * boolean param_2 = obj.insertLast(value); + * boolean param_3 = obj.deleteFront(); + * boolean param_4 = obj.deleteLast(); + * int param_5 = obj.getFront(); + * int param_6 = obj.getRear(); + * boolean param_7 = obj.isEmpty(); + * boolean param_8 = obj.isFull(); + */ diff --git a/Week 01/id_658/LeetCode_66_658.java b/Week 01/id_658/LeetCode_66_658.java new file mode 100644 index 000000000..1a6df43de --- /dev/null +++ b/Week 01/id_658/LeetCode_66_658.java @@ -0,0 +1,25 @@ +/* + * @lc app=leetcode.cn id=66 lang=java + * + * [66] 加一 + */ + +// @lc code=start +class Solution { + public int[] plusOne(int[] digits) { + // 给最后一个元素做加一操作,如果满足进位 就往前进一位 + for (int i = digits.length - 1; i >= 0; i--) { + digits[i]++; + // 判断是否加一后需要进位 + digits[i] %= 10; + // 如果不需要就加完了直接返回 + if (digits[i] != 0) { + return digits; + } + } + // 如果循环完没返回就是都进了一位,就变为比原来多一位的1xxxx数 + digits = new int[digits.length + 1]; + digits[0] = 1; + return digits; + } +} diff --git a/Week 01/id_658/LeetCode_88_658.java b/Week 01/id_658/LeetCode_88_658.java new file mode 100644 index 000000000..7da7549a8 --- /dev/null +++ b/Week 01/id_658/LeetCode_88_658.java @@ -0,0 +1,25 @@ +/* + * @lc app=leetcode.cn id=88 lang=java + * + * [88] 合并两个有序数组 + */ + +// @lc code=start +class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + // 暴力解法 copy一个数组 然后双重遍历往里加 + // 暴力的合并后排序 + // 双指针 从最大的元素往前加 + for (int l = m + n - 1, i = m - 1, j = n - 1; l >= 0; l--) { + if (i < 0) { + System.arraycopy(nums2, 0, nums1, 0, j + 1); + break; + } + if (j < 0) { + break; + } + nums1[l] = (nums1[i] < nums2[j]) ? nums2[j--] : nums1[i--]; + } + + } +} diff --git a/Week 01/id_658/NOTE.md b/Week 01/id_658/NOTE.md index a6321d6e2..158223042 100644 --- a/Week 01/id_658/NOTE.md +++ b/Week 01/id_658/NOTE.md @@ -1,4 +1,162 @@ -# NOTE +# 第一周学习总结 +## 数组 + +- 申请数组时,计算机实际上是在内从中开辟了一段连续内存地址,每一个地址可以通过内存管理器访问 +- 数组的访问时间快,访问任一元素的时间复杂度为常数级的**O(1)** +- 数组插入元素时,需要先进行元素的移动,所以时间复杂度为线性的**O(n)** +- 数组在删除元素后,需要将剩余的元素进行移动,所以时间复杂度也是**O(n)** + +## 链表 + +- 元素由 `value` 和 `next` 构成,`next` 指向下一个元素 +- 每一个元素一般有一个 `Node` 类来定义,有一个 `next` 指针的为单链表,存在 `next` 和 `prev` 指针的为双向链表 + 例如 LinkedList + + ```java + class LinkedList { + Node head; + class Node { + int value; + Node next; + Node(int d) { + value = d; + } + } + } + ``` + +- 头指针一般用 `Head` 表示,尾指针用 `Tail` 表示, 一般情况下尾指针的 `next` 指向 *Null* ,若尾指针 `Tail` 指向 `Head`则叫做循环链表 +- 链表的插入是将前一个节点的 `next` 指向要插入的 `Node` 并将此 `Node` 的 `next` 指向下一个节点,该操作的时间复杂度为**O(1)** +- 链表的删除可以视为插入的逆操作,将前一个节点的 `next` 指向要删除的节点的 `next` 指向的节点,该操作的时间复杂度为**O(1)** +- 随机访问链表中元素的时间复杂度为**O(n)** + +## 跳表 + +- 优化链表查找的线性时间复杂度 +- 两个重要思想 **升维** 和 **空间换时间** +- 对链表进行升维优化 + - 增加头尾指针,方便查找头尾附近的元素 + - 升维,多添加一些指针,例如中间指针 +- 添加索引 + - 第一级索引:第一个指针指向头指针,之后指向 `next + 1` 的位置,即每次跨越两个元素 + - 第二级索引:在第一级索引的基础上乘以二,即每次跨越四个元素 + - 多级索引:增加 `log2n` 个多级索引 +- 跳表中查询任意数据的时间复杂度为**O(logn)**,空间复杂度为**O(n)** + +## 栈、队列、双端队列 + +### 栈 + +- Stack +- 后进先出 `Last in - First out (LIFO)` +- 添加与删除的时间复杂度均为**O(1)**,查找的时间复杂度为**O(n)** +- Java 中的实现是一个类,继承自 `Vector` + +### 队列 + +- Queue +- 先进先出 `First in - First out (FIFO)` +- 添加与删除的时间复杂度均为**O(1)**,查找的时间复杂度为**O(n)** +- Java 中是一个接口,有多种实现 + +### 双端队列 + +- Deque,Double - End Queue +- 一个 `Stack` 和 `Queue` 的结合体,可以在队列前端和尾端进行 `push` 和 `pop` 操作 +- 添加与删除的时间复杂度均为**O(1)**,查找的时间复杂度为**O(n)** +- Java 中是一个接口 + +## 优先队列 + +- Priority Queue +- 定义的一种抽象数据结构,可以有多种实现 +- 插入操作的时间复杂度为**O(1)** +- 取出操作的时间复杂度为**O(logn)**,按照元素的优先级取出 +- 底层具体实现的数据结构较为多样和复杂 + - heap,也是可以多种实现的堆 + - bst,`binary search tree` 二叉搜索树 + - treap +- Java 中的实现是 `PriorityQueue` 类,继承自 `AbstractQueue`,实现了 `Queue` 接口 + - 通过 `compartor()` 函数来定义优先级 + +## 解题总结 + +### 步骤 + +1. 读题后5~10分钟想解题办法(实在想不出来直接去看解析) +2. 把所有解法列出来实现,找到最优解 +3. 做完后看别人的解题思路和结果,可以切换到 LeetCode 国际站再解一遍题并看一遍讨论 +4. 刷五遍题 + +### 思想 + +- 升维思想 +- 空间换时间 +- 找重复子,例如 Fibonacci 列表问题 +- 快慢指针(环形链表) + +### 技巧 + +- 一些常用的解题代码要记住 +- 用两个队列实现栈,用两个栈实现队列 +- 滑动窗口 `Sliding Window` 问题,用一个双端队列处理 + +## 总结 + +- 一些算法题还是第一时间没有使用响应数据结构解题的意识,还需要自己多去练习并刻意的培养习惯 +- 有一些题少考虑了一些边界情况,需要注意 +- 多拿笔在纸上画一下,有助于理清思路 +- 还需要多去看看视频中提到的相关源码 + +## 作业 + +- 用add first或add last这套新的API改写Deque的代码 + ```java + /** + * Before + */ + public static void srcDemo(){ + Deque deque = new LinkedList<>(); + + deque.push("a"); + deque.push("b"); + deque.push("c"); + + System.out.println(deque); + + String str = deque.peek(); + System.out.println(str); + System.out.println(deque); + + while (deque.size() > 0) { + System.out.println(deque.pop()); + } + + System.out.println(deque); + } + + /** + * After + */ + public static void targetDemo(){ + Deque deque = new LinkedList<>(); + + deque.addFirst("a"); + deque.addFirst("b"); + deque.addFirst("c"); + + System.out.println(deque); + + String str = deque.peekFirst(); + System.out.println(str); + System.out.println(deque); + + while (deque.size() > 0) { + System.out.println(deque.removeFirst()); + } + System.out.println(deque); + } + ``` diff --git a/Week 01/id_663/.gitignore b/Week 01/id_663/.gitignore new file mode 100644 index 000000000..a8c1963e8 --- /dev/null +++ b/Week 01/id_663/.gitignore @@ -0,0 +1,3 @@ +*.class +.idea +.DS_Store diff --git a/Week 01/id_663/LeetCode_189_663.java b/Week 01/id_663/LeetCode_189_663.java new file mode 100644 index 000000000..3faa732df --- /dev/null +++ b/Week 01/id_663/LeetCode_189_663.java @@ -0,0 +1,23 @@ +class Solution { + public void rotate(int[] nums, int k) { + int newK = k % nums.length; + System.out.println(newK); + if (0 != newK) { + int[] temp = new int[newK]; + System.arraycopy(nums, nums.length - newK, temp, 0, newK); + for (int i = nums.length - newK - 1; i >= 0; --i) { + nums[i + newK] = nums[i]; + } + System.arraycopy(temp, 0, nums, 0, newK); + } + return; + } +} + +public class LeetCode_189_663 { + public static void main(String[] args) { + Solution s = new Solution(); + int[] a = new int[]{1,2}; + s.rotate(a, 3); + } +} diff --git a/Week 01/id_663/LeetCode_26_663.java b/Week 01/id_663/LeetCode_26_663.java new file mode 100644 index 000000000..ed651d97d --- /dev/null +++ b/Week 01/id_663/LeetCode_26_663.java @@ -0,0 +1,19 @@ +class Solution { + public int removeDuplicates(int[] nums) { + if (0 == nums.length) + return 0; + int distinctIdx = 0; + for (int it = 1; it < nums.length; ++it) { + if (nums[it] != nums[distinctIdx]) + nums[++distinctIdx] = nums[it]; + } + return distinctIdx + 1; + } +} + +public class LeetCode_26_663 { + public static void main(String[] args) { + Solution solution = new Solution(); + System.out.println(solution.removeDuplicates(new int[]{1, 1, 2, 3})); + } +} diff --git a/Week 01/id_668/NOTE.md b/Week 01/id_668/NOTE.md index a6321d6e2..7b4022fd6 100644 --- a/Week 01/id_668/NOTE.md +++ b/Week 01/id_668/NOTE.md @@ -1,4 +1,4 @@ # NOTE - - +tony.fang 668 +again diff --git a/Week 01/id_668/leetcode_189_668.py b/Week 01/id_668/leetcode_189_668.py new file mode 100644 index 000000000..f3ed34185 --- /dev/null +++ b/Week 01/id_668/leetcode_189_668.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: rotate_array.py + @time: 2019/10/17 20:14 +""" + + +class Solution(object): + """ + 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。 + + 示例 1: + 输入: [1,2,3,4,5,6,7] 和 k = 3 + 输出: [5,6,7,1,2,3,4] + 解释: + 向右旋转 1 步: [7,1,2,3,4,5,6] + 向右旋转 2 步: [6,7,1,2,3,4,5] + 向右旋转 3 步: [5,6,7,1,2,3,4] + + 示例 2: + 输入: [-1,-100,3,99] 和 k = 2 + 输出: [3,99,-1,-100] + 解释: + 向右旋转 1 步: [99,-1,-100,3] + 向右旋转 2 步: [3,99,-1,-100] + + + 说明: + 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。 + 要求使用空间复杂度为 O(1) 的 原地 算法。 + + 分析:题目中是要求将数组中的元素向右移动k个位置,首先想到的是需要对k进行数组长度的求模运算, + 若k值很大,则对数组元素移动套圈是没有必要的,比如数组长度为7,这里移动8个位置和移动1个位置 + 是等价的。其次从题目中的解释来看,想到将元素移动一步作为一个操作单元,然后做k个步骤的操作, + 这是一个思路,对应了如下的1、2两个实现方法。反转元素的做法是别的地方提示的一点思路。 + 题解中环状替换的解法其实自己是首先想到的,但由于实现没成功,所以觉得这种方法可能不会成功, + 后来看题解讲到了这个方法,所以觉得有必要自己再重新分析,重新求解。所以这个环状替换解法后面再补充上, + 这个地方自己有点跟自己较劲。 + """ + + def rotate1(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: None Do not return anything, modify nums in-place instead. + + 时间复杂度:O(k * n) + 空间复杂度:O(1) + """ + k %= len(nums) + + while k > 0: + pre = nums[len(nums) - 1] + + for i in range(len(nums)): + nums[i], pre = pre, nums[i] + + k -= 1 + + def rotate2(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: None Do not return anything, modify nums in-place instead. + + 时间复杂度:O(k * n) + 空间复杂度:O(1) + """ + k %= len(nums) + + while k > 0: + e = nums.pop() + nums.insert(0, e) + + k -= 1 + + def rotate3(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: None Do not return anything, modify nums in-place instead. + + 时间复杂度:O(n) + 空间复杂度:O(1) + """ + + def reverse(a, i, j): + """ + 数组反转操作 + a:数组 + i:起始索引 + j:终止索引 + """ + while i < j: + a[i], a[j] = a[j], a[i] + i += 1 + j -= 1 + + k %= len(nums) + reverse(nums, 0, len(nums) - 1) + reverse(nums, 0, k - 1) + reverse(nums, k, len(nums) - 1) + diff --git a/Week 01/id_668/leetcode_1_668.py b/Week 01/id_668/leetcode_1_668.py new file mode 100644 index 000000000..e73f7f1b1 --- /dev/null +++ b/Week 01/id_668/leetcode_1_668.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: two_sum.py + @time: 2019/10/19 08:15 +""" + + +class Solution(object): + """ + 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数, + 并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 + + 示例: + 给定 nums = [2, 7, 11, 15], target = 9 + + 因为 nums[0] + nums[1] = 2 + 7 = 9 + 所以返回 [0, 1] + + 分析:寻找数组中两个不同位置的元素的和等于target的唯一解。这里首先想到的是两层枚举方法,因为是不同位置 + 元素,所以i指针范围为0 ~ len(nums) - 2,指针j范围为i + 1 ~ len(nums) - 1。时间复杂度为O(n * n), + 空间复杂度为O(1)。另一种方法是使用空间换时间的思想,引入哈希表结构,哈希表结构查找一个元素的时间复杂度为 + O(1),所以能够提升运算效率。因此这种方式的时间复杂度为O(n),空间复杂度为O(n)。 + """ + + def two_sum(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: List[int] + + 时间复杂度:O(n * n) + 空间复杂度:O(1) + """ + _len = len(nums) + + for i in range(_len - 1): + for j in range(i + 1, _len): + if nums[i] + nums[j] == target: + return [i, j] + + return [] + + def two_sum2(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: List[int] + + 空间换时间 + 时间复杂度:O(n) + 空间复杂度:O(n) + """ + _dict = {} + + for k, v in enumerate(nums): + val = target - v + + if val in _dict: + return [_dict[val], k] + + _dict[v] = k + + return [] + diff --git a/Week 01/id_668/leetcode_21_668.py b/Week 01/id_668/leetcode_21_668.py new file mode 100644 index 000000000..e7483670e --- /dev/null +++ b/Week 01/id_668/leetcode_21_668.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: merge_two_sorted_list.py + @time: 2019/10/18 07:58 +""" + + +# Definition for singly-linked list. +class ListNode(object): + def __init__(self, x): + self.val = x + self.next = None + + +class Solution(object): + """ + 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。  + + 示例: + 输入:1->2->4, 1->3->4 + 输出:1->1->2->3->4->4 + + 分析:因为合并的是两个有序链表,所以很容易想到从头至尾一个一个的比较两个链表中的元素,然后 + 将其串起来即可。觉得这里题意说的有序应该特指一下两个链表具有相同的顺序,如果一个升序,一个 + 降序,则需要处理一下。另外这里引入一个哨兵结点dummy,用于后面的处理具有一致性。 + """ + + def merge_two_lists(self, l1, l2): + """ + :type l1: ListNode + :type l2: ListNode + :rtype: ListNode + + 最少需要遍历的元素个数为min(len_of_l1, len_of_l2), + 最多的元素个数为len_of_l1 + len_of_l2,空间使用上可忽略不计 + 时间复杂度:O(min(l1, l2)) ~ O(l1 + l2) + 空间复杂度:O(1) + """ + dummy = cur = ListNode(0) + + while l1 and l2: + if l1.val <= l2.val: + cur.next, cur = l1, l1 + l1 = l1.next + else: + cur.next, cur = l2, l2 + l2 = l2.next + + cur.next = l1 if l1 else l2 + + return dummy.next + diff --git a/Week 01/id_668/leetcode_26_668.py b/Week 01/id_668/leetcode_26_668.py new file mode 100644 index 000000000..f3862838e --- /dev/null +++ b/Week 01/id_668/leetcode_26_668.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: remove_duplicate.py + @time: 2019/10/16 19:53 +""" + + +class Solution(object): + """ + 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 + 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 + + 示例 1: + 给定数组 nums = [1,1,2], + 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 + 你不需要考虑数组中超出新长度后面的元素。 + + 示例 2: + 给定 nums = [0,0,1,1,1,2,2,3,3,4], + 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 + 你不需要考虑数组中超出新长度后面的元素。 + """ + + def remove_duplicates(self, nums): + """ + :type nums: List[int] + :rtype: int + + 思考:应该是学习到了超哥课堂上讲到的关于数组解题的双指针技巧,题目要求使用O(1)的额外空间,所以首先尝试了 + 双指针的解题方法。首先这是个有序数组,两个指针,指针j用于正常遍历数组,指针i标示当前没有重复区域的最后一个元素, + 当遍历中遇到j位置元素与i位置元素不相等时,这个时候i向后移动一位,然后将j位置的元素赋值到i位置, + 赋值完之后i位置元素依然是非重复区域的最后一个元素。类似的操作直到数组遍历结束。i位置元素始终是非重复区域 + 的最后一个元素的关键是数组是有序的,所以在往下的遍历中,不会遇到j位置与i位置之前的元素相等的情况。 + 时间复杂度:O(n) + 空间复杂度:O(1) + """ + i, j = 0, 0 + + for j in range(len(nums)): + if nums[i] != nums[j]: + i += 1 + nums[i] = nums[j] + + return i + 1 + diff --git a/Week 01/id_668/leetcode_283_668.py b/Week 01/id_668/leetcode_283_668.py new file mode 100644 index 000000000..fb4d842bd --- /dev/null +++ b/Week 01/id_668/leetcode_283_668.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: move_zero.py + @time: 2019/10/19 19:28 +""" + + +class Solution(object): + """ + 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 + + 示例: + + 输入: [0,1,0,3,12] + 输出: [1,3,12,0,0] + 说明: + + 必须在原数组上操作,不能拷贝额外的数组。 + 尽量减少操作次数 + + 分析:对数组类型的题目,自己首先考虑的是使用双指针法,题目中又规定不能使用额外的数组,因此 + 空间换时间的做法也就无法使用了。 + """ + + def move_zeroes(self, nums): + """ + :type nums: List[int] + :rtype: None Do not return anything, modify nums in-place instead. + + 这是自己实现的交换元素的做法,i指针标示第一个0元素所在的位置,当前元素0做完交换操作后, + i则继续往下寻找0元素的位置,指针j正常遍历数组,遇到不为0的元素则与i位置进行交换。重复 + 操作,直到遍历结束。 + 时间复杂度:O(n) + 空间复杂度:O(1) + """ + i = None + + for j in range(len(nums)): + if nums[j] == 0: + if i is None: + i = j + else: + # 不为0的元素,参与元素交换 + if i is not None: + nums[i], nums[j], i = nums[j], nums[i], i + 1 + + while i <= j: + if nums[i] == 0: + # 交换完成后,则继续寻找下一个0元素的位置 + break + + i += 1 + + def move_zeroes2(self, nums): + """ + :type nums: List[int] + :rtype: None Do not return anything, modify nums in-place instead. + + 这是leetcode上比较好的写法,处理的特别巧妙,因此代码才会如此简洁。觉得唯一不好的地方是 + 元素交换不仅会在0与非0之间,也会发生在非0与非0之间,因此这个处理有点冗余。所以下一个方法 + 的改进之处是只让元素交换发生在0与非0之间。经过大量测试发现,这个方法的实现效率低于方法1与 + 方法3。 + 时间复杂度:O(n) + 空间复杂度:O(1) + """ + zero = 0 + + for i in range(len(nums)): + if nums[i] != 0: + nums[i], nums[zero] = nums[zero], nums[i] + zero += 1 + + def move_zeroes3(self, nums): + """ + :type nums: List[int] + :rtype: None Do not return anything, modify nums in-place instead. + + 对方法2的改进,使得元素交换的操作只发生在0与非0之间 + 时间复杂度:O(n) + 空间复杂度:O(1) + """ + zero = 0 + + for i in range(len(nums)): + if nums[i] != 0: + if i != zero: + # 交换只发生在0与非0之间 + nums[i], nums[zero] = nums[zero], nums[i] + + zero += 1 + diff --git a/Week 01/id_673/design-circular-deque.py b/Week 01/id_673/design-circular-deque.py new file mode 100644 index 000000000..98b574446 --- /dev/null +++ b/Week 01/id_673/design-circular-deque.py @@ -0,0 +1,275 @@ +class Node: + + def __init__(self, v): + + self.pre, self.nxt, self.v = None, None, v + + + +class MyCircularDeque: + + def __init__(self, k: 'int'): + + self.head, self.tail, self.k, self.cnt = Node(0), Node(0), k, 0 + + self.head.nxt, self.tail.pre = self.tail, self.head + + + + def _addAfter(self, node, toAdd): + + nxt = node.nxt + + node.nxt = toAdd + + toAdd.pre = node + + toAdd.nxt = nxt + + nxt.pre = toAdd + + + + def _delAfter(self, node): + + nxt = node.nxt.nxt + + node.nxt = nxt + + nxt.pre = node + + + + def insertFront(self, value: 'int') -> 'bool': + + + + if self.cnt == self.k: + + return False + + self.cnt += 1 + + self._addAfter(self.head, Node(value)) + + return True + + + + + + + + + + + + + + + + def insertLast(self, value: 'int') -> 'bool': + + + + + + + + if self.cnt == self.k: return False + + + + + + + + self.cnt += 1 + + + + + + + + self._addAfter(self.tail.pre, Node(value)) + + + + + + + + return True + + + + + + + + + + + + + + + + def deleteFront(self) -> 'bool': + + + + + + + + if self.cnt == 0: return False + + + + + + + + self.cnt -= 1 + + + + + + + + self._delAfter(self.head) + + + + + + + + return True + + + + + + + + + + + + + + + + def deleteLast(self) -> 'bool': + + + + + + + + if self.cnt == 0: return False + + + + + + + + self.cnt -= 1 + + + + + + + + self._delAfter(self.tail.pre.pre) + + + + + + + + return True + + + + + + + + + + + + + + + + def getFront(self) -> 'int': + + + + + + + + return self.head.nxt.v if self.cnt else -1 + + + + + + + + + + + + + + + + def getRear(self) -> 'int': + + + + + + + + return self.tail.pre.v if self.cnt else -1 + + + + + + + + + + + + + + + + def isEmpty(self) -> 'bool': + + + + + + + + return self.cnt == 0 + + def isFull(self) -> 'bool': + + return self.cnt == self.k diff --git a/Week 01/id_673/rotate-array.py b/Week 01/id_673/rotate-array.py new file mode 100644 index 000000000..800fcee62 --- /dev/null +++ b/Week 01/id_673/rotate-array.py @@ -0,0 +1,7 @@ + class Solution: + def rotate(self, nums: List[int], k: int) -> None: + for i in range(k): + tmp = nums[-1] + for j in range(len(nums)-1, 0, -1): + nums[j] = nums[j-1] + nums[0] = tmp diff --git a/Week 01/id_683/LeetCode_11_683.java b/Week 01/id_683/LeetCode_11_683.java new file mode 100644 index 000000000..30a7821dd --- /dev/null +++ b/Week 01/id_683/LeetCode_11_683.java @@ -0,0 +1,31 @@ +class Solution { + + /** + * 从两边向中间收敛 + * @param height + * @return + */ + public int maxArea(int[] height) { + int max = 0; + for (int i = 0, j = height.length - 1; i < j; ) { + int minHeight = height[i] > height[j] ? height[j--] : height[i++]; + max = Math.max(max, (j - i + 1) * minHeight); + } + return max; + } + + /** + * 暴力解法 + * @param height + * @return + */ + public int maxArea2(int[] height) { + int max = 0; + for (int x = 0; x < height.length - 1; ++x) { + for (int y = x + 1; y < height.length; ++y) { + max = Math.max(max, (y - x) * Math.min(height[x], height[y])); + } + } + return max; + } +} \ No newline at end of file diff --git a/Week 01/id_683/LeetCode_15_683.java b/Week 01/id_683/LeetCode_15_683.java new file mode 100644 index 000000000..db84b61e3 --- /dev/null +++ b/Week 01/id_683/LeetCode_15_683.java @@ -0,0 +1,77 @@ +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +class Solution { + + public List> threeSum(int[] nums) { + List> result = new ArrayList<>(); + Arrays.sort(nums); + for (int k = 0; k < nums.length - 2; ++k) { + if (nums[k] > 0) break; + if (k > 0 && nums[k] == nums[k - 1]) continue; + int i = k + 1, j = nums.length - 1; + while (i < j) { + int sum = nums[k] + nums[i] + nums[j]; + if (sum > 0) { + j--; + while (i < j && nums[j] == nums[j + 1]) j--; + } else if (sum < 0) { + i++; + while (i < j && nums[i] == nums[i - 1]) i++; + } else { + result.add(new ArrayList<>(Arrays.asList(nums[k], nums[i], nums[j]))); + j--; i++; + while (i < j && nums[j] == nums[j + 1]) j--; + while (i < j && nums[i] == nums[i - 1]) i++; + } + } + } + return result; + + } + + public List> threeSum2(int[] nums) { + List> result = new ArrayList<>(); + Arrays.sort(nums); + HashMap map = new HashMap<>(); + for (int i = 0; i < nums.length; ++i) { + map.put(nums[i], i); + } + for (int i = 0; i < nums.length - 2; ++i) { + for (int j = i + 1; j < nums.length - 1; ++j) { + int target = 0 - nums[i] - nums[j]; + if (map.containsKey(target) && map.get(target) > j) { + result.add(new ArrayList<>(Arrays.asList(nums[i], nums[j], target))); + // 因为map在put时会覆盖重复元素,所以直接get可以获取重复元素的最后一个索引 + j = map.get(nums[j]); + } + i = map.get(nums[i]); + } + } + return result; + } + /** + * 暴力法 + * @param nums + * @return + */ + public List> threeSum1(int[] nums) { + + List> result = new ArrayList<>(); + Arrays.sort(nums); + for (int i = 0; i < nums.length - 2; ++i) { + for (int j = i + 1; j < nums.length - 1; ++j) { + for (int k = j + 1; k < nums.length; ++k) { + if (i > 0 && nums[i] == nums[i - 1]) continue; + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + if (k > j + 1 && nums[k] == nums[k - 1]) continue; + if (nums[i] + nums[j] + nums[k] == 0) { + result.add(new ArrayList<>(Arrays.asList(nums[i], nums[j], nums[k]))); + } + } + } + } + return result; + } +} \ No newline at end of file diff --git a/Week 01/id_683/LeetCode_189_683.java b/Week 01/id_683/LeetCode_189_683.java new file mode 100644 index 000000000..372e5d029 --- /dev/null +++ b/Week 01/id_683/LeetCode_189_683.java @@ -0,0 +1,56 @@ +class Solution { + + + /** + * 通过3次翻转进行rotate + * @param nums + * @param k + */ + public void rotate(int[] nums, int k) { + if (nums == null || nums.length == 0) return ; + k %= nums.length; + reverseArray(nums, 0, nums.length - 1 -k); + reverseArray(nums, nums.length - k, nums.length - 1); + reverseArray(nums, 0, nums.length - 1); + } + private void reverseArray(int[] nums, int i, int j) { + while (i < j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + i++; + j--; + } + } + + + + public void rotate2(int[] nums, int k) { + if (nums == null || nums.length == 0) return ; + int[] newNums = new int[nums.length]; + for (int i = 0; i < nums.length; ++i) { + newNums[(i + k) % nums.length] = nums[i]; + } + + for (int i = 0; i < nums.length; ++i) { + nums[i] = newNums[i]; + } + } + + /** + * 暴力法 + */ + public void rotate1(int[] nums, int k) { + if (nums == null || nums.length == 0) return ; + for (int i = 0; i < k; i++) { + moveLastToFirst(nums); + } + } + private void moveLastToFirst(int[] nums) { + int target = nums[nums.length - 1]; + for (int i = nums.length - 1; i > 0; --i) { + nums[i] = nums[i - 1]; + } + nums[0] = target; + } +} \ No newline at end of file diff --git a/Week 01/id_683/LeetCode_1_683.java b/Week 01/id_683/LeetCode_1_683.java new file mode 100644 index 000000000..9b489ab93 --- /dev/null +++ b/Week 01/id_683/LeetCode_1_683.java @@ -0,0 +1,17 @@ +import java.util.HashMap; + +class Solution { + public int[] twoSum(int[] nums, int target) { + + HashMap map = new HashMap<>(); + + for (int i = 0; i < nums.length; ++i) { + if (map.containsKey(target - nums[i])) { + return new int[] {map.get(target - nums[i]), i}; + } + map.put(nums[i], i); + } + + throw new IllegalArgumentException("Finding no solution"); + } +} \ No newline at end of file diff --git a/Week 01/id_683/LeetCode_26_683.java b/Week 01/id_683/LeetCode_26_683.java new file mode 100644 index 000000000..dd36c56a6 --- /dev/null +++ b/Week 01/id_683/LeetCode_26_683.java @@ -0,0 +1,14 @@ +class Solution { + public int removeDuplicates(int[] nums) { + if (nums == null || nums.length == 0) return 0; + + int j = 0; + for (int i = 1; i < nums.length; ++i) { + if (nums[i] != nums[j]) { + j++; + nums[j] = nums[i]; + } + } + return j + 1; + } +} \ No newline at end of file diff --git a/Week 01/id_683/LeetCode_283_683.java b/Week 01/id_683/LeetCode_283_683.java new file mode 100644 index 000000000..129a8808a --- /dev/null +++ b/Week 01/id_683/LeetCode_283_683.java @@ -0,0 +1,43 @@ +class Solution { + + /** + * 使用辅助指针j,从0位置开始,遍历整个数组,将非0元素前移,将0后移 + * @param nums + */ + public void moveZeroes(int[] nums) { + + int j = 0; + for (int i = 0; i < nums.length; ++i) { + // 如果当前元素不为0,则将其放在数组前部,即j的位置。 + if (nums[i] != 0) { + nums[j] = nums[i]; + // 如果i, j不相等,则说明有0存在,将0后移 + if (i != j) { + nums[i] = 0; + } + j++; + } + } + + } + /** + * 两次循环,第一次循环将非0的元素逐个赋值到数组前面部分,第二次循环将剩余位置全部赋值为0 + * @param nums + */ + public void moveZeroes2(int[] nums) { + int zeroNum = 0; + int index = 0; + for (int num : nums) { + if (num == 0) { + zeroNum++; + } else { + nums[index++] = num; + } + } + + for (int i = 0; i < zeroNum; i++) { + nums[nums.length - zeroNum + i] = 0; + } + } + +} \ No newline at end of file diff --git a/Week 01/id_683/LeetCode_66_683.java b/Week 01/id_683/LeetCode_66_683.java new file mode 100644 index 000000000..21cd76d22 --- /dev/null +++ b/Week 01/id_683/LeetCode_66_683.java @@ -0,0 +1,22 @@ +class Solution { + public int[] plusOne(int[] digits) { + if (digits == null || digits.length == 0) return null; + int carry = 0; + + for (int i = digits.length - 1; i >= 0; --i) { + int d = i == digits.length - 1 ? digits[i] + 1 + carry : digits[i] + carry; + carry = d / 10; + digits[i] = d % 10; + } + if (carry == 1) { + int[] result = new int[digits.length + 1]; + result[0] = 1; + for (int i = 1; i < result.length; ++i) { + result[i] = digits[i - 1]; + } + return result; + } + + return digits; + } +} \ No newline at end of file diff --git a/Week 01/id_683/LeetCode_70_683.java b/Week 01/id_683/LeetCode_70_683.java new file mode 100644 index 000000000..41c01178e --- /dev/null +++ b/Week 01/id_683/LeetCode_70_683.java @@ -0,0 +1,20 @@ +class Solution { + + public int climbStairs(int n) { + if (n == 1) return 1; + if (n == 2) return 2; + + // return climbStairs(n - 1) + climbStairs(n - 2); + + int f1 = 1, f2 = 2; + int f3 = 0; + + for (int i = 3; i <= n; ++i) { + f3 = f2 + f1; + f1 = f2; + f2 = f3; + } + return f3; + } + +} \ No newline at end of file diff --git a/Week 01/id_683/NOTE.md b/Week 01/id_683/NOTE.md index a6321d6e2..0f459ba4a 100644 --- a/Week 01/id_683/NOTE.md +++ b/Week 01/id_683/NOTE.md @@ -1,4 +1,82 @@ -# NOTE +# 第一周学习总结 - +经过一周的算法学习,感觉已经逐渐进入状态,最终要的是走出了从前学习的**误区**:刷题只刷一遍! 老师在视频中反复提到刷题五毒神掌,在过遍数的同时,思考多种解法,没有思路一定要立刻看题解避免效率低下,并且要到国际站学习别人优秀的代码,再改进自己的代码,达到真正刻意练习的目的。 +在刷题时要时刻记着**升维**、**空间换时间**的思想。比如跳表就是基于这个思想设计出来的,其通过增加索引的方式提高链表的查询速度。 + +下面先简单总结一下本周学习的几个基本数据结构。 + +## 数组 + +数组在内存中是一段连续的内存空间,所以它可以通过计算内存地址的方式来达到O(1)时间的随机查询速度,但正是因为其存储方式的限制,数组的插入删除操作的时间复杂度为O(n),较为耗时。因为比如在数组头部插入一个新的元素,需要将数组内的元素全部后移一个位置再做插入操作;删除同理,需要将删除元素后面的所有元素前移一个位置。 + +数组各种操作的时间复杂度如下表所示: + +| 操作 | 时间复杂度 | +:-:|:-: +| prepend | O(n) | +| append | O(1) | +| lookup | O(1) | +| insert | O(n) | +| delete | O(n) | + + +## 链表 + +链表是逻辑上连续的顺序表结构,它是一个个Node节点链接起来的,Node节点是由value:节点保存的数据、next:指向后继节点地址的指针组成的,双向链表则是在每个Node节点中包含一个prev:指向前继节点地址的指针。 + +| 操作 | 时间复杂度 | +:-:|:-: +| prepend | O(1) | +| append | O(1) | +| lookup | O(n) | +| insert | O(1) | +| delete | O(1) | + +## 栈 + +栈是一个后进先出(LIFO)的结构可以由数组或链表实现,其对数据的操作是受限的,只能在数组或链表的一侧进行。因为栈的特殊性,只能对栈顶元素进行操作,所以其增加删除操作的时间复杂度均为O(1)。 + +## 队列 + +队列也是一个特殊的并且操作受限的线性表,它是一个(FIFO)的结构可以由数组或链表实现,队列只能在队头进行删除操作也就是出队操作,在队尾进行增加操作也就是入队操作。双向队列可以在队头和队尾进行出队和入队操作。 + +## 改写Deque操作代码 + + Deque deque = new LinkedList(); + deque.addFirst("a"); + deque.addFirst("b"); + deque.addFirst("c"); + System.out.println(deque); + String str = deque.getFirst(); + System.out.println(str); + System.out.println(deque); + while (deque.size() > 0) { + System.out.println(deque.removeFirst()); + } + System.out.println(deque); + +## Java Queue 源码分析 + +Java中Queue是java.util包中的一个接口,定义了队列操作的一系列方法,并且每个方法有两种形式,一种当操作失败时抛出异常,另一种返回特殊值如null、false。 + +||Throws exception|Returns special value| +:-:|:-:|:-: +Insert|add(e)|offer(e) +Remove|remove()|poll() +Examine|element()|peek() + +- boolean add(E e); 在队尾插入一个元素,如果队列容量不足则抛出异常 +- boolean offer(E e); 在队尾插入一个元素,如果队列容量不足则返回false +- E remove(); 移除队头元素,如果队列为空则抛出异常。 +- E poll(); 移除队头元素,如果队列为空则返回null。 +- E element(); 查看队头元素,如果队列为空则抛出异常。 +- E peek(); 查看队头元素,如果队列为空则返回null。 + +## Java PriorityQueue 源码分析 + +PriorityQueue是基于堆实现的,其内部的元素根据自然序排序,如果在构造函数中提供自定义的Comparator则会按照定义的方式排序。 + +PriorityQueue内部的元素保存在一个Object数组中,也就是说一个由数组实现的堆中。 + +PriorityQueue的查看操作的时间复杂度是O(1),offer和poll操作的时间复杂度是O(log(n))。offer操作将元素插入至堆的尾部,然后进行上浮的堆化调整操作,poll操作,先将第一个元素保存留作返回,然后将最后一个元素放至队首的位置,再进行下沉的堆化操作。 \ No newline at end of file diff --git "a/Week 01/id_688/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" "b/Week 01/id_688/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" new file mode 100644 index 000000000..ce53b857a Binary files /dev/null and "b/Week 01/id_688/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" differ diff --git "a/Week 01/id_688/\347\256\227\346\263\225\344\270\216\346\225\260\346\215\256\347\273\223\346\236\204.xmind" "b/Week 01/id_688/\347\256\227\346\263\225\344\270\216\346\225\260\346\215\256\347\273\223\346\236\204.xmind" new file mode 100644 index 000000000..271c2d576 Binary files /dev/null and "b/Week 01/id_688/\347\256\227\346\263\225\344\270\216\346\225\260\346\215\256\347\273\223\346\236\204.xmind" differ diff --git a/Week 01/id_693/LeetCode_189_693.java b/Week 01/id_693/LeetCode_189_693.java new file mode 100644 index 000000000..86861ffa0 --- /dev/null +++ b/Week 01/id_693/LeetCode_189_693.java @@ -0,0 +1,28 @@ +package id_693; + +/** + * @Desc 189. 旋转数组 :https://leetcode-cn.com/problems/rotate-array/ + * @Auther 李雷(KyLin) + * @Date 2019/10/14 + */ +public class LeetCode_189_693 { + public void rotate(int[] nums,int k) { + int count = k % nums.length; + reverse(nums,0,nums.length - 1); + reverse(nums,0,count - 1); + reverse(nums,count,nums.length - 1); + } + + private void reverse(int[] nums,int left,int right) { + int temp; + while (left < right) { + temp = nums[left]; + nums[left++] = nums[right]; + nums[right--] = temp; + } + } + + public static void main(String[] args) { + new LeetCode_189_693().rotate(new int[]{1,2,3,4,5,6,7},3); + } +} diff --git a/Week 01/id_693/LeetCode_1_693_1.java b/Week 01/id_693/LeetCode_1_693_1.java new file mode 100644 index 000000000..a35fe7110 --- /dev/null +++ b/Week 01/id_693/LeetCode_1_693_1.java @@ -0,0 +1,69 @@ +package id_693; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * @Desc 1. 两数之和 https://leetcode-cn.com/problems/two-sum/ + * @Auther 李雷(KyLin) + * @Date 2019/10/16 + */ +public class LeetCode_1_693_1 { + //暴力破解,直接双层循环 时间复杂度 0(n^2) + public int[] twoSum(int[] nums,int target) { + for (int i = 0; i < nums.length - 1; ++i) { + for (int j = i + 1; j < nums.length; ++j) { + if (nums[i] + nums[j] == target) { + return new int[]{i,j}; + } + } + } + return new int[0]; + } + + /** + * 1、开始优化, + * 首先用 new LeetCode_1_693().twoSum(new int[]{2,7,11,15,16,17,18,19,20,21,22,23,24},91); + * 获得次数最高 为78,数据越多次数越多。 + * 升维,空间换时间 + * 用hash表 + * 结果:0(n) + */ + + //使用hash 2个 for 0(n) + public int[] twoSum2(int[] nums,int target) { + Map map = new HashMap<>(nums.length,1); + for (int i = 0; i < nums.length; i++) { + map.put(nums[i],i); + } + for (int i = 0; i < nums.length; i++) { + int complement = target - nums[i]; + if (map.containsKey(complement) && map.get(complement) != i) { + return new int[]{i,map.get(complement)}; + } + } + return new int[0]; + } + + //再次优化 + public int[] twoSum3(int[] nums,int target) { + Map map = new HashMap<>(nums.length,1); + for (int i = 0; i < nums.length; i++) { + int complement = target - nums[i]; + if (map.containsKey(complement)) { + //因为后面才会知道结果,那么返回下标就需要反向 + return new int[]{map.get(complement),i}; + } + map.put(nums[i],i); + } + return new int[0]; + } + + public static void main(String[] args) { + System.out.println(Arrays.toString(new LeetCode_1_693_1().twoSum3(new int[]{3,2,4},6))); + ; + System.out.println(Arrays.toString(new LeetCode_1_693_1().twoSum(new int[]{2,7,11,15,16,17,18,19,20,21,22,23,24},91))); + System.out.println(Arrays.toString(new LeetCode_1_693_1().twoSum2(new int[]{2,7,11,15,16,17,18,19,20,21,22,23,24},91))); + } +} diff --git a/Week 01/id_693/LeetCode_21_693.java b/Week 01/id_693/LeetCode_21_693.java new file mode 100644 index 000000000..e18fe22a3 --- /dev/null +++ b/Week 01/id_693/LeetCode_21_693.java @@ -0,0 +1,22 @@ +package id_693; + +/** + * @Desc 21.合并两个有序链表 https://leetcode-cn.com/problems/merge-two-sorted-lists/submissions/ + * @Auther 李雷(KyLin) + * @Date 2019/10/14 + */ +public class LeetCode_21_693 { + public ListNode mergeTwoLists(ListNode l1,ListNode l2) { + ListNode head = new ListNode(0); + ListNode r = head; + while (l1 != null && l2 != null) { + boolean is = l1.val < l2.val; + head.next = is ? l1 : l2; + l1 = is ? l1.next : l1; + l2 = is ? l2 : l2.next; + head = head.next; + } + head.next = l1 == null ? l2 : l1; + return r.next; + } +} diff --git a/Week 01/id_693/LeetCode_26_693.java b/Week 01/id_693/LeetCode_26_693.java new file mode 100644 index 000000000..5cb5dbd34 --- /dev/null +++ b/Week 01/id_693/LeetCode_26_693.java @@ -0,0 +1,27 @@ +package id_693; + +/** + * @Desc 26. 删除排序数组中的重复项:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/ + * @Auther 李雷(KyLin) + * @Date 2019/10/14 + */ +public class LeetCode_26_693 { + public int removeDuplicates(int[] nums) { + + int len = nums.length; + if (len == 0) return len; + int i = 0; + for (int j = 1; j < len; ++j) { + if (nums[i] != nums[j]) { + i++; + nums[i] = nums[j]; + } + } + return i + 1; + } + + public static void main(String[] args) { + new LeetCode_26_693().removeDuplicates(new int[0]); + new LeetCode_26_693().removeDuplicates(new int[]{0,0,1,1,1,2,2,3,3,4}); + } +} diff --git a/Week 01/id_693/LeetCode_283_693.java b/Week 01/id_693/LeetCode_283_693.java new file mode 100644 index 000000000..e0ac9fd8e --- /dev/null +++ b/Week 01/id_693/LeetCode_283_693.java @@ -0,0 +1,37 @@ +package id_693; + +/** + * @Desc 283. 移动零 https://leetcode-cn.com/problems/move-zeroes/ + * @Auther 李雷(KyLin) + * @Date 2019/10/14 + */ +public class LeetCode_283_693 { + public void moveZeroes(int[] nums) { + int index = 0; + int temp = 0; + int len = nums.length; + for (int i = 0; i < len; ++i) { + if (nums[i] != 0) { + temp = nums[index]; + nums[index] = nums[i]; + nums[i] = temp; + index++; + } + } + } + + public void moveZeroes_2(int[] nums) { + int len = nums.length; + for (int i = 0, j = 0; i < len; ++i) { + if (nums[i] != 0) { + nums[j] = nums[i]; + if (j != i) { + nums[i] = 0; + } + j++; + } + } + } + + +} diff --git a/Week 01/id_693/LeetCode_42_693.java b/Week 01/id_693/LeetCode_42_693.java new file mode 100644 index 000000000..e781eb7c4 --- /dev/null +++ b/Week 01/id_693/LeetCode_42_693.java @@ -0,0 +1,96 @@ +package id_693; + +/** + * @Desc 42. 接雨水 https://leetcode-cn.com/problems/trapping-rain-water/ + * @Auther 李雷(KyLin) + * @Date 2019/10/20 + */ +public class LeetCode_42_693 { + //暴力破解:直接找出当前左边最大和右边最大值比较谁最小,然后减去自身就是数量。 时间O(n^2) 空间O(1) + public int trap(int[] height) { + int max = 0; + for (int i = 0; i < height.length; i++) { + int leftMax = 0; + int rightMax = 0; + //以当前为起点,找出左边的最大值 + for (int j = i; j >= 0; j--) { + leftMax = Math.max(leftMax,height[j]); + } + //以当前为起点找出右边的最大值 + for (int j = i; j < height.length; j++) { + rightMax = Math.max(rightMax,height[j]); + } + //获得左右两边最大值中的最小值 减去自身即可(如果自身是最大值,那么就会是自身-自身) + max += Math.min(leftMax,rightMax) - height[i]; + } + return max; + } + + //优化:使用栈或数组来优化,数据保存最大的,根据暴力破解优化,升维思想 时间O(n) 空间O(n) + public int trap2(int[] height) { + int max = 0; + int left = 0; + int right = 0; + int[] leftMax = new int[height.length];//保存左边最大的 + int[] rightMax = new int[height.length];//保存右边最大的 + for (int i = 0; i < height.length; i++) { + left = Math.max(left,height[i]); + leftMax[i] = left; + right = Math.max(right,height[height.length - i - 1]); + rightMax[height.length - i - 1] = right; + } + for (int i = 0; i < height.length; i++) { + max += Math.min(leftMax[i],rightMax[i]) - height[i]; + } + return max; + } + + //优化:对第二步优化,可以发现左边的和计算结果的循环是重复的,那么就消去左边的 数组 + public int trap3(int[] height) { + int max = 0; + int left = 0; + int right = 0; + int[] rightMax = new int[height.length];//保存右边最大的 + for (int i = 0; i < height.length; i++) { + right = Math.max(right,height[height.length - i - 1]); + rightMax[height.length - i - 1] = right; + } + for (int i = 0; i < height.length; i++) { + left = Math.max(left,height[i]); + max += Math.min(left,rightMax[i]) - height[i]; + } + return max; + } + + //再次优化,使用双指针,还是利用上一次优化进行优化,达到 时间O(n) 空间O(1) + //根据上一步,如果左边最大值小于右边最大值,那么取左的值,根据之前优化得出,找出左右的最大值,然后找出他们中最小的。 + // 如果左边最大值大于右边最大值,那么取右的值,同上 + // 左右指针谁的值小就进行计算,计算的时候直接看是否是原本的最大值,然后减去自身即可。(不同的时候,建议从上面几步解法一步步的来) + public int trap4(int[] height) { + int max = 0; + + int leftMax = 0; + int rightMax = 0; + int left = 0; + int right = height.length - 1; + while (left < right) { + if (height[left] < height[right]) { + leftMax = Math.max(leftMax,height[left]); + max += leftMax - height[left]; + left++; + } else { + rightMax = Math.max(rightMax,height[right]); + max += rightMax - height[right]; + right--; + } + } + return max; + } + + public static void main(String[] args) { + System.out.println(new LeetCode_42_693().trap(new int[]{0,1,0,2,1,0,1,3,2,1,2,1})); + System.out.println(new LeetCode_42_693().trap2(new int[]{0,1,0,2,1,0,1,3,2,1,2,1})); + System.out.println(new LeetCode_42_693().trap3(new int[]{0,1,0,2,1,0,1,3,2,1,2,1})); + System.out.println(new LeetCode_42_693().trap4(new int[]{0,1,0,2,1,0,1,3,2,1,2,1})); + } +} diff --git a/Week 01/id_693/LeetCode_641_693.java b/Week 01/id_693/LeetCode_641_693.java new file mode 100644 index 000000000..9ff67bdde --- /dev/null +++ b/Week 01/id_693/LeetCode_641_693.java @@ -0,0 +1,122 @@ +package id_693; + +/** + * @Desc 641. 设计循环双端队列 https://leetcode-cn.com/problems/design-circular-deque/ + * @Auther 李雷(KyLin) + * @Date 2019/10/20 + */ +public class LeetCode_641_693 { + /* + 注意事项,front:直接把元素赋值到当前front的下标,然后把元素下标 - 1 (用求余避免下标越界),那么front指向 (front - 1 + capacity) % capacity 也就是上一个元素下标的位置,如果是0则指向最后 + rear: 直接把rear下标 + 1 (同样用求雨避免越界),然后把元素赋值给新的rear,那么rear指着最后的元素 + + 特别注意:front 和rear都是0的时候处理 + * */ + int front; + int rear; + int capacity; + int size; + int[] elements; + + /** + * Initialize your data structure here. Set the size of the deque to be k. + */ + public LeetCode_641_693(int k) { + this.elements = new int[k]; + this.front = 0; + this.rear = 0; + this.size = 0; + this.capacity = k; + } + + /** + * Adds an item at the front of Deque. Return true if the operation is successful. + */ + public boolean insertFront(int value) { + if (front == rear && size == capacity) { + return false; + } + elements[front] = value; + front = (front - 1 + capacity) % capacity; + size++; + return true; + } + + /** + * Adds an item at the rear of Deque. Return true if the operation is successful. + */ + public boolean insertLast(int value) { + if (front == rear && size == capacity) { + return false; + } + rear = (rear + 1 + capacity) % capacity; + elements[rear] = value; + size++; + return true; + } + + /** + * Deletes an item from the front of Deque. Return true if the operation is successful. + */ + public boolean deleteFront() { + if (size == 0) { + return false; + } + front = (front + 1 + capacity) % capacity; + size--; + return true; + } + + /** + * Deletes an item from the rear of Deque. Return true if the operation is successful. + */ + public boolean deleteLast() { + if (size == 0) { + return false; + } + rear = (rear - 1 + capacity) % capacity; + size--; + return true; + } + + /** + * Get the front item from the deque. + */ + public int getFront() { + return size == 0 ? -1 : elements[(front + 1 + capacity) % capacity]; + } + + /** + * Get the last item from the deque. + */ + public int getRear() { + return size == 0 ? -1 : elements[rear]; + } + + /** + * Checks whether the circular deque is empty or not. + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * Checks whether the circular deque is full or not. + */ + public boolean isFull() { + return size == capacity; + } +} + +/** + * Your LeetCode_641_693 object will be instantiated and called as such: + * LeetCode_641_693 obj = new LeetCode_641_693(k); + * boolean param_1 = obj.insertFront(value); + * boolean param_2 = obj.insertLast(value); + * boolean param_3 = obj.deleteFront(); + * boolean param_4 = obj.deleteLast(); + * int param_5 = obj.getFront(); + * int param_6 = obj.getRear(); + * boolean param_7 = obj.isEmpty(); + * boolean param_8 = obj.isFull(); + */ \ No newline at end of file diff --git a/Week 01/id_693/LeetCode_66_693.java b/Week 01/id_693/LeetCode_66_693.java new file mode 100644 index 000000000..475e61bba --- /dev/null +++ b/Week 01/id_693/LeetCode_66_693.java @@ -0,0 +1,79 @@ +package id_693; + + +import java.util.Arrays; + +/** + * @Author 李雷(KyLin) + * @Desc 66.加一 https://leetcode-cn.com/problems/plus-one/ + * @Date 2019/10/18 + */ +public class LeetCode_66_693 { + public int[] plusOne(int[] digits) { + int f = 1; + for (int i = digits.length - 1; i >= 0; i--) { + int temp = digits[i]; + digits[i] = (digits[i] + f) % 10; + f = (temp + f) / 10; + } + if (f == 1) { + int[] r = new int[digits.length + 1]; + r[0] = 1; + System.arraycopy(digits,0,r,1,r.length - 1); + return r; + } + return digits; + } + + //对于同学的review 进行优化,非常感谢这位同学 + /* + 1、f 为进位,不是0就是1,没必要除以10进行计算 + 2、如果f为0,那么循环可以结束了,没必要继续下去 + * */ + public int[] plusOne2(int[] digits) { + boolean f = true; + for (int i = digits.length - 1; i >= 0; i--) { + if (digits[i] + 1 > 9) { + digits[i] = digits[i] + 1 - 10; + } else { + digits[i] += 1; + f = false; + break; + } + } + if (f) { + int[] r = new int[digits.length + 1]; + r[0] = 1; + System.arraycopy(digits,0,r,1,r.length - 1); + return r; + } + return digits; + } + + //根据效率最高的代码再次进行优化, + //思路:直接修改digits[i] 的值,如果不等于0 说明没有超出,则直接return,否则继续改变 + // 后面的新建数组的思路很巧妙, + // 因为到了最后一个就代表,数组的值全是9的情况下才会出现,那么说明结果只需要在数组头增加一个1 后面全是0, + // 利用数组的默认0来实现,666 + public int[] plusOne3(int[] digits) { + boolean is = true; + for (int i = digits.length - 1; i >= 0; i--) { + digits[i] = (digits[i] + 1) % 10; + if (digits[i] != 0) { + return digits; + } + } + digits = new int[digits.length + 1]; + digits[0] = 1; + return digits; + } + + public static void main(String[] args) { + System.out.println(Arrays.toString(new LeetCode_66_693().plusOne2(new int[]{1,2,3}))); + System.out.println(Arrays.toString(new LeetCode_66_693().plusOne2(new int[]{1,2,9}))); + System.out.println(Arrays.toString(new LeetCode_66_693().plusOne2(new int[]{1,9,9}))); + System.out.println(Arrays.toString(new LeetCode_66_693().plusOne2(new int[]{9,9,9}))); + System.out.println(Arrays.toString(new LeetCode_66_693().plusOne2(new int[]{9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9}))); + } +} + diff --git a/Week 01/id_693/LeetCode_88_693.java b/Week 01/id_693/LeetCode_88_693.java new file mode 100644 index 000000000..072e59814 --- /dev/null +++ b/Week 01/id_693/LeetCode_88_693.java @@ -0,0 +1,53 @@ +package id_693; + +import java.util.Arrays; + +/** + * @Desc 88.合并两个有序数组 https://leetcode-cn.com/problems/merge-sorted-array/ + * @Auther 李雷(KyLin) + * @Date 2019/10/20 + */ +public class LeetCode_88_693 { + + public void merge(int[] nums1,int m,int[] nums2,int n) { + for (int i = n + m - 1; i >= 0; i--) { + if (m == 0) { + nums1[i] = nums2[--n]; + continue; + } + if (n == 0) { + nums1[i] = nums1[--m]; + continue; + } + if (nums1[m - 1] > nums2[n - 1]) { + nums1[i] = nums1[--m]; + } else { + nums1[i] = nums2[--n]; + } + } + } + + //简化 + public void merge2(int[] nums1,int m,int[] nums2,int n) { + //倒叙比较,水大放进去,然后自身下标-1,最后需要防止m和n的下表为0,所以就需要处理一下 + for (int i = n + m - 1; i >= 0; i--) { + if (m == 0 || n == 0) { + nums1[i] = m == 0 ? nums2[--n] : nums1[--m]; + } else { + nums1[i] = nums1[m - 1] > nums2[n - 1] ? nums1[--m] : nums2[--n]; + } + } + } + //最简解法 + public void merge3(int[] nums1, int m, int[] nums2, int n) { + while (m != 0 && n != 0) nums1[m + n - 1] = nums1[m - 1] > nums2[n - 1] ? nums1[--m] : nums2[--n]; + while (n != 0) nums1[m + n - 1] = nums2[--n]; + } + + public static void main(String[] args) { + int[] m = new int[]{1,2,3,0,0,0}; + int[] n = new int[]{2,5,6}; + new LeetCode_88_693().merge2(m,3,n,3); + System.out.println(Arrays.toString(m)); + } +} diff --git a/Week 01/id_693/ListNode.java b/Week 01/id_693/ListNode.java new file mode 100644 index 000000000..969e02f3c --- /dev/null +++ b/Week 01/id_693/ListNode.java @@ -0,0 +1,16 @@ +package id_693; + +/** + * @Desc + * @Auther 李雷(KyLin) + * @Date 2019/10/17 + */ +public class ListNode { + + public int val; + public ListNode next; + + public ListNode(int x) { + val = x; + } +} \ No newline at end of file diff --git a/Week 01/id_693/NOTE.md b/Week 01/id_693/NOTE.md index a6321d6e2..6af9d20cc 100644 --- a/Week 01/id_693/NOTE.md +++ b/Week 01/id_693/NOTE.md @@ -1,4 +1,228 @@ -# NOTE +## 第一周算法总结+源码分析+改写Deque代码 - +[TOC] + +### 一、学习总结 + +##### 精华: + +- 解题曲线:五毒神掌之少一掌 +- 解题思路:重复点,重复规律 +- 效率优化:升维,用空间换时间(典型的就有redis的跳表) +- 左右指针:左右边界向中间收敛 +- 快慢指针:判断是否有环等 +- 最新相关性问题:可以直接考虑用栈来解决 + + +##### 知识点: + +- 数组,链表、栈、队列、堆排 + + +Leetcode: + +- 链接(欢迎指正错误): + +##### 补充: + +**1、刷题五步法:** + +- 第一步:用5-15分钟对题目进行思考,想出头绪 + - 如果5-15分钟还没想出头绪,那么直接看答案,理解答案并背诵 +- 第二步:关闭答案,自己写程序,直到测试通过 +- 第三步:第二天对前一天的程序进行重新写 +- 第四步:一周后对程序进行重新写 +- 第五步:面试准备前半周或者一周,对题目进行重新写 + +**2、切题四件套:** + +- 读懂题目,理解题目,和面试官做好足够的沟通,防止自己对题意误解 +- 想出所有能想到的解法 + - 分析时间/空间复杂度 + - 选出最优解法 +- 写程序 +- 写测试样例进行测试 + +### 二、源码分析 + +#### 1、Queue源码 + +##### 继承关系 + +- 继承Collection、Iterable接口 + +##### 函数说明 + +```java +add(E e):boolean + //添加元素 + //如果没有队列空间已满,抛出异常 + +offer(E e):boolean + //添加元素 + //如果没有队列空间已满,进行扩容添加 + +remove():E + //压出栈顶元素 + //如果队列为null,抛出异常 + +poll():E + //压出栈顶元素 + //如果队列为null,返回null + +element():E + //检索栈顶元素 + //如果队列为null,抛出异常 + +peek():E + //检索栈顶元素 + //如果队列为null,返回null +``` + +#### 2、Priority Deque源码 + +##### 继承关系 + +- 实现了Serializable、Queue、Collection、Iterable接口 +- 实现了AbStractQueue、AbstractCollection抽象类 + + + +##### 属性说明 + +```java +//默认初始化大小 +private static final intDEFAULT_INITIAL_CAPACITY = 11; +//用数组实现的二叉堆 +private transient Object[] queue ; +//队列的元素数量 +private int size = 0; +//比较器 +private final Comparaotr comparator; +//修改版本 +private transient int modCount = 0; +``` + + + +##### 常用函数 + +1、offer 添加元素 + +```java +public boolean offer(E e) { + if (e == null) + throw new NullPointerException(); + modCount++; + int i = size; + if (i >= queue.length) + //如果队列size大于等于队列元素,则进行扩容 + //如果队列size小于64 则size * 2 + 2,否则扩容一半(size + size >> 1) + grow(i + 1); + size = i + 1; + if (i == 0) + //如果队列为null,直接添加元素 + queue[0] = e; + else + //函数插入尾部,并对其上浮判断 + siftUp(i, e); + return true; +} +//上浮 +private void siftUp(int k, E x) { + //如果没有实现比较器则需要对插入的元素实现Comparaotr接口 + if (comparator != null) + siftUpUsingComparator(k, x); + else + siftUpComparable(k, x); +} + +private void siftUpComparable(int k, E x) { + //比较器comparator为null,就需要对插入的元素实现Comparator接口,用于比较大小 + Comparable key = (Comparable) x; + //k判断是否是根, + while (k > 0) { + //计算元素x的父节点位置 + int parent = (k - 1) >>> 1; + //取出x 的父节点 + Object e = queue[parent]; + //如果新增元素比父元素大或等于,则不用上浮,跳出循环 + if (key.compareTo((E) e) >= 0) + break; + //x比父亲小,需要进行上浮元素,和父元素进行交换,然后进入下一个循环, + //直到k >= 父元素 或 为根节点 + queue[k] = e; + k = parent; + } + queue[k] = key; +} +``` + +2、remove 删除元素 + +```java +public boolean remove(Object o) { + int i = indexOf(o); + if (i == -1) + return false; + else { + removeAt(i); + return true; + } +} +private E removeAt(int i) { + // assert i >= 0 && i < size; + modCount++; + int s = --size; + if (s == i) + queue[i] = null; + else { + E moved = (E) queue[s]; + queue[s] = null; + //从删除的元素位,开始下沉,原理类似上浮, + //只是时会考虑左子节点和右子节点的大小,会把小的给左节点,挺有意思的 + siftDown(i, moved); + //下沉后元素不变,就需要考虑元素的上浮。 + if (queue[i] == moved) { + siftUp(i, moved); + if (queue[i] != moved) + return moved; + } + } + return null; +} + + +``` + + + + + +### 三、改写Deque + +##### LinkedList + +```java +Deque deque = new LinkedList(); +//deque.push("a") +//deque.push("b") +//deque.push("c") + +deque.addFirst("a"); +deque.addFirst("b"); +deque.addFirst("c"); +System.out.println(deque); + +//String str = deque.peek(); +String str = deque.peekFirst(); +System.out.println(str); +System.out.println(deque); + +while (deque.size() > 0) { + //System.out.println(deque.pop()); + System.out.println(deque.removeFirst()); +} +System.out.println(deque); +``` diff --git a/Week 01/id_693/practise/LeetCode_11_693.java b/Week 01/id_693/practise/LeetCode_11_693.java new file mode 100644 index 000000000..16a62f355 --- /dev/null +++ b/Week 01/id_693/practise/LeetCode_11_693.java @@ -0,0 +1,33 @@ +package id_693.practise; + +/** + * @Desc 11. 盛最多水的容器 https://leetcode-cn.com/problems/container-with-most-water/ + * @Auther 李雷(KyLin) + * @Date 2019/10/17 + */ +public class LeetCode_11_693 { + //暴力破解,直接计算所有可能,返回最大值 O(n^2) + public int maxArea(int[] height) { + int max = 0; + int tempArea; + for (int i = 0; i < height.length - 1; ++i) { + for (int j = i + 1; j < height.length; ++j) { + tempArea = (j - i) * Math.min(height[i],height[j]); + max = Math.max(max,tempArea); + } + } + return max; + } + + //双指针优化,0(n) + public int marArea2(int[] height) { + int max = 0; + int left = 0; + int right = height.length - 1; + while (left < right) { + int minHeight = height[left] < height[right] ? height[left++] : height[right--]; + max = Math.max(max,minHeight * (right - left + 1)); + } + return max; + } +} diff --git a/Week 01/id_693/practise/LeetCode_141_693.java b/Week 01/id_693/practise/LeetCode_141_693.java new file mode 100644 index 000000000..2b20da80c --- /dev/null +++ b/Week 01/id_693/practise/LeetCode_141_693.java @@ -0,0 +1,48 @@ +package id_693.practise; + +import id_693.ListNode; + +import java.util.HashSet; +import java.util.Set; + +/** + * @Desc 141 环形链表 https://leetcode-cn.com/problems/linked-list-cycle/submissions/ + * @Auther 李雷(KyLin) + * @Date 2019/10/16 + */ +public class LeetCode_141_693 { + //暴力求解,用hash或者set + public boolean hasCycle(ListNode head) { + Set a = new HashSet<>(); + while (head != null) { + if (a.contains(head)) { + return true; + } + a.add(head); + head = head.next; + } + return false; + } + + //进阶,快慢指针 + /* + 题解: + 1、如果快指针后面为null 那么说明不是环形 + 2、如果当slow == fast 说明是环形 + */ + public boolean hasCycle2(ListNode head) { + if (head == null || head.next == null) { + return false; + } + ListNode slow = head; + ListNode fast = head.next; + while (slow != fast) { + if (fast == null || fast.next == null) { + return false; + } + slow = slow.next; + fast = fast.next.next; + } + return true; + } +} diff --git a/Week 01/id_693/practise/LeetCode_142_693.java b/Week 01/id_693/practise/LeetCode_142_693.java new file mode 100644 index 000000000..3f4d77106 --- /dev/null +++ b/Week 01/id_693/practise/LeetCode_142_693.java @@ -0,0 +1,48 @@ +package id_693.practise; + +import id_693.ListNode; + +import java.util.HashSet; +import java.util.Set; + +/** + * @Desc 142. 环形链表 II https://leetcode-cn.com/problems/linked-list-cycle-ii/ + * @Auther 李雷(KyLin) + * @Date 2019/11/04 + */ +public class LeetCode_142_693 { + //暴力解(哈希表),使用Set去重 + public ListNode detectCycle2(ListNode head) { + Set set = new HashSet<>(); + while (head != null) { + if (set.contains(head)) { + return head; + } + set.add(head); + head = head.next; + } + return null; + } + + //双指针:双指针找环点,这时候需要返回环点的上一个指针,那么让其中一个到开头,另外一个在环点,同时逐步出发,最后在一起的就是环点前一个点。 + public ListNode detectCycle(ListNode head) { + ListNode slow = head; + ListNode fast = head; + while (true) { + if (fast == null || fast.next == null) { + return null; + } + slow = slow.next; + fast = fast.next.next; + if (fast == slow) { + break; + } + } + fast = head; + while (fast != slow) { + fast = fast.next; + slow = slow.next; + } + return fast; + } +} diff --git a/Week 01/id_693/practise/LeetCode_155_693.java b/Week 01/id_693/practise/LeetCode_155_693.java new file mode 100644 index 000000000..be252da96 --- /dev/null +++ b/Week 01/id_693/practise/LeetCode_155_693.java @@ -0,0 +1,61 @@ +package id_693.practise; + +import java.util.Stack; + +/** + * @Desc 155.最小栈 https://leetcode-cn.com/problems/min-stack/ + * @Auther 李雷(KyLin) + * @Date 2019/10/18 + */ +public class LeetCode_155_693 { + Stack stack = new Stack<>(); + Stack minStack = new Stack<>(); + + /** + * initialize your data structure here. + */ + public LeetCode_155_693() { + + } + + public void push(int x) { + stack.push(x); + if (minStack.isEmpty() || x <= minStack.peek()) { + minStack.push(x); + } + } + + public void pop() { + if (stack.pop().equals(minStack.peek())) { + minStack.pop(); + } + } + + public int top() { + return stack.peek(); + } + + public Integer getMin() { + try { + return minStack.peek(); + } catch (Exception e) { + return null; + } + } + + //原来还考虑。后面的数 push更大。会不会出问题。才发现自己理解错误了,栈是先进后出。 + // 汗颜啊。所以后面的513 不录入,然后pop的时候 根本不影响minStack 同时也不影响获得最小的Min。 + // 毕竟最小的 min 是不移除元素的 + public static void main(String[] args) { + LeetCode_155_693 stack = new LeetCode_155_693(); + //[null,null,null,null,null,null,-1024,null,-1024,null,512] + System.out.println(stack.getMin()); + stack.push(512); + stack.push(513); + stack.pop(); + System.out.println(stack.getMin()); + System.out.println(stack.getMin()); + stack.pop(); + System.out.println(stack.getMin()); + } +} diff --git a/Week 01/id_693/practise/LeetCode_15_693.java b/Week 01/id_693/practise/LeetCode_15_693.java new file mode 100644 index 000000000..c1214b11d --- /dev/null +++ b/Week 01/id_693/practise/LeetCode_15_693.java @@ -0,0 +1,76 @@ +package id_693.practise; + +import java.util.*; + +/** + * @Desc 15. 三数之和 https://leetcode-cn.com/problems/3sum/ + * @Auther 李雷(KyLin) + * @Date 2019/10/16 + */ + +public class LeetCode_15_693 { + /* + 1、对元素排序。(这是关键) + 2、k值 > 0 直接排除,并且加1 且去重复元素 + 3、k > 0 且与之前元素相等,属于重复元素,去除 + 4、s (i值+j值+k值) + 4.1 s > 0 那么说明结果太大了。那么久把j下标上移,也就是j,同时也考虑去重 + 4.2 s < 0 那么说明结果太小了。那么久把i下标下移,也就是i,同时也考虑去重 + 4.3 s = 0 那么就是结果了,直接i下标下移,j下标上移,同时ij都需要考虑去重 + */ + public List> threeSum(int[] nums) { + Arrays.sort(nums); + int len = nums.length; + int k; + int i; + int j; + int complement; + List> result = new ArrayList<>(); + for (k = 0; k < len - 2; k++) { + if (nums[k] > 0) break; + if (k == 0 || nums[k] != nums[k - 1]) { + i = k + 1; + j = len - 1; + while (i < j) { + complement = nums[k] + nums[i] + nums[j]; + if (complement == 0) { + result.add(Arrays.asList(nums[k], nums[i], nums[j])); + while (i < j && nums[i] == nums[i + 1]) i++; + while (i < j && nums[j] == nums[j - 1]) j--; + i++; + j--; + } else if (complement < 0) i++; + else j--; + } + } + } + return result; + } + + //参考国际站,但是这个原来的 多了一个k>0的判断,可以不用,因为k肯定大于0 (真正严格的代码是不允许这样写的) + public List> threeSum2(int[] nums) { + Arrays.sort(nums); + List> result = new ArrayList<>(); + for (int k = 0; k < (nums.length - 2); k++) { + if (nums[k] > 0) break; + if (k == 0 || nums[k] != nums[k - 1]) { + int i = k + 1, j = nums.length - 1, sum = 0 - nums[k]; + while (i < j) { + if (nums[i] + nums[j] == sum) { + result.add(Arrays.asList(nums[k], nums[i], nums[j])); + while (i < j && nums[i] == nums[i + 1]) i++; + while (i < j && nums[j] == nums[j - 1]) j--; + i++; + j--; + } else if (nums[i] + nums[j] < sum) i++; + else j--; + } + } + } + return result; + } + + public static void main(String[] args) { + System.out.println(new LeetCode_15_693().threeSum(new int[]{-1, 0, 1, 2, -1, -4})); + } +} diff --git a/Week 01/id_693/practise/LeetCode_206_693.java b/Week 01/id_693/practise/LeetCode_206_693.java new file mode 100644 index 000000000..788a2d20e --- /dev/null +++ b/Week 01/id_693/practise/LeetCode_206_693.java @@ -0,0 +1,34 @@ +package id_693.practise; + +import id_693.ListNode; + +/** + * @Desc 206. 反转链表 https://leetcode-cn.com/problems/reverse-linked-list/submissions/ + * @Auther 李雷(KyLin) + * @Date 2019/10/17 + */ +public class LeetCode_206_693 { + //迭代 + public ListNode reverseList(ListNode head) { + ListNode prev = null; + ListNode curr = head; + while (curr != null) { + ListNode temp = curr.next; + curr.next = prev; + prev = curr; + curr = temp; + } + return prev; + } + + //递归 + public ListNode reverseList2(ListNode head) { + if (head == null || head.next == null) { + return head; + } + ListNode prev = reverseList2(head.next); + head.next.next = head; + head.next = null; + return prev; + } +} diff --git a/Week 01/id_693/practise/LeetCode_20_693.java b/Week 01/id_693/practise/LeetCode_20_693.java new file mode 100644 index 000000000..91904677f --- /dev/null +++ b/Week 01/id_693/practise/LeetCode_20_693.java @@ -0,0 +1,71 @@ +package id_693.practise; + +import java.util.Stack; + +/** + * @Desc 20. 有效的括号 https://leetcode-cn.com/problems/valid-parentheses/ + * @Auther 李雷(KyLin) + * @Date 2019/10/17 + */ +public class LeetCode_20_693 { + //暴力破解 采用数组 + /* + 123 -> { + 125 -> } + 91 -> [ + 93 -> ] + 40 -> ( + 41 -> ) + */ + + //执行用时 : 1 ms , 在所有 java 提交中击败了 99.80% 的用户 + //内存消耗 : 33.9 MB , 在所有 java 提交中击败了 90.43% 的用户 + public boolean isValid(String s) { + int len = s.length(); + int f = 1; + char[] temp = new char[len + 1]; + for (int i = 0; i < len; ++i) { + char c = s.charAt(i); + if ('(' == c || '[' == c || '{' == c) { + temp[f++] = c; + } else if (c - temp[f - 1] == 1 || c - temp[f - 1] == 2) { + f--; + } else { + return false; + } + } + return f == 1; + } + + //利用栈 执行用时 : 2 ms , 在所有 java 提交中击败了 96.81% 的用户 + //内存消耗 : 34.4 MB , 在所有 java 提交中击败了 84.27% 的用户 + public boolean isValid2(String s) { + Stack stack = new Stack<>(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if ('[' == c) { + stack.push(']'); + } else if ('{' == c) { + stack.push('}'); + } else if ('(' == c) { + stack.push(')'); + } else if (stack.isEmpty() || stack.pop() != c) { + return false; + } + } + + return stack.isEmpty(); + } + + public static void main(String[] args) { + System.out.println(new LeetCode_20_693().isValid("()"));//true + System.out.println(new LeetCode_20_693().isValid("()[]{}"));//true + System.out.println(new LeetCode_20_693().isValid("(]"));//false + System.out.println(new LeetCode_20_693().isValid("([]{}"));//false + + System.out.println(new LeetCode_20_693().isValid2("()"));//true + System.out.println(new LeetCode_20_693().isValid2("()[]{}"));//true + System.out.println(new LeetCode_20_693().isValid2("(]"));//false + System.out.println(new LeetCode_20_693().isValid2("([]{}"));//false + } +} diff --git a/Week 01/id_693/practise/LeetCode_239_693.java b/Week 01/id_693/practise/LeetCode_239_693.java new file mode 100644 index 000000000..e23330543 --- /dev/null +++ b/Week 01/id_693/practise/LeetCode_239_693.java @@ -0,0 +1,124 @@ +package id_693.practise; + +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Deque; + +/** + * @Desc 239. 滑动窗口最大值 https://leetcode-cn.com/problems/sliding-window-maximum/ + * @Auther 李雷(KyLin) + * @Date 2019/10/19 + */ +public class LeetCode_239_693 { + + //自己暴力破解 O(nk) + //你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。 + // 所以 k 是 大于等于数组,且k 在数组不为null的时候会是大于等于1 + public int[] maxSlidingWindow(int[] nums, int k) { + if (nums.length == 0) { + return new int[0]; + } + int[] resule = new int[nums.length - k + 1]; + for (int i = 0; i < resule.length; i++) { + int maxIndex = i; + for (int j = i + 1; j < i + k; j++) { + if (nums[j] > nums[maxIndex]) { + maxIndex = j; + } + } + resule[i] = nums[maxIndex]; + } + return resule; + } + + //对第一个进行代码优化 + public int[] maxSlidingWindow2(int[] nums, int k) { + if (nums.length == 0) { + return new int[0]; + } + int[] resule = new int[nums.length - k + 1]; + for (int i = 0; i < resule.length; i++) { + int max = nums[i]; + for (int j = i + 1; j < i + k; j++) { + max = Math.max(nums[j], max); + } + resule[i] = max; + } + return resule; + } + + //使用双端队列试试 达到 O(n log(k)) 参考国际站,利用了双端队列的两头可取值的特点 + //原理: + //1、for循环中的 第一个while循环 是把超出边界的下标都pop + //2、for循环中的 第二个while循环 是把所有前面小于或者等于自己的pop (从开始就这样那么前面的都是从大到小排序的,而且每次都循环pop了) + //3、i >= K - 1 说明下标已经在 0 - (k-1) 的范围,已经有k个数了。可以开始计算,然后拿出头即可 + public int[] maxSlidingWindow3(int[] nums, int k) { + if (nums == null || k == 0) { + return new int[0]; + } + int[] result = new int[nums.length - k + 1]; + int ri = 0; + Deque deque = new ArrayDeque<>(k); + for (int i = 0; i < nums.length; i++) { + //循环删除超出左边界的,并移除 + while (!deque.isEmpty() && deque.peek() < i - k + 1) { + deque.pop(); + } + //循环检查 最后的元素小于当前,则删除 + while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) { + deque.pollLast(); + } + //添加 + deque.offer(i); + //当数组元素检索到可 k - 1 则可以开始返回数组元素赋值了。 + if (i >= k - 1) { + result[ri++] = nums[deque.peek()]; + } + } + return result; + } + + //网上效率最高的 0(n) 采用的动态规划 + //如果每移动一次就去遍历窗口的值,这样时间复杂度是O(n*k) + //可以改进成用一个变量存储窗口最大值,每进来一个新值就进行跟最大值比较即可,当最大值出窗口了再遍历当前窗口的最大值。 + public int[] maxSlidingWindow4(int[] nums, int k) { + if (nums.length == 0) { + return new int[0]; + } + int[] result = new int[nums.length + 1 - k]; + int index = -1; + int maximum = nums[0]; + + for (int i = 0; i < result.length; i++) { + if (index < i) { + maximum = nums[i]; + index = i; + for (int j = i + 1; j < k + i; j++) { + if (!(maximum > nums[j])) { + maximum = nums[j]; + index = j; + } + } + } else { + if (!(maximum > nums[i + k - 1])) { + maximum = nums[i + k - 1]; + index = i + k - 1; + } + } + result[i] = maximum; + } + return result; + } + + + public static void main(String[] args) { +// System.out.println(Arrays.toString(new LeetCode_239_693().maxSlidingWindow(new int[]{1, 3, -1, -3, 5, 3, 6, 7}, 3))); +// System.out.println(Arrays.toString(new LeetCode_239_693().maxSlidingWindow(new int[]{1, 3, -1, -3, 5, 3, 6, 7}, 4))); +// System.out.println(Arrays.toString(new LeetCode_239_693().maxSlidingWindow(new int[]{1, 3}, 3))); +// System.out.println(Arrays.toString(new LeetCode_239_693().maxSlidingWindow(new int[]{1, 3}, 1))); +// System.out.println(Arrays.toString(new LeetCode_239_693().maxSlidingWindow(new int[]{1}, 1))); +// System.out.println(Arrays.toString(new LeetCode_239_693().maxSlidingWindow(new int[0], 0))); +// System.out.println(Arrays.toString(new LeetCode_239_693().maxSlidingWindow(new int[0], 2))); + System.out.println(Arrays.toString(new LeetCode_239_693().maxSlidingWindow(new int[]{1}, 2))); + } +} diff --git a/Week 01/id_693/practise/LeetCode_70_693.java b/Week 01/id_693/practise/LeetCode_70_693.java new file mode 100644 index 000000000..6847d1d9c --- /dev/null +++ b/Week 01/id_693/practise/LeetCode_70_693.java @@ -0,0 +1,57 @@ +package id_693.practise; + +import java.util.HashMap; +import java.util.Map; + +/** + * @Desc 70.爬楼 https://leetcode-cn.com/problems/climbing-stairs/ + * @Auther 李雷(KyLin) + * @Date 2019/10/17 + */ +public class LeetCode_70_693 { + //使用map来优化递归的重复项 + public int climbStairs(int n) { + Map map = new HashMap<>(n,1); + return a(n,map); + } + + private int a(int n,Map map) { + if (n == 1 || n == 2) { + return n; + } + if (map.containsKey(n)) { + return map.get(n); + } + int ret = a(n - 1,map) + a(n - 2,map); + map.put(n,ret); + return ret; + } + + // 使用数组来进行拉波西 + public int climbStairs2(int n) { + if (n < 3) return n; + int[] nums = new int[n]; + nums[0] = 1; + nums[1] = 2; + for (int i = 2; i < n; ++i) { + nums[i] = nums[i - 1] + nums[i - 2]; + } + return nums[n - 1]; + } + //使用递归 + + public int climbStairs3(int n) { + if (n == 1 || n == 2) { + return n; + } + int a = 1; + int b = 2; + int count = 2; + for (int j = 3; j < n + 1; ++j) { + count = a + b; + a = b; + b = count; + } + return count; + } +} diff --git a/Week 01/id_693/practise/LeetCode_84_693.java b/Week 01/id_693/practise/LeetCode_84_693.java new file mode 100644 index 000000000..d01cb9121 --- /dev/null +++ b/Week 01/id_693/practise/LeetCode_84_693.java @@ -0,0 +1,105 @@ +package id_693.practise; + +import java.util.Stack; + +/** + * @Desc 84. 柱状图中最大的矩形 https://leetcode-cn.com/problems/largest-rectangle-in-histogram/submissions/ + * @Auther 李雷(KyLin) + * @Date 2019/10/19 + */ +public class LeetCode_84_693 { + //暴力破解 但是数据太大就会over O(n^20 + public int largestRectangleArea2(int[] h) { + int maxResult = 0; + for (int i = 0; i < h.length; ++i) { + int minHeight = Integer.MAX_VALUE; + for (int j = i; j < h.length; j++) {//2, 1, 5, 6, 2, 3 + //获得区间最小高度 + minHeight = Math.min(minHeight,h[j]); + //计算 + maxResult = Math.max(maxResult,(j - i + 1) * minHeight); + } + + } + return maxResult; + } + + //使用栈 0(n) + public int largestRectangleArea(int[] h) { + Stack stack = new Stack<>(); + //表示没有元素没计算了,且用于值的计算 + stack.push(-1); + int area = 0; + // 如果压入的数据小于上一个,那么就直接 用栈中上一个下标 - 1计算 + for (int i = 0; i < h.length; i++) { + while (stack.peek() != -1 && h[stack.peek()] >= h[i]) { + area = Math.max(area,h[stack.pop()] * (i - stack.peek() - 1)); + } + stack.push(i); + + } + //在这个时候,留下的元素值,处于伪排序中,然后把这些下标的元素 一个个的取出,假设是从当前下标开始到结尾的区间,然后计算得出结果 + while (stack.peek() != -1) { + area = Math.max(area,h[stack.pop()] * (h.length - stack.peek() - 1)); + } + return area; + } + + //学习一下,看看大佬的递归思想,不是我做得 + public int largestRectangleArea3(int[] heights) { + return findArea(heights,0,heights.length - 1); + } + + private int findArea(int[] heights,int l,int r) { + if (r < l) { + return 0; + } else if (r == l) { + return heights[l]; + } + int minIndex = l; + boolean sort = true; + for (int i = l + 1; i <= r; i++) { + if (heights[i - 1] > heights[i]) { + sort = false; + } + if (heights[i] < heights[minIndex]) { + minIndex = i; + } + } + int max = heights[minIndex] * (r - l + 1); + if (sort) { + for (int i = l + 1; i <= r; i++) { + max = Math.max(max,heights[i] * (r - i + 1)); + } + return max; + } + return Math.max(max,Math.max(findArea(heights,l,minIndex - 1),findArea(heights,minIndex + 1,r))); + } + + public static void main(String[] args) { +// //0 +// System.out.println(new LeetCode_84_693().largestRectangleArea(new int[]{0})); +// //1 +// System.out.println(new LeetCode_84_693().largestRectangleArea(new int[]{1})); +// //9 +// System.out.println(new LeetCode_84_693().largestRectangleArea(new int[]{9})); + //10 + System.out.println(new LeetCode_84_693().largestRectangleArea(new int[]{2,1,5,6,2,3})); + //2 + System.out.println(new LeetCode_84_693().largestRectangleArea(new int[]{1,1})); + //2 + System.out.println(new LeetCode_84_693().largestRectangleArea(new int[]{1,2})); + //9 + System.out.println(new LeetCode_84_693().largestRectangleArea(new int[]{0,9})); + //18 + System.out.println(new LeetCode_84_693().largestRectangleArea(new int[]{9,9})); + //19182 + System.out.println(new LeetCode_84_693().largestRectangleArea(new int[]{12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276})); + + int[] length = new int[200]; + for (int i = 0; i < length.length; i++) { + length[i] = i; + } + System.out.println(new LeetCode_84_693().largestRectangleArea(length)); + } +} diff --git a/Week 01/id_693/practise/leetCode_24_693.java b/Week 01/id_693/practise/leetCode_24_693.java new file mode 100644 index 000000000..5182858a7 --- /dev/null +++ b/Week 01/id_693/practise/leetCode_24_693.java @@ -0,0 +1,45 @@ +package id_693.practise; + +import id_693.ListNode; + +/** + * @Author 李雷(KyLin) + * @Desc 24. 两两交换链表中的节点 https://leetcode-cn.com/problems/swap-nodes-in-pairs/ + * @Date 2019/10/21 + */ +public class leetCode_24_693 { + + //使用递归的方式实现 + public ListNode swapPairs(ListNode head) { + if (head == null || head.next == null) { + return head; + } + ListNode next = head.next; + head.next = swapPairs(next.next); + next.next = head; + return next; + } + + //循环方式实现 + public ListNode swapPairs2(ListNode head) { + + ListNode pre = new ListNode(0); + pre.next = head; + ListNode result = pre; + while (result.next != null && result.next.next != null) { + ListNode temp1 = result.next;//缓存第一个节点的地址 1 + ListNode temp2 = result.next.next.next;//缓存下一个头节点的地址 3 + + result.next = result.next.next; //把第二个节点转变为第一个节点。 + result.next.next = temp1;//原来的第一个节点转变为第二个节点 + temp1.next = temp2;//把原来第三各节点放在temp1下面(temp现在时第二个节点) + result = temp1; + } + return pre.next; + } + + public static void main(String[] args) { + + + } +} diff --git a/Week 01/id_698/LeetCode_21_698.java b/Week 01/id_698/LeetCode_21_698.java new file mode 100644 index 000000000..d630ce2bc --- /dev/null +++ b/Week 01/id_698/LeetCode_21_698.java @@ -0,0 +1,34 @@ +/** + * 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 + * @author gning + * + */ + + +public class LeetCode_21_698 { + public ListNode mergeTwoLists(ListNode l1, ListNode 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(l2.next,l1); + return l2; + } + } + + class ListNode { + int val; + ListNode next; + ListNode(int val) { + this.val = val; + } + } + +} \ No newline at end of file diff --git a/Week 01/id_698/LeetCode_26_698.java b/Week 01/id_698/LeetCode_26_698.java new file mode 100644 index 000000000..f128758c1 --- /dev/null +++ b/Week 01/id_698/LeetCode_26_698.java @@ -0,0 +1,48 @@ +/** + * 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 + * 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 + * @author gning + */ + +public class LeetCode_26_698 { + + /** + * 暴力解法。自己想的 + */ + public int removeDulicates(int[] nums) { + int len = nums.length; + for (int i=0; i < len-1;) { + if (nums[i] == nums[i+1]) { + for(int k=i+1; k < len - 1; k++) { + nums[k] = nums[k+1]; + } + len--; + i=0; + } else { + i++; + } + + } + + return len; + } + + /** + * 默写官方解法 + */ + public int removeDulicates2(int[] nums) { + if (nums.length==0) + return 0; + + int i=0; + for(int j=1; j < nums.length; j++) { + if (nums[i] != nums[j]) { + i++; + nums[i] = nums[j]; + } + } + return i+1; + } + + +} diff --git a/Week 01/id_703/LeetCode_1_703.py b/Week 01/id_703/LeetCode_1_703.py new file mode 100644 index 000000000..29971c9b2 --- /dev/null +++ b/Week 01/id_703/LeetCode_1_703.py @@ -0,0 +1,62 @@ +# +# @lc app=leetcode.cn id=1 lang=python3 +# +# [1] 两数之和 +# + +# @lc code=start +from typing import List + +# 法一: 两层for循环,时间复杂度O(n^2) +# class Solution: +# def twoSum(self, nums: List[int], target: int) -> List[int]: +# length = len(nums) +# for i in range(0, length): +# for j in range(i+1, length): +# if ((nums[i] + nums[j]) == target): +# return [i, j] +# return [] + + + # """这样写更直观,遍历列表同时查字典""" + # dct = {} + # for i, n in enumerate(nums): + # if target - n in dct: + # return [dct[target - n], i] + # dct[n] = i + +# 法二: 时间复杂度0(n) +class Solution: + def twoSum(self, nums: List[int], target: int) -> List[int]: + if len(nums) <= 1: + return False + + dict = {} # 新建空字典 + for key, value in enumerate(nums): + # print(index, value) + # 枚举nums, 如果空字典中有“目标值-整数”的差,则返回差在字典中的下标、整数的下标 + if target - value in dict: + # print(dict) + return [dict[target-value], key] + else: + # key和value交换后传入字典,使得可以通过value获得key的值 + dict[value] = key + # print(dict) + return [-1, -1] + + +# if __name__ == "__main__": +# nums = [2,11,7,5,5] +# target = 10 +# s = Solution() +# w = s.twoSum(nums, target) +# print(w) + + + + + + + +# @lc code=end + diff --git a/Week 01/id_703/LeetCode_283_703.py b/Week 01/id_703/LeetCode_283_703.py new file mode 100644 index 000000000..bf145e8de --- /dev/null +++ b/Week 01/id_703/LeetCode_283_703.py @@ -0,0 +1,29 @@ +# +# @lc app=leetcode.cn id=283 lang=python3 +# +# [283] 移动零 +# + +# @lc code=start + +from typing import List + +class Solution: + def moveZeroes(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + i = j = 0 + + for i in range(len(nums)): + if nums[i] != 0: + nums[j] = nums[i] + j += 1 + + for p in range(j, len(nums)): + nums[p] = 0 + + + +# @lc code=end + diff --git a/Week 01/id_708/LeetCode_189_708.java b/Week 01/id_708/LeetCode_189_708.java new file mode 100644 index 000000000..e3fb17173 --- /dev/null +++ b/Week 01/id_708/LeetCode_189_708.java @@ -0,0 +1,34 @@ +class Solution { + public void rotate(int[] nums, int k) { + k %= nums.length; + reverse(nums, 0, nums.length-1); + reverse(nums, 0, k-1); + reverse(nums, k, nums.length-1); + } + + private void reverse(int[] nums, int i, int j) { + while (i < j) swap(nums, i++, j--); + } + + private void swap(int[] nums, int i, int j) { + nums[i] ^= nums[j]; + nums[j] ^= nums[i]; + nums[i] ^= nums[j]; + } +} + + +// Brute force +class Solution2 { + public void rotate(int[] nums, int k) { + while (k-- > 0) { + // one rotation at a time + int prev = nums[nums.length-1]; + for (int i = 0; i < nums.length; i++) { + int temp = nums[i]; + nums[i] = prev; + prev = temp; + } + } + } +} \ No newline at end of file diff --git a/Week 01/id_708/LeetCode_1_708.go b/Week 01/id_708/LeetCode_1_708.go new file mode 100644 index 000000000..fc692fc89 --- /dev/null +++ b/Week 01/id_708/LeetCode_1_708.go @@ -0,0 +1,12 @@ +package leetcode + +func twoSum(nums []int, target int) []int { + m := make(map[int]int) + for i := range nums { + if v, ok := m[nums[i]]; ok == true { + return []int{v, i} + } + m[target-nums[i]] = i + } + return []int{} +} diff --git a/Week 01/id_708/LeetCode_21_708.java b/Week 01/id_708/LeetCode_21_708.java new file mode 100644 index 000000000..0b3bf3d78 --- /dev/null +++ b/Week 01/id_708/LeetCode_21_708.java @@ -0,0 +1,37 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +class Solution { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + ListNode head = new ListNode(0); + ListNode current = head; + while (l1 != null && l2 != null) { + int val; + if (l1.val < l2.val) { + val = l1.val; + l1 = l1.next; + } else { + val = l2.val; + l2 = l2.next; + } + current.next = new ListNode(val); + current = current.next; + } + while (l1 != null) { + current.next = new ListNode(l1.val); + current = current.next; + l1 = l1.next; + } + while (l2 != null) { + current.next = new ListNode(l2.val); + current = current.next; + l2 = l2.next; + } + return head.next; + } +} \ No newline at end of file diff --git a/Week 01/id_708/LeetCode_26_708.java b/Week 01/id_708/LeetCode_26_708.java new file mode 100644 index 000000000..ff52a2451 --- /dev/null +++ b/Week 01/id_708/LeetCode_26_708.java @@ -0,0 +1,13 @@ +class Solution { + public int removeDuplicates(int[] nums) { + int count = 1; + int p = 1; // index of the last non-repeating element + for (int i = 1; i < nums.length; i++) { + if (nums[i] != nums[i-1]) { + nums[p++] = nums[i]; + count++; + } + } + return count; + } +} \ No newline at end of file diff --git a/Week 01/id_708/LeetCode_42_708.java b/Week 01/id_708/LeetCode_42_708.java new file mode 100644 index 000000000..ffc85bc2d --- /dev/null +++ b/Week 01/id_708/LeetCode_42_708.java @@ -0,0 +1,19 @@ +class Solution { + public int trap(int[] height) { + int lo = 0, hi = height.length-1; + int maxLeft = 0, maxRight = 0; // maxLeft: biggest between 0..lo; maxRight: biggest between hi..len(height) + int ret = 0; + while (lo <= hi) { + maxLeft = Math.max(maxLeft, height[lo]); + maxRight = Math.max(maxRight, height[hi]); + if (maxLeft < maxRight) { + ret += (maxLeft - height[lo]); // add water that you can save at height[lo] + lo++; + } else { + ret += (maxRight - height[hi]); + hi--; + } + } + return ret; + } +} \ No newline at end of file diff --git a/Week 01/id_708/LeetCode_641_708.java b/Week 01/id_708/LeetCode_641_708.java new file mode 100644 index 000000000..03b86e475 --- /dev/null +++ b/Week 01/id_708/LeetCode_641_708.java @@ -0,0 +1,79 @@ +class MyCircularDeque { + private int[] data; + private int front = 0; + private int rear = 0; + + /** Initialize your data structure here. Set the size of the deque to be k. */ + public MyCircularDeque(int k) { + data = new int[k]; + } + + /** Adds an item at the front of Deque. Return true if the operation is successful. */ + public boolean insertFront(int value) { + if (isFull()) return false; + int index = (front+size()+1) % size(); + data[index] = value; + front = index; + return true; + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + public boolean insertLast(int value) { + if (isFull()) return false; + int index = (rear+size()-1) % size(); + data[index] = value; + rear = index; + return true; + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + public boolean deleteFront() { + if (isEmpty()) return false; + front = (front+size()-1) % size(); + return true; + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + public boolean deleteLast() { + if (isEmpty()) return false; + rear = (rear+size()+1) % size(); + return true; + } + + /** Get the front item from the deque. */ + public int getFront() { + return data[front]; + } + + /** Get the last item from the deque. */ + public int getRear() { + return data[rear]; + } + + /** Checks whether the circular deque is empty or not. */ + public boolean isEmpty() { + return rear == front; + } + + /** Checks whether the circular deque is full or not. */ + public boolean isFull() { + return (front+1) % size() == rear; + } + + private int size() { + return data.length; + } +} + +/** + * Your MyCircularDeque object will be instantiated and called as such: + * MyCircularDeque obj = new MyCircularDeque(k); + * boolean param_1 = obj.insertFront(value); + * boolean param_2 = obj.insertLast(value); + * boolean param_3 = obj.deleteFront(); + * boolean param_4 = obj.deleteLast(); + * int param_5 = obj.getFront(); + * int param_6 = obj.getRear(); + * boolean param_7 = obj.isEmpty(); + * boolean param_8 = obj.isFull(); + */ \ No newline at end of file diff --git a/Week 01/id_708/LeetCode_66_708.go b/Week 01/id_708/LeetCode_66_708.go new file mode 100644 index 000000000..382fe8b85 --- /dev/null +++ b/Week 01/id_708/LeetCode_66_708.go @@ -0,0 +1,16 @@ +package leetcode + +func plusOne(digits []int) []int { + carray := 1 + for i := len(digits) - 1; i >= 0 && carray != 0; i-- { + sum := digits[i] + carray + digits[i] = sum % 10 + carray = sum / 10 + } + if carray != 0 { + temp := []int{1} + temp = append(temp, digits...) + return temp + } + return digits +} diff --git a/Week 01/id_708/LeetCode_88_708.java b/Week 01/id_708/LeetCode_88_708.java new file mode 100644 index 000000000..a23bebd07 --- /dev/null +++ b/Week 01/id_708/LeetCode_88_708.java @@ -0,0 +1,35 @@ +// Compare from the end of the array so that we don't need to move a lot of elements +class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + int p1 = m-1, p2 = n-1; + + while (p1 >= 0 && p2 >= 0) { + if (nums1[p1] > nums2[p2]) { + nums1[p1+p2+1] = nums1[p1]; + p1--; + } else { + nums1[p1+p2+1] = nums2[p2]; + p2--; + } + } + while (p2 >= 0) { + nums1[p1+p2+1] = nums2[p2]; + p2--; + } + } +} + +// Compare from the start of array +class Solution2 { + public void merge(int[] nums1, int m, int[] nums2, int n) { + int p = 0; + for (int i = 0; i < nums2.length; i++) { + // Ignore the smaller Numbers in nums1 + int nums1NewSize = m + i; + while (p < nums1NewSize && nums1[p] <= nums2[i]) p++; + // Moves array elements and inserts nums1's elements into nums1 + for (int j = nums1NewSize; j > p; j--) nums1[j] = nums1[j-1]; + nums1[p] = nums2[i]; + } + } +} \ No newline at end of file diff --git a/Week 01/id_713/MergeTwoSortedLists.java b/Week 01/id_713/MergeTwoSortedLists.java new file mode 100644 index 000000000..d8600ecdf --- /dev/null +++ b/Week 01/id_713/MergeTwoSortedLists.java @@ -0,0 +1,59 @@ +package id_713; + +/** + * 合并两个有序链表 + * Leetcode题号: 21 + * 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 + * 输入:1->2->4, 1->3->4 + * 输出:1->1->2->3->4->4 + */ +public class MergeTwoSortedLists { + /* + 整体思路: + 1. 前置需要 + 1. 定义带头节点, 常用于 链表的删除和修改, 以及输出结果时返回带头结点的后继即可 + 2. 带头节点的指针, 用于作为要合并的节点的前驱 + 3. 2个链表各自的指针 + 2. 比较 + 2个链表中, 哪个链表的值小, 作为带头指针的后继 + 3. 循环跳出条件 + 2个链表指针, 哪个先到NULL, 也就是哪个先遍历完毕, 就跳出来 + 4. 补偿机制 + 可能出现, 2个链表长度不一样的情况, 需要没有遍历完的链表作为合并后的后继 + */ + + + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + + ListNode dummy = new ListNode(0), p0 = dummy, p1 = l1, p2 = l2; // 定义带头节点, 带头指针, 2个链表各自的指针 + + while (p1 != null && p2 != null) { + if (p1.val < p2.val) { // 当链表1的指针的值更小, 将作为带头指针的后继 + p0.next = p1; + p1 = p1.next; // 链表1的指针向后移动 + } else { + p0.next = p2; + p2 = p2.next; + } + + p0 = p0.next; // 带头指针向后移动 + } + + p0.next = (p1 == null ? p2 : p1); // 谁还没有遍历完, 作为带头指针的后继 + + return dummy.next; + } + + + private class ListNode { + int val; + ListNode next; + + ListNode(int x) { + val = x; + } + } + +} + + diff --git a/Week 01/id_713/MoveZeroes.java b/Week 01/id_713/MoveZeroes.java new file mode 100644 index 000000000..48dd77eda --- /dev/null +++ b/Week 01/id_713/MoveZeroes.java @@ -0,0 +1,31 @@ +package id_713; + +public class MoveZeroes { + + /* + 思路: + 1. 定义一个计数器, 记录0出现的次数, 同时也将作为索引在后面使用 + 2. 当前值如果不为0, 则覆盖到为0的索引, 0值计数器+1 + 正常请况下, 相当于赋值给自己当前的索引, 数据也没发生变化 + 在之前出现过0情况下, 当前索引的数组 会覆盖 最近的0值的索引 + 3. 遍历完成后, 非0值已经覆盖0值, 且在数组前面的部分, 现在需要将后面部分都置成0, 即完毕 + */ + + public void moveZeroes(int[] nums) { + int zeroIndex = 0; + + for (int i = 0; i < nums.length; i++) { + if (nums[i] != 0) { + nums[zeroIndex] = nums[i]; + zeroIndex++; + } + + } + + for (int i = zeroIndex; i < nums.length; i++) { // 0值的计数器 到 数组长度 即为要赋值成0的区间 + nums[i] = 0; + } + } +} + + diff --git a/Week 01/id_713/NOTE.md b/Week 01/id_713/NOTE.md index a6321d6e2..a8082e025 100644 --- a/Week 01/id_713/NOTE.md +++ b/Week 01/id_713/NOTE.md @@ -1,4 +1,582 @@ # NOTE - +* 名词对应 + +| 英文 | 中文 | +| ----------- | ---- | +| array | 数组 | +| linked list | 链表 | +| skip list | 跳表 | + + + +### 复杂度分析 + +#### 数组时间复杂度 + +| 操作 | 复杂度 | +| ------- | ------ | +| prepend | O(1) | +| apppend | O(1) | +| lookup | O(1) | +| insert | O(n) | +| delete | O(n) | + + + +#### 链表时间复杂度 + +| 方式 | 复杂度 | +| ------- | ------ | +| prepend | O(1) | +| append | O(1) | +| lookup | O(n) | +| Insert | O(1) | +| delete | O(1) | + + + +### 跳表 + +* 解决**链表**的 **随机访问** 复杂度高的问题 + + + +### 优化思想 + +* 空间换时间 (升级维度) + + + +### 栈 + +* FILO 后进先出 + +* 什么样的问题可以用栈解决 -> **最近相关性** + +### 队列 + +* FIFO 先进先出 +* 广度搜索 + + + +--- + +### 数组的常见算法 + +* 遍历左右边界 + +```java +// 一维数组的坐标变换 i,j + +// 遍历左右边界 +int max = 0; +for (int i = 0; i < a.length - 1; i++) { + for (int j = i + 1; j < a.length; i++) { + // 可以自顶向下抽象: int area = getArea(i, j); + int area = (j - i) * Math.min(a[i], a[j]); + max = Math.max(area, max); + } +} +``` + +* 左右双指针 + +```java +// 双指针左右夹逼 +// 第一件事是排序 O(NlogN) + +int i = 0; j = a.length - 1; +while (i < j) { + if (条件) { + // 操作 + i++; + } else { + // 操作 + j--; + } +} + + +``` + + + +### 链表的常见算法 + +* 双指针 + +```java +// 初始化快慢指针 +ListNode slow = head; +ListNode fast = head; +/** + * 根据不同问题, 调整条件 + * 注意: 避免空指针 + */ +while (slow != null && fast != null && fast.next != null) { + slow = slow.next; // move slow pointer one step each time + fast = fast.next.next; // move fast pointer two steps each time + if (slow == fast) { // change this condition to fit specific problem + return true; + } +} +return false; // change return value to fit specific problem +``` + + + +### 改写 + +```java +//////////////////////改写前//////////////////////////////// +Deque deque = new LinkedList<>(); + +deque.push("a"); +deque.push("b"); +deque.push("c"); + +System.out.println(deque); + +String str = deque.peek(); +System.out.println(str); +System.out.println(deque); + +while (deque.size > 0) { + System.out.println(deque.pop()); +} + +System.out.println(deque); + + +//////////////////////改写后//////////////////////////////// +Deque deque = new LinkedList<>(); + +deque.addFirst("a"); // push(e) -> addFirst(e) +deque.addFirst("b"); +deque.addFirst("c"); + +System.out.println(deque); + +String str = deque.peekFirst(); // peek() -> peekFirst() +System.out.println(str); +System.out.println(deque); + +while (deque.size > 0) { + System.out.println(deque.removeFirst()); // pop() -> removeFirst() +} + +System.out.println(deque); + +``` + +--- + +### 源码分析 + +#### PriorityQueue + +* 参考: https://www.cnblogs.com/linghu-java/p/9467805.html +* 概念 + * 区别于FIFO(先进先出)的队列, 每次出队都是优先级值高的元素 + * 元素比较方法需要实现Comparator + * 底层数据结构是 二叉堆 + * 堆是完全数 + * 堆中某节点的值总是不大于或者不小于其父节点的值 + * 最大堆 (大顶堆, 大根堆) + * 父节点的值 >= 任何一个子节点的值 + * 最小堆 (小顶堆, 小根堆) + * 父节点的值 <= 任何一个子节点的值 + * 实现方式 + * 树 + * 数组 + * PriorityQueue底层是数组实现 + * 左子树位置 2 * n - 1 + * 右子树位置 2 * (n - 1) -> 2 * n - 2 + * 父亲节点位置 (n - 1) / 2 + +```java +// 底层存储, 默认大小 +private static final int DEFAULT_INITIAL_CAPACITY = 11; +// 数组实现 +private transient Object[] queue; +// 初始容量值 +private int size = 0; +// 比较器, 后面需要客户端传入或元素实现这个接口 +private final Comparator comparator; +// 模数, 修改的次数 +private transient int modCount = 0; + + +// 默认构造方法, 比较器为空, 则需要传入的元素, 实现Comparator接口 +public PriorityQueue() { + this(DEFAULT_INITIAL_CAPACITY, null); +} + +// 指定容量 +public PriorityQueue(int initialCapacity) { + this(initialCapacity, null); +} + +// 指定容量, 和比较器 +public PriorityQueue( int initialCapacity, Comparator comparator) { + // 初始大小不允许小于1 + if (initialCapacity < 1) + throw new IllegalArgumentException(); + // 使用指定初始大小创建数组 + this.queue = new Object[initialCapacity]; + // 初始化比较器 + this.comparator = comparator; +} + + +// 数组扩容 +private void grow(int minCapacity) { + // 如果最小容量 < 0, 则超出int取值范围, 抛出异常 + if (minCapacity < 0) // overflow + throw new OutOfMemoryError(); + // 留存当前长度, 用于后面比较 + int oldCapacity = queue.length; + // 如果当前队列长度小于64则扩容2倍, 否则扩容1.5倍 + int newCapacity = ((oldCapacity < 64)? + ((oldCapacity + 1) * 2): + ((oldCapacity / 2) * 3)); + // 如果扩容后的容量值 > int 取值范围, 则将newCapacity赋值为Integer.Max_VALUE + if (newCapacity < 0) // overflow + newCapacity = Integer. MAX_VALUE; + // 如果扩容后, newCapacity小于最小需要的容量大小minCapacity, 则按找minCapacity长度进行扩容 + if (newCapacity < minCapacity) + newCapacity = minCapacity; + // 数组copy, 进行扩容 + queue = Arrays.copyOf(queue, newCapacity); +} + + +// 入队 +public boolean offer(E e) { + // 校验: 入队元素空, 则抛空指针异常 + if (e == null) + throw new NullPointerException(); + // 修改模数(版本)+1 + modCount++; + // 记录当前队列中元素的个数 + int i = size ; + // 如果当前元素个数大于等于队列底层数组的长度, 则进行扩容 + if (i >= queue .length) + grow(i + 1); + // 元素个数+1 + size = i + 1; + // 如果队列中没有元素, 则将元素e直接添加至根(数组小标0的位置) + if (i == 0) + queue[0] = e; + else + // 否则调用siftUp方法, 将元素添加到尾部, 进行上移判断 + siftUp(i, e); + return true; +} + + + + + + +// 上移, x表示新插入元素, k表示新插入元素在数组的位置 +private void siftUp(int k, E x) { + // 如果比较器comparator不为空, 则调用siftUpUsingComparator方法进行上移操作 + if (comparator != null) + siftUpUsingComparator(k, x); + // 如果比较器comparator为空, 则调用siftUpComparable方法进行上移操作 + else + siftUpComparable(k, x); +} + + +private void siftUpComparable(int k, E x) { + // 比较器comparator为空, 需要插入的元素实现Comparable接口, 用于比较大小 + Comparable key = (Comparable) x; + // k>0表示判断k不是根的情况下, 也就是元素x有父节点 + while (k > 0) { + // 计算元素x的父节点位置[(n-1)/2] + int parent = (k - 1) >>> 1; + // 取出x的父亲e + Object e = queue[parent]; + // 如果新增的元素k比其父亲e大, 则不需要"上移", 跳出循环结束 + if (key.compareTo((E) e) >= 0) + break; + // x比父亲小, 则需要进行"上移" + // 交换元素x和父亲e的位置 + queue[k] = e; + // 将新插入元素的位置k指向父亲的位置, 进行下一层循环 + k = parent; + } + // 找到新增元素x的合适位置k之后进行赋值 + queue[k] = key; +} + +// 同上, 区别在于 使用元素实现的Comparator接口, 还是构造传入Comparator接口的实现类 +private void siftUpUsingComparator(int k, E x) { + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = queue[parent]; + if (comparator .compare(x, (E) e) >= 0) + break; + queue[k] = e; + k = parent; + } + queue[k] = x; +} + + +// 删除并返回队头的元, , 如果队列为空则抛出NoSuchElementException异常(该方法在AbstractQueue中) +public E remove() { + E x = poll(); + if (x != null) + return x; + else + throw new NoSuchElementException(); +} + +// 删除并返回队头的, 素, 如果队列为空则返回null +public E poll() { + // 队列为空, 返回null + if (size == 0) + return null; + // 队列元素个数-1 + int s = --size ; + // 修改版本+1 + modCount++; + // 队头的元素 + E result = (E) queue[0]; + // 队尾的元素 + E x = (E) queue[s]; + // 先将队尾赋值为null + queue[s] = null; + // 如果队列中不止队尾一个元素, 则调用siftDown方法进行"下移"操作 + if (s != 0) + siftDown(0, x); + return result; +} + +// 上移, x表示队尾的元素, k表示被删除元素在数组的位置 +private void siftDown(int k, E x) { + // 如果比较器comparator不为空, 则调用siftDownUsingComparator方法进行下移操作 + if (comparator != null) + siftDownUsingComparator(k, x); + // 比较器comparator为空, 则调用siftDownComparable方法进行下移操作 + else + siftDownComparable(k, x); +} + +private void siftDownComparable(int k, E x) { + // 比较器comparator为空, 需要插入的元素实现Comparable接口, 用于比较大小 + Comparable key = (Comparable)x; + // 通过size/2找到一个没有叶子节点的元素 + int half = size >>> 1; // loop while a non-leaf + // 比较位置k和half, 如果k小于half, 则k位置的元素就不是叶子节点 + while (k < half) { + // 找到根元素的左孩子的位置[2n+1] + int child = (k << 1) + 1; // assume left child is least + // 左孩子的元素 + Object c = queue[child]; + // 找到根元素的右孩子的位置[2(n+1)] + int right = child + 1; + // 如果左孩子大于右孩子, 则将c复制为右孩子的值, 这里也就是找出左右孩子哪个最小 + if (right < size && + ((Comparable) c).compareTo((E) queue [right]) > 0) + c = queue[child = right]; + // 如果队尾元素比根元素孩子都要小, 则不需"下移", 结束 + if (key.compareTo((E) c) <= 0) + break; + // 队尾元素比根元素孩子都大, 则需要"下移" + // 交换跟元素和孩子c的位置 + queue[k] = c; + // 将根元素位置k指向最小孩子的位置, 进入下层循环 + k = child; + } + // 找到队尾元素x的合适位置k之后进行赋值 + queue[k] = key; +} + +// 同上, 区别在于 使用元素实现的Comparator接口, 还是构造传入Comparator接口的实现类 +private void siftDownUsingComparator(int k, E x) { + int half = size >>> 1; + while (k < half) { + int child = (k << 1) + 1; + Object c = queue[child]; + int right = child + 1; + if (right < size && + comparator.compare((E) c, (E) queue [right]) > 0) + c = queue[child = right]; + if (comparator .compare(x, (E) c) <= 0) + break; + queue[k] = c; + k = child; + } + queue[k] = x; +} + + +``` + + + +#### ArrayDeque + +* 参考: https://www.cnblogs.com/tstd/p/5112177.html + +```java +// 存储方式为数组 +private transient E[] elements; +// 头指针 +private transient int head; +// 尾指针 +private transient int tail; +// 最小的初始化容量大小,需要为2的n次幂 +private static final int MIN_INITIAL_CAPACITY = 8; + + + +// 默认构造, 初始容量是16 +public ArrayDeque() { + elements = (E[]) new Object[16]; +} + +// 指定初始容量的构造 +public ArrayDeque( int numElements) { + allocateElements(numElements); +} + +// 传入一个集合, 分配空间并放置元素 +public ArrayDeque(Collection c) { + allocateElements(c.size()); + addAll(c); +} + +// 分配合适容量大小的数组,确保初始容量是大于指定numElements的最小的2的n次幂 +private void allocateElements(int numElements) { + int initialCapacity = MIN_INITIAL_CAPACITY; + // 找到大于指定容量的最小的2的n次幂 + // Find the best power of two to hold elements. + // Tests "<=" because arrays aren't kept full. + // 如果指定的容量小于初始容量8,则执行一下if中的逻辑操作 + if (numElements >= initialCapacity) { + initialCapacity = numElements; + initialCapacity |= (initialCapacity >>> 1); + initialCapacity |= (initialCapacity >>> 2); + initialCapacity |= (initialCapacity >>> 4); + initialCapacity |= (initialCapacity >>> 8); + initialCapacity |= (initialCapacity >>> 16); + initialCapacity++; + + if (initialCapacity < 0) // Too many elements, must back off + initialCapacity >>>= 1; // Good luck allocating 2 ^ 30 elements + } + elements = (E[]) new Object[initialCapacity]; +} + + + +// 带异常处理的增加元素 +public boolean add(E e) { + // 调用addLast方法,将元素添加到队尾 + addLast(e); + return true; +} + + +// 添加一个元素 +public boolean offer(E e) { + // 调用offerLast方法,将元素添加到队尾 + return offerLast(e); +} + +// 在队尾添加一个元素 +public boolean offerLast(E e) { + // 调用addLast方法,将元素添加到队尾 + addLast(e); + return true; +} + +// 将元素添加到队尾 +public void addLast(E e) { + // 如果元素为null,咋抛出空指针异常 + if (e == null) + throw new NullPointerException(); + // 将元素e放到数组的tail位置 + elements[tail ] = e; + // 判断tail和head是否相等,如果相等则对数组进行扩容 + if ( (tail = (tail + 1) & ( elements.length - 1)) == head) + // 进行两倍扩容 + doubleCapacity(); +} + + +// 移除并返回队列头部的元素,如果队列为空,则抛出一个NoSuchElementException异常 +public E remove() { + // 调用removeFirst方法,移除队头的元素 + return removeFirst(); +} + + +public E removeFirst() { + // 调用pollFirst方法,移除并返回队头的元素 + E x = pollFirst(); + // 如果队列为空,则抛出NoSuchElementException异常 + if (x == null) + throw new NoSuchElementException(); + return x; +} + +// 移除并返问队列头部的元素,如果队列为空,则返回null +public E poll() { + // 调用pollFirst方法,移除并返回队头的元素 + return pollFirst(); +} + +public E pollFirst() { + int h = head ; + // 取出数组队头位置的元素 + E result = elements[h]; // Element is null if deque empty + // 如果数组队头位置没有元素,则返回null值 + if (result == null) + return null; + // 将数组队头位置置空,也就是删除元素 + elements[h] = null; // Must null out slot + // 将head指针往前移动一个位置 + head = (h + 1) & (elements .length - 1); + // 将队头元素返回 + return result; +} + + + +// 返回队列头部的元素,如果队列为空,则抛出一个NoSuchElementException异常 +public E element() { + // 调用getFirst方法,获取队头的元素 + return getFirst(); +} + +public E getFirst() { + // 取得数组head位置的元素 + E x = elements[head ]; + // 如果数组head位置的元素为null,则抛出异常 + if (x == null) + throw new NoSuchElementException(); + return x; +} + +// 返回队列头部的元素,如果队列为空,则返回null +public E peek() { + // 调用peekFirst方法,获取队头的元素 + return peekFirst(); +} + +public E peekFirst() { + // 取得数组head位置的元素并返回 + return elements [head]; // elements[head] is null if deque empty +} +``` + + diff --git a/Week 01/id_713/RemoveDuplicatesFromSortedArray.java b/Week 01/id_713/RemoveDuplicatesFromSortedArray.java new file mode 100644 index 000000000..cd2336f8a --- /dev/null +++ b/Week 01/id_713/RemoveDuplicatesFromSortedArray.java @@ -0,0 +1,79 @@ +package id_713; + +/** + * 26. 删除排序数组中的重复项 + * 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 + * 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 + *

+ * 给定数组 nums = [1,1,2], + * 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 + * 你不需要考虑数组中超出新长度后面的元素。 + *

+ * 给定 nums = [0,0,1,1,1,2,2,3,3,4], + * 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 + * 你不需要考虑数组中超出新长度后面的元素。 + */ +public class RemoveDuplicatesFromSortedArray { + + /* + 读题: + 1. 返回值是新数组长度 + 2. 原数组需要变换 + 思路: + 1. 记录重复度的次数 + 2. 需要把后面的值往前覆盖, 索引即是 当前 i - 重复的次数 + 3. 数组长度 - 重复的次数 = 要返回的值 + */ + + public int removeDuplicates(int[] nums) { + int max = Integer.MIN_VALUE, mod = 0; // 定义当前最大值, 和重复次数(某种程度的模数) + + for (int i = 0; i < nums.length; i++) { + if (i != 0 && nums[i] == max) { // 出现重复, 记录重复的次数 + mod++; + } else { + nums[i - mod] = nums[i]; // 把当前值 赋值给 从当前到最近重复的索引, 因为一定是 mod <= i + max = (max > nums[i] ? max : nums[i]); + } + } + return nums.length - mod; + } + + /** + * 66. 加一 + *

+ * 给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。 + * 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 + * 你可以假设除了整数 0 之外,这个整数不会以零开头。 + *

+ * 示例 1: + * 输入: [1,2,3] + * 输出: [1,2,4] + * 解释: 输入数组表示数字 123。 + *

+ * 示例 2: + * 输入: [4,3,2,1] + * 输出: [4,3,2,2] + * 解释: 输入数组表示数字 4321。 + */ + public static class PlusOne { + + public int[] plusOne(int[] digits) { + + for (int i = digits.length - 1; i >= 0; i--) { + digits[i]++; + digits[i] = digits[i] % 10; // 为了不超过2位数, 保留个位数 + + // 只增加一次且不进位的话, 或者一直加到不进位时, 直接退出 + if (digits[i] != 0) { + return digits; + } + } + + int[] ans = new int[digits.length + 1]; + ans[0] = 1; + return ans; + } + + } +} \ No newline at end of file diff --git a/Week 01/id_713/ReverseLinkedList.java b/Week 01/id_713/ReverseLinkedList.java new file mode 100644 index 000000000..f05e6c7d1 --- /dev/null +++ b/Week 01/id_713/ReverseLinkedList.java @@ -0,0 +1,220 @@ +package id_713; + +import java.util.Stack; + +/** + * 206. Reverse Linked List + */ +public class ReverseLinkedList { + + public ListNode reverseList(ListNode head) { + ListNode prev = null, curr = head, next = null; + + while (curr != null) { + next = curr.next; + curr.next = prev; + prev = curr; + curr = next; + } + + return prev; + } + + + private class ListNode { + int val; + ListNode next; + + ListNode(int x) { + val = x; + } + } + + /** + * 88. 合并两个有序数组 + */ + public static class MergeSortedArray { + /* + 思路: + 1. nums1 的空间都集中在尾部, 可以考虑从后向前处理数据 + 2. 设计指针分别指向 nums1 和 nums2 的有效数据, 和一个指针, 指向nums1 的尾部 + 3. 从后往前遍历, 空值的槽(slot)接收大的数字, 谁大赋值过去, 并往前移动指针 + 4. 遍历结束后, nums2 有数据没有拷贝完全, 拷贝到nums1 的前面 + */ + + public void merge(int[] nums1, int m, int[] nums2, int n) { + + int p1 = m - 1, p2 = n - 1, p0 = m + n - 1; + + while (p1 >= 0 && p2 >= 0) { + nums1[p0--] = (nums1[p1] < nums2[p2] ? nums2[p2--] : nums1[p1--]); + } + + System.arraycopy(nums2, 0, nums1, 0, p2 + 1); + } + + } + + /** + * 42. 接雨水 + * 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 + * 示例: + * 输入: [0,1,0,2,1,0,1,3,2,1,2,1] + * 输出: 6 + */ + public static class TrappingRainWater { + + public int trap(int[] height) { + int sum = 0; + Stack stack = new Stack<>(); + int current = 0; + while (current < height.length) { + //如果栈不空并且当前指向的高度大于栈顶高度就一直循环 + while (!stack.empty() && height[current] > height[stack.peek()]) { + int h = height[stack.peek()]; //取出要出栈的元素 + stack.pop(); //出栈 + if (stack.empty()) { // 栈空就出去 + break; + } + int distance = current - stack.peek() - 1; //两堵墙之前的距离。 + int min = Math.min(height[stack.peek()], height[current]); + sum = sum + distance * (min - h); + } + stack.push(current); //当前指向的墙入栈 + current++; //指针后移 + } + return sum; + + } + } + + /** + * 641. 设计循环双端队列 + */ + public static class MyCircularDeque { + /* + 思路: + 控制头尾指针, 两者之间的元素为队列中的数据 + */ + + // 定义存储结构为 数组 + private int[] data; + // 定义头指针 + private int head; + // 定义尾指针 + private int tail; + // 定义容量 + private int capacity; + // 定义当前存储数据的量值 + private int size; + + /** + * 构造方法 + * + * @param k 初始容量 + */ + public MyCircularDeque(int k) { + data = new int[k]; + head = tail = -1; + capacity = k; + size = 0; + } + + + /** + * 头部插入 + * + * @param value 值 + * @return 是否成功 + */ + public boolean insertFront(int value) { + if (isFull()) return false; // 如果满仓, 返回失败 + // 头部插入从数组右侧开始, 插入后head向左移动 + head = (head - 1 + capacity) % capacity; + data[head] = value; + size++; + if (size == 1) { // 首次插入时, 头=尾 + tail = head; + } + return true; + } + + /** + * 尾部插入 + * + * @param value 值 + * @return 是否成功 + */ + public boolean insertLast(int value) { + if (isFull()) return false; + tail = (tail + 1) % capacity; + data[tail] = value; + size++; + if (size == 1) { + head = tail; + } + return true; + } + + /** + * 从头部删除 + * + * @return 是否成功 + */ + public boolean deleteFront() { + if (isEmpty()) return false; + head = (head + 1) % capacity; + size--; + return true; + } + + + /** + * 从尾部删除 + * + * @return 是否成功 + */ + public boolean deleteLast() { + if (isEmpty()) return false; + tail = (tail - 1 + capacity) % capacity; + size--; + return true; + } + + /** + * 查询头部元素 + * + * @return 元素值 + */ + public int getFront() { + if (isEmpty()) return -1; + return data[head]; + } + + /** + * 查询尾部元素 + * + * @return 元素值 + */ + public int getRear() { + if (isEmpty()) return -1; + return data[tail]; + } + + /** + * 是否为空 + * @return + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * 是否满仓 + */ + public boolean isFull() { + return size == capacity; + } + + } +} diff --git a/Week 01/id_713/RotateArray.java b/Week 01/id_713/RotateArray.java new file mode 100644 index 000000000..bb403e1d8 --- /dev/null +++ b/Week 01/id_713/RotateArray.java @@ -0,0 +1,39 @@ +package id_713; + +/** + * 189. 旋转数组 + * 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。 + * 示例 1: + * 输入: [1,2,3,4,5,6,7] 和 k = 3 + * 输出: [5,6,7,1,2,3,4] + * 示例 2: + * 输入: [-1,-100,3,99] 和 k = 2 + * 输出: [3,99,-1,-100] + */ +public class RotateArray { + + /* + 思路: + 1. 整体翻转 + 2. 轴之前翻转 + 3. 轴之后翻转 + */ + public void rotate(int[] nums, int k) { + k = k % nums.length; + + reverse(nums, 0, nums.length - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, nums.length - 1); + } + + private void reverse(int[] nums, int i, int j) { + int tmp = 0; + while (i < j) { + tmp = nums[i]; + nums[i] = j; + nums[j] = tmp; + i++; + j--; + } + } +} diff --git a/Week 01/id_713/TwoSum.java b/Week 01/id_713/TwoSum.java new file mode 100644 index 000000000..9e91e7448 --- /dev/null +++ b/Week 01/id_713/TwoSum.java @@ -0,0 +1,36 @@ +package id_713; + +import java.util.HashMap; +import java.util.Map; + +/** + * 两数之和 + * LeetCode 题号: 1 + * 给定 nums = [2, 7, 11, 15], target = 9 + * 因为 nums[0] + nums[1] = 2 + 7 = 9 + * 所以返回 [0, 1] + */ +public class TwoSum { + /* + 思路: + 1. 使用HashMap作为缓存, 并记录数字的索引值 + 2. 遍历数组, 当 target 减去 当前值, 出现在缓存中时, 就是满足了 这2个数之和等于target + 3. 返回缓存中记录的索引值即可 + */ + public int[] twoSum(int[] nums, int target) { + int[] res = new int[2]; + Map map = new HashMap<>(); // 定义map, key为数值, value为索引值 + + for (int i = 0; i < nums.length; i++) { + int tmp = target - nums[i]; + if (map.containsKey(tmp)) { // target - 当前值, 出现在map中, 即找到了这2个元素 + res[0] = map.get(tmp); // 获取对应数值的索引值 + res[1] = i; + return res; + } + map.put(nums[i], i); // 无需else, 因为上面if进入的话, 会return, 且遍历任何一个元素时, 都要加入到缓存map中 + } + + return res; + } +} \ No newline at end of file diff --git a/Week 01/id_713/more/ClimbingStairs.java b/Week 01/id_713/more/ClimbingStairs.java new file mode 100644 index 000000000..7d8682361 --- /dev/null +++ b/Week 01/id_713/more/ClimbingStairs.java @@ -0,0 +1,4 @@ +package id_713.more; + +public class ClimbingStairs { +} diff --git a/Week 01/id_713/more/ContainerWithMostWater.java b/Week 01/id_713/more/ContainerWithMostWater.java new file mode 100644 index 000000000..1021b0000 --- /dev/null +++ b/Week 01/id_713/more/ContainerWithMostWater.java @@ -0,0 +1,4 @@ +package id_713.more; + +public class ContainerWithMostWater { +} diff --git a/Week 01/id_713/more/LargestRectangleInHistogram.java b/Week 01/id_713/more/LargestRectangleInHistogram.java new file mode 100644 index 000000000..77c2c2d0e --- /dev/null +++ b/Week 01/id_713/more/LargestRectangleInHistogram.java @@ -0,0 +1,4 @@ +package id_713.more; + +public class LargestRectangleInHistogram { +} diff --git a/Week 01/id_713/more/LinkedListCycle.java b/Week 01/id_713/more/LinkedListCycle.java new file mode 100644 index 000000000..0afc31689 --- /dev/null +++ b/Week 01/id_713/more/LinkedListCycle.java @@ -0,0 +1,34 @@ +package id_713.more; + + +public class LinkedListCycle { + + + public boolean hasCycle(ListNode head) { + ListNode slow = head, fast = head; + + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + if (slow == fast) return true; + } + + return false; + } + + + private class ListNode { + int val; + ListNode next; + + ListNode(int x) { + val = x; + } + } +} + + + + + + diff --git a/Week 01/id_713/more/LinkedListCycleII.java b/Week 01/id_713/more/LinkedListCycleII.java new file mode 100644 index 000000000..4c85eae0d --- /dev/null +++ b/Week 01/id_713/more/LinkedListCycleII.java @@ -0,0 +1,43 @@ +package id_713.more; + +public class LinkedListCycleII { + + + public ListNode detectCycle(ListNode head) { + ListNode node = this.helper(head); + + if (node == null) return null; + + ListNode curr = head; + while (curr != node) { + curr = curr.next; + node = node.next; + } + return curr; + } + + private ListNode helper(ListNode head) { + ListNode slow = head, fast = head; + + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + if (slow == fast) { + return slow; + } + } + return null; + } + + + + + private class ListNode { + int val; + ListNode next; + + ListNode(int x) { + val = x; + } + } +} diff --git a/Week 01/id_713/more/MinStack.java b/Week 01/id_713/more/MinStack.java new file mode 100644 index 000000000..2aa9bc5ec --- /dev/null +++ b/Week 01/id_713/more/MinStack.java @@ -0,0 +1,4 @@ +package id_713.more; + +public class MinStack { +} diff --git a/Week 01/id_713/more/ReverseNodesInKGroup.java b/Week 01/id_713/more/ReverseNodesInKGroup.java new file mode 100644 index 000000000..3998e4e89 --- /dev/null +++ b/Week 01/id_713/more/ReverseNodesInKGroup.java @@ -0,0 +1,18 @@ +package id_713.more; + +public class ReverseNodesInKGroup { + + + public ListNode reverseKGroup(ListNode head, int k) { + return null; + } + + private class ListNode { + int val; + ListNode next; + + ListNode(int x) { + val = x; + } + } +} diff --git a/Week 01/id_713/more/SlidingWindowMaximum.java b/Week 01/id_713/more/SlidingWindowMaximum.java new file mode 100644 index 000000000..515aa2161 --- /dev/null +++ b/Week 01/id_713/more/SlidingWindowMaximum.java @@ -0,0 +1,4 @@ +package id_713.more; + +public class SlidingWindowMaximum { +} diff --git a/Week 01/id_713/more/SwapNodesInPairs.java b/Week 01/id_713/more/SwapNodesInPairs.java new file mode 100644 index 000000000..6657bb463 --- /dev/null +++ b/Week 01/id_713/more/SwapNodesInPairs.java @@ -0,0 +1,21 @@ +package id_713.more; + +/** + * 24. 两两交换链表中的节点 + */ +public class SwapNodesInPairs { + + public ListNode swapPairs(ListNode head) { + return null; + } + + + private class ListNode { + int val; + ListNode next; + + ListNode(int x) { + val = x; + } + } +} diff --git a/Week 01/id_713/more/ThreeSum.java b/Week 01/id_713/more/ThreeSum.java new file mode 100644 index 000000000..4ad0881e0 --- /dev/null +++ b/Week 01/id_713/more/ThreeSum.java @@ -0,0 +1,4 @@ +package id_713.more; + +public class ThreeSum { +} diff --git a/Week 01/id_713/more/ValidParentheses.java b/Week 01/id_713/more/ValidParentheses.java new file mode 100644 index 000000000..c7d5d3b3c --- /dev/null +++ b/Week 01/id_713/more/ValidParentheses.java @@ -0,0 +1,34 @@ +package id_713.more; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +/** + * 1. 暴力求解, 不断的replace括号, 复杂度 O(n^2) + *

+ * 2. Stack 如果左括号压入栈, 如果是右括号与栈顶元素进行匹配 + * 匹配上栈顶元素弹出栈, 否则不合法, 直到栈为空则认为是有效的 + */ +public class ValidParentheses { + + public boolean isValid(String s) { + Stack stack = new Stack<>(); + Map map = new HashMap<>(); + map.put('(', ')'); + map.put('[', ']'); + map.put('{', '}'); + + for (int i = 0; i < s.length(); i++) { + Character tmp = s.charAt(i); + if (!stack.empty() && map.get(stack.peek()) == tmp) { + stack.pop(); + } else { + stack.push(tmp); + } + } + + return stack.empty(); + } + +} \ No newline at end of file diff --git a/Week 01/id_718/101.symmetric-tree.py b/Week 01/id_718/101.symmetric-tree.py new file mode 100644 index 000000000..37c1b0680 --- /dev/null +++ b/Week 01/id_718/101.symmetric-tree.py @@ -0,0 +1,19 @@ +# +# @lc app=leetcode id=101 lang=python3 +# +# [101] Symmetric Tree +# + +# @lc code=start +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + def isSymmetric(self, root: TreeNode) -> bool: + +# @lc code=end + diff --git a/Week 01/id_718/11.container-with-most-water.py b/Week 01/id_718/11.container-with-most-water.py new file mode 100644 index 000000000..1e786afde --- /dev/null +++ b/Week 01/id_718/11.container-with-most-water.py @@ -0,0 +1,21 @@ +# +# @lc app=leetcode id=11 lang=python3 +# +# [11] Container With Most Water +# + +# @lc code=start +class Solution: + def maxArea(self, height: List[int]) -> int: + l, r = 0, len(height) - 1 + ret = 0 + while l < r: + min_height = min(height[l], height[r]) + ret = max(ret, min_height * (r - l)) + if height[l] < height[r]: + l += 1 + else: + r -= 1 + return ret +# @lc code=end + diff --git a/Week 01/id_718/15.3-sum.py b/Week 01/id_718/15.3-sum.py new file mode 100644 index 000000000..be8b43ebf --- /dev/null +++ b/Week 01/id_718/15.3-sum.py @@ -0,0 +1,31 @@ +# +# @lc app=leetcode id=15 lang=python3 +# +# [15] 3Sum +# + +# @lc code=start +class Solution: + def threeSum(self, nums: List[int]) -> List[List[int]]: + ret = [] + n = len(nums) + nums.sort() + for i in range(0, n - 1): + if nums[i] > 0: break + if i != 0 and nums[i] == nums[i-1]: continue + l, r = i + 1, n - 1 + target = 0 - nums[i] + while l < r: + if nums[l] + nums[r] == target: + ret.append([nums[i], nums[l], nums[r]]) + l += 1 + r -= 1 + elif nums[l] + nums[r] < target: + l += 1 + else: + r -= 1 + while i + 1 < l < r and nums[l] == nums[l-1]: l += 1 + while l < r and r < n -1 and nums[r] == nums[r+1]: r -= 1 + return ret +# @lc code=end + diff --git a/Week 01/id_718/155.min-stack.py b/Week 01/id_718/155.min-stack.py new file mode 100644 index 000000000..ee50577ac --- /dev/null +++ b/Week 01/id_718/155.min-stack.py @@ -0,0 +1,41 @@ +# +# @lc app=leetcode id=155 lang=python3 +# +# [155] Min Stack +# + +# @lc code=start +from collections import deque +class MinStack: + + def __init__(self): + """ + initialize your data structure here. + """ + self.stack, self.minstack = deque(), deque() + + def push(self, x: int) -> None: + self.stack.append(x) + if not self.minstack or self.minstack[-1] >= x: + self.minstack.append(x) + + def pop(self) -> None: + x = self.stack.pop() + if x == self.minstack[-1]: + self.minstack.pop() + return x + + def top(self) -> int: + return self.stack[-1] + + def getMin(self) -> int: + return self.minstack[-1] + +# Your MinStack object will be instantiated and called as such: +# obj = MinStack() +# obj.push(x) +# obj.pop() +# param_3 = obj.top() +# param_4 = obj.getMin() +# @lc code=end + diff --git a/Week 01/id_718/20.valid-parentheses.py b/Week 01/id_718/20.valid-parentheses.py new file mode 100644 index 000000000..3558075a4 --- /dev/null +++ b/Week 01/id_718/20.valid-parentheses.py @@ -0,0 +1,20 @@ +# +# @lc app=leetcode id=20 lang=python3 +# +# [20] Valid Parentheses +# + +# @lc code=start +from collections import deque +class Solution: + def isValid(self, s: str) -> bool: + dic = {")":"(", "]":"[", "}":"{"} + stack = deque() + for c in s: + if c not in dic: + stack.append(c) + elif not stack or dic[c] != stack.pop(): + return False + return not stack +# @lc code=end + diff --git a/Week 01/id_718/206.reverse-linked-list.py b/Week 01/id_718/206.reverse-linked-list.py new file mode 100644 index 000000000..d55819b82 --- /dev/null +++ b/Week 01/id_718/206.reverse-linked-list.py @@ -0,0 +1,31 @@ +# +# @lc app=leetcode id=206 lang=python3 +# +# [206] Reverse Linked List +# + +# @lc code=start +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None + +class Solution: + def reverseList(self, head: ListNode) -> ListNode: + cur, prev = head, None + while cur: + cur.next, cur, prev = prev, cur.next, cur + return prev + +''' + class Solution: + def reverseList(self, head: ListNode) -> ListNode: + if not head or not head.next: + return head + p, head = head, self.reverseList(head.next) + p.next.next, p.next = p, None + return head +''' +# @lc code=end + diff --git a/Week 01/id_718/239.sliding-window-maximum.py b/Week 01/id_718/239.sliding-window-maximum.py new file mode 100644 index 000000000..919edc729 --- /dev/null +++ b/Week 01/id_718/239.sliding-window-maximum.py @@ -0,0 +1,24 @@ +# +# @lc app=leetcode id=239 lang=python3 +# +# [239] Sliding Window Maximum +# + +# @lc code=start +from collections import deque +class Solution: + def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: + ret = [] + if not nums: return ret + window = deque() + for i, v in enumerate(nums): + if i >= k and window[0] <= (i-k): + window.popleft() + while window and nums[window[-1]] <= v: + window.pop() + window.append(i) + if i >= k - 1: + ret.append(nums[window[0]]) + return ret +# @lc code=end + diff --git a/Week 01/id_718/24.swap-nodes-in-pairs.py b/Week 01/id_718/24.swap-nodes-in-pairs.py new file mode 100644 index 000000000..84e3ab308 --- /dev/null +++ b/Week 01/id_718/24.swap-nodes-in-pairs.py @@ -0,0 +1,28 @@ +# +# @lc app=leetcode id=24 lang=python3 +# +# [24] Swap Nodes in Pairs +# + +# @lc code=start +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None + +class Solution: + def swapPairs(self, head: ListNode) -> ListNode: + self.next = head + prev, cur = self, head + while cur and cur.next: + next = cur.next + cur.next = cur.next.next + next.next = prev.next + prev.next = next + # move + prev, cur = cur, prev.next + return self.next + +# @lc code=end + diff --git a/Week 01/id_718/283.move-zeroes.py b/Week 01/id_718/283.move-zeroes.py new file mode 100644 index 000000000..ed6fc3c7d --- /dev/null +++ b/Week 01/id_718/283.move-zeroes.py @@ -0,0 +1,20 @@ +# +# @lc app=leetcode id=283 lang=python3 +# +# [283] Move Zeroes +# + +# @lc code=start +class Solution: + def moveZeroes(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + i = 0 # slow pointer + for j, v in enumerate(nums): + if v != 0: + nums[i] = nums[j] + if i != j: nums[j] = 0 + i += 1 +# @lc code=end + diff --git a/Week 01/id_718/70.climbing-stairs.py b/Week 01/id_718/70.climbing-stairs.py new file mode 100644 index 000000000..071df2a61 --- /dev/null +++ b/Week 01/id_718/70.climbing-stairs.py @@ -0,0 +1,30 @@ +# +# @lc app=leetcode id=70 lang=python3 +# +# [70] Climbing Stairs +# + +# @lc code=start +class Solution: + def climbStairs(self, n: int) -> int: + # rolling array + x, y = 1, 0 + for _ in range(n): + x, y = x + y, x + return x + +''' +class Solution: + def moveZeroes(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + # two pointers + j = 0 + for i, v in enumerate(nums): + if v: + nums[i], nums[j] = nums[j], nums[i] + j += 1 +''' +# @lc code=end + diff --git a/Week 01/id_718/84.largest-rectangle-in-histogram.py b/Week 01/id_718/84.largest-rectangle-in-histogram.py new file mode 100644 index 000000000..93d973ea1 --- /dev/null +++ b/Week 01/id_718/84.largest-rectangle-in-histogram.py @@ -0,0 +1,23 @@ +# +# @lc app=leetcode id=84 lang=python3 +# +# [84] Largest Rectangle in Histogram +# + +# @lc code=start +from collections import deque +class Solution: + def largestRectangleArea(self, heights: List[int]) -> int: + stack = deque([-1]) + ret = 0 + heights.append(0) + # increasing stack + for i in range(len(heights)): + while heights[i] < heights[stack[-1]]: + h = heights[stack.pop()] + w = i - stack[-1] - 1 + ret = max(ret, h * w) + stack.append(i) + return ret +# @lc code=end + diff --git a/Week 01/id_718/NOTE.md b/Week 01/id_718/NOTE.md index a6321d6e2..a3b5cd7f3 100644 --- a/Week 01/id_718/NOTE.md +++ b/Week 01/id_718/NOTE.md @@ -1,4 +1,9 @@ -# NOTE +### 关于算法面试的准备与刻意练习 +--- +[Prepare Coding Interview](https://github.com/XinheLIU/GitBook_AlgorithmandDataStructure/blob/master/programming-interview-tips.md) +### 第一周学习内容-数据结构-线性表总结 +--- +[Linear List](https://github.com/XinheLIU/GitBook_AlgorithmandDataStructure/blob/master/data-structures/linear-list.md) diff --git a/Week 01/id_728/189_Rotate.java b/Week 01/id_728/189_Rotate.java new file mode 100644 index 000000000..7eeb6ac67 --- /dev/null +++ b/Week 01/id_728/189_Rotate.java @@ -0,0 +1,17 @@ +public class Solution { + public void rotate(int[] nums, int k) { + k %= nums.length; + reverse(nums, 0, nums.length - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, nums.length - 1); + } + public void reverse(int[] nums, int start, int end) { + while (start < end) { + int temp = nums[start]; + nums[start] = nums[end]; + nums[end] = temp; + start++; + end--; + } + } +} diff --git a/Week 01/id_728/26_RemoveDuplicates.java b/Week 01/id_728/26_RemoveDuplicates.java new file mode 100644 index 000000000..feff4daa5 --- /dev/null +++ b/Week 01/id_728/26_RemoveDuplicates.java @@ -0,0 +1,15 @@ +class Solution { + public int removeDuplicates(int[] nums) { + if (nums.length == 0) { + return 0; + } + int i = 0; + for (int j = 1; j < nums.length; j++) { + if (nums[j] != nums[i]) { + i++; + nums[i] = nums[j]; + } + } + return i + 1; + } +} diff --git a/Week 01/id_733/LeetCode_189_733.go b/Week 01/id_733/LeetCode_189_733.go new file mode 100644 index 000000000..ecf54de8b --- /dev/null +++ b/Week 01/id_733/LeetCode_189_733.go @@ -0,0 +1,57 @@ +func rotate(nums []int, k int) { + if len(nums) <= 1 || k <= 0 { + return + } + + res := make([]int, len(nums)) + for i := 0; i < len(nums); i++ { + res[(i + k) % len(nums)] = nums[i] + } + + for i := 0; i < len(nums); i++ { + nums[i] = res[i] + } +} + +func rotateV2(nums []int, k int) { + if len(nums) <= 1 || k <= 0 { + return + } + + for i, cnt := 0, 0; cnt < len(nums); i++ { + idx := i + val := nums[idx] + for { + next := (idx + k) % len(nums) + nums[next], val = val, nums[next] + cnt++ + if next == i { + break + } + + idx = next + } + } +} + +func rotateV3(nums []int, k int) { + + + if len(nums) <= 1 || k <= 0 { + return + } + + k = k % len(nums) + reverse(nums) + reverse(nums[:k]) + reverse(nums[k:]) +} + +func reverse(nums []int) { + start, end := 0, len(nums)-1 + for start < end { + nums[start], nums[end] = nums[end], nums[start] + start++ + end-- + } +} \ No newline at end of file diff --git a/Week 01/id_733/LeetCode_1_733.go b/Week 01/id_733/LeetCode_1_733.go new file mode 100644 index 000000000..5ec808f4f --- /dev/null +++ b/Week 01/id_733/LeetCode_1_733.go @@ -0,0 +1,12 @@ +func twoSum(nums []int, target int) []int { + m := make(map[int]int, len(nums)) + for i := 0; i < len(nums); i++ { + if idx, ok := m[nums[i]]; ok { + return []int{idx, i} + } + + m[target-nums[i]] = i + } + + return []int{} +} \ No newline at end of file diff --git a/Week 01/id_733/LeetCode_21_733.go b/Week 01/id_733/LeetCode_21_733.go new file mode 100644 index 000000000..6be60aedf --- /dev/null +++ b/Week 01/id_733/LeetCode_21_733.go @@ -0,0 +1,35 @@ +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode { + dummy := &ListNode{} + cur := dummy + + var l *ListNode + for l1 != nil && l2 != nil { + if l1.Val > l2.Val { + l = l2 + l2 = l2.Next + } else { + l = l1 + l1 = l1.Next + } + + cur.Next = l + cur = cur.Next + } + + if l1 == nil { + l = l2 + } else { + l = l1 + } + + cur.Next = l + return dummy.Next +} + diff --git a/Week 01/id_733/LeetCode_26_733.go b/Week 01/id_733/LeetCode_26_733.go new file mode 100644 index 000000000..fee2dbad3 --- /dev/null +++ b/Week 01/id_733/LeetCode_26_733.go @@ -0,0 +1,15 @@ +func removeDuplicates(nums []int) int { + if len(nums) == 0 { + return 0 + } + + end := 0 + for idx := 1; idx < len(nums); idx++ { + if nums[end] != nums[idx] { + end++ + nums[end] = nums[idx] + } + } + + return end + 1 +} \ No newline at end of file diff --git a/Week 01/id_733/LeetCode_283_733.go b/Week 01/id_733/LeetCode_283_733.go new file mode 100644 index 000000000..e369911b5 --- /dev/null +++ b/Week 01/id_733/LeetCode_283_733.go @@ -0,0 +1,10 @@ +func moveZeros(nums []int) { + for i, idx := 0, 0; i < len(nums); i++ { + if nums[i] == 0 { + continue + } + + nums[i], nums[idx] = nums[idx], nums[i] + idx++ + } +} diff --git a/Week 01/id_733/LeetCode_641_733.go b/Week 01/id_733/LeetCode_641_733.go new file mode 100644 index 000000000..49f6e8fda --- /dev/null +++ b/Week 01/id_733/LeetCode_641_733.go @@ -0,0 +1,107 @@ +type MyCircularDeque struct { + queue []int + head int + rear int +} + + +/** Initialize your data structure here. Set the size of the deque to be k. */ +func Constructor(k int) MyCircularDeque { + deque := MyCircularDeque{} + deque.queue = make([]int, k + 1) + return deque +} + + +/** Adds an item at the front of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) InsertFront(value int) bool { + if this.IsFull() { + return false + } + + idx := (this.head + len(this.queue) - 1) % len(this.queue) + this.queue[idx] = value + this.head = idx + return true +} + + +/** Adds an item at the rear of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) InsertLast(value int) bool { + if this.IsFull() { + return false + } + + this.queue[this.rear] = value + this.rear = (this.rear + 1) % len(this.queue) + return true +} + + +/** Deletes an item from the front of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) DeleteFront() bool { + if this.IsEmpty() { + return false + } + + this.head = (this.head + 1) % len(this.queue) + return true +} + + +/** Deletes an item from the rear of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) DeleteLast() bool { + if this.IsEmpty() { + return false + } + + this.rear = (this.rear + len(this.queue) - 1) % len(this.queue) + return true +} + + +/** Get the front item from the deque. */ +func (this *MyCircularDeque) GetFront() int { + if this.IsEmpty() { + return -1 + } + + return this.queue[this.head] +} + + +/** Get the last item from the deque. */ +func (this *MyCircularDeque) GetRear() int { + if this.IsEmpty() { + return -1 + } + + idx := (this.rear + len(this.queue) - 1) % len(this.queue) + return this.queue[idx] +} + + +/** Checks whether the circular deque is empty or not. */ +func (this *MyCircularDeque) IsEmpty() bool { + return this.head == this.rear +} + + +/** Checks whether the circular deque is full or not. */ +func (this *MyCircularDeque) IsFull() bool { + return (this.rear + 1) % len(this.queue) == this.head +} + + +/** + * Your MyCircularDeque object will be instantiated and called as such: + * obj := Constructor(k); + * param_1 := obj.InsertFront(value); + * param_2 := obj.InsertLast(value); + * param_3 := obj.DeleteFront(); + * param_4 := obj.DeleteLast(); + * param_5 := obj.GetFront(); + * param_6 := obj.GetRear(); + * param_7 := obj.IsEmpty(); + * param_8 := obj.IsFull(); + */ diff --git a/Week 01/id_733/LeetCode_66_733.go b/Week 01/id_733/LeetCode_66_733.go new file mode 100644 index 000000000..f9ad3e54d --- /dev/null +++ b/Week 01/id_733/LeetCode_66_733.go @@ -0,0 +1,27 @@ +func plusOne(digits []int) []int { + if len(digits) == 0 { + return []int{} + } + + res := make([]int, len(digits)) + i := len(digits) - 1 + for ; i >= 0; i-- { + if digits[i] < 9 { + res[i] = digits[i] + 1 + break + } + + res[i] = 0 + } + + for i--; i >= 0; i-- { + res[i] = digits[i] + } + + if res[0] == 0 { + res = make([]int, len(res)+1) + res[0] = 1 + } + + return res +} \ No newline at end of file diff --git a/Week 01/id_733/LeetCode_88_733.go b/Week 01/id_733/LeetCode_88_733.go new file mode 100644 index 000000000..9b6a59402 --- /dev/null +++ b/Week 01/id_733/LeetCode_88_733.go @@ -0,0 +1,20 @@ +func merge(nums1 []int, m int, nums2 []int, n int) { + i, j, idx := m-1, n-1, len(nums1)-1; + for i >= 0 && j >= 0 { + if nums1[i] < nums2[j] { + nums1[idx] = nums2[j] + j-- + } else { + nums1[idx] = nums1[i] + i-- + } + + idx-- + } + + for j >= 0 { + nums1[idx] = nums2[j] + j-- + idx-- + } +} diff --git a/Week 01/id_738/LeetCode_189_738.py b/Week 01/id_738/LeetCode_189_738.py new file mode 100644 index 000000000..a14a9e0a4 --- /dev/null +++ b/Week 01/id_738/LeetCode_189_738.py @@ -0,0 +1,55 @@ +class Solution(object): + def rotate(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: None Do not return anything, modify nums in-place instead. + """ + #方法1:暴力法,一步一步移动,时间复杂度O(k * n) + #提交的时候会超时.. + # if len(nums) <= 1: + # return + # for i in range(k): + # next_val = nums[0] + # for cur_index in range(len(nums)): + # next_index = (cur_index + 1) % len(nums) + # nums[next_index], next_val = next_val, nums[next_index] + + # 方法2: + # 数组长度为n,要移动n次是肯定的 + # 考虑能否一次到位,直接将i移动到i+k位置 + # n % k == 0 和 != 0的情况不同 + # l = len(nums) + # k = k % l + # count = 0 + # start = 0 + # while count < l: + # tmp = nums[start] + # cur_index = start + # while True: + # next_index = (cur_index + k) % l + # nums[next_index], tmp = tmp, nums[next_index] + # cur_index = next_index + # count += 1 + # if start == cur_index: + # break + # start += 1 + + #下面是方法2的另一种写法 + count = LEN = len(nums) + k = k % LEN + CUR = START = 0 + CUR_TMP = nums[0] + while True: + NEXT = (CUR + k) % LEN + NEXT_TMP = nums[NEXT] + nums[NEXT] = CUR_TMP + CUR_TMP = NEXT_TMP + CUR = NEXT + count -= 1 + if count == 0: + break + if START == CUR: + START += 1 + CUR += 1 + CUR_TMP = nums[CUR] diff --git a/Week 01/id_738/LeetCode_1_738.py b/Week 01/id_738/LeetCode_1_738.py new file mode 100644 index 000000000..b935c86a5 --- /dev/null +++ b/Week 01/id_738/LeetCode_1_738.py @@ -0,0 +1,24 @@ +class Solution(object): + def twoSum(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: List[int] + """ + #暴力求解两重循环 + # for i in range(len(nums) - 1): + # k = target - nums[i] + # for j in range(i + 1, len(nums)): + # if nums[j] == k: return [i, j] + + #放入Hash表中,然后查找的时候就是O(1)复杂度,加上自身N个元素就是O(n)复杂度 + #python中的 map就是用hash表实现的 + result = [] + dictionary = {} + for i in range(len(nums)): + a = target - nums[i] + if nums[i] in dictionary: + result = [i, dictionary[nums[i]]] + dictionary[a] = i + return result + diff --git a/Week 01/id_738/LeetCode_21_738.py b/Week 01/id_738/LeetCode_21_738.py new file mode 100644 index 000000000..d101ba94a --- /dev/null +++ b/Week 01/id_738/LeetCode_21_738.py @@ -0,0 +1,50 @@ +# Definition for singly-linked list. +# class ListNode(object): +# def __init__(self, x): +# self.val = x +# self.next = None + +class Solution(object): + def mergeTwoLists(self, l1, l2): + """ + :type l1: ListNode + :type l2: ListNode + :rtype: ListNode + """ + # 递归法 + if not l1: + return l2 + elif not l2: + return l1 + else: + if l1.val < l2.val: + l1.next = self.mergeTwoLists(l1.next, l2) + return l1 + else: + l2.next = self.mergeTwoLists(l1, l2.next) + return l2 + + + + # 迭代法,时间复杂度O(n + m), 空间复杂度O(1) + # prev = head = ListNode(0) + # prev.next = l1 + # cur_1 = l1 + # cur_2 = l2 + # while cur_1 and cur_2: + # if cur_1.val <= cur_2.val: + # prev = cur_1 + # cur_1 = cur_1.next + # else: + # prev.next = cur_2 + # tmp = cur_2.next + # cur_2.next = cur_1 + # prev = prev.next + # cur_2 = tmp + # if not cur_1: + # prev.next = cur_2 + # return head.next + + + + \ No newline at end of file diff --git a/Week 01/id_738/LeetCode_26_738.py b/Week 01/id_738/LeetCode_26_738.py new file mode 100644 index 000000000..8fe6441d1 --- /dev/null +++ b/Week 01/id_738/LeetCode_26_738.py @@ -0,0 +1,13 @@ +class Solution(object): + def removeDuplicates(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + #快慢指针 + i = 0 + for j in range(1, len(nums)): + if nums[i] != nums[j]: + i += 1 + nums[i] = nums[j] + return i + 1 \ No newline at end of file diff --git a/Week 01/id_738/LeetCode_283_738.py b/Week 01/id_738/LeetCode_283_738.py new file mode 100644 index 000000000..093adfe62 --- /dev/null +++ b/Week 01/id_738/LeetCode_283_738.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# +# @lc app=leetcode.cn id=283 lang=python +# +# [283] 移动零 +# + +# @lc code=start +class Solution(object): + def moveZeroes(self, nums): + """ + :type nums: List[int] + :rtype: None Do not return anything, modify nums in-place instead. + """ + #利用双指针,假设指针i,j,i指向数组最左边的0元素,j指向数组最左边的非0元素 + #每次只要swap i,j指向元素的值即可 + #遍历一次时间复杂度O(n) + i = 0 + for j in range(len(nums)): + if nums[j] != 0: + nums[i], nums[j] = nums[j], nums[i] + i += 1 + +# @lc code=end \ No newline at end of file diff --git a/Week 01/id_738/LeetCode_42_738.py b/Week 01/id_738/LeetCode_42_738.py new file mode 100644 index 000000000..4fb210ae7 --- /dev/null +++ b/Week 01/id_738/LeetCode_42_738.py @@ -0,0 +1,72 @@ +class Solution(object): + def trap(self, height): + """ + :type height: List[int] + :rtype: int + """ + result = 0 + #暴力求解,提交超时 + #时间复杂度O(n^2) + # for i in range(len(height)): + # max_left = max_right = 0 + # j = i + # while j >= 0: + # max_left = max(max_left, height[j]) + # j -= 1 + # j = i + # while j < len(height): + # max_right = max(max_right, height[j]) + # j += 1 + # result += (min(max_right, max_left) - height[i]) + # return result + + #考虑到每次计算元素最大容纳高度,每次都要扫描左右元素的最大值 + #其实扫描左边元素最大值可以依赖于前面元素得出的结果,只要比较之前元素最左边最大值和当前元素的大小,即可知道最大值,右边同理 + #所以用2个数组,存储已经扫描过的元素的左右两边最大值,数组的第i个元素的值依赖于数组第i-1个元素的值和第i个元素的值之间的最大值 + #时间复杂度O(3n) + if not height: + return 0 + max_left = [0] * len(height) + max_right = [0] * len(height) + max_left[0] = height[0] + max_right[-1] = height[-1] + length = len(height) + result = 0 + for i in range(length): + max_left[i] = max(max_left[i - 1], height[i]) + i = length - 2 + while i >= 0: + max_right[i] = max(max_right[i + 1], height[i]) + i -= 1 + for i in range(length): + result += min(max_left[i], max_right[i]) - height[i] + return result + + #盛水容器的多少取决于左右两个最近的不相邻柱子,并且两个柱子之间的柱子不能高于这两个柱子 + #这种最近相关性问题可以用栈解决 + #时间复杂度O(n) + # stack = [] + # result = 0 + # for i in range(len(height)): + # while stack and height[stack[-1]] < height[i]: + # top = stack.pop() + # if not stack: + # break + # width = i - stack[-1] - 1 + # min_height = min(height[stack[-1]], height[i]) - height[top] + # result += width * min_height + # stack.append(i) + # return result + + + + + + + + + + + + + \ No newline at end of file diff --git a/Week 01/id_738/LeetCode_641_738.py b/Week 01/id_738/LeetCode_641_738.py new file mode 100644 index 000000000..f0699a344 --- /dev/null +++ b/Week 01/id_738/LeetCode_641_738.py @@ -0,0 +1,122 @@ +class Node: + def __init__(self, value): + self.val = value + self.next = self.pre = None + +class MyCircularDeque(object): + + def __init__(self, k): + """ + Initialize your data structure here. Set the size of the deque to be k. + :type k: int + """ + self.head = self.tail = Node(-1) + self.head.next = self.tail + self.tail.pre = self.head + self.size = k + self.curSize = 0 + + + def add(self, value, preNode): + new = Node(value) + new.pre = preNode + new.next = preNode.next + new.pre.next = new.next.pre = new + self.curSize += 1 + + def remove(self, preNode): + node = preNode.next + node.pre.next = node.next + node.next.pre = node.pre + self.curSize -= 1 + + + + def insertFront(self, value): + """ + Adds an item at the front of Deque. Return true if the operation is successful. + :type value: int + :rtype: bool + """ + if self.curSize < self.size: + self.add(value, self.head) + return True + return False + + def insertLast(self, value): + """ + Adds an item at the rear of Deque. Return true if the operation is successful. + :type value: int + :rtype: bool + """ + if self.curSize < self.size: + self.add(value, self.tail.pre) + return True + return False + + + def deleteFront(self): + """ + Deletes an item from the front of Deque. Return true if the operation is successful. + :rtype: bool + """ + if self.curSize: + self.remove(self.head) + return True + return False + + + def deleteLast(self): + """ + Deletes an item from the rear of Deque. Return true if the operation is successful. + :rtype: bool + """ + if self.curSize: + self.remove(self.tail.pre.pre) + return True + return False + + + def getFront(self): + """ + Get the front item from the deque. + :rtype: int + """ + if self.curSize: + return self.head.next.val + return -1 + + def getRear(self): + """ + Get the last item from the deque. + :rtype: int + """ + if self.curSize: + return self.tail.pre.val + return -1 + + def isEmpty(self): + """ + Checks whether the circular deque is empty or not. + :rtype: bool + """ + return self.curSize == 0 + + def isFull(self): + """ + Checks whether the circular deque is full or not. + :rtype: bool + """ + return self.curSize == self.size + + +# Your MyCircularDeque object will be instantiated and called as such: +# obj = MyCircularDeque(k) +# param_1 = obj.insertFront(value) +# param_2 = obj.insertLast(value) +# param_3 = obj.deleteFront() +# param_4 = obj.deleteLast() +# param_5 = obj.getFront() +# param_6 = obj.getRear() +# param_7 = obj.isEmpty() +# param_8 = obj.isFull() \ No newline at end of file diff --git a/Week 01/id_738/LeetCode_66_738.py b/Week 01/id_738/LeetCode_66_738.py new file mode 100644 index 000000000..6f2a13b72 --- /dev/null +++ b/Week 01/id_738/LeetCode_66_738.py @@ -0,0 +1,19 @@ +class Solution(object): + def plusOne(self, digits): + """ + :type digits: List[int] + :rtype: List[int] + """ + pl = 1 + for i in range(1, len(digits) + 1): + if digits[-i] + pl >= 10: + digits[-i] = digits[-i] + pl - 10 + pl = 1 + else: + digits[-i] += pl + pl = 0 + break + if pl == 1: + digits.insert(0, 1) + return digits + \ No newline at end of file diff --git a/Week 01/id_738/LeetCode_88_738.py b/Week 01/id_738/LeetCode_88_738.py new file mode 100644 index 000000000..912a03b43 --- /dev/null +++ b/Week 01/id_738/LeetCode_88_738.py @@ -0,0 +1,44 @@ +class Solution(object): + def merge(self, nums1, m, nums2, n): + """ + :type nums1: List[int] + :type m: int + :type nums2: List[int] + :type n: int + :rtype: None Do not return anything, modify nums1 in-place instead. + """ + #双指针从前往后,遍历两个数组 m + n,但是插入的时候还需要logn + #所以时间复杂度为 O((m+n)log(n+m)) + # cur_1, cur_2 = 0, 0 + # while cur_1 < m and cur_2 < n: + # while cur_1 < m and nums1[cur_1] < nums2[cur_2]: + # cur_1 += 1 + # nums1.insert(cur_1, nums2[cur_2]) + # nums1.pop() + # cur_1 += 1 + # cur_2 += 1 + # m += 1 + # if cur_1 >= m: + # while cur_2 < n: + # nums1[cur_1] = nums2[cur_2] + # cur_2 += 1 + # cur_1 += 1 + + # 由于nums1后面为留空位置,如果从后面插入,那么不需要移动元素 + cur_1, cur_2, p = m - 1, n - 1, len(nums1) - 1 + while cur_1 >=0 and cur_2 >= 0: + if nums1[cur_1] > nums2[cur_2]: + nums1[p] = nums1[cur_1] + cur_1 -= 1 + else: + nums1[p] = nums2[cur_2] + cur_2 -= 1 + p -= 1 + if cur_2 >= 0: + nums1[:cur_2 + 1] = nums2[:cur_2 + 1] + + + + + + \ No newline at end of file diff --git a/Week 01/id_738/NOTE.md b/Week 01/id_738/NOTE.md index a6321d6e2..4ff6dc9b2 100644 --- a/Week 01/id_738/NOTE.md +++ b/Week 01/id_738/NOTE.md @@ -1,4 +1,267 @@ # NOTE +### 周总结 + +本周是算法课堂的第一周。在本周里面,感受到了本门课的训练强度。按照覃超老师提供的五步刷题法建议,我对视频里面的实战题目,对全部课后题都按照五步刷题法的要求去完成。 + +1. 通过一周的训练,我对 数组/链表/栈/队列 的算法更加熟练了。也学习了一些解题的基本思路。大致有: + +- 快慢指针法 + +- 左右指针夹逼法 + +- 递归法 + +- 空间换时间法 + +- 升维提升运行效率法 + +- 遇到最近相关性问题,可以直接考虑栈解决 +2. 做题过遍数很重要,一道题做一次肯定不够,经过多次练习,会不对加深对题目的理解,以及这类题的理解。 + +3. 一道题目不要局限于一个解法,最基本的暴力法解决虽然看起来比较拙,但是它能够训练最基本的变成功力。而多思考其他方法,能够达到刻意训练洋葱降低编程时间复杂度的良好习惯。 + +4. 通过作业里面的源码分析,对Priority Queue的底层理解更加透彻了,也从源码学习到堆的操作技巧。 + +### 本周题目代码连接: + +https://github.com/zhengxin12345/algorithm004-03/tree/master/Week%2001/id_738 + + +--- + +### 改写Deque +``` +Deque deque = new LinkedList(); +//deque.push("a") +//deque.push("b") +//deque.push("c") + +deque.addFirst("a"); +deque.addFirst("b"); +deque.addFirst("c"); +System.out.println(deque); + +//String str = deque.peek(); +String str = deque.peekFirst(); +System.out.println(str); +System.out.println(deque); + +while (deque.size() > 0) { + //System.out.println(deque.pop()); + System.out.println(deque.removeFirst()); +} +System.out.println(deque); +``` + +### 分析Queue源码 +- 【[Queue源码](http://fuseyism.com/classpath/doc/java/util/Queue-source.html)】 +- 【[Queue文档](http://fuseyism.com/classpath/doc/java/util/Queue.html)】 +- 通过上面的源码链接,我们可以看到 +1. Queue只是一个接口,继承了Collection接口 +2. Queue里面的元素使用了泛型,可为任意类型元素 +3. 方法作用: +``` +//往队列头部添加元素,如果成功返回True +//如果添加失败,抛出以下异常 +//IllegalStateException : 此时队列容量满,添加不进去 +//ClassCastException : 添加的元素类型和队列定义的泛型E不一致 +//NullPointerException : 如果队列不允许加入null元素,但是add了一个Null元素 +//IllegalArgumentException 如果待添加的元素自身的某些属性,阻止了该元素加入队列 +boolean add(E e); +//查找元素e是否在队列里面,如果是则返回True,否则返回False +boolean offer(E e); +//返回并删除队列头部元素,如果队列为空抛出异常:NoSuchElementException +E remove(); +//返回并删除队列头部元素,如果队列为空返回Null +E poll(); +//返回队列头部元素(注意不删除),如果队列为空抛出异常:NoSuchElementException +E element(); +//返回队列头部元素(注意不删除),如果队列为空返回Null +E peek(); +``` +4. 实现Queue的类 +通过 【[Queue文档](http://fuseyism.com/classpath/doc/java/util/Queue.html)】,可以查阅到实现Queue的类有: +``` +AbstractQueue, ArrayBlockingQueue, ArrayDeque, ConcurrentLinkedQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, LinkedList, PriorityBlockingQueue, PriorityQueue, SynchronousQueue +``` + +### 分析Priority Queue源码 +从【分析Queue源码】可以看到,PriorityQueue实现了Queue接口。 +-【[PriorityQueue文档](http://fuseyism.com/classpath/doc/java/util/PriorityQueue.html)】 +- 源码分析 +1. 继承AbstractQueue类,实现Serializable接口,也就是说可被序列化 +2. 提供了多种PriorityQueue的方式 +PriorityQueue():默认创建一个容量为11的空优先队列 + +PriorityQueue(Collection c):初始化优先队列,并将集合c的元素添加到优先队列中 + +PriorityQueue(int cap):初始化一个容量为cap的空优先队列 + +PriorityQueue(int cap, Comparator comp):初始化一个容量为cap的空优先队列,并指定队列元素的优先排列规则Comparator + +public PriorityQueue(PriorityQueue c):将参数的优先队列c的元素复制到该优先队列中,并且该优先队列的初始容量为1或者c的容量的1.1倍: +``` +this(Math.max(1, (int) (1.1 * c.size())), + (Comparator)c.comparator()); +``` + +public PriorityQueue(SortedSet c):将SortedSet中的元素初始化到优先队列中,优先队列的Comparator为SortSet的Comparator,并且优先队列的容量为1或者SortedSet的1.1倍 +``` +this(Math.max(1, (int) (1.1 * c.size())), + (Comparator)c.comparator()); +``` +3. PriorityQueue实现了iterator方法 +4. 其他普通的public这里就不赘述了,具体看doc文档 +5. PriorityQueue的底层数据结构为数组:并且在初始化的时候已经固定了容量 +``` +this.storage = (E[]) new Object[cap]; +``` +5. PriorityQueue添加元素的时候,会比较队列已经存储的元素数量size和队列的大小queue.length,当队列 size >= queue.length的时候,会进行扩容 + +6. 对新加入队列的元素,会对元素进行比较,插入到合适的队列位置中。如果队列的comparator为null,则调用siftUpComparable否则调用siftUpUsingComparator +``` +private void siftUp(int k, E x) { + if (comparator != null) + siftUpUsingComparator(k, x); + else + siftUpComparable(k, x); + } + +private void siftUpComparable(int k, E x) { + Comparable key = (Comparable) x; + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = queue[parent]; + if (key.compareTo((E) e) >= 0) + break; + queue[k] = e; + k = parent; + } + queue[k] = key; + } + + @SuppressWarnings("unchecked") + private void siftUpUsingComparator(int k, E x) { + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = queue[parent]; + if (comparator.compare(x, (E) e) >= 0) + break; + queue[k] = e; + k = parent; + } + queue[k] = x; + } +``` + +7. 基本操作remove,add方法都依赖于siftUp或者siftDown操作 + +8. 下面重点关注siftUpComparable的底层实现: +``` + private void siftUpComparable(int k, E x) { + Comparable key = (Comparable) x; + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = queue[parent]; + if (key.compareTo((E) e) >= 0) + break; + queue[k] = e; + k = parent; + } + queue[k] = key; + } +``` +这段代码的逻辑,明显看到,PriorityQueue的逻辑结构是一颗完全二叉树,而且是小顶堆(因此如果要变成大顶堆,要自己实现comparator.compare),而底层结构是一个数组。 + +siftUpComparable就是当前元素与父节点不断比较如果比父节点"小"就交换然后继续向上比较,否则停止比较的过程。 + +9. 有siftUp方法必然对应有siftDown方法: +``` +private void siftDownComparable(int k, E x) { + Comparable key = (Comparable)x; + int half = size >>> 1; // loop while a non-leaf + while (k < half) { + int child = (k << 1) + 1; // assume left child is least + Object c = queue[child]; + int right = child + 1; + if (right < size && + ((Comparable) c).compareTo((E) queue[right]) > 0) + c = queue[child = right]; + if (key.compareTo((E) c) <= 0) + break; + queue[k] = c; + k = child; + } + queue[k] = key; + } +``` + +这个方法就是从堆的第一个元素往下比较,如果比左右孩子节点的最小值大则与最小值交换,交换后继续向下比较,否则停止比较。 + +10. 分析了上面2个siftUp和siftDown方法,我们就可以来继续看队列的添加删除方法了 + +- add +追踪代码最后定位到实际代码是: +``` +public boolean offer(E e) { + if (e == null) + throw new NullPointerException(); + modCount++; + int i = size; + if (i >= queue.length) + grow(i + 1); + size = i + 1; + if (i == 0) + queue[0] = e; + else + siftUp(i, e); + return true; + } +``` +所以add的操作就是:在处理完队列容量之后,对待加入的元素进行在最后一个位置进行siftUp操作,也就是说,将待加入的元素放到树的最后一个叶子节点位置,然后通过调整小顶堆,将元素挪动到合适的位置 + +- remove/poll:删除指定元素/移出队列第一个元素,原理相同。 +``` +private E removeAt(int i) { + // assert i >= 0 && i < size; + modCount++; + int s = --size; + if (s == i) // removed last element + queue[i] = null; + else { + E moved = (E) queue[s]; + queue[s] = null; + siftDown(i, moved); + if (queue[i] == moved) { + siftUp(i, moved); + if (queue[i] != moved) + return moved; + } + } + return null; + } +``` + +同样,remove最后定位到removeAt方法,可以看到,小顶堆的基本操作:将第i个位置值替换为堆中最后个节点的值,然后将替换后的值,进行堆调整,堆的调整先向下调整,如果向下不需要调整则向上调整,最后重新形成一个合格的小顶堆。 + + + + + + + + + + + + + + + + + + + diff --git a/Week 01/id_748/LeetCode_01_748_TwoSum.java b/Week 01/id_748/LeetCode_01_748_TwoSum.java new file mode 100644 index 000000000..78317754e --- /dev/null +++ b/Week 01/id_748/LeetCode_01_748_TwoSum.java @@ -0,0 +1,41 @@ +/*** + * + * 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 + * + * 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 + * + * 示例: + * + * 给定 nums = [2, 7, 11, 15], target = 9 + * + * 因为 nums[0] + nums[1] = 2 + 7 = 9 + * 所以返回 [0, 1] + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/two-sum + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + +public class LeetCode_01_TwoSum{ + + + public int[] twoSum(int[] nums, int target) { + int[] res = new int[2]; + for(int i =0; i< nums.length; i++){ + int tmp = target - nums[i]; + + for(int j = i+1; j< nums.length; j++){ + if(nums[j] == tmp){ + res[0]=i; + res[1]=j; + return res; + } + } + + } + return res; + } + + +} \ No newline at end of file diff --git a/Week 01/id_748/LeetCode_26_748_RemoveDuplicatesFromSortedArray.java b/Week 01/id_748/LeetCode_26_748_RemoveDuplicatesFromSortedArray.java new file mode 100644 index 000000000..ccd68d488 --- /dev/null +++ b/Week 01/id_748/LeetCode_26_748_RemoveDuplicatesFromSortedArray.java @@ -0,0 +1,35 @@ +/*** + * + * 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 + * + * 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 + * + */ + +public class LeetCode_26_RemoveDuplicatesFromSortedArray { + + + public int removeDuplicates(int[] nums) { + public int removeDuplicates(int[] nums) { + if(nums == null || nums.length==0) return 0; + + int i =0; + for(int j = 1; j< nums.length; j++){ + if(nums[j] != nums[i]){ + i++; + nums[i] = nums[j]; + } + } + return i+1; + } + } + + + public static void main(String[]args){ + RemoveDuplicatesFromSortedArray r = new RemoveDuplicatesFromSortedArray(); + int[] array = new int[]{1,1,2}; + int i = r.removeDuplicates(array); + System.out.println(i); + } + +} \ No newline at end of file diff --git a/Week 01/id_748/NOTE.md b/Week 01/id_748/NOTE.md index a6321d6e2..739de07b7 100644 --- a/Week 01/id_748/NOTE.md +++ b/Week 01/id_748/NOTE.md @@ -2,3 +2,12 @@ +# 原地算法 +> 在计算机科学中,一个原地算法(in-place algorithm)基本上不需要额外辅助的数据结构,然而,允许少量额外的辅助变量来转换数据的算法。当算法运行时,输入的数据通常会被要输出的部分覆盖掉。不是原地算法有时候称为非原地(not-in-place)或不得其所(out-of-place)。 + + +--- + +# 递归算法 +> 在数学与计算机科学中,递归(Recursion)是指在函数的定义中使用函数自身的方法。实际上,递归,顾名思义,其包含了两个意思:递 和 归,这正是递归思想的精华所在。 + diff --git a/Week 01/id_768/LeeCode_189_768.java b/Week 01/id_768/LeeCode_189_768.java new file mode 100644 index 000000000..ae36b9899 --- /dev/null +++ b/Week 01/id_768/LeeCode_189_768.java @@ -0,0 +1,15 @@ +class Solution { + public void rotate(int[] nums, int k) { + int[] newNums = new int[nums.length]; + int right = k%nums.length; + for (int i = 0; i< nums.length; i++) { + if (i + right >= nums.length) { + int move = i + right - nums.length; + newNums[move] = nums[i]; + continue; + } + newNums[i+right] = nums[i]; + } + nums = newNums; + } +} \ No newline at end of file diff --git a/Week 01/id_768/LeeCode_29_768.java b/Week 01/id_768/LeeCode_29_768.java new file mode 100644 index 000000000..8924faee2 --- /dev/null +++ b/Week 01/id_768/LeeCode_29_768.java @@ -0,0 +1,17 @@ +class Solution { + public int removeDuplicates(int[] nums) { + int j = 0; + for (int i = 0; i <= nums.length - 1; i++) { + if (i == 0) { + nums[j] = nums[i]; + j++; + continue; + } + if (nums[i] != nums[i-1]) { + nums[j] = nums[i]; + j++; + } + } + return (j+1); + } +} \ No newline at end of file diff --git a/Week 01/id_768/NOTE.md b/Week 01/id_768/NOTE.md index a6321d6e2..553075735 100644 --- a/Week 01/id_768/NOTE.md +++ b/Week 01/id_768/NOTE.md @@ -2,3 +2,17 @@ +2019.10.20 + +1. ArrayListLinked ListSkip List +ʱ临Ӷȼ㡪һЩϰ +Skip ListRedisʹõĽ϶ࡣ + +Ż˼룺άռ任ʱ + + +ʦˢЩ㷨⣬Լ֪ʶȽϲĻ + +LeetCodeʱĪύԡ + +~~~ \ No newline at end of file diff --git a/Week 02/id_003/NOTE.md b/Week 02/id_003/NOTE.md index a6321d6e2..ffe635009 100644 --- a/Week 02/id_003/NOTE.md +++ b/Week 02/id_003/NOTE.md @@ -1,4 +1,422 @@ # NOTE +## 第一课 哈希表、映射、集合的实现与特性 +哈希表(hash table), 也叫散列表,是根据关键码值(Key value)而进行直接访问的数据结构 +他通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。 +这个映射函数叫做散列函数(hash function),存放记录的数组叫做哈希表(或散列表)。 +可通过拉链存储法存储多个值解决碰撞 +查询时间复杂度O(1) +### homework +写一个hashMap的小总结 +## 项目实战 +*[242. 有效的字母异位词](https://leetcode-cn.com/problems/valid-anagram/description/)* + +> 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 +> 示例 1: +> 输入: s = "anagram", t = "nagaram" +> 输出: true +> 示例 2: +> +> 输入: s = "rat", t = "car" +> +> 输出: false +> 说明: +> 你可以假设字符串只包含小写字母。 +> +> 进阶: +> 如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况? +``` +//方法一: 先排序,后比较 +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + //先将字符串大散成数组之后排序,排序后再比较即可 +var isAnagram = function(s, t) { + + if(s.length != t.length) return false; + s = sortStr(s); + t = sortStr(t); + + return s === t; +}; +sortStr = function(s){ + let arr = s.split(''); + arr = arr.sort(); + s = arr.join(''); + return s; +} +//方法二 +public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + int[] counter = new int[26]; + for (int i = 0; i < s.length(); i++) { + counter[s.charAt(i) - 'a']++; + counter[t.charAt(i) - 'a']--; + } + for (int count : counter) { + if (count != 0) { + return false; + } + } + return true; +} +``` +*[1.两数之和](https://leetcode-cn.com/problems/two-sum/description/)* +>给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 +> +>你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 +> +>示例: +> +>给定 nums = [2, 7, 11, 15], target = 9 +> +>因为 nums[0] + nums[1] = 2 + 7 = 9 +>所以返回 [0, 1] +>进阶: +>如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况? + +> 解答: +> 使用哈希表而不是固定大小的计数器。想象一下,分配一个大的数组来适应整个 Unicode 字符范围,这个范围可能超过 100万。哈希表是一种更通用的解决方案,可以适应任何字符范围。 +``` +1.暴力求解法: +var twoSum = function(nums, target) { + for (var i = 0; i < nums.length; i++) { + for ( var j = i+1; j < nums.length; j++) + if (nums[i] == target - nums[j]) { + return [i,j] + } + } +}; + +2.两遍哈希法 +var twoSum = function(nums, target) { + var obj = {}; + for( let i = 0; i < nums.length; i++) { + obj[nums[i]] = i; + } + + for( let i = 0; i < nums.length; i++ ) { + let complement = target - nums[i]; + //obj[nums[i]] != i用于判断该属性不是同一个属性下面的值 + if (obj.hasOwnProperty(complement) && obj[complement] != i ){ + return [ i, obj[ complement ] ] + } + } + +}; + +3.一遍哈希法 + +//在放入的同时查看是否已存在该目标值 +var twoSum = function(nums, target) { + let map = {} + for (let i = 0; i < nums.length; i++) { + const complement = target - nums[i]; + if(map[complement] != undefined) + return [map[complement],i] + map[nums[i]] = i + } +}; + +``` +## 作业 + +*[49. 字母异位词分组 - 力扣(LeetCode)](https://leetcode-cn.com/problems/group-anagrams/)* + +给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 + +示例: + +输入: ["eat", "tea", "tan", "ate", "nat", "bat"], + +输出: +[ + ["ate","eat","tea"], + ["nat","tan"], + ["bat"] +] + +说明: + +所有输入均为小写字母。 +不考虑答案输出的顺序。 +``` +//将排序后的值作为key进行判断 +var groupAnagrams = function(strs) { + if (strs.length == 0) { + retuurn [null]; + } + let map = {}; + strs.map(val => { + let ans = sort(val); + // console.log(ans,val); + if (map[ans] !=undefined) { + map[ans].push(val) + } else { + map[ans] = [val] + } + }) + let resArr = []; + for (const val in map) { + resArr.push(map[val]); + } + return resArr; +}; + +const sort = function(s){ + s = s.split(""); + s = s.sort(); + return s.join(''); +} +``` + +## 第二课 树、二叉树、二叉搜索树的实现和特性 +**二叉树** + + 结点只有两个 + +**二叉搜索树** + + [VisuAlgo - 二叉搜索树,高度平衡树](https://visualgo.net/zh/bst) + +也称二叉搜索树、有序二叉树(ordered binary tree)、排序二叉树(Sorted Binary Tree),是指一棵空树或者具有下列性质的二叉树: + 1. 左子树上所有结点的值均小于它的根节点的值; + 2. 右子树上所有结点的值均大于它的根节点的值; + 3. 以此类推:左右子树也分别为二叉查找树。 + + +## 作业 + +[429. N叉树的层序遍历 - 力扣(LeetCode)](https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/) + +``` + +// 思路总结: +/* + 1. 把当前的结点放入队列 + 2. 将队列的值依次弹出,弹出是顺便将结点值的孩子结点中的内容全部放入队列 + +*/ +var levelOrder = function(root) { + if (root == null) return []; + + let res = [] + let queue = []; + + queue.push(root) + + while (queue.length != 0) { + let i = queue.length; + let list = [] + while (i--) { + let cur = queue.shift(); + + list.push(cur.val); + for (const node of cur.children) { + if (node!=null) { + queue.push(node); + } + } + } + res.push(list) + } + return res + }; +``` + +[589. N叉树的前序遍历 - 力扣(LeetCode)](https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/description/) + +``` +var postorder = function(root) { + let res = [] + if (root == null) { + return res; + } + fun(root) + function fun(root){ + if(root == null) return; + res.push(root.val) + for (let i = 0; i < root.children.length; i++){ + fun(root.children[i]); + } + } + return res; +}; + +``` + +[590. N叉树的后序遍历 - 力扣(LeetCode)](https://leetcode-cn.com/problems/n-ary-tree-postorder-traversal/) + +``` +var postorder = function(root) { + let res = [] + if (root == null) { + return res; + } + fun(root) + function fun(root){ + if(root == null) return; + for (let i = 0; i < root.children.length; i++){ + fun(root.children[i]); + } + res.push(root.val) + } + return res; +}; +``` + +[144. 二叉树的前序遍历 - 力扣(LeetCode)](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/) +``` +var preorderTraversal = function(root) { + var result = []; + function pushRoot(node){ + if(node != null){ + result.push(node.val); + if(node.left != null){ + pushRoot(node.left); + } + if(node.right != null){ + pushRoot(node.right); + } + } + } + pushRoot(root); + return result; +}; + +``` + +[94. 二叉树的中序遍历 - 力扣(LeetCode)](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/) +``` +var preorderTraversal = function(root) { + var result = []; + function pushRoot(node){ + if(node != null){ + if(node.left != null){ + pushRoot(node.left); + } + result.push(node.val); + if(node.right != null){ + pushRoot(node.right); + } + } + } + pushRoot(root); + return result; +}; + +``` + +## 第三课 泛型递归 树的递归 + + +***实战题目*** +* [297. 二叉树的序列化与反序列化 - 力扣(LeetCode)](https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree/) +* [111. 二叉树的最小深度 - 力扣(LeetCode)](https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/) +* [104. 二叉树的最大深度 - 力扣(LeetCode)](https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/) +* [98. 验证二叉搜索树 - 力扣(LeetCode)](https://leetcode-cn.com/problems/validate-binary-search-tree/) +* [226. 翻转二叉树 - 力扣(LeetCode)](https://leetcode-cn.com/problems/invert-binary-tree/description/) +* [22. 括号生成 - 力扣(LeetCode)](https://leetcode-cn.com/problems/generate-parentheses/) +* [70. 爬楼梯 - 力扣(LeetCode)](https://leetcode-cn.com/problems/climbing-stairs/) + +***课后作业*** +* [47. 全排列 II - 力扣(LeetCode)](https://leetcode-cn.com/problems/permutations-ii/) +* [46. 全排列 - 力扣(LeetCode)](https://leetcode-cn.com/problems/permutations/) +* [77. 组合 - 力扣(LeetCode)](https://leetcode-cn.com/problems/combinations/) +* [105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)](https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/) +* [236. 二叉树的最近公共祖先 - 力扣(LeetCode)](https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/) +## 第四课 分治回溯1. 分治 + 将一个大问题分成几个子问题 + 本质:找重复性以及分解问题和最后组合每个子问题的结果(区分变与不变,重复的问题属于不变的) + + 代码模板: +``` + def divide_conquer(problem, param1, param2, ...): + # recursion terminator + if problem is None: + print_result + return + + # prepare data + data = prepare_data(problem) + subproblems = split_problem(problem, data) + + # conquer subproblems + subresult1 = self.divide_conquer(subproblems[0], p1, ...) + subresult2 = self.divide_conquer(subproblems[1], p1, ...) + subresult3 = self.divide_conquer(subproblems[2], p1, ...) + … + + # process and generate the final result + result = process_result(subresult1, subresult2, subresult3, …) + + # revert the current level states +``` +***预习题目*** +* [78. 子集 - 力扣(LeetCode)](https://leetcode-cn.com/problems/subsets/) +``` + var myPow = function(x, n) { + function pow(x,n){ + if (n==0) { + return 1.0; + } + + let half = pow(x,Math.floor(n/2)); + + if(n % 2 == 0){ + return half * half; + } else { + return half * half * x; + } + } + let N = n; + if (N<0) { + x = 1 / x; + N = -N; + } + return pow(x , N) +}; +``` +* [Pow(x, n) - Pow(x, n) - 力扣(LeetCode)](https://leetcode-cn.com/problems/powx-n/solution/powx-n-by-leetcode/) + +``` + var subsets = function(nums) { + ans = []; + if ( nums == null ) return ans; + dfs(ans, nums, [], 0); + + function dfs(ans, nums, list, index) { + if (index == nums.length) { + ans.push(list); + return; + } + + dfs(ans,nums, [...list], index+1); + // dfs(ans,nums, list, index+1); + + list.push(nums[index]); + dfs(ans,nums, [...list], index+1); + // dfs(ans, nums, list, index+1); + + // list.pop(); //这里不行??? + } + return ans; + }; +``` + +* [51. N皇后 - 力扣(LeetCode)](https://leetcode-cn.com/problems/n-queens/) + +* [17. 电话号码的字母组合 - 力扣(LeetCode)](https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/) + +* [169. 求众数 - 力扣(LeetCode)](https://leetcode-cn.com/problems/majority-element/description/) + +* [Leetcode Sqrt(x):牛顿迭代法和Quake-III中的神奇方法 - 程序园](http://www.voidcn.com/article/p-eudisdmk-zm.html) + +* [牛顿迭代法快速寻找平方根 | Matrix67: The Aha Moments](http://www.matrix67.com/blog/archives/361) + \ No newline at end of file diff --git "a/Week 02/id_003/N\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.js" "b/Week 02/id_003/N\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.js" new file mode 100644 index 000000000..328cfa200 --- /dev/null +++ "b/Week 02/id_003/N\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.js" @@ -0,0 +1,15 @@ +var postorder = function(root) { + let res = [] + if (root == null) { + return res; + } + fun(root) + function fun(root){ + if(root == null) return; + res.push(root.val) + for (let i = 0; i < root.children.length; i++){ + fun(root.children[i]); + } + } + return res; +}; \ No newline at end of file diff --git "a/Week 02/id_003/N\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.js" "b/Week 02/id_003/N\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.js" new file mode 100644 index 000000000..d61415f02 --- /dev/null +++ "b/Week 02/id_003/N\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.js" @@ -0,0 +1,15 @@ +var postorder = function(root) { + let res = [] + if (root == null) { + return res; + } + fun(root) + function fun(root){ + if(root == null) return; + for (let i = 0; i < root.children.length; i++){ + fun(root.children[i]); + } + res.push(root.val) + } + return res; +}; \ No newline at end of file diff --git "a/Week 02/id_003/N\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.js" "b/Week 02/id_003/N\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.js" new file mode 100644 index 000000000..b53534c01 --- /dev/null +++ "b/Week 02/id_003/N\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.js" @@ -0,0 +1,32 @@ +// 思路总结: +/* + 1. 把当前的结点放入队列 + 2. 将队列的值依次弹出,弹出是顺便将结点值的孩子结点中的内容全部放入队列 + +*/ +var levelOrder = function(root) { + if (root == null) return []; + + let res = [] + let queue = []; + + queue.push(root) + + while (queue.length != 0) { + let i = queue.length; + let list = [] + while (i--) { + let cur = queue.shift(); + + list.push(cur.val); + for (const node of cur.children) { + if (node!=null) { + queue.push(node); + } + } + } + res.push(list) + } + return res + }; + \ No newline at end of file diff --git "a/Week 02/id_003/\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.js" "b/Week 02/id_003/\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.js" new file mode 100644 index 000000000..d2058152b --- /dev/null +++ "b/Week 02/id_003/\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.js" @@ -0,0 +1,16 @@ +var preorderTraversal = function(root) { + var result = []; + function pushRoot(node){ + if(node != null){ + if(node.left != null){ + pushRoot(node.left); + } + result.push(node.val); + if(node.right != null){ + pushRoot(node.right); + } + } + } + pushRoot(root); + return result; +}; \ No newline at end of file diff --git "a/Week 02/id_003/\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.js" "b/Week 02/id_003/\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.js" new file mode 100644 index 000000000..bb1d8d311 --- /dev/null +++ "b/Week 02/id_003/\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.js" @@ -0,0 +1,17 @@ + +var preorderTraversal = function(root) { + var result = []; + function pushRoot(node){ + if(node != null){ + result.push(node.val); + if(node.left != null){ + pushRoot(node.left); + } + if(node.right != null){ + pushRoot(node.right); + } + } + } + pushRoot(root); + return result; +}; \ No newline at end of file diff --git "a/Week 02/id_003/\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204.js" "b/Week 02/id_003/\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204.js" new file mode 100644 index 000000000..a513673a4 --- /dev/null +++ "b/Week 02/id_003/\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204.js" @@ -0,0 +1,26 @@ +var groupAnagrams = function(strs) { + if (strs.length == 0) { + retuurn [null]; + } + let map = {}; + strs.map(val => { + let ans = sort(val); + // console.log(ans,val); + if (map[ans] !=undefined) { + map[ans].push(val) + } else { + map[ans] = [val] + } + }) + let resArr = []; + for (const val in map) { + resArr.push(map[val]); + } + return resArr; +}; + +const sort = function(s){ + s = s.split(""); + s = s.sort(); + return s.join(''); +} \ No newline at end of file diff --git a/Week 02/id_008/LeetCode_104_008.js b/Week 02/id_008/LeetCode_104_008.js new file mode 100644 index 000000000..be7dc2dcc --- /dev/null +++ b/Week 02/id_008/LeetCode_104_008.js @@ -0,0 +1,30 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {number} + */ +var maxDepth = function(root) { + var max = -Infinity; + search(root, 0); + + function search(node, deep){ + if (!node) { + if (deep > max) { + max = deep; + } + + return null; + } + + search(node.left, deep + 1); + search(node.right, deep + 1); + } + + return max; +}; diff --git a/Week 02/id_008/LeetCode_111_008.js b/Week 02/id_008/LeetCode_111_008.js new file mode 100644 index 000000000..8ee210972 --- /dev/null +++ b/Week 02/id_008/LeetCode_111_008.js @@ -0,0 +1,36 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {number} + */ +var minDepth = function(root) { + if (!root) { + return 0; + } + + var min = Infinity; + search(root, 1); + + function search(node, deep){ + if (!node) { + return null; + } + + if (!node.left && !node.right) { + if (deep < min) { + min = deep; + } + } + + search(node.left, deep + 1); + search(node.right, deep + 1); + } + + return min; +}; diff --git a/Week 02/id_008/LeetCode_144_008.js b/Week 02/id_008/LeetCode_144_008.js new file mode 100644 index 000000000..b29f6cbbb --- /dev/null +++ b/Week 02/id_008/LeetCode_144_008.js @@ -0,0 +1,27 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {number[]} + */ +var preorderTraversal = function(root) { + var result = []; + search(root); + + function search(node){ + if (!node) { + return null; + } + + result.push(node.val); + search(node.left); + search(node.right); + } + + return result; +}; diff --git a/Week 02/id_008/LeetCode_169_008.js b/Week 02/id_008/LeetCode_169_008.js new file mode 100644 index 000000000..5043bd804 --- /dev/null +++ b/Week 02/id_008/LeetCode_169_008.js @@ -0,0 +1,24 @@ +/** + * @param {number[]} nums + * @return {number} + */ +var majorityElement = function(nums) { + var total = []; + var max = 0; + var result = 0; + + for (var i = 0; i < nums.length; ++i) { + if (typeof total[nums[i]] === "undefined") { + total[nums[i]] = 0; + } + + ++total[nums[i]]; + + if (total[nums[i]] > max) { + max = total[nums[i]]; + result = nums[i] + } + } + + return result; +}; diff --git a/Week 02/id_008/LeetCode_17_008.js b/Week 02/id_008/LeetCode_17_008.js new file mode 100644 index 000000000..7fa4a12ba --- /dev/null +++ b/Week 02/id_008/LeetCode_17_008.js @@ -0,0 +1,35 @@ +/** + * @param {string} digits + * @return {string[]} + */ +var letterCombinations = function(digits) { + var map = [[], [], ["a", "b", "c"], ["d", "e", "f"], ["g", "h", "i"], ["j", "k", "l"], ["m", "n", "o"], ["p", "q", "r", "s"], ["t", "u", "v"], ["w", "x", "y", "z"]]; + var cache = {}; + digits = digits.replace(/0/g, "").replace(/1/g, ""); + return digits.length ? gen(digits) : []; + + function gen(nums){ + if (typeof cache[nums] !== "undefined") { + return cache[nums]; + } + + var a = +nums.charAt(0); + + if (nums.length === 1) { + cache[nums] = map[a]; + return map[a]; + } + + var sub = gen(nums.substring(1)); + var result = []; + + for (var i = 0; i < map[a].length; ++i) { + for (var j = 0; j < sub.length; ++j) { + result.push(map[a][i] + sub[j]); + } + } + + cache[nums] = result; + return result; + } +}; diff --git a/Week 02/id_008/LeetCode_1_008.js b/Week 02/id_008/LeetCode_1_008.js new file mode 100644 index 000000000..51f176b6e --- /dev/null +++ b/Week 02/id_008/LeetCode_1_008.js @@ -0,0 +1,54 @@ +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = function(nums, target) { + for (var i = 0; i < nums.length - 1; ++i) { + for (var j = i + 1; j < nums.length; ++j) { + if (nums[i] + nums[j] === target) { + return [i, j] + } + } + } + + return []; +}; + +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = function(nums, target) { + var clone = nums.slice(0); + nums.sort(function(a, b){ return a - b; }); + + var A = 0, B = nums.length - 1, sum = 0; + + while (A < B) { + sum = nums[A] + nums[B]; + + if (sum > target) { + --B; + } + else if (sum < target) { + ++A; + } + else { + var a = 0, b = clone.length - 1; + + while (clone[a] != nums[A]) { + ++a; + } + + while (clone[b] != nums[B]) { + --b; + } + + return [a, b]; + } + } + + return []; +}; diff --git a/Week 02/id_008/LeetCode_226_008.js b/Week 02/id_008/LeetCode_226_008.js new file mode 100644 index 000000000..fd005cc2e --- /dev/null +++ b/Week 02/id_008/LeetCode_226_008.js @@ -0,0 +1,27 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {TreeNode} + */ +var invertTree = function(root) { + changeNode(root); + return root; + + function changeNode(node){ + if (!node) { + return null; + } + + var temp = node.left; + node.left = node.right; + node.right = temp; + changeNode(node.left); + changeNode(node.right); + } +}; diff --git a/Week 02/id_008/LeetCode_22_008.js b/Week 02/id_008/LeetCode_22_008.js new file mode 100644 index 000000000..0a06d7129 --- /dev/null +++ b/Week 02/id_008/LeetCode_22_008.js @@ -0,0 +1,75 @@ +/** + * @param {number} n + * @return {string[]} + */ +var generateParenthesis = function(n) { + var cache = [[]]; + + gen(n); + return cache[n]; + + function gen(k){ + if (cache[k]) { + return cache[k]; + } + + if (k <= 0) { + return []; + } + + var next = gen(k - 1); + var _r = []; + + _r.push("()" + (next[0] || "")); + + for (var i = 0; i < next.length; ++i) { + var arr = next[i].split(""); + + for (var j = 0; j < arr.length; ++j) { + var temp = arr.slice(0); + temp[j] += "()"; + _r.push(temp.join("")); + } + } + + _r = distinct(_r); + cache[k] = _r; + return _r; + } + + function distinct(arr){ + var result = []; + + for (var i = 0; i < arr.length; ++i) { + !~result.indexOf(arr[i]) && result.push(arr[i]); + } + + return result; + } +}; + +/** + * @param {number} n + * @return {string[]} + */ +var generateParenthesis = function(n) { + var result = []; + putChar("", 0, 0); + + function putChar(s, L, R){ + if (L === n && R === n) { + result.push(s); + return null; + } + + if (L < n) { + putChar(s + "(", L + 1, R); + } + + if (R < L) { + putChar(s + ")", L, R + 1); + } + } + + return result; +}; diff --git a/Week 02/id_008/LeetCode_242_008.js b/Week 02/id_008/LeetCode_242_008.js new file mode 100644 index 000000000..dce30a7a6 --- /dev/null +++ b/Week 02/id_008/LeetCode_242_008.js @@ -0,0 +1,83 @@ +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function(s, t) { + if (s.length !== t.length) { + return false; + } + + var S = s.split(""); + S.sort(function(a, b){ return a.charCodeAt(0) - b.charCodeAt(0); }); + s = S.join(""); + + var T = t.split(""); + T.sort(function(a, b){ return a.charCodeAt(0) - b.charCodeAt(0); }); + t = T.join(""); + + return s === t; +}; + +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function(s, t) { + if (s.length !== t.length) { + return false; + } + + var map = {}; + + for (var i = 0; i < s.length; ++i) { + var S = s.charAt(i); + var T = t.charAt(i); + + if (typeof map[S] === "undefined") { + map[S] = 0; + } + + if (typeof map[T] === "undefined") { + map[T] = 0; + } + + ++map[S]; + --map[T]; + } + + for (var i in map) { + if (map[i] !== 0) { + return false; + } + } + + return true; +}; + +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function(s, t) { + if (s.length !== t.length) { + return false; + } + + var map = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + for (var i = 0; i < s.length; ++i) { + ++map[s.charCodeAt(i) - 97]; + --map[t.charCodeAt(i) - 97]; + } + + for (var i = 0; i < 26; ++i) { + if (map[i]) { + return false; + } + } + + return true; +}; diff --git a/Week 02/id_008/LeetCode_297_008.js b/Week 02/id_008/LeetCode_297_008.js new file mode 100644 index 000000000..eaa5b0223 --- /dev/null +++ b/Week 02/id_008/LeetCode_297_008.js @@ -0,0 +1,95 @@ + /** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ + +/** + * Encodes a tree to a single string. + * + * @param {TreeNode} root + * @return {string} + */ +var serialize = function(root) { + if (!root) { + return "[]"; + } + + var nodeList = [root]; + var node; + var result = []; + + while (nodeList.length) { + node = nodeList.shift(); + + if (node) { + result.push(node.val); + nodeList.push(node.left); + nodeList.push(node.right); + } + else { + result.push(null); + } + } + + while (result.length) { + if (typeof result[result.length - 1] === "number") { + break; + } + + --result.length; + } + + return JSON.stringify(result); +}; + +/** + * Decodes your encoded data to tree. + * + * @param {string} data + * @return {TreeNode} + */ +var deserialize = function(data) { + var data = JSON.parse(data); + + if (data.length <= 0) { + return null; + } + + var root = { val: data[0], left: null, right: null }; + var nodeList = [root]; + + for (var i = 1; i < data.length; i += 2) { + var node = nodeList.shift(); + + if (!node) { + i -= 2; + continue; + } + + if (typeof data[i] === "number") { + node.left = { val: data[i], left: null, right: null }; + nodeList.push(node.left); + } + else { + nodeList.push(null); + } + + if (typeof data[i + 1] === "number") { + node.right = { val: data[i + 1], left: null, right: null }; + nodeList.push(node.right); + } + else { + nodeList.push(null); + } + } + + return root; +}; + +/** + * Your functions will be called as such: + * deserialize(serialize(root)); + */ diff --git a/Week 02/id_008/LeetCode_429_008.js b/Week 02/id_008/LeetCode_429_008.js new file mode 100644 index 000000000..776a4ede3 --- /dev/null +++ b/Week 02/id_008/LeetCode_429_008.js @@ -0,0 +1,40 @@ +/** + * // Definition for a Node. + * function Node(val,children) { + * this.val = val; + * this.children = children; + * }; + */ +/** + * @param {Node} root + * @return {number[][]} + */ +var levelOrder = function(root) { + if (!root) { + return []; + } + + var result = []; + result.push([root.val]); + search(root, 1); + + function search(node, k){ + if (!node) { + return null; + } + + for (var i = 0; i < node.children.length; ++i) { + if (typeof result[k] === "undefined") { + result[k] = []; + } + + result[k].push(node.children[i].val); + } + + for (var i = 0; i < node.children.length; ++i) { + search(node.children[i], k + 1); + } + } + + return result; +}; diff --git a/Week 02/id_008/LeetCode_49_008.js b/Week 02/id_008/LeetCode_49_008.js new file mode 100644 index 000000000..e4ef4e2bb --- /dev/null +++ b/Week 02/id_008/LeetCode_49_008.js @@ -0,0 +1,60 @@ +/** + * @param {string[]} strs + * @return {string[][]} + */ +var groupAnagrams = function(strs) { + var map = {}, result = []; + + for (var i = 0; i < strs.length; ++i) { + var sArr = strs[i].split(""); + sArr.sort(function(a, b){ return a.charCodeAt(0) - b.charCodeAt(0); }); + var key = sArr.join(""); + + if (typeof map[key] === "undefined") { + map[key] = []; + } + + map[key].push(strs[i]); + } + + for (var i in map) { + result.push(map[i]); + } + + return result; +}; + +/** + * @param {string[]} strs + * @return {string[][]} + */ +var groupAnagrams = function(strs) { + var map = [], result = []; + + for (var i = 0; i < strs.length; ++i) { + var key = Key(strs[i]); + + if (typeof map[key] === "undefined") { + map[key] = []; + } + + map[key].push(strs[i]); + } + + for (var i in map) { + result.push(map[i]); + } + + return result; + + function Key(str){ + var nums = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]; + var total = 1; + + for (var i = 0; i < str.length; ++i) { + total *= nums[str.charCodeAt(i) - 97]; + } + + return total; + } +}; diff --git a/Week 02/id_008/LeetCode_50_008.js b/Week 02/id_008/LeetCode_50_008.js new file mode 100644 index 000000000..338f564ec --- /dev/null +++ b/Week 02/id_008/LeetCode_50_008.js @@ -0,0 +1,58 @@ +/** + * @param {number} x + * @param {number} n + * @return {number} + */ +var myPow = function(x, n) { + var result = x; + + if (n > 0) { + var m = false; + } + else if (n < 0) { + n = -n; + var m = true; + } + else { + return 1; + } + + for (var i = 1; i < n; ++i) { + result *= x; + } + + return m ? 1 / result : result; +}; + +/** + * @param {number} x + * @param {number} n + * @return {number} + */ +var myPow = function(x, n) { + if (n > 0) { + var m = false; + } + else if (n < 0) { + n = -n; + var m = true; + } + else { + return 1; + } + + if (n === 2) { + return m ? 1 / x / x : x * x; + } + + if (n % 2) { + var k = myPow(x, (n - 1) / 2); + var result = k * k * x; + } + else { + var k = myPow(x, n / 2); + var result = k * k; + } + + return m ? 1 / result : result; +}; diff --git a/Week 02/id_008/LeetCode_51_008.js b/Week 02/id_008/LeetCode_51_008.js new file mode 100644 index 000000000..d23c61c91 --- /dev/null +++ b/Week 02/id_008/LeetCode_51_008.js @@ -0,0 +1,108 @@ +/** + * @param {number} n + * @return {string[][]} + */ +var solveNQueens = function(n) { + // 生成地图,外围扩大一圈,用于边界判断 + var map = []; + + for (var i = 0; i < n + 2; ++i) { + map.push(0); + } + + for (var i = 0; i < n; ++i) { + map.push(0); + + for (j = 0; j < n; ++j) { + map.push("."); + } + + map.push(0); + } + + for (var i = 0; i < n + 2; ++i) { + map.push(0); + } + + // 生成步进,只搜索上面,左右及下面都不需要搜索 + var step = [-n - 3, -n - 2, -n - 1]; + + var result = []; + + // 从第一行开始递归 + putPieceToLine(0); + + return result; + + // 指定行放棋子 + function putPieceToLine(line){ + if (line === n) { + pushResult(); + return true; + } + + var blankPoint = findBlankFromLine(line); + + if (blankPoint.length === 0) { + return false; + } + + for (var i = 0; i < blankPoint.length; ++i) { + map[blankPoint[i]] = "Q"; + putPieceToLine(line + 1); + map[blankPoint[i]] = "."; + } + } + + // 搜索指定行可放棋子点位 + function findBlankFromLine(line){ + var start = (line + 1) * (n + 2) + 1; + var end = start + n; + var result = []; + + // 该行空白点都检查一遍 + find: for (var i = start; i < end; ++i) { + // 三个方向都检查一遍 + for (var j = 0; j < step.length; ++j) { + var k = i; + + while (1) { + // 向指定方向步进 + k += step[j]; + + // 已到达边界,跳出该方向 + if (!map[k]) { + break; + } + + // 发现棋子,跳过该空白点 + if (map[k] === "Q") { + continue find; + } + } + } + + // 合格点位,记录 + result.push(i); + } + + return result; + } + + // 将当前 map 输出为题目要求的格式 + function pushResult(){ + var lines = []; + + for (var i = 1; i <= n; ++i) { + var str = ""; + + for (var j = 1; j <= n; ++j) { + str += map[i * (n + 2) + j]; + } + + lines.push(str); + } + + result.push(lines); + } +}; diff --git a/Week 02/id_008/LeetCode_589_008.js b/Week 02/id_008/LeetCode_589_008.js new file mode 100644 index 000000000..c54b66d0e --- /dev/null +++ b/Week 02/id_008/LeetCode_589_008.js @@ -0,0 +1,29 @@ +/** + * // Definition for a Node. + * function Node(val,children) { + * this.val = val; + * this.children = children; + * }; + */ +/** + * @param {Node} root + * @return {number[]} + */ +var preorder = function(root) { + var result = []; + search(root); + + function search(node){ + if (!node) { + return null; + } + + result.push(node.val); + + for (var i = 0; i < node.children.length; ++i) { + search(node.children[i]); + } + } + + return result; +}; diff --git a/Week 02/id_008/LeetCode_590_008.js b/Week 02/id_008/LeetCode_590_008.js new file mode 100644 index 000000000..0f3f06a52 --- /dev/null +++ b/Week 02/id_008/LeetCode_590_008.js @@ -0,0 +1,29 @@ +/** + * // Definition for a Node. + * function Node(val,children) { + * this.val = val; + * this.children = children; + * }; + */ +/** + * @param {Node} root + * @return {number[]} + */ +var postorder = function(root) { + var result = []; + search(root); + + function search(node){ + if (!node) { + return null; + } + + for (var i = 0; i < node.children.length; ++i) { + search(node.children[i]); + } + + result.push(node.val); + } + + return result; +}; diff --git a/Week 02/id_008/LeetCode_70_008.js b/Week 02/id_008/LeetCode_70_008.js new file mode 100644 index 000000000..7ba0e577b --- /dev/null +++ b/Week 02/id_008/LeetCode_70_008.js @@ -0,0 +1,93 @@ +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function(n) { + var t = Math.sqrt(5); + return Math.round(1/t * (Math.pow((1+t)/2, n+1) - Math.pow((1-t)/2, n+1))); +}; + +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function(n) { + if (n < 2) { + return 1; + } + + var a = 1; + var b = 1; + + for (var i = 1; i < n; ++i) { + b += a; + a = b - a; + } + + return b; +}; + +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function(n) { + switch(n){ + case 0: return 1; + case 1: return 1; + case 2: return 2; + case 3: return 3; + case 4: return 5; + case 5: return 8; + case 6: return 13; + case 7: return 21; + case 8: return 34; + case 9: return 55; + case 10: return 89; + case 11: return 144; + case 12: return 233; + case 13: return 377; + case 14: return 610; + case 15: return 987; + case 16: return 1597; + case 17: return 2584; + case 18: return 4181; + case 19: return 6765; + case 20: return 10946; + case 21: return 17711; + case 22: return 28657; + case 23: return 46368; + case 24: return 75025; + case 25: return 121393; + case 26: return 196418; + case 27: return 317811; + case 28: return 514229; + case 29: return 832040; + case 30: return 1346269; + case 31: return 2178309; + case 32: return 3524578; + case 33: return 5702887; + case 34: return 9227465; + case 35: return 14930352; + case 36: return 24157817; + case 37: return 39088169; + case 38: return 63245986; + case 39: return 102334155; + case 40: return 165580141; + case 41: return 267914296; + case 42: return 433494437; + case 43: return 701408733; + case 44: return 1134903170; + case 45: return 1836311903; + } + + return 1; +}; + +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function(n) { + return [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903][n]; +}; diff --git a/Week 02/id_008/LeetCode_78_008.js b/Week 02/id_008/LeetCode_78_008.js new file mode 100644 index 000000000..6b5b58061 --- /dev/null +++ b/Week 02/id_008/LeetCode_78_008.js @@ -0,0 +1,22 @@ +/** + * @param {number[]} nums + * @return {number[][]} + */ +var subsets = function(nums) { + var k = 1 << nums.length; + var result = []; + + for (var i = 0; i < k; ++i) { + var answer = []; + + for (j = 0; j < nums.length; ++j) { + if (i >> j & 1) { + answer.push(nums[j]); + } + } + + result.push(answer); + } + + return result; +}; diff --git a/Week 02/id_008/LeetCode_94_008.js b/Week 02/id_008/LeetCode_94_008.js new file mode 100644 index 000000000..04d4aa77a --- /dev/null +++ b/Week 02/id_008/LeetCode_94_008.js @@ -0,0 +1,27 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {number[]} + */ +var inorderTraversal = function(root) { + var result = []; + search(root); + + function search(node){ + if (!node) { + return null; + } + + search(node.left); + result.push(node.val); + search(node.right); + } + + return result; +}; diff --git a/Week 02/id_008/LeetCode_98_008.js b/Week 02/id_008/LeetCode_98_008.js new file mode 100644 index 000000000..e313dd1cc --- /dev/null +++ b/Week 02/id_008/LeetCode_98_008.js @@ -0,0 +1,184 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {boolean} + */ + +// 双重检查 + +var isValidBST = function(root) { + return check(root); + + function check(node){ + if (!node) { + return true; + } + + if (node.left) { + if (!checkMax(node.left, node.val)) { + return false; + } + } + + if (node.right) { + if (!checkMin(node.right, node.val)) { + return false; + } + } + + if (node.left) { + if (!check(node.left)) { + return false; + } + } + + if (node.right) { + if (!check(node.right)) { + return false; + } + } + + return true; + } + + function checkMax(node, max){ + if (node.val >= max) { + return false; + } + + if (node.left) { + if (node.left.val >= max) { + return false; + } + + if (!checkMax(node.left, max)) { + return false; + } + } + + if (node.right) { + if (node.right.val >= max) { + return false; + } + + if (!checkMax(node.right, max)) { + return false; + } + } + + return true; + } + + function checkMin(node, min){ + if (node.val <= min) { + return false; + } + + if (node.left) { + if (node.left.val <= min) { + return false; + } + + if (!checkMin(node.left, min)) { + return false; + } + } + + if (node.right) { + if (node.right.val <= min) { + return false; + } + + if (!checkMin(node.right, min)) { + return false; + } + } + + return true; + } +}; + +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {boolean} + */ + +// 范围收敛 + +var isValidBST = function(root) { + return check(root, -Infinity, Infinity); + + function check(node, L, R){ + if (!node) { + return true; + } + + if (node.val <= L || node.val >= R) { + return false; + } + + if (node.left && !check(node.left, L, node.val)) { + return false; + } + + if (node.right && !check(node.right, node.val, R)) { + return false; + } + + return true; + } +}; + +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {boolean} + */ + +// 中序遍历从小到大有序 + +var isValidBST = function(root) { + if (!root) { + return root; + } + + var min = -Infinity; + return search(root); + + function search(node){ + if (node.left && !search(node.left)) { + return false; + } + + if (node.val <= min) { + return false; + } + + min = node.val; + + if (node.right && !search(node.right)) { + return false; + } + + return true; + } +}; diff --git a/Week 02/id_013/LeetCode_01_013.py b/Week 02/id_013/LeetCode_01_013.py new file mode 100644 index 000000000..9879a43f1 --- /dev/null +++ b/Week 02/id_013/LeetCode_01_013.py @@ -0,0 +1,48 @@ +""" +第五课第一题:242. 有效的字母异位词 +https://leetcode-cn.com/problems/valid-anagram/description/ +""" +import collections + +""" +解法一: +使用Python的Counter() +""" +class Solution_1: + def isAnagram(self, s: str, t: str) -> bool: + return collections.Counter(t) == collections.Counter(s) + +""" +解法二: +排序比较 +""" +class Solution_2: + def isAnagram(self, s: str, t: str) -> bool: + return sorted(s) == sorted(t) + + +""" +解法三: +使用字典存储字母出现的次数 +""" + + +class Solution_3: + def isAnagram(self, s: str, t: str) -> bool: + dic = {} + + for i in s: + if i in dic: + dic[i] += 1 + else: + dic[i] = 1 + + for i in t: + if i not in dic or dic[i] <= 0: + return False + dic[i] -= 1 + + for i in dic: + if dic[i] != 0: + return False + return True \ No newline at end of file diff --git a/Week 02/id_013/LeetCode_02_013.py b/Week 02/id_013/LeetCode_02_013.py new file mode 100644 index 000000000..e2426a8df --- /dev/null +++ b/Week 02/id_013/LeetCode_02_013.py @@ -0,0 +1,32 @@ +""" +第二题:49. 字母异位词分组 +https://leetcode-cn.com/problems/group-anagrams/ +""" + +# 有点难,参考官方解答多一点,:( +import collections + +""" +解法一: +当且仅当它们的排序字符串相等时,两个字符串是字母异位词 +""" +class Solution_1(object): + def groupAnagrams(self, strs): + ans = collections.defaultdict(list) + for s in strs: + ans[tuple(sorted(s))].append(s) + return ans.values() + +""" +解法二: +计数分类,当且仅当它们的字符计数(每个字符的出现次数)相同时,两个字符串是字母异位词。 +""" +class Solution_2: + def groupAnagrams(strs): + ans = collections.defaultdict(list) + for s in strs: + count = [0] * 26 + for c in s: + count[ord(c) - ord('a')] += 1 + ans[tuple(count)].append(s) + return ans.values() \ No newline at end of file diff --git a/Week 02/id_013/LeetCode_04_013.py b/Week 02/id_013/LeetCode_04_013.py new file mode 100644 index 000000000..1f07cf49d --- /dev/null +++ b/Week 02/id_013/LeetCode_04_013.py @@ -0,0 +1,51 @@ +""" +第四题:94. 二叉树的中序遍历 +https://leetcode-cn.com/problems/binary-tree-inorder-traversal/ +""" + +""" +解法一:使用递归求解 +""" + + +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution_1: + def inorderTraversal(self, root: TreeNode) -> List[int]: + f = self.inorderTraversal + return f(root.left) + [root.val] + f(root.right) if root else [] + + + +""" +解法二: +使用迭代求解 +""" + + +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution_2: + def postorderTraversal(self, root: TreeNode) -> List[int]: + res = [] + if not root: + return res + stack = [root] + while stack: + node = stack.pop() + if node.left: + stack.append(node.left) + if node.right: + stack.append(node.right) + res.append(node.val) + return res[::-1] \ No newline at end of file diff --git a/Week 02/id_013/LeetCode_05_013.py b/Week 02/id_013/LeetCode_05_013.py new file mode 100644 index 000000000..38b7b5585 --- /dev/null +++ b/Week 02/id_013/LeetCode_05_013.py @@ -0,0 +1,19 @@ +""" +第五题:144. 二叉树的前序遍历 +https://leetcode-cn.com/problems/binary-tree-preorder-traversal/ +""" + +""" +解法一:递归 +""" +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + def preorderTraversal(self, root: TreeNode) -> List[int]: + f = self.preorderTraversal + return [root.val] + f(root.left) + f(root.right) if root else [] \ No newline at end of file diff --git a/Week 02/id_013/LeetCode_10_013.py b/Week 02/id_013/LeetCode_10_013.py new file mode 100644 index 000000000..332074837 --- /dev/null +++ b/Week 02/id_013/LeetCode_10_013.py @@ -0,0 +1,25 @@ +""" +第十题:105. 从前序与中序遍历序列构造二叉树 +https://leetcode-cn.com/problems/valid-anagram/description/ +""" + +""" +解法一:递归构建 +时间和空间复杂度都不好 +""" +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode: + if len(inorder) == 0: + return None + root = TreeNode(preorder[0]) + mid = inorder.index(preorder[0]) + root.left = self.buildTree(preorder[1:mid+1],inorder[:mid]) + root.right = self.buildTree(preorder[mid+1:],inorder[mid+1:]) + return root \ No newline at end of file diff --git a/Week 02/id_013/LeetCode_11_013.py b/Week 02/id_013/LeetCode_11_013.py new file mode 100644 index 000000000..2db1bdc42 --- /dev/null +++ b/Week 02/id_013/LeetCode_11_013.py @@ -0,0 +1,23 @@ +""" +第十一题:77. 组合 +https://leetcode-cn.com/problems/combinations/ +""" + +""" +解法一:回溯 +""" + + +class Solution: + def combine(self, n: int, k: int) -> List[List[int]]: + def backtrack(first=1, curr=[]): + if len(curr) == k: + output.append(curr[:]) + for i in range(first, n + 1): + curr.append(i) + backtrack(i + 1, curr) + curr.pop() + + output = [] + backtrack() + return output \ No newline at end of file diff --git a/Week 02/id_013/LeetCode_14_013.py b/Week 02/id_013/LeetCode_14_013.py new file mode 100644 index 000000000..3eca1d60f --- /dev/null +++ b/Week 02/id_013/LeetCode_14_013.py @@ -0,0 +1,12 @@ +""" +第十四题:169. 求众数 +https://leetcode-cn.com/problems/majority-element/ +""" + +""" +解法一:排序取中位数 +""" +class Solution: + def majorityElement(self,nums): + nums.sort() + return nums[len(nums)//2] \ No newline at end of file diff --git a/Week 02/id_013/LeetCode_16_013.py b/Week 02/id_013/LeetCode_16_013.py new file mode 100644 index 000000000..e58e37e30 --- /dev/null +++ b/Week 02/id_013/LeetCode_16_013.py @@ -0,0 +1,21 @@ +""" +第十六题:51. N皇后 +https://leetcode-cn.com/problems/n-queens +""" + +""" +解法:深度优先搜索 +""" +class Solution: + def solveNQueens(self,n): + def DFS(queens,xy_diff,xy_sum): + p = len(queens) + if p == n: + result.append(queens) + return None + for q in range(n): + if q not in queens and p-q not in xy_diff and p+q not in xy_sum: + DFS(queens+[q],xy_diff+[p-q],xy_sum+[p+q]) + result = [] + DFS([],[],[]) + return [['.'*i + 'Q'+'.'*(n-i-1) for i in sol] for sol in result] \ No newline at end of file diff --git a/Week 02/id_013/NOTE.md b/Week 02/id_013/NOTE.md index a6321d6e2..3f3de03cd 100644 --- a/Week 02/id_013/NOTE.md +++ b/Week 02/id_013/NOTE.md @@ -1,4 +1,336 @@ -# NOTE - - - +# NOTE + +## 递归与调用栈 + +### 递归 + +- 伪代码:伪代码是对手问题的简要描述,看着像代码,但其实更接近自然语言。 +- 递归只是让解决方案更清晰,并没有性能上的优势。有些情况下,使用循环的性能更好。正如《Stack Overfloe》上说的:‘如果使用循环,程序的性能可能会更高;如果使用递归,程序可能更容易理解,如何选择要看什么对你来说更重要。’ + +#### 基线条件和递归条件 + +编写函数时,必须告诉它何时停止调用。正因为如此,每个递归函数都有两个部分:基线条件(base case)和递归条件(recursive case)。递归条件指的是函数调用自己,而基线条件是函数不再调用自己,从而避免无限循环。 +例如,编写一个倒计时函数: + +``` +def countdown(i): + print(i) + return countdown(i-1) + +print(countdown(9)) +``` + +这样编写程序会一直倒数下去,我们需要添加基线条件告诉函数何时停止递归。 + +``` +def countdown(i): + print(i) + if i <= 1: #基线条件:递归退出的条件 + return + else: #递归条件 + return countdown(i - 1) +``` + +### 栈 + +- 栈是一种**特殊的列表**,栈内的元素只能通过列表的一端访问,这一端称为**栈顶**。咖啡厅内的一摞盘子是现实世界中常见的栈的例子。只能从最上面取盘子,盘子洗净后,也只能摞在这一摞盘子的最上面。栈被称为一种**后入先出(LIFO,last-in-first-out)**的数据结构。 +- 由于栈具有后入先出的特点,所以任何不在栈顶的元素都无法访问。为了得到栈底的元素,必须先拿掉上面的元素。 +- 对栈的两种主要操作是将一个元素压入栈和将一个元素弹出栈。入栈使用push()方法,出栈使用pop()方法。下图演示了入栈和出栈的过程。 + ![https://essay-1256185664.cos.ap-guangzhou.myqcloud.com/%E5%8D%9A%E6%96%87%E9%85%8D%E5%9B%BE/%E7%AE%97%E6%B3%95%E7%B1%BB%E9%85%8D%E5%9B%BE/3bf1b51d-4582-4586-ba69-4d0e524040a5.jpg](https://essay-1256185664.cos.ap-guangzhou.myqcloud.com/%E5%8D%9A%E6%96%87%E9%85%8D%E5%9B%BE/%E7%AE%97%E6%B3%95%E7%B1%BB%E9%85%8D%E5%9B%BE/3bf1b51d-4582-4586-ba69-4d0e524040a5.jpg) +- 一个常用的操作是预览栈顶的元素。pop()方法虽然可以访问栈顶的元素,但是调用该方法后,栈顶元素也从栈中被永久性地删除了。peek()方法则只返回栈顶元素,而不删除它。 +- 为了记录栈顶元素的位置,同时也为了标记哪里可以加入新元素,我们使用变量top,当向栈内压入元素时,该变量增大;从栈内弹出元素时,该变量减小。push()、pop()和peek()是栈的3个主要方法,但是栈还有其他方法和属性,如下表: + +| Stack() | 建立一个空的栈对象 | +| --------- | ---------------------------------- | +| push() | 把一个元素添加到栈的最顶层 | +| pop() | 删除栈最顶层的元素,并返回这个元素 | +| peek() | 返回最顶层的元素,并不删除它 | +| isEmpty() | 判断栈是否为空 | +| size() | 返回栈中元素的个数 | + +其中:push()、pop()和peek()是栈的3个主要方法。 + +操作示例: + +| 操作 | 描述 | +| :---------- | :----------------- | +| s = [] | 创建一个栈 | +| s.append(x) | 往栈内添加一个元素 | +| s.pop() | 在栈内删除一个元素 | +| not s | 判断是否为空栈 | +| len(s) | 获取栈内元素的数量 | +| s[-1] | 获取栈顶的元素 | + +使用Python中的列表对象: + +``` +class Stack: + """模拟栈""" + def __init__(self): + self.items = [] + + def isEmpty(self): + return len(self.items)==0 + + def push(self, item): + self.items.append(item) + + def pop(self): + return self.items.pop() + + def peek(self): + if not self.isEmpty(): + return self.items[len(self.items)-1] + + def size(self): + return len(self.items) +``` + +#### 调用栈(call stack) + +调用栈(call stack)是重要的编程概念,使用递归也必须理解这个概念。 + +简单例子: + +``` +def greet(name): + print('Hello '+name+'!') + greet2(name) + print('getting ready to say bye...') + bye() + +def greet2(name): + print('How are you '+name+'?') + +def bye(): + print('ok!bye') + +print(greet('kanghaov')) + t('Kanghaov')) +``` + +结果: + +``` +Hello kanghaov! +How are you kanghaov? +getting ready to say bye... +ok!bye +None +``` + +代码中可以看到当调用greet('kanghaov')时,计算机会为该函数分配一块内存,变量name被设置为kanghaov。接下来打印Hello kanghaov时,再调用greet2('kanghaov'),并同样的分配它一块内存。 +计算机使用一个栈来表示这些内存块,其中第二快内存块位于第一块内存块的上面,当打印完:How are you kanghaov?,greet2()(位于栈顶)就被弹出。此时栈顶的内存为函数greet(),意味着我们返回到了函数greet(),这是非常重要的:**调用另一个函数时,当前函数暂停并处于未完成状态。该函数说有变量的值都还在内存中。**接着函数往下执行bye()函数,使用后被弹出,返回到greet()函数,结束。 + +**这个栈用于存储多个函数的变量,被称为调用栈** + +#### 递归调用栈 + +递归函数 + +``` +def fact(x): + if x == 1: + return x + else: + return x * fact(x-1) +``` + +每个栈都有自己的变量x。在同一个函数调用中不能访问另一个x的变量。 + +### 总结 + +- 递归指的是调用自己的函数 +- 每个递归函数都有两个条件:基线条件、递归条件 +- 栈有两种操作:压入和弹出 +- 所有函数调用都进入调用栈 +- 调用栈可能很长,这将占用大量内存。 + + ## 树与递归 + +### Python中树的实现 + +**思路:** + +1. 先定义一个节点 node 类,存储数据 data 和左子节点 left 以及 右子节点 right。 +2. 再实现二叉树 binary_tree 的类,应至少有以下属性和函数: 属性:有一个根节点(root) , 它是 node 类。 函数:添加子节点 add ,返回父节点 get_parent,删除子节点 delete。 + +**1. 创建 Node 类** + +创建一个 Node 的类,作为基础数据结构:链点,并初始化对应的内参。 + +具体实现代码如下: + +```python +class Node(object): + def __init__(self,item): + self.item = item #表示对应的元素 + self.left=None #表示左子节点 + self.right=None #表示右子节点 + def __str__(self): + return str(self.item) #print 一个 Node 类时会打印 __str__ 的返回值 +``` + +**2. 创建 Tree 类** + +创建一个 Tree 的类,定义根节点。 + +具体实现代码如下: + +```python +class Tree(object): + def __init__(self): + self.root=Node('root') #根节点定义为 root 永不删除,作为哨兵使用。 +``` + +**3. 添加 add 函数** + +添加一个 add(item) 的函数,功能是添加子节点到树里面。 + +具体实现代码如下: + +```python + def add(self,item): + node = Node(item) + if self.root is None: #如果二叉树为空,那么生成的二叉树最终为新插入树的点 + self.root = node + else: + q = [self.root] # 将q列表,添加二叉树的根节点 + while True: + pop_node = q.pop(0) + if pop_node.left is None: #左子树为空则将点添加到左子树 + pop_node.left = node + return + elif pop_node.right is None: #右子树为空则将点添加到右子树 + pop_node.right = node + return + else: + q.append(pop_node.left) + q.append(pop_node.right) +``` + +**4. 添加 get_parent 函数** + +添加一个 get_parent(item) 函数,功能是找到 item 的父节点。 + +具体实现代码如下: + +```python + def get_parent(self, item): + if self.root.item == item: + return None # 根节点没有父节点 + tmp = [self.root] # 将tmp列表,添加二叉树的根节点 + while tmp: + pop_node = tmp.pop(0) + if pop_node.left and pop_node.left.item == item: #某点的左子树为寻找的点 + return pop_node #返回某点,即为寻找点的父节点 + if pop_node.right and pop_node.right.item == item: #某点的右子树为寻找的点 + return pop_node #返回某点,即为寻找点的父节点 + if pop_node.left is not None: #添加tmp 元素 + tmp.append(pop_node.left) + if pop_node.right is not None: + tmp.append(pop_node.right) + return None +``` + + + +**5. 添加 delete 函数** + +添加一个 delete(item) 函数,功能是从二叉树中删除一个子节点。 + +```python + def delete(self, item): + if self.root is None: # 如果根为空,就什么也不做 + return False + + parent = self.get_parent(item) + if parent: + del_node = parent.left if parent.left.item == item else parent.right # 待删除节点 + if del_node.left is None: + if parent.left.item == item: + parent.left = del_node.right + else: + parent.right = del_node.right + del del_node + return True + elif del_node.right is None: + if parent.left.item == item: + parent.left = del_node.left + else: + parent.right = del_node.left + del del_node + return True + else: # 左右子树都不为空 + tmp_pre = del_node + tmp_next = del_node.right + if tmp_next.left is None: + # 替代 + tmp_pre.right = tmp_next.right + tmp_next.left = del_node.left + tmp_next.right = del_node.right + + else: + while tmp_next.left: # 让tmp指向右子树的最后一个叶子 + tmp_pre = tmp_next + tmp_next = tmp_next.left + # 替代 + tmp_pre.left = tmp_next.right + tmp_next.left = del_node.left + tmp_next.right = del_node.right + if parent.left.item == item: + parent.left = tmp_next + else: + parent.right = tmp_next + del del_node + return True + else: + return False +``` + + + +### 树的递归遍历 + +#### 前序遍历 + +```python +def preoder(self, root): + if root is None: + return + else: + print root.data + self.preoder(root.left) + self.preoder(root.right) + + +``` + +#### 中序遍历 + +```python +def midoder(self, root): + if root is None: + return + else: + self.midoder(root.left) + print root.data + self.midoder(root.right) +``` + +#### 后序遍历 + +```python +def postoder(self, root): + if root is None: + return + else: + self.postoder(root.left) + self.postoder(root.right) + print root.data +``` + + + + + diff --git a/Week 02/id_023/leetCode_051_023.js b/Week 02/id_023/leetCode_051_023.js new file mode 100644 index 000000000..d4f2496d0 --- /dev/null +++ b/Week 02/id_023/leetCode_051_023.js @@ -0,0 +1,55 @@ +/** + * @param {number} n + * @return {string[][]} + */ +var solveNQueens = function (n) { + let result = new Array(n); + let results = []; + let dfs = (row, column) => { + let leftColumn = column - 1; + let rightColumn = column + 1; + for (let i = row - 1; i >= 0; i--) { + if (result[i] == column) { + return false; + } + if (leftColumn >= 0 && result[i] == leftColumn) { + return false; + } + if (rightColumn < n && result[i] == rightColumn) { + return false; + } + leftColumn--; + rightColumn++; + } + return true; + } + let format = (result) => { + let tmpResult = []; + for (let i = 0; i < n; i++) { + let str = ''; + for (let j = 0; j < n; j++) { + if (result[i] == j) { + str += 'Q'; + } else { + str += '.'; + } + } + tmpResult.push(str); + } + return tmpResult; + } + let Nqueens = (row) => { + if (row == n) { + results.push(format(result)); + return; + } + for (let j = 0; j < n; j++) { + if (dfs(row, j)) { + result[row] = j; + Nqueens(row + 1) + } + } + } + Nqueens(0); + return results; +}; diff --git a/Week 02/id_023/leetCode_242_023.js b/Week 02/id_023/leetCode_242_023.js new file mode 100644 index 000000000..e7170374c --- /dev/null +++ b/Week 02/id_023/leetCode_242_023.js @@ -0,0 +1,33 @@ +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function (s, t) { + const map = new Map() + for (let i = 0; i < s.length; i++) { + const getMap = map.get(s[i]) + if (!getMap) { + map.set(s[i], 1) + } else { + map.set(s[i], getMap + 1) + } + } + + for (let i = 0; i < t.length; i++) { + const getMap = map.get(t[i]) + if (getMap === 1) { + map.delete(t[i]) + } else if (getMap) { + map.set(t[i], getMap - 1) + } else { + map.set(t[i], 1) + } + } + + if (map.size) { + return false + } else { + return true + } +}; \ No newline at end of file diff --git a/Week 02/id_028/LeetCode_1_028.java b/Week 02/id_028/LeetCode_1_028.java new file mode 100644 index 000000000..9a43ef582 --- /dev/null +++ b/Week 02/id_028/LeetCode_1_028.java @@ -0,0 +1,36 @@ +package com.jane.part1; + +import java.util.HashMap; +import java.util.Map; + +/** + * 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 + * + * 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 + * + * 示例: + * + * 给定 nums = [2, 7, 11, 15], target = 9 + * + * 因为 nums[0] + nums[1] = 2 + 7 = 9 + * 所以返回 [0, 1] + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/two-sum + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +public class Solution { + public static int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + int key = target - nums[i]; + if (map.containsKey(key)){ + return new int[] {map.get(key),i}; + } + map.put(nums[i],i); + } + + return null; + } +} diff --git a/Week 02/id_028/LeetCode_242_028.java b/Week 02/id_028/LeetCode_242_028.java new file mode 100644 index 000000000..a3e1e2187 --- /dev/null +++ b/Week 02/id_028/LeetCode_242_028.java @@ -0,0 +1,86 @@ +package com.jane.part242; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.Arrays; + +/** + * 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 + * + * 示例 1: + * + * 输入: s = "anagram", t = "nagaram" + * 输出: true + * 示例 2: + * + * 输入: s = "rat", t = "car" + * 输出: false + * 说明: + * 你可以假设字符串只包含小写字母。 + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/valid-anagram + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class Solution { + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + char[] str1 = s.toCharArray(); + char[] str2 = t.toCharArray(); + Arrays.sort(str1); + Arrays.sort(str2); + return Arrays.equals(str1, str2); + } + + public boolean isAnagram1(String s, String t) { + if (s.length() != t.length()) { + return false; + } + + int[] chars = new int[26]; + for (int i = 0; i < s.length(); i++) { + chars[s.charAt(i) - 'a']++; + } + + for (int i = 0; i < t.length(); i++) { + chars[t.charAt(i) - 'a']--; + if (chars[t.charAt(i) - 'a'] < 0) { + return false; + } + } + return true; + } + + @Test + public void test() { + String s = "anagram", t = "nagaram"; + Assert.assertEquals(isAnagram(s, t), true); + } + + @Test + public void test1() { + String s = "rat", t = "car"; + Assert.assertEquals(isAnagram(s, t), false); + } + + @Test + public void test2() { + String s = "", t = ""; + Assert.assertEquals(isAnagram(s, t), true); + } + + @Test + public void test3() { + String s = "aacc", t = "ccac"; + Assert.assertEquals(isAnagram(s, t), false); + } + + @Test + public void test4() { + String s = "aaccssssssssssssssssssssssssssssssssssssssssssssssssssssss", t = "ccaccssssssssssssssssssssssssssssssssssssssssssssssssssssss"; + Assert.assertEquals(isAnagram(s, t), false); + } +} diff --git a/Week 02/id_028/LeetCode_49_028.java b/Week 02/id_028/LeetCode_49_028.java new file mode 100644 index 000000000..d819d8b75 --- /dev/null +++ b/Week 02/id_028/LeetCode_49_028.java @@ -0,0 +1,57 @@ +package com.jane.part49; + +import org.testng.annotations.Test; + +import java.util.*; + +/** + * 给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 + * + * 示例: + * + * 输入: ["eat", "tea", "tan", "ate", "nat", "bat"], + * 输出: + * [ + * ["ate","eat","tea"], + * ["nat","tan"], + * ["bat"] + * ] + * 说明: + * + * 所有输入均为小写字母。 + * 不考虑答案输出的顺序。 + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/group-anagrams + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class Solution { + public List> groupAnagrams(String[] strs) { + Map map = new HashMap<>(); + if (strs.length == 0) { + return new ArrayList(); + } + + for (int i = 0; i < strs.length; i++) { + char[] c = strs[i].toCharArray(); + Arrays.sort(c); + String s = String.valueOf(c); + + if (!map.containsKey(s)){ + map.put(s, new ArrayList()); + } + + map.get(s).add(strs[i]); + } + + return new ArrayList(map.values()); + } + + @Test + public void test(){ + String[] strs = {"eat", "tea", "tan", "ate", "nat", "bat"}; + List> list = groupAnagrams(strs); + + list.forEach(System.out::println); + } +} diff --git a/Week 02/id_038/week-02-038/.gitignore b/Week 02/id_038/week-02-038/.gitignore new file mode 100644 index 000000000..168781b21 --- /dev/null +++ b/Week 02/id_038/week-02-038/.gitignore @@ -0,0 +1,165 @@ +.vertx +*.ipr +# log files +*.log +*.log.2* +.log +out +output +dist +dist-3rd +*.txt +!agent-shell-banner.txt +!shell-banner.txt +!VERSION.txt + +# For Eclipse + +.metadata +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders +*.iml +.DS_Store + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + + +# For Java + +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# For Maven + +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# For JetBrains + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml +../../.idea/ + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +#data module ignore +node_modules + +static +public + +BETHUNE_PRO_HOST_PID +BETHUNE_PRO_AGENT_PID +*PID +APP_PID +PID_* +alert-rules-*.yml +altmng-configs.yml +alertmanager.yml +app-info-*.properties +app-info.properties +inspectResult/ + +/file-uploads/ diff --git a/Week 02/id_038/week-02-038/pom.xml b/Week 02/id_038/week-02-038/pom.xml new file mode 100644 index 000000000..e12eb049c --- /dev/null +++ b/Week 02/id_038/week-02-038/pom.xml @@ -0,0 +1,95 @@ + + 4.0.0 + com.github.kylefeng + week-02-038 + jar + 1.0-SNAPSHOT + week-01-038 + http://maven.apache.org + + UTF-8 + 1.8 + 4.12 + 5.0.0 + ${junit.version}.0 + 1.0.0 + + + + + + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + + + + maven-surefire-plugin + 2.19.1 + + + **/Test*.java + **/*Test.java + **/*Tests.java + **/*TestCase.java + + + + slow + + + + + + org.junit.platform + junit-platform-surefire-provider + ${junit.platform.version} + + + + + + + + + com.google.guava + guava + 28.1-jre + + + + org.assertj + assertj-core + 3.13.2 + test + + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test + + + + org.junit.platform + junit-platform-launcher + ${junit.platform.version} + test + + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + diff --git a/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/Helper.java b/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/Helper.java new file mode 100644 index 000000000..f1241b6b2 --- /dev/null +++ b/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/Helper.java @@ -0,0 +1,58 @@ +package com.github.kylefeng.week02; + +import com.google.common.collect.Lists; + +import java.util.List; + +/** + * @author kylefeng + * @time 2019/10/26 10:49 + */ +public class Helper { + + /** + * 方便构建树的辅助方法: + * + * @param values + * @return + */ + public static Node node(Object... values) { + if (values.length == 1) { + return new Node((Integer) values[0], null); + } + + List children = Lists.newArrayListWithCapacity(values.length - 1); + for (int i = 1; i < values.length; i++) { + Object val = values[i]; + if (val instanceof Node) { + children.add((Node) val); + } else { + children.add(new Node((Integer) values[i], null)); + } + } + return new Node((Integer) values[0], children); + } + + public static class Node { + public int val; + public List children; + + public Node() { + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } + } + + public static class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } +} diff --git a/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_144_038.java b/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_144_038.java new file mode 100644 index 000000000..861c58cee --- /dev/null +++ b/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_144_038.java @@ -0,0 +1,35 @@ +package com.github.kylefeng.week02; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static com.github.kylefeng.week02.Helper.TreeNode; + +/** + * 144. 二叉树的前序遍历 + * + * @author kylefeng + * @time 2019/10/26 21:45 + */ +public class LeetCode_144_038 { + public static List preorderTraversal(TreeNode root) { + if (root == null) { + return Collections.emptyList(); + } + + List collector = new ArrayList<>(); + recurTraverse(root, collector); + return collector; + } + + private static void recurTraverse(TreeNode node, List collector) { + if (node == null) { + return; + } + + collector.add(node.val); + recurTraverse(node.left, collector); + recurTraverse(node.right, collector); + } +} diff --git a/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_242_038.java b/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_242_038.java new file mode 100644 index 000000000..e17935abb --- /dev/null +++ b/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_242_038.java @@ -0,0 +1,103 @@ +package com.github.kylefeng.week02; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * 242. 有效的字母异位词 + * + * @author kylefeng + * @time 2019/10/26 10:32 + */ +public class LeetCode_242_038 { + + + /** + * 判断两个字符串是否为两个有效的字母异位词。 + * + * @param str1 字符串1 + * @param str2 字符串2 + * @return boolean + */ + public static boolean isAnagram(String str1, String str2) { + if (str1 == null && str2 == null) { + return true; + } + + if (str1 == null || str2 == null) { + return false; + } + + if (str1.equals("") || str2.equals("")) { + return true; + } + + char[] chars1 = str1.toCharArray(); + char[] chars2 = str2.toCharArray(); + + if (chars1.length != chars2.length) { + return false; + } + + Map hash1 = makeHash(chars1); + Map hash2 = makeHash(chars2); + + for (Map.Entry entry : hash1.entrySet()) { + Integer key = entry.getKey(); + if (!Objects.equals(hash1.get(key), (hash2.get(key)))) { + return false; + } + } + return true; + } + + private static Map makeHash(char[] chars) { + Map hash = new HashMap<>(chars.length); + for (int i = 0; i < chars.length; i++) { + Integer key = (int) chars[i]; + Integer counter = hash.get(key); + if (counter == null) { + hash.put(key, 1); + } else { + hash.put(key, counter + 1); + } + } + return hash; + } + + /** + * 解法二,直接使用库函数排序字符串数组,然后比较 + * + * @param str1 + * @param str2 + * @return + */ + public static boolean isAnagram2(String str1, String str2) { + if (str1 == null && str2 == null) { + return true; + } + + if (str1 == null || str2 == null) { + return false; + } + + if (str1.equals("") || str2.equals("")) { + return true; + } + + char[] chars1 = str1.toCharArray(); + char[] chars2 = str2.toCharArray(); + + if (chars1.length != chars2.length) { + return false; + } + + Arrays.sort(chars1); + Arrays.sort(chars2); + + return Arrays.equals(chars1, chars2); + } + +} diff --git a/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_429_038.java b/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_429_038.java new file mode 100644 index 000000000..1d7dc55c6 --- /dev/null +++ b/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_429_038.java @@ -0,0 +1,70 @@ +package com.github.kylefeng.week02; + +import com.github.kylefeng.week02.Helper.Node; + +import java.util.*; + +/** + * 429. N叉树的层序遍历 + * + * @author kylefeng + * @time 2019/10/26 10:48 + */ +public class LeetCode_429_038 { + + /** + * 进行层序遍历 + * + * @param root + * @return + */ + public static List> levelOrder(Node root) { + if (root == null) { + return Collections.emptyList(); + } + + Map> table = new HashMap<>(); + traverse(root, 1, table); + + // Add first element first + List> result = new ArrayList<>(); + List first = new ArrayList<>(); + first.add(root.val); + result.add(first); + + // Collect children + for (int i = 0; i < table.size(); i++) { + result.add(table.get(i + 1)); + } + + return result; + } + + private static void traverse(Node parent, + int level, + Map> table) { + if (parent.children == null || parent.children.isEmpty()) { + return; + } + + // Visit children + int childrenSize = parent.children.size(); + List childrenData = new ArrayList<>(childrenSize); + for (Node child : parent.children) { + childrenData.add(child.val); + } + + List sameLevelData = table.get(level); + if (sameLevelData == null) { + sameLevelData = new ArrayList<>(); + table.put(level, sameLevelData); + } + sameLevelData.addAll(childrenData); + + + // Drill down + for (Node child : parent.children) { + traverse(child, level + 1, table); + } + } +} diff --git a/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_49_038.java b/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_49_038.java new file mode 100644 index 000000000..fd1a016c5 --- /dev/null +++ b/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_49_038.java @@ -0,0 +1,43 @@ +package com.github.kylefeng.week02; + +import com.google.common.collect.Lists; + +import java.util.*; + +/** + * 49. 字母异位词分组 + * + * @author kylefeng + * @time 2019/10/27 10:44 + */ +public class LeetCode_49_038 { + + public static List> groupAnagrams(String[] strs) { + if (strs == null || strs.length == 0) { + return Collections.emptyList(); + } + + + Map> hash = new HashMap<>(); + for (String s : strs) { + char[] parts = s.toCharArray(); + Arrays.sort(parts); + + String key = new String(parts); + List subResult = hash.get(key); + if (subResult == null) { + subResult = new LinkedList<>(); + hash.put(key, subResult); + } + subResult.add(s); + } + + List> result = new ArrayList<>(hash.size()); + for (String key : hash.keySet()) { + result.add(hash.get(key)); + } + return result; + } + + +} diff --git a/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_589_038.java b/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_589_038.java new file mode 100644 index 000000000..08520ae80 --- /dev/null +++ b/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_589_038.java @@ -0,0 +1,54 @@ +package com.github.kylefeng.week02; + +import java.util.*; + +import static com.github.kylefeng.week02.Helper.Node; + +/** + * 589. N叉树的前序遍历 + * + * @author kylefeng + * @time 2019/10/27 10:37 + */ +public class LeetCode_589_038 { + + public static List preorder(Node root) { + if (root == null) { + return Collections.emptyList(); + } + + if (root.children == null || root.children.isEmpty()) { + List result = new ArrayList<>(1); + result.add(root.val); + return result; + } + + List result = new ArrayList<>(); + traverse(root, new HashSet<>(), result); + return result; + } + + private static void traverse(Node node, Set visited, List collector) { + if (node.children == null || node.children.isEmpty()) { + return; + } + + if (!visited.contains(node)) { + collector.add(node.val); + visited.add(node); + } + + // Visit children + Set set = new HashSet<>(); + for (Node child : node.children) { + traverse(child, visited, collector); + set.add(child.val); + if (!visited.contains(child)) { + collector.add(child.val); + visited.add(child); + } + } + + + } +} diff --git a/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_590_038.java b/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_590_038.java new file mode 100644 index 000000000..806f29442 --- /dev/null +++ b/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_590_038.java @@ -0,0 +1,51 @@ +package com.github.kylefeng.week02; + +import java.util.*; + +import static com.github.kylefeng.week02.Helper.Node; + +/** + * @author kylefeng + * @time 2019/10/26 22:22 + */ +public class LeetCode_590_038 { + + public static List postorder(Node root) { + if (root == null) { + return Collections.emptyList(); + } + + if (root.children == null || root.children.isEmpty()) { + List result = new ArrayList<>(1); + result.add(root.val); + return result; + } + + List result = new ArrayList<>(); + traverse(root, new HashSet<>(), result); + return result; + } + + private static void traverse(Node node, Set visited, List collector) { + if (node.children == null || node.children.isEmpty()) { + return; + } + + // Visit children + Set set = new HashSet<>(); + for (Node child : node.children) { + traverse(child, visited, collector); + set.add(child.val); + if (!visited.contains(child)) { + collector.add(child.val); + visited.add(child); + } + } + + if (!visited.contains(node)) { + collector.add(node.val); + visited.add(node); + } + } + +} diff --git a/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_94_038.java b/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_94_038.java new file mode 100644 index 000000000..bda14320f --- /dev/null +++ b/Week 02/id_038/week-02-038/src/main/java/com/github/kylefeng/week02/LeetCode_94_038.java @@ -0,0 +1,36 @@ +package com.github.kylefeng.week02; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static com.github.kylefeng.week02.Helper.TreeNode; + +/** + * 94. 二叉树的中序遍历 + * + * @author kylefeng + * @time 2019/10/26 12:08 + */ +public class LeetCode_94_038 { + + public static List inorderTraversal(TreeNode root) { + if (root == null) { + return Collections.emptyList(); + } + + List collector = new ArrayList<>(); + recurTraverse(root, collector); + return collector; + } + + private static void recurTraverse(TreeNode node, List collector) { + if (node == null) { + return; + } + recurTraverse(node.left, collector); + collector.add(node.val); + recurTraverse(node.right, collector); + } + +} diff --git a/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_144_038_Test.java b/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_144_038_Test.java new file mode 100644 index 000000000..376cff929 --- /dev/null +++ b/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_144_038_Test.java @@ -0,0 +1,31 @@ +package com.github.kylefeng.week02; + +import com.google.common.collect.Lists; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static com.github.kylefeng.week02.Helper.TreeNode; +import static com.github.kylefeng.week02.LeetCode_144_038.preorderTraversal; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author kylefeng + * @time 2019/10/26 22:01 + */ +public class LeetCode_144_038_Test { + + @Test + void testCases() { + TreeNode root = new TreeNode(1); + TreeNode n1 = new TreeNode(2); + TreeNode n2 = new TreeNode(3); + root.right = n1; + n1.left = n2; + + List result = preorderTraversal(root); + + assertThat(result).isEqualTo(Lists.newArrayList(1, 2, 3)); + + } +} diff --git a/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_242_038_Test.java b/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_242_038_Test.java new file mode 100644 index 000000000..87ad08afe --- /dev/null +++ b/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_242_038_Test.java @@ -0,0 +1,46 @@ +package com.github.kylefeng.week02; + +import org.junit.jupiter.api.Test; + +import static com.github.kylefeng.week02.LeetCode_242_038.isAnagram; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author kylefeng + * @time 2019/10/26 10:34 + */ +public class LeetCode_242_038_Test { + + + @Test + void testCases() { + String A1 = "anagram"; + String A2 = "nagaram"; + String B1 = "rat"; + String B2 = "car"; + String C1 = "ccac"; + String C2 = "aacc"; + + assertThat(isAnagram(A1, A2)).isTrue(); + assertThat(isAnagram(B1, B2)).isFalse(); + assertThat(isAnagram(C1, C2)).isFalse(); + } + + @Test + void abnormalTestCases() { + String nullStr = null; + String empty = ""; + + String diffLen1 = "abc"; + String diffLen2 = "ab"; + + + assertThat(isAnagram(nullStr, nullStr)).isTrue(); + assertThat(isAnagram(empty, empty)).isTrue(); + assertThat(isAnagram(nullStr, empty)).isFalse(); + assertThat(isAnagram(empty, nullStr)).isFalse(); + assertThat(isAnagram(diffLen1, diffLen2)).isFalse(); + + + } +} diff --git a/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_429_038_Test.java b/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_429_038_Test.java new file mode 100644 index 000000000..2688329fb --- /dev/null +++ b/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_429_038_Test.java @@ -0,0 +1,40 @@ +package com.github.kylefeng.week02; + +import com.google.common.collect.Lists; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static com.github.kylefeng.week02.Helper.Node; +import static com.github.kylefeng.week02.Helper.node; +import static com.github.kylefeng.week02.LeetCode_429_038.levelOrder; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author kylefeng + * @time 2019/10/26 11:36 + */ +public class LeetCode_429_038_Test { + + @Test + void testCases() { + Node tree = node(1, node(3, 5, 6), 2, 4); + List> expected = Lists.newArrayList(); + expected.add(Lists.newArrayList(1)); + expected.add(Lists.newArrayList(3, 2, 4)); + expected.add(Lists.newArrayList(5, 6)); + List> actual = levelOrder(tree); + assertThat(actual).isEqualTo(expected); + } + + @Test + void testCase2() { + Node root = node(1, node(10, 5, 0), node(3, 6)); + List> expected = Lists.newArrayList(); + expected.add(Lists.newArrayList(1)); + expected.add(Lists.newArrayList(10, 3)); + expected.add(Lists.newArrayList(5, 0, 6)); + List> actual = levelOrder(root); + assertThat(actual).isEqualTo(expected); + } +} diff --git a/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_49_038_Test.java b/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_49_038_Test.java new file mode 100644 index 000000000..4efe92b04 --- /dev/null +++ b/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_49_038_Test.java @@ -0,0 +1,62 @@ +package com.github.kylefeng.week02; + +import com.google.common.collect.Lists; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; + +import static com.github.kylefeng.week02.LeetCode_49_038.groupAnagrams; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author kylefeng + * @time 2019/10/27 10:54 + */ +public class LeetCode_49_038_Test { + + @Test + void testCases() { + String[] input = new String[]{"eat", "tea", "tan", "ate", "nat", "bat"}; + List> expected = Lists.newArrayList(); + List e1 = Lists.newArrayList("ate", "eat", "tea"); + List e2 = Lists.newArrayList("nat", "tan"); + List e3 = Lists.newArrayList("bat"); + expected.add(e1); + expected.add(e2); + expected.add(e3); + List> actual = groupAnagrams(input); + + assertTrue(compare(expected, actual)); + + } + + + static boolean compare(List> rs1, List> rs2) { + if (rs1.size() != rs2.size()) { + return false; + } + + Comparator> comp = new Comparator>() { + @Override + public int compare(List o1, List o2) { + return o1.size() - o2.size(); + } + }; + + Collections.sort(rs1, comp); + Collections.sort(rs2, comp); + + for (int i = 0; i < rs1.size(); i++) { + List elems1 = rs1.get(i); + List elems2 = rs2.get(i); + + if (!new HashSet<>(elems1).containsAll(elems2)) { + return false; + } + } + return true; + } +} diff --git a/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_589_038_Test.java b/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_589_038_Test.java new file mode 100644 index 000000000..bd21ec477 --- /dev/null +++ b/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_589_038_Test.java @@ -0,0 +1,26 @@ +package com.github.kylefeng.week02; + +import com.google.common.collect.Lists; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static com.github.kylefeng.week02.Helper.Node; +import static com.github.kylefeng.week02.Helper.node; +import static com.github.kylefeng.week02.LeetCode_589_038.preorder; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author kylefeng + * @time 2019/10/27 10:39 + */ +public class LeetCode_589_038_Test { + + @Test + void testCases() { + Node root = node(1, node(3, 5, 6), 2, 4); + List expected = Lists.newArrayList(1, 3, 5, 6, 2, 4); + List actual = preorder(root); + assertThat(actual).isEqualTo(expected); + } +} diff --git a/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_590_038_Test.java b/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_590_038_Test.java new file mode 100644 index 000000000..04b6e6d88 --- /dev/null +++ b/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_590_038_Test.java @@ -0,0 +1,27 @@ +package com.github.kylefeng.week02; + +import com.google.common.collect.Lists; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static com.github.kylefeng.week02.Helper.Node; +import static com.github.kylefeng.week02.Helper.node; +import static com.github.kylefeng.week02.LeetCode_590_038.postorder; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author kylefeng + * @time 2019/10/26 22:56 + */ +public class LeetCode_590_038_Test { + + @Test + void testCases() { + Node root = node(1, node(10, 5, 0), node(3, 6)); + List expected = Lists.newArrayList(5, 0, 10, 6, 3, 1); + List actual = postorder(root); + assertThat(actual).isEqualTo(expected); + } + +} diff --git a/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_94_038_Test.java b/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_94_038_Test.java new file mode 100644 index 000000000..000c18248 --- /dev/null +++ b/Week 02/id_038/week-02-038/src/test/java/com/github/kylefeng/week02/LeetCode_94_038_Test.java @@ -0,0 +1,31 @@ +package com.github.kylefeng.week02; + +import com.google.common.collect.Lists; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static com.github.kylefeng.week02.Helper.TreeNode; +import static com.github.kylefeng.week02.LeetCode_94_038.inorderTraversal; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +/** + * @author kylefeng + * @time 2019/10/26 16:11 + */ +public class LeetCode_94_038_Test { + + @Test + void testCases() { + TreeNode root = new TreeNode(1); + TreeNode n1 = new TreeNode(2); + TreeNode n2 = new TreeNode(3); + root.right = n1; + n1.left = n2; + + List result = inorderTraversal(root); + + assertThat(result).isEqualTo(Lists.newArrayList(1, 3, 2)); + } + +} diff --git a/Week 02/id_048/Solution.java b/Week 02/id_048/Solution.java new file mode 100644 index 000000000..27c200b52 --- /dev/null +++ b/Week 02/id_048/Solution.java @@ -0,0 +1,74 @@ +package com.leetcode.week02; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by tim on 2019/10/27. + */ +public class Solution { + public static void main(String[] args) { + Solution solution = new Solution(); + Node node = solution.generateNode(3); + solution.levelOrder(node); + } + + private Node generateNode(int depth) { + if (depth <= 0) { + return null; + } + Node node = new Node(); + node.val =depth -1; + node = generateNode(depth - 1); + return node; + } + + //https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/ + // 429. N叉树的层序遍历 + public List> levelOrder(Node root) { + List> res = new ArrayList>(); + //判断条件 + if (root == null) { + return null; + } + + // 本层逻辑 + helper(root, 0, res); + return res; + //drill down + //clear state + } + + private void helper(Node root, int depth, List> res) { + if (root == null) return; + //判断是否是新的一层 + if (depth + 1 > res.size()) { + res.add(new ArrayList()); + } + res.get(depth).add(root.val); + + //处理子节点 + for (Node node : root.children) { + if (node != null) { + helper(node, depth + 1, res); + } + } + } + + + + + +} + +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +} \ No newline at end of file diff --git a/Week 02/id_048/Solution2.java b/Week 02/id_048/Solution2.java new file mode 100644 index 000000000..148b574fe --- /dev/null +++ b/Week 02/id_048/Solution2.java @@ -0,0 +1,55 @@ +package com.leetcode.week02; + +/** + * Created by tim on 2019/10/27. + */ +public class Solution2 { + private TreeNode ans; + + public Solution2() { + // Variable to store LCA node.变量保存在LCA节点 + this.ans = null; + } + + //https://leetcode-cn.com/problems/permutations/ + //236. 二叉树的最近公共祖先 + + /** + * 是否递归树 + * @param currentNode + * @param p + * @param q + * @return + */ + private boolean recurseTree(TreeNode currentNode, TreeNode p, TreeNode q) { + + // If reached the end of a branch, return false. + if (currentNode == null) { + return false; + } + + // Left Recursion. If left recursion returns true, set left = 1 else 0 + int left = this.recurseTree(currentNode.left, p, q) ? 1 : 0; + + // Right Recursion + int right = this.recurseTree(currentNode.right, p, q) ? 1 : 0; + + // If the current node is one of p or q + int mid = (currentNode == p || currentNode == q) ? 1 : 0; + + + // If any two of the flags left, right or mid become True + if (mid + left + right >= 2) { + this.ans = currentNode; + } + + // Return true if any one of the three bool values is True. + return (mid + left + right > 0); + } + + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { +// Traverse the tree + this.recurseTree(root, p, q); + return this.ans; + } +} diff --git a/Week 02/id_048/Summary.md b/Week 02/id_048/Summary.md new file mode 100644 index 000000000..702e58f58 --- /dev/null +++ b/Week 02/id_048/Summary.md @@ -0,0 +1,20 @@ +树的面试题解法一般都是递归,为什么? +说明:同学们可以将自己的思考写在课程下方的留言区一起讨论,也可以写在第 2 周的学习总结中 +因为从逻辑上,用递归比较容易理解分析树的结构,如果用循环就会繁琐的多。 + + +HashMap的实现原理: +首先有一个每个元素都是链表的数组,当添加一个元素(key-value)时, +就首先计算元素key的hash值,以此确定插入数组中的位置,但是可能存在同一hash值的元素已经被放在数组同一位置了, +这时就添加到同一hash值的元素的后面,他们在数组的同一位置,但是形成了链表,同一各链表上的Hash值是相同的,所以说数组存放的是链表。 +而当链表长度太长时,链表就转换为红黑树,这样大大提高了查找的效率。 + + +本周总结: +本周学习了递归的思想。 +一:递归的模板写法是: +1 结束条件,2本层逻辑,3.本递归函数调用,4,清除状态 +如果递归函数里面只递归调用一次本函数. + + + diff --git a/Week 02/id_048/TreeNode.java b/Week 02/id_048/TreeNode.java new file mode 100644 index 000000000..c8a6c0f5e --- /dev/null +++ b/Week 02/id_048/TreeNode.java @@ -0,0 +1,11 @@ +package com.leetcode.week02; + +/** + * Created by tim on 2019/10/27. + */ +public class TreeNode { + public int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { val = x; } +} diff --git "a/Week 02/id_053/[144]\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" "b/Week 02/id_053/[144]\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" new file mode 100644 index 000000000..856fcbfea --- /dev/null +++ "b/Week 02/id_053/[144]\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" @@ -0,0 +1,49 @@ +//给定一个二叉树,返回它的 前序 遍历。 +// +// 示例: +// +// 输入: [1,null,2,3] +// 1 +// \ +// 2 +// / +// 3 +// +//输出: [1,2,3] +// +// +// 进阶: 递归算法很简单,你可以通过迭代算法完成吗? +// Related Topics 栈 树 + + + +//leetcode submit region begin(Prohibit modification and deletion) +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List preorderTraversal(TreeNode root) { + List result = new ArrayList<>(); + recursionTask(root,result); + return result; + } + private void recursionTask(TreeNode root,List result) { + //前序遍历,根左右 + if (root != null) { + result.add(root.val); + if (root.left != null) { + recursionTask(root.left,result); + } + if (root.right != null ) { + recursionTask(root.right,result); + } + } + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week 02/id_053/[169]\346\261\202\344\274\227\346\225\260.java" "b/Week 02/id_053/[169]\346\261\202\344\274\227\346\225\260.java" new file mode 100644 index 000000000..651a2b7e8 --- /dev/null +++ "b/Week 02/id_053/[169]\346\261\202\344\274\227\346\225\260.java" @@ -0,0 +1,41 @@ +//给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。 +// +// 你可以假设数组是非空的,并且给定的数组总是存在众数。 +// +// 示例 1: +// +// 输入: [3,2,3] +//输出: 3 +// +// 示例 2: +// +// 输入: [2,2,1,1,1,2,2] +//输出: 2 +// +// Related Topics 位运算 数组 分治算法 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int majorityElement(int[] nums) { + //1、只能想到暴力法,对数组进行遍历后,在遍历哈希表得到众数,时间复杂度为O(n) + Map map = new HashMap<>(); + for (int i =0;i nums.length/2) { + return key; + } + } + return -1; + } + +} +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week 02/id_053/[17]\347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\345\255\227\346\257\215\347\273\204\345\220\210.java" "b/Week 02/id_053/[17]\347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\345\255\227\346\257\215\347\273\204\345\220\210.java" new file mode 100644 index 000000000..fee4f7185 --- /dev/null +++ "b/Week 02/id_053/[17]\347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\345\255\227\346\257\215\347\273\204\345\220\210.java" @@ -0,0 +1,62 @@ +//给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。 +// +// 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 +// +// +// +// 示例: +// +// 输入:"23" +//输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]. +// +// +// 说明: +//尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。 +// Related Topics 字符串 回溯算法 + + +import java.util.Map; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + static Map map = new HashMap<>(); + + static{ + map.put('2',"abc"); + map.put('3',"def"); + map.put('4',"ghi"); + map.put('5',"jkl"); + map.put('6',"mno"); + map.put('7',"pqrs"); + map.put('8',"tuv"); + map.put('9',"wxyz"); + } + + public List letterCombinations(String digits) { + if (digits == null || "".equals(digits)) { + return new ArrayList<>(); + } + List result = new ArrayList<>(); + recursionTask("",result,0,digits); + return result; + } + + private void recursionTask(String s,List result,int level,String digits) { + //terminator + if (level == digits.length()) { + result.add(s); + return; + } + // process current level logic + //找到当前的数字字符,对应的字母范围,然后每个范围遍历输出 + char currentChar = digits.charAt(level); + String foreachStr = map.get(currentChar); + for (int i = 0; i map = new HashMap<>(); + for (int i = 0;i < nums.length; i++){ + //保存该值,和下标 + map.put(nums[i],i); + } + for (int i = 0;i> subsets(int[] nums) { + if (nums == null) { + return new ArrayList<>(); + } + List> result = new ArrayList<>(); + recursiveProcess(result, nums, new ArrayList(), 0); + return result; + } + + public void recursiveProcess(List> result, int[] nums, List middle, int index) { + //terminator + if (index == nums.length) { + result.add(new ArrayList<>(middle)); + return; + } + //process current logic + //不处理这个元素 + recursiveProcess(result, nums, middle, index + 1); + //处理这个元素 + middle.add(nums[index]); + recursiveProcess(result, nums, middle, index + 1); + //drill down + //reverse status if need + middle.remove(middle.size() - 1); + } + +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week 02/id_053/[94]\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.java" "b/Week 02/id_053/[94]\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.java" new file mode 100644 index 000000000..3e89aebe7 --- /dev/null +++ "b/Week 02/id_053/[94]\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.java" @@ -0,0 +1,61 @@ +//给定一个二叉树,返回它的中序 遍历。 +// +// 示例: +// +// 输入: [1,null,2,3] +// 1 +// \ +// 2 +// / +// 3 +// +//输出: [1,3,2] +// +// 进阶: 递归算法很简单,你可以通过迭代算法完成吗? +// Related Topics 栈 树 哈希表 + + + +//leetcode submit region begin(Prohibit modification and deletion) + +import java.util.ArrayList; + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List inorderTraversal(TreeNode root) { + List result = new ArrayList<>(); + process(root,result); + return result; + } + public void process(TreeNode root,List result) { + if (root != null) { + if (root.left != null) { + process(root.left,result); + } + result.add(root.val); + if (root.right != null) { + process(root.right,result); + } + } + } +} +//class TreeNode{ +// int val; +// TreeNode left; +// TreeNode right; +// +// TreeNode(int val){ +// this.val = val; +// this.left = null; +// this.right = null; +// } +// } +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_063/C++/LeetCode_105_063.cpp b/Week 02/id_063/C++/LeetCode_105_063.cpp new file mode 100644 index 000000000..d0c727b30 --- /dev/null +++ b/Week 02/id_063/C++/LeetCode_105_063.cpp @@ -0,0 +1,66 @@ +/* +思路 +进行递归构建, +找出左子树的前序和中序序列,构建左子树 +找出右子树的前序和中序序列,构建右子树 +左子树的根 右子树的根 和 当前的根节点共同组成新的树 +查找子树的前序序列方法: +preorder[0] 一定是当前的根, 在inorder中找preorder[0]的下标x, x 就是左子树中元素的数量,那么preorder[0]后面跟着x个数值就是左子树的前序序列, +preorder再后面的就是右子树的前序序列 +查找子树的中序序列的方法: +inorder序列中下标是x的元素就是根,x左边的就是左子树中序序列,x右边就是右子树的中序序列 +*/ + + +#include +#include +using namespace std; + +struct TreeNode { + int val; + TreeNode *left; + TreeNode *right; + TreeNode(int x) : val(x), left(NULL), right(NULL) {} +}; + +class Solution { +public: + TreeNode* buildTree(vector& preorder, int pre_start, int pre_end, + vector& inorder, int in_start, int in_end) { + int rootVal = preorder[pre_start]; + + int i; + for (i = 0; i < inorder.size(); i++) { + if (inorder[i] == rootVal) { + break; + } + } + + TreeNode* p_left_root = nullptr; + TreeNode* p_right_root = nullptr; + + if (i > in_start) { + p_left_root = buildTree(preorder, pre_start+1, pre_start + (i - in_start), + inorder, in_start, i-1); + } + + if (i < in_end) { + p_right_root = buildTree(preorder, pre_start + 1 + (i-in_start), pre_end, + inorder, i+1, in_end); + } + + TreeNode* p_root = new TreeNode(rootVal); + p_root->left = p_left_root; + p_root->right = p_right_root; + + return p_root; + } + + TreeNode* buildTree(vector& preorder, vector& inorder) { + if (preorder.size() == 0) { + return nullptr; + } + + return buildTree(preorder, 0, preorder.size()-1, inorder, 0, inorder.size() - 1); + } +}; \ No newline at end of file diff --git a/Week 02/id_063/C++/LeetCode_144_063.cpp b/Week 02/id_063/C++/LeetCode_144_063.cpp new file mode 100644 index 000000000..5fb7834a1 --- /dev/null +++ b/Week 02/id_063/C++/LeetCode_144_063.cpp @@ -0,0 +1,30 @@ +#include +#include +using namespace std; + +struct TreeNode { + int val; + TreeNode *left; + TreeNode *right; + TreeNode(int x) : val(x), left(NULL), right(NULL) {} +}; + +class Solution { +public: + + void dfs(vector& result, TreeNode* p_cur_node) { + if (p_cur_node == nullptr) { + return; + } + + result.push_back(p_cur_node->val); + dfs(result, p_cur_node->left); + dfs(result, p_cur_node->right); + } + + vector preorderTraversal(TreeNode* root) { + vector result; + dfs(result, root); + return result; + } +}; \ No newline at end of file diff --git a/Week 02/id_063/C++/LeetCode_169_063.cpp b/Week 02/id_063/C++/LeetCode_169_063.cpp new file mode 100644 index 000000000..eecf553c8 --- /dev/null +++ b/Week 02/id_063/C++/LeetCode_169_063.cpp @@ -0,0 +1,68 @@ +/* +思路 + +众数x是数量超过了总数一半的数字,那么如果把序列以任意比例切成两半,那么x至少是两部分子 +序列其中一个序列的众数,这个可以进行反证,如果x在两个子序列中数量都没有超过一半,两个子序列 +并在一起,x总数怎么可能超过总数一半呢? + +所以子序列中的众数是全序列众数的候选者,两个子序列至少一个序列有众数,否则不可能全序列有众数(题目说了输入数据保证全局有众数) +如果只有一个候选者,那最终众数必然就是这个候选者 +如果两个子序列都产生了候选者,需要统计两个候选者在总序列中的频数,必然有一个胜出者 + +基于以上的逻辑进行分治查找 +可以用ForkJoin线程池实现多线程的分治 +*/ + + +#include +using namespace std; + +class Solution { +public: + + bool is_major(vector& nums, int start, int end, int num) { + int cnt = 0; + for (int i = start; i <= end; i++) { + if (nums[i] == num) { + cnt++; + } + } + + return (cnt > ((end - start) + 1) >> 1); + } + + int get_major(vector& nums, int start, int end) { + bool find_major; + return get_major(nums, start, end, find_major); + } + + int get_major(vector& nums, int start, int end, bool& find_major) { + if (start == end) { + find_major = true; + return nums[start]; + } + + bool find_left_major = false; + bool find_right_major = false; + + int left_major = get_major(nums, start, start + (end-start+1)/2 -1, find_left_major); + int right_major = get_major(nums, start + (end-start+1)/2, end, find_right_major); + int major = 0; + + if (find_left_major && is_major(nums, start, end, left_major)) { + find_major = true; + major = left_major; + } + + if (find_right_major && is_major(nums, start, end, right_major)) { + find_major = true; + major = right_major; + } + + return major; + } + + int majorityElement(vector& nums) { + return get_major(nums, 0, nums.size()-1); + } +}; \ No newline at end of file diff --git a/Week 02/id_063/C++/LeetCode_17_063.cpp b/Week 02/id_063/C++/LeetCode_17_063.cpp new file mode 100644 index 000000000..e22f39d35 --- /dev/null +++ b/Week 02/id_063/C++/LeetCode_17_063.cpp @@ -0,0 +1,55 @@ + +/* +输入数字从左到右每一个代表递归的一层,每一层递归顺序选择本层数字对应的字符中的一个,然后继续下一层 +递归,递归深度到数字总数时候保存结果并且进行回溯即可 +*/ + +#include +#include +using namespace std; + +class Solution { + static string char_map[]; + +public: + + void dfs(vector& result, char* path, string& digits, int cur_level, int max_level) { + if (cur_level == max_level) { + result.emplace_back(path, max_level); + return; + } + + string chars = char_map[digits[cur_level]-'0']; + for (int i = 0; i < chars.length(); i++) { + path[cur_level] = chars[i]; + dfs(result, path, digits, cur_level + 1, max_level); + } + } + + vector letterCombinations(string digits) { + char* path = new char[digits.length()]; + vector result; + + if (digits.length() == 0) { + return result; + } + + dfs(result, path, digits, 0, digits.length()); + delete[] path; + return result; + } +}; + + +string Solution::char_map[] = { + "", + "", + "abc", + "def", + "ghi", + "jkl", + "mno", + "pqrs", + "tuv", + "wxyz" +}; \ No newline at end of file diff --git a/Week 02/id_063/C++/LeetCode_1_063.cpp b/Week 02/id_063/C++/LeetCode_1_063.cpp new file mode 100644 index 000000000..5abac0535 --- /dev/null +++ b/Week 02/id_063/C++/LeetCode_1_063.cpp @@ -0,0 +1,29 @@ +/* +从左到右迭代数组中元素,每一个元素迭代时候判断已经迭代的元素中有没有匹配的 +判断是否匹配需要额外的哈希结构缓存已经迭代过的元素的数值和下标 + +向Hash中插入数值或者是触发了Rehash会带来一定开销,严格讲时间复杂度比O(n)高 + */ + + +#include +#include +using namespace std; + +class Solution { +public: + vector twoSum(vector& nums, int target) { + unordered_map val2idx(nums.size()); + + val2idx[nums[0]] = 0; + for (int i = 1; i < nums.size(); i++) { + if (val2idx.find(target - nums[i]) != val2idx.end()) { + return vector { val2idx[target - nums[i]], i }; + } + + val2idx[nums[i]] = i; + } + + return vector { 0, 0 }; + } +}; \ No newline at end of file diff --git a/Week 02/id_063/C++/LeetCode_242_063.cpp b/Week 02/id_063/C++/LeetCode_242_063.cpp new file mode 100644 index 000000000..83e501e1e --- /dev/null +++ b/Week 02/id_063/C++/LeetCode_242_063.cpp @@ -0,0 +1,47 @@ +#include + +/* +思路: +用Hash保存每一个字符出现的频次,遍历s中所有字符统计字符频次,然后遍历t中所有字符 +将hash中对应字符的频次进行递减,t中出现任何不在hash中的字符或者有任何一个字符频次 +减少到0以下,都说明t和s不匹配,可以立即判断失败,不必在最后去统计是不是所有字符的频次 +都减到0 +自己用数组实现Hash比起用库里面的hash表要快一些 + */ + +#include +#include +using namespace std; + + +class Solution { +public: + bool isAnagram(string s, string t) { + unordered_map char2cnt; + + if (s.length() != t.length()) { + return false; + } + + for (int i = 0; i < s.length(); i++) { + if (char2cnt.find(s[i]) == char2cnt.end()) { + char2cnt[s[i]] = 1; + } else { + char2cnt[s[i]]++; + } + } + + for (int i = 0; i < t.length(); i++) { + if (char2cnt.find(t[i]) == char2cnt.end()) { + return false; + } + + char2cnt[t[i]]--; + if (char2cnt[t[i]] < 0) { + return false; + } + } + + return true; + } +}; \ No newline at end of file diff --git a/Week 02/id_063/C++/LeetCode_429_063.cpp b/Week 02/id_063/C++/LeetCode_429_063.cpp new file mode 100644 index 000000000..228cf8f0e --- /dev/null +++ b/Week 02/id_063/C++/LeetCode_429_063.cpp @@ -0,0 +1,51 @@ +/* +思路 +进行DFS,各层节点被遍历到的时候将自己的数值加入到其层数对应的List中, +节点访问顺序并没有要求要按照其层次顺序进行,没必要进行BFS + */ + + +#include +using namespace std; + +// Definition for a Node. +/* +class Node { +public: + int val; + vector children; + + Node() {} + + Node(int _val, vector _children) { + val = _val; + children = _children; + } +}; + */ + +class Solution { +public: + + void dfs(vector>& result, Node* p_cur_node, int level) { + if (p_cur_node == nullptr) { + return; + } + + while (result.size() < level + 1) { + result.emplace_back(); + } + + result[level].push_back(p_cur_node->val); + for (Node* p_sub_node : p_cur_node->children) { + dfs(result, p_sub_node, level + 1); + } + } + + vector> levelOrder(Node* root) { + vector> result; + dfs(result, root, 0); + + return result; + } +}; \ No newline at end of file diff --git a/Week 02/id_063/C++/LeetCode_46_063.cpp b/Week 02/id_063/C++/LeetCode_46_063.cpp new file mode 100644 index 000000000..0066f37f7 --- /dev/null +++ b/Week 02/id_063/C++/LeetCode_46_063.cpp @@ -0,0 +1,40 @@ +/* +思路 +设序列数字个数为n +进行n层递归,每一层从剩余数字中从小到大选择一个数字,去掉选择的数字剩下的数字再进行下一层递归, +递归深度为n时候可以生成一种结果,所有路径遍历完,所有可能的结果也就得到了 + */ + +#include +using namespace std; + +class Solution { +public: + + void dfs(vector>& result, vector& choice, vector& path, vector& nums, int cur_level, int max_level) { + if (cur_level == max_level) { + result.push_back(path); + return; + } + + for (int i = 0; i < choice.size(); i++) { + if (choice[i] == 0) { + choice[i] = 1; + path[cur_level] = nums[i]; + + dfs(result, choice, path, nums, cur_level + 1, max_level); + + choice[i] = 0; + } + } + } + + vector> permute(vector& nums) { + vector> result; + vector choice(nums.size()); + vector path(nums.size()); + dfs(result, choice, path, nums, 0, nums.size()); + + return result; + } +}; \ No newline at end of file diff --git a/Week 02/id_063/C++/LeetCode_47_063.cpp b/Week 02/id_063/C++/LeetCode_47_063.cpp new file mode 100644 index 000000000..b900a4471 --- /dev/null +++ b/Week 02/id_063/C++/LeetCode_47_063.cpp @@ -0,0 +1,53 @@ +/* +思路 +跟普通的全排列没本质区别,增强的点在于递归过程中需要判断当前产生的新序列是不是已经产生过的, +如果判断到当前生成的序列已经出现过了,再继续递归下去只可能得到已经出现过的序列, +这种情况进行减枝回溯即可, 只需要保证每一层递归添加的元素数值不重复就可以保证不会产生重复序列 +先对序列进行排序,在递归时候就能比较快发现重复, 因为递归时候每一层都是顺序从左到右找新的元素 +如果从左到右迭代时候元素数值是有序的,那么只需要维护一个上一次迭代选择的数值,就可以快速知道当前待选择 +的元素是不是和前面已经选择过的重复了 + */ + +#include +#include +using namespace std; + +class Solution { +public: + + void dfs(vector>& result, vector& choice, vector& path, vector& nums, int cur_level, int max_level) { + if (cur_level == max_level) { + result.push_back(path); + return; + } + + bool first = true; + int last_val = 0; + for (int i = 0; i < choice.size(); i++) { + if (choice[i] == 0) { + if ( first || ((!first) && (last_val != nums[i])) ) { + choice[i] = 1; + path[cur_level] = nums[i]; + + dfs(result, choice, path, nums, cur_level + 1, max_level); + + choice[i] = 0; + } + + first = false; + last_val = nums[i]; + } + } + } + + vector> permuteUnique(vector& nums) { + vector> result; + vector choice(nums.size()); + vector path(nums.size()); + + sort(nums.begin(), nums.end()); + dfs(result, choice, path, nums, 0, nums.size()); + + return result; + } +}; \ No newline at end of file diff --git a/Week 02/id_063/C++/LeetCode_49_063.cpp b/Week 02/id_063/C++/LeetCode_49_063.cpp new file mode 100644 index 000000000..eac7ba7ac --- /dev/null +++ b/Week 02/id_063/C++/LeetCode_49_063.cpp @@ -0,0 +1,65 @@ +/* +思路 +把每一个字符串映射成一个统计字符频率和所有字符个数的结构,用该结构作为Map的key,Map的值 +为容纳相同的key的字符串的List,迭代每一个字符串,更新Map结构,最后把Map中所有的Value全 +放到结果的List中去即可 + */ + +#include +#include +#include +using namespace std; + +struct Node { + int char_cnt[26]; + int char_num; +}; + +bool operator < (const Node& node1, const Node& node2) { + if (node1.char_num < node2.char_num) { + return true; + } else if (node1.char_num > node2.char_num) { + return false; + } + + for (int i = 0; i < sizeof(node1.char_cnt)/sizeof(node1.char_cnt[0]); i++) { + if (node1.char_cnt[i] < node2.char_cnt[i]) { + return true; + } else if (node1.char_cnt[i] > node2.char_cnt[i]) { + return false; + } + } + + return false; +} + + +class Solution { +private: + void get_node(string& str, Node& node) { + memset((void*)node.char_cnt, 0, sizeof(node.char_cnt)); + for (int i = 0; i < str.length(); i++) { + node.char_cnt[str[i]-'a']++; + } + + node.char_num = (int)str.size(); + } + +public: + vector> groupAnagrams(vector& strs) { + vector> v; + Node key; + map> m; + + for (string& s : strs) { + get_node(s, key); + m[key].push_back(s); + } + + for (auto itr = m.begin(); itr != m.end(); itr++) { + v.push_back(itr->second); + } + + return v; + } +}; \ No newline at end of file diff --git a/Week 02/id_063/C++/LeetCode_51_063.cpp b/Week 02/id_063/C++/LeetCode_51_063.cpp new file mode 100644 index 000000000..94eea4aef --- /dev/null +++ b/Week 02/id_063/C++/LeetCode_51_063.cpp @@ -0,0 +1,62 @@ +#include +#include +#include +#include +using namespace std; + +class Solution { +public: + + void choice2slist(vector& choice, vector& v) { + char* arr = new char[choice.size()]; + for (int i = 0; i < choice.size(); i++) { + memset(arr, '.', choice.size() * sizeof(char)); + arr[choice[i]] = 'Q'; + v[i] = string(arr, choice.size()); + } + + + delete[] arr; + } + + void dfs(vector>& result, vector& choice, vector& print_info, int cur_level, int max_level) { + if (cur_level == max_level) { + choice2slist(choice, print_info); + result.push_back(print_info); + } + + int cur_j; + for (cur_j = 0; cur_j < max_level; cur_j++) { + + bool conflict = false; + for (int i = 0; i < cur_level; i++) { + if (choice[i] == cur_j) { + conflict = true; + break; + } + + if (abs(cur_level-i) == abs(cur_j - choice[i])) { + conflict = true; + break; + } + } + + if (conflict) { + continue; + } + + choice[cur_level] = cur_j; + dfs(result, choice, print_info, cur_level + 1, max_level); + } + + } + + vector> solveNQueens(int n) { + vector> v; + vector choice(n); + vector print_info(n); + dfs(v, choice, print_info, 0, n); + + return v; + } +}; \ No newline at end of file diff --git a/Week 02/id_063/C++/LeetCode_589_063.cpp b/Week 02/id_063/C++/LeetCode_589_063.cpp new file mode 100644 index 000000000..ce2272214 --- /dev/null +++ b/Week 02/id_063/C++/LeetCode_589_063.cpp @@ -0,0 +1,39 @@ +#include +using namespace std; + +/* +class Node { +public: + int val; + vector children; + + Node() {} + + Node(int _val, vector _children) { + val = _val; + children = _children; + } +}; +*/ + +class Solution { +public: + + void dfs(vector& result, Node* p_cur_node) { + if (p_cur_node == nullptr) { + return; + } + + result.push_back(p_cur_node->val); + for (Node* p_sub_node : p_cur_node->children) { + dfs(result, p_sub_node); + } + } + + vector preorder(Node* root) { + vector v; + dfs(v, root); + + return v; + } +}; \ No newline at end of file diff --git a/Week 02/id_063/C++/LeetCode_590_063.cpp b/Week 02/id_063/C++/LeetCode_590_063.cpp new file mode 100644 index 000000000..f684cdd4f --- /dev/null +++ b/Week 02/id_063/C++/LeetCode_590_063.cpp @@ -0,0 +1,40 @@ +#include +using namespace std; + +/* +class Node { +public: + int val; + vector children; + + Node() {} + + Node(int _val, vector _children) { + val = _val; + children = _children; + } +}; +*/ + +class Solution { +public: + + void dfs(vector& result, Node* p_cur_node) { + if (p_cur_node == nullptr) { + return; + } + + for (Node* p_sub_node : p_cur_node->children) { + dfs(result, p_sub_node); + } + + result.push_back(p_cur_node->val); + } + + vector postorder(Node* root) { + vector v; + dfs(v, root); + + return v; + } +}; \ No newline at end of file diff --git a/Week 02/id_063/C++/LeetCode_77_063.cpp b/Week 02/id_063/C++/LeetCode_77_063.cpp new file mode 100644 index 000000000..df7f4be01 --- /dev/null +++ b/Week 02/id_063/C++/LeetCode_77_063.cpp @@ -0,0 +1,46 @@ +/* +思路 +递归算法练习 +进行1层到n层的递归,每一层递归选择当前层对应的数字是否选择,当选择是的层数总和达到k时候生成一个结果并且进行回溯 +*/ + +#include +using namespace std; + +class Solution { +public: + + void dfs(vector>& result, vector& choice, int choice_num, int target_choice_num, int cur_level, int max_level) { + if (cur_level == max_level) { + return; + } + + choice[cur_level] = false; + dfs(result, choice, choice_num, target_choice_num, cur_level + 1, max_level); + + choice[cur_level] = true; + if (choice_num+1 == target_choice_num) { + vector v; + for (int i = 0; i <= cur_level; i++) { + if (choice[i]) { + v.push_back(i+1); + } + } + result.push_back(v); + } else { + dfs(result, choice, choice_num + 1, target_choice_num, cur_level + 1, max_level); + } + } + + vector> combine(int n, int k) { + vector choice(n); + vector> v; + + if (k == 0) { + return v; + } + + dfs(v, choice, 0, k, 0, n); + return v; + } +}; \ No newline at end of file diff --git a/Week 02/id_063/C++/LeetCode_94_063.cpp b/Week 02/id_063/C++/LeetCode_94_063.cpp new file mode 100644 index 000000000..35fe0ffd5 --- /dev/null +++ b/Week 02/id_063/C++/LeetCode_94_063.cpp @@ -0,0 +1,33 @@ + +#include +#include +using namespace std; + + + struct TreeNode { + int val; + TreeNode *left; + TreeNode *right; + TreeNode(int x) : val(x), left(NULL), right(NULL) {} + }; + +class Solution { +public: + + void dfs(vector& result, TreeNode* p_cur_node) { + if (p_cur_node == nullptr) { + return; + } + + dfs(result, p_cur_node->left); + result.push_back(p_cur_node->val); + dfs(result, p_cur_node->right); + } + + vector inorderTraversal(TreeNode* root) { + vector v; + dfs(v, root); + + return v; + } +}; \ No newline at end of file diff --git a/Week 02/id_063/C++/Leetcode_236_063.cpp b/Week 02/id_063/C++/Leetcode_236_063.cpp new file mode 100644 index 000000000..3030d6123 --- /dev/null +++ b/Week 02/id_063/C++/Leetcode_236_063.cpp @@ -0,0 +1,73 @@ +/* +思路 +dfs分别查找从根节点到两个节点的路径,然后查找两条路径中离根最远的且数值相等的节点 + */ + +#include +#include +#include +using namespace std; + +/* +struct TreeNode { + int val; + TreeNode *left; + TreeNode *right; + TreeNode(int x) : val(x), left(NULL), right(NULL) {} +}; + */ + +class Solution { +public: + bool dfs(TreeNode* p_cur_node, int target, vector& path) { + if (p_cur_node == nullptr) { + return false; + } + + path.push_back(p_cur_node); + + if (p_cur_node->val == target) { + return true; + } + + if (dfs(p_cur_node->left, target, path)) { + return true; + } + + if (dfs(p_cur_node->right, target, path)) { + return true; + } + + path.erase(path.end() - 1); + return false; + } + + + TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { + vector path1, path2; + vector* p_long_path = &path1; + vector* p_short_path = &path2; + + dfs(root, p->val, path1); + dfs(root, q->val, path2); + + if (p_long_path->size() < p_short_path->size()) { + vector* p_tmp = p_long_path; + p_long_path = p_short_path; + p_short_path = p_tmp; + } + + unordered_set set; + for (TreeNode* ptr : *p_long_path) { + set.insert(ptr); + } + + for (int i = p_short_path->size()-1; i >= 0; i--) { + if (set.find((*p_short_path)[i]) != set.end()) { + return (*p_short_path)[i]; + } + } + + return nullptr; + } +}; diff --git a/Week 02/id_063/Java/LeetCode_105_063.java b/Week 02/id_063/Java/LeetCode_105_063.java new file mode 100644 index 000000000..b558660bd --- /dev/null +++ b/Week 02/id_063/Java/LeetCode_105_063.java @@ -0,0 +1,68 @@ + +class TreeNode { + int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { val = x; } + } + + +/* +思路 + +进行递归构建, +找出左子树的前序和中序序列,构建左子树 +找出右子树的前序和中序序列,构建右子树 +左子树的根 右子树的根 和 当前的根节点共同组成新的树 + +查找子树的前序序列方法: +preorder[0] 一定是当前的根, 在inorder中找preorder[0]的下标x, x 就是左子树中元素的数量,那么preorder[0]后面跟着x个数值就是左子树的前序序列, +preorder再后面的就是右子树的前序序列 + +查找子树的中序序列的方法: +inorder序列中下标是x的元素就是根,x左边的就是左子树中序序列,x右边就是右子树的中序序列 + +*/ + +class Solution { + + private TreeNode buildTree(int[] pre, int preStart, int preEnd, int[] in, int inStart, int intEnd) { + if (preStart == preEnd) { + return new TreeNode(pre[preStart]); + } + + int i; + for (i = inStart; i <= intEnd; i++) { + if (in[i] == pre[preStart]) { + break; + } + } + + TreeNode leftRoot = null; + if (i > inStart) { + leftRoot = buildTree(pre, preStart+1, preStart + (i - inStart), + in, inStart, inStart + (i - inStart) - 1); + } + + TreeNode rightRoot = null; + if (i < intEnd) { + rightRoot = buildTree(pre, preStart + 1 + (i - inStart), preEnd, + in, i + 1, intEnd); + } + + TreeNode root = new TreeNode(pre[preStart]); + root.left = leftRoot; + root.right = rightRoot; + + return root; + } + + + public TreeNode buildTree(int[] preorder, int[] inorder) { + if (preorder.length == 0) { + return null; + } + + return buildTree(preorder, 0, preorder.length-1, inorder, 0, inorder.length-1); + } +} \ No newline at end of file diff --git a/Week 02/id_063/Java/LeetCode_144_063.java b/Week 02/id_063/Java/LeetCode_144_063.java new file mode 100644 index 000000000..81eda9c90 --- /dev/null +++ b/Week 02/id_063/Java/LeetCode_144_063.java @@ -0,0 +1,19 @@ +/* +思路 +简单前序遍历,没什么特殊算法 +*/ + +public class Solution { + private List dfs(List result, TreeNode curNode) { + if (curNode == null) { + return result; + } + + result.add(curNode.val); + return dfs(dfs(result, curNode.left), curNode.right); + } + + public List preorderTraversal(TreeNode root) { + return dfs(new LinkedList<>(), root); + } +} \ No newline at end of file diff --git a/Week 02/id_063/Java/LeetCode_169_063.java b/Week 02/id_063/Java/LeetCode_169_063.java new file mode 100644 index 000000000..597dcaed5 --- /dev/null +++ b/Week 02/id_063/Java/LeetCode_169_063.java @@ -0,0 +1,77 @@ +/* +思路 + +众数x是数量超过了总数一半的数字,那么如果把序列以任意比例切成两半,那么x至少是两部分子 +序列其中一个序列的众数,这个可以进行反证,如果x在两个子序列中数量都没有超过一半,两个子序列 +并在一起,x总数怎么可能超过总数一半呢? + +所以子序列中的众数是全序列众数的候选者,两个子序列至少一个序列有众数,否则不可能全序列有众数(题目说了输入数据保证全局有众数) +如果只有一个候选者,那最终众数必然就是这个候选者 +如果两个子序列都产生了候选者,需要统计两个候选者在总序列中的频数,必然有一个胜出者 + +基于以上的逻辑进行分治查找 +可以用ForkJoin线程池实现多线程的分治 +*/ + + +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.RecursiveTask; + +class ComputeTask extends RecursiveTask { + private int[] nums; + private int start; + private int end; + + // 判断是否是众数 + private boolean isMajorElement(int[] nums, int start, int end, int num) { + int cnt = 0; + for (int i = start; i <= end; i++) { + if (nums[i] == num) { + cnt++; + } + } + + return (cnt > (end - start + 1) / 2); + } + + + public ComputeTask(int[] nums, int start, int end) { + this.nums = nums; + this.start = start; + this.end = end; + } + + @Override + protected Integer compute() { + if (end == start) { + return nums[end]; + } + + ComputeTask tLeft = new ComputeTask(nums, start, start + ((end-start)/2)); + ComputeTask tRight = new ComputeTask(nums, start+(end-start)/2 + 1, end); + invokeAll(tLeft, tRight); + + Integer leftRet = tLeft.join(); + Integer rightRet = tRight.join(); + Integer major = null; + + if ((leftRet != null) && isMajorElement(nums, start, end, leftRet)) { + major = leftRet; + } + + if ((rightRet != null) && isMajorElement(nums, start, end, rightRet)) { + major = rightRet; + } + + return major; + } +} + + +class Solution { + public int majorityElement(int[] nums) { + RecursiveTask task = new ComputeTask(nums, 0, nums.length-1); + ForkJoinPool.commonPool().execute(task); + return task.join(); + } +} \ No newline at end of file diff --git a/Week 02/id_063/Java/LeetCode_1_063.java b/Week 02/id_063/Java/LeetCode_1_063.java new file mode 100644 index 000000000..c616e61f5 --- /dev/null +++ b/Week 02/id_063/Java/LeetCode_1_063.java @@ -0,0 +1,28 @@ +import java.util.HashMap; +import java.util.Map; + + +/* +从左到右迭代数组中元素,每一个元素迭代时候判断已经迭代的元素中有没有匹配的 +判断是否匹配需要额外的哈希结构缓存已经迭代过的元素的数值和下标 + +向Hash中插入数值或者是触发了Rehash会带来一定开销,严格讲时间复杂度比O(n)高 + */ + +class Solution { + public int[] twoSum(int[] nums, int target) { + Map val2idx = new HashMap<>(nums.length + 1, 1); + + val2idx.put(nums[0], 0); + + for (int i = 1; i < nums.length; i++) { + if (val2idx.containsKey(target - nums[i])) { + return new int[] { val2idx.get(target - nums[i]), i }; + } + + val2idx.put(nums[i], i); + } + + return new int[] {0, 0}; + } +} \ No newline at end of file diff --git a/Week 02/id_063/Java/LeetCode_236_063.java b/Week 02/id_063/Java/LeetCode_236_063.java new file mode 100644 index 000000000..2238e65fd --- /dev/null +++ b/Week 02/id_063/Java/LeetCode_236_063.java @@ -0,0 +1,68 @@ +/* +思路 + +dfs分别查找从根节点到两个节点的路径,然后查找两条路径中离根最远的且数值相等的节点 + */ + + +import java.util.*; + +class TreeNode { + int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { val = x; } + } + + +class Solution { + + boolean dfs(TreeNode curNode, int target, List path) { + if (curNode == null) { + return false; + } + + path.add(curNode.val); + if (curNode.val == target) { + return true; + } + + if (dfs(curNode.left, target, path)) { + return true; + } + + if (dfs(curNode.right, target, path)) { + return true; + } + + path.remove(path.size() - 1); + return false; + } + + + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + List path1 = new ArrayList(); + List path2 = new ArrayList(); + + dfs(root, p.val, path1); + dfs(root, q.val, path2); + + List tmp = null; + if (path1.size() > path2.size()) { + tmp = path1; + path1 = path2; + path2 = tmp; + } + + Set set = new HashSet<>(path2); + TreeNode retNode = new TreeNode(0); + for (int i = path1.size()-1; i >= 0; i--) { + if (set.contains(path1.get(i))) { + retNode.val = path1.get(i); + break; + } + } + + return retNode; + } +} \ No newline at end of file diff --git a/Week 02/id_063/Java/LeetCode_242_063.java b/Week 02/id_063/Java/LeetCode_242_063.java new file mode 100644 index 000000000..423a6a67b --- /dev/null +++ b/Week 02/id_063/Java/LeetCode_242_063.java @@ -0,0 +1,42 @@ +package problem242.Solution1; + +import java.util.HashMap; +import java.util.Map; + + +/* +思路: +用Hash保存每一个字符出现的频次,遍历s中所有字符统计字符频次,然后遍历t中所有字符 +将hash中对应字符的频次进行递减,t中出现任何不在hash中的字符或者有任何一个字符频次 +减少到0以下,都说明t和s不匹配,可以立即判断失败,不必在最后去统计是不是所有字符的频次 +都减到0 + +自己用数组实现Hash比起用库里面的hash表要快一些 + */ + +class Solution { + public boolean isAnagram(String s, String t) { + int[] cnt = new int[256]; + + + if (s.length() != t.length()) { + return false; + } + + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + cnt[(int)ch]++; + } + + for (int i = 0; i < t.length(); i++) { + char ch = t.charAt(i); + cnt[(int)ch]--; + + if (cnt[(int)ch] < 0) { + return false; + } + } + + return true; + } +} diff --git a/Week 02/id_063/Java/LeetCode_429_063.java b/Week 02/id_063/Java/LeetCode_429_063.java new file mode 100644 index 000000000..b0e6077a5 --- /dev/null +++ b/Week 02/id_063/Java/LeetCode_429_063.java @@ -0,0 +1,49 @@ +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +}; + +/* +思路 +进行DFS,各层节点被遍历到的时候将自己的数值加入到其层数对应的List中, +节点访问顺序并没有要求要按照其层次顺序进行,没必要进行BFS + */ + +class Solution { + private List> dfs(List> result, Node curNode, int level) { + if (curNode == null) { + return result; + } + + if (result.size() < level+1) { + while (result.size() < level+1) { + result.add(new LinkedList<>()); + } + } + + result.get(level).add(curNode.val); + if (curNode.children != null) { + for (Node subNode : curNode.children) { + dfs(result, subNode, level+1); + } + } + + return result; + } + + + public List> levelOrder(Node root) { + return dfs(new ArrayList>(1000), root, 0); + } +} \ No newline at end of file diff --git a/Week 02/id_063/Java/LeetCode_46_063.java b/Week 02/id_063/Java/LeetCode_46_063.java new file mode 100644 index 000000000..3089c4e7a --- /dev/null +++ b/Week 02/id_063/Java/LeetCode_46_063.java @@ -0,0 +1,52 @@ + +import java.util.ArrayList; +import java.util.List; + + + +/* +思路 + +设序列数字个数为n +进行n层递归,每一层从剩余数字中从小到大选择一个数字,去掉选择的数字剩下的数字再进行下一层递归, +递归深度为n时候可以生成一种结果,所有路径遍历完,所有可能的结果也就得到了 + + */ + +class Solution { + + private List> dfs(List> result, List path, int[] choice, int[] nums, int curLevel, int maxLevel) { + if (curLevel == maxLevel) { + result.add(new ArrayList(path)); + return result; + } + + for (int i = 0; i < choice.length; i++) { + if (choice[i] == 0) { + choice[i] = 1; + path.add(nums[i]); + dfs(result, path, choice, nums, curLevel+1, maxLevel); + + // 恢复现场 + choice[i] = 0; + path.remove(path.size() - 1); + } + } + + return result; + } + + // 计算排列数 + private int getPermuteNum(int n) { + int total = 1; + for (int i = 1; i <= n; i++) { + total *= i; + } + + return total; + } + + public List> permute(int[] nums) { + return dfs(new ArrayList>(getPermuteNum(nums.length)), new ArrayList(nums.length), new int[nums.length], nums, 0, nums.length); + } +} diff --git a/Week 02/id_063/Java/LeetCode_47_063.java b/Week 02/id_063/Java/LeetCode_47_063.java new file mode 100644 index 000000000..3e4853081 --- /dev/null +++ b/Week 02/id_063/Java/LeetCode_47_063.java @@ -0,0 +1,49 @@ +import java.util.*; + +/* +思路 + +跟普通的全排列没本质区别,增强的点在于递归过程中需要判断当前产生的新序列是不是已经产生过的, +如果判断到当前生成的序列已经出现过了,再继续递归下去只可能得到已经出现过的序列, +这种情况进行减枝回溯即可, 只需要保证每一层递归添加的元素数值不重复就可以保证不会产生重复序列 +先对序列进行排序,在递归时候就能比较快发现重复, 因为递归时候每一层都是顺序从左到右找新的元素 +如果从左到右迭代时候元素数值是有序的,那么只需要维护一个上一次迭代选择的数值,就可以快速知道当前待选择 +的元素是不是和前面已经选择过的重复了 + */ + + +class Solution { + private List> dfs(List> result, List path, int[] choice, int[] nums, int curLevel, int maxLevel) { + if (curLevel == maxLevel) { + result.add(new ArrayList(path)); + return result; + } + + Integer lastVal = null; + for (int i = 0; i < choice.length; i++) { + if (choice[i] == 0) { + if ((lastVal != null) && (lastVal == nums[i])) { + continue; + } + + choice[i] = 1; + path.add(nums[i]); + + lastVal = nums[i]; + dfs(result, path, choice, nums, curLevel+1, maxLevel); + + // 恢复现场 + choice[i] = 0; + path.remove(path.size() - 1); + } + } + + return result; + } + + public List> permuteUnique(int[] nums) { + Arrays.sort(nums); + return dfs(new ArrayList>(), new ArrayList(nums.length), new int[nums.length], nums, 0, nums.length); + } +} + diff --git a/Week 02/id_063/Java/LeetCode_49_063.java b/Week 02/id_063/Java/LeetCode_49_063.java new file mode 100644 index 000000000..271c1285a --- /dev/null +++ b/Week 02/id_063/Java/LeetCode_49_063.java @@ -0,0 +1,68 @@ +/* +思路 +把每一个字符串映射成一个统计字符频率和所有字符个数的结构,用该结构作为Map的key,Map的值 +为容纳相同的key的字符串的List,迭代每一个字符串,更新Map结构,最后把Map中所有的Value全 +放到结果的List中去即可 + */ + + +import java.util.*; + + +class Solution { + private class KeyNode implements Comparable { + int[] cnt; + int totalCharNum; + + public KeyNode(int[] cnt, int totalCharNum) { + this.cnt = cnt; + this.totalCharNum = totalCharNum; + } + + @Override + public int compareTo(KeyNode o) { + if (totalCharNum < o.totalCharNum) { + return -1; + } else if (totalCharNum > o.totalCharNum) { + return 1; + } + + for (int i = 0; i < cnt.length; i++) { + if (cnt[i] != o.cnt[i]) { + return cnt[i] - o.cnt[i]; + } + } + + return 0; + } + } + + private KeyNode string2KeyNode(String str) { + int[] arr = new int[26]; + + for (int i = 0; i < str.length(); i++) { + arr[str.charAt(i) - 'a']++; + } + + return new KeyNode(arr, str.length()); + } + + + public List> groupAnagrams(String[] strs) { + Map> map = new TreeMap<>(); + + for (String str : strs) { + KeyNode key = string2KeyNode(str); + if (!map.containsKey(key)) { + List list = new LinkedList<>(); + list.add(str); + map.put(key, list); + } else { + List list = map.get(key); + list.add(str); + } + } + + return new LinkedList<>(map.values()); + } +} diff --git a/Week 02/id_063/Java/LeetCode_51_063.java b/Week 02/id_063/Java/LeetCode_51_063.java new file mode 100644 index 000000000..1b0b696c8 --- /dev/null +++ b/Week 02/id_063/Java/LeetCode_51_063.java @@ -0,0 +1,63 @@ +/* +思路 + +递归求解,没什么可说的,注意横竖和斜线上是否有皇后,进行减枝回溯即可 + */ + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +public class Solution { + + private List choice2str_list(int[] choice) { + List retList = new ArrayList<>(choice.length); + String initString = ""; + for (int i = 0; i < choice.length; i++) initString = initString + "."; + + for (int queuePos : choice) { + StringBuilder s = new StringBuilder(initString); + s.setCharAt(queuePos, 'Q'); + retList.add(s.toString()); + } + + return retList; + } + + private List> dfs(List> result, int[] choice, int curLevel, int maxLevel) { + if (curLevel == maxLevel) { + result.add(choice2str_list(choice)); + return result; + } + + for (int cur_j = 0; cur_j < maxLevel; cur_j++) { + boolean conflict = false; + for (int i = 0; i < curLevel; i++) { + // 列冲突 + if (choice[i] == cur_j) { + conflict = true; + break; + } + + // 对角线冲突 + if (Math.abs(curLevel -i) == Math.abs(cur_j-choice[i])) { + conflict = true; + break; + } + } + + if (conflict) { + continue; + } + + choice[curLevel] = cur_j; + dfs(result, choice, curLevel + 1, maxLevel); + } + + return result; + } + + public List> solveNQueens(int n) { + return dfs(new LinkedList>(), new int[n], 0, n); + } +} diff --git a/Week 02/id_063/Java/LeetCode_589_063.java b/Week 02/id_063/Java/LeetCode_589_063.java new file mode 100644 index 000000000..bf177d266 --- /dev/null +++ b/Week 02/id_063/Java/LeetCode_589_063.java @@ -0,0 +1,42 @@ +import java.util.LinkedList; +import java.util.List; + +/* +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +}; +*/ + +/* +思路 +简单递归前序遍历 +*/ + +public class Solution { + private List dfs(List result, Node curNode) { + if (curNode == null) { + return result; + } + + result.add(curNode.val); + if (curNode.children != null) { + for (Node subNode : curNode.children) { + dfs(result, subNode); + } + } + + return result; + } + + public List preorder(Node root) { + return dfs(new LinkedList(), root); + } +} \ No newline at end of file diff --git a/Week 02/id_063/Java/LeetCode_590_063.java b/Week 02/id_063/Java/LeetCode_590_063.java new file mode 100644 index 000000000..094a624e3 --- /dev/null +++ b/Week 02/id_063/Java/LeetCode_590_063.java @@ -0,0 +1,42 @@ +import java.util.LinkedList; +import java.util.List; + +/* +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +}; +*/ + +/* +思路 +简单递归后序遍历 + */ + +class Solution { + private List dfs(List result, Node curNode) { + if (curNode == null) { + return result; + } + + if (curNode.children != null) { + for (Node subNode : curNode.children) { + dfs(result, subNode); + } + } + + result.add(curNode.val); + return result; + } + + public List postorder(Node root) { + return dfs(new LinkedList(), root); + } +} \ No newline at end of file diff --git a/Week 02/id_063/Java/LeetCode_77_063.java b/Week 02/id_063/Java/LeetCode_77_063.java new file mode 100644 index 000000000..7ad92b8bb --- /dev/null +++ b/Week 02/id_063/Java/LeetCode_77_063.java @@ -0,0 +1,60 @@ +/* +思路 + +递归算法练习 + +进行1层到n层的递归,每一层递归选择当前层对应的数字是否选择,当选择是的层数总和达到k时候生成一个结果并且进行回溯 + */ + + + +public class Solution { + + private List> dfs(List> result, List curChoice, int maxChoiceNum, int curLevel, int maxLevel) { + if (curChoice.size() == maxChoiceNum) { + result.add(new ArrayList(curChoice)); + return result; + } + + if (curLevel > maxLevel) { + return result; + } + + if ((maxLevel - curLevel + 1) + curChoice.size() < maxChoiceNum) { + // 剪枝操作,就算剩下所有的数字全部选择也凑不齐maxChoiceNum个数字,直接回溯 + return result; + } + + // 当前层数据不选择 + dfs(result, curChoice, maxChoiceNum, curLevel+1, maxLevel); + + // 当前层数据选择 + curChoice.add(curLevel); + dfs(result, curChoice, maxChoiceNum, curLevel+1, maxLevel); + curChoice.remove(curChoice.size() - 1); // 恢复现场 + + return result; + } + + // 计算组合数 + private int getComposeNum(int n, int k) { + if (k > n - k) { + k = n-k; + } + + int totalNum = 1; + for (int i = 0; i < k; i++) { + totalNum *= n - i; + } + + for (int i = 0; i < k; i++) { + totalNum /= k; + } + + return totalNum; + } + + public List> combine(int n, int k) { + return dfs(new ArrayList>(getComposeNum(n, k)), new ArrayList(k), k, 1, n); + } +} \ No newline at end of file diff --git a/Week 02/id_063/Java/LeetCode_94_063.java b/Week 02/id_063/Java/LeetCode_94_063.java new file mode 100644 index 000000000..aa0ad91ec --- /dev/null +++ b/Week 02/id_063/Java/LeetCode_94_063.java @@ -0,0 +1,20 @@ +/* +思路 +简单中序遍历,没什么特殊算法 +*/ + +public class Solution { + private List dfs(List result, TreeNode curNode) { + if (curNode == null) { + return result; + } + + dfs(result, curNode.left).add(curNode.val); + return dfs(result, curNode.right); + } + + + public List inorderTraversal(TreeNode root) { + return dfs(new LinkedList<>(), root); + } +} \ No newline at end of file diff --git a/Week 02/id_063/Java/Leetcode_17_063.java b/Week 02/id_063/Java/Leetcode_17_063.java new file mode 100644 index 000000000..420055202 --- /dev/null +++ b/Week 02/id_063/Java/Leetcode_17_063.java @@ -0,0 +1,45 @@ +import java.util.LinkedList; +import java.util.List; + + +/* +输入数字从左到右每一个代表递归的一层,每一层递归顺序选择本层数字对应的字符中的一个,然后继续下一层 +递归,递归深度到数字总数时候保存结果并且进行回溯即可 +*/ + +class Solution { + private static String[] num2str = new String[] { + "", + "", + "abc", + "def", + "ghi", + "jkl", + "mno", + "pqrs", + "tuv", + "wxyz" + }; + + List dfs(List result, String path, String digits, int curLevel, int maxLevel) { + if (curLevel == maxLevel) { + result.add(path); + return result; + } + + String chars = num2str[digits.charAt(curLevel)-'0']; + for (int i = 0; i < chars.length(); i++) { + dfs(result, path + chars.charAt(i), digits, curLevel+1, maxLevel); + } + + return result; + } + + public List letterCombinations(String digits) { + if (digits.equals("")) { + return new LinkedList(); + } + + return dfs(new LinkedList(), "", digits, 0, digits.length()); + } +} \ No newline at end of file diff --git a/Week 02/id_068/InorderTraverseTree.cpp b/Week 02/id_068/InorderTraverseTree.cpp new file mode 100644 index 000000000..930a3c81b --- /dev/null +++ b/Week 02/id_068/InorderTraverseTree.cpp @@ -0,0 +1,22 @@ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ +class Solution { +public: + vector inorderTraversal(TreeNode* root) { + vectorans; + if(root) inorderT(ans, root); + return ans; + } + void inorderT(vector&v, TreeNode* root) { + if(root->left) inorderT(v, root->left); + v.push_back(root->val); + if(root->right) inorderT(v, root->right); + } +}; diff --git a/Week 02/id_068/Parenthesis.cpp b/Week 02/id_068/Parenthesis.cpp new file mode 100644 index 000000000..c7e5bd358 --- /dev/null +++ b/Week 02/id_068/Parenthesis.cpp @@ -0,0 +1,17 @@ +class Solution { +public: + vector generateParenthesis(int n) { + vectorans; + addptr(ans, "", n, 0); + return ans; + } + void addptr(vector&v, string str, int n, int m) { + if (n == 0 && m == 0) { + v.push_back(str); + } + + if(n > 0) addptr(v, str+"(", n-1, m+1); + if(m > 0) addptr(v, str+")", n, m-1); + } + +}; diff --git a/Week 02/id_078/LeetCode_144_078.java b/Week 02/id_078/LeetCode_144_078.java new file mode 100644 index 000000000..89cf5be85 --- /dev/null +++ b/Week 02/id_078/LeetCode_144_078.java @@ -0,0 +1,51 @@ +//һ ǰ +// +// ʾ: +// +// : [1,null,2,3] +// 1 +// \ +// 2 +// / +// 3 +// +//: [1,2,3] +// +// +// : ݹ㷨ܼ򵥣ͨ㷨 +// Related Topics ջ + + + +//leetcode submit region begin(Prohibit modification and deletion) +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List preorderTraversal(TreeNode root) { + LinkedList stack = new LinkedList<>(); + LinkedList output = new LinkedList<>(); + if (root == null) { + return output; + } + stack.add(root); + while (!stack.isEmpty()) { + TreeNode node = stack.pollLast(); + output.add(node.val); + if (node.right != null) { + stack.add(node.right); + } + if (node.left != null) { + stack.add(node.left); + } + } + return output; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_078/LeetCode_49_078.java b/Week 02/id_078/LeetCode_49_078.java new file mode 100644 index 000000000..83439ec7c --- /dev/null +++ b/Week 02/id_078/LeetCode_49_078.java @@ -0,0 +1,44 @@ +//һַ飬ĸλһĸλָĸͬвַͬ +// +// ʾ: +// +// : ["eat", "tea", "tan", "ate", "nat", "bat"], +//: +//[ +// ["ate","eat","tea"], +// ["nat","tan"], +// ["bat"] +//] +// +// ˵ +// +// +// ΪСдĸ +// Ǵ˳ +// +// Related Topics ϣ ַ + + + +//leetcode submit region begin(Prohibit modification and deletion) +//˳ÿʣ͵ⷨơ +class Solution { + public List> groupAnagrams(String[] strs) { + HashMap> hash = new HashMap<>(); + for (int i = 0; i < strs.length; i++) { + char[] s_arr = strs[i].toCharArray(); + Arrays.sort(s_arr); + String key = String.valueOf(s_arr); + if (hash.containsKey(key)) { + hash.get(key).add(strs[i]); + } else { + List temp = new ArrayList(); + temp.add(strs[i]); + hash.put(key, temp); + } + + } + return new ArrayList>(hash.values()); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_078/LeetCode_589_078.java b/Week 02/id_078/LeetCode_589_078.java new file mode 100644 index 000000000..3f8dfa22c --- /dev/null +++ b/Week 02/id_078/LeetCode_589_078.java @@ -0,0 +1,47 @@ +//һ N ڵֵǰ +// +// 磬һ 3 : +// +// +// +// +// +// +// +// ǰ: [1,3,5,6,2,4] +// +// +// +// ˵: ݹ鷨ܼ򵥣ʹõɴ? Related Topics + + + +//leetcode submit region begin(Prohibit modification and deletion) +/* +// Definition for a Node. +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +}; +*/ +class Solution { + List list = new ArrayList<>(); + public List preorder(Node root) { + if(root == null){ + return list; + } + list.add(root.val); + for(Node n : root.children){ + preorder(n); + } + return list; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_078/LeetCode_590_078.java b/Week 02/id_078/LeetCode_590_078.java new file mode 100644 index 000000000..a2ae36c53 --- /dev/null +++ b/Week 02/id_078/LeetCode_590_078.java @@ -0,0 +1,55 @@ +//һ N ڵֵĺ +// +// 磬һ 3 : +// +// +// +// +// +// +// +// : [5,6,3,2,4,1]. +// +// +// +// ˵: ݹ鷨ܼ򵥣ʹõɴ? Related Topics + + + +//leetcode submit region begin(Prohibit modification and deletion) +/* +// Definition for a Node. +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +}; +*/ + +class Solution { + public List postorder(Node root) { + Stack s = new Stack<>(); + List result = new LinkedList<>(); + Node top; + int i,len; + if(root==null)return result; + s.push(root); + while(!s.isEmpty()){ + top = s.pop(); + len = top.children.size(); + result.add(0,top.val); + for(i=0;i inorderTraversal(TreeNode root) { + List < Integer > res = new ArrayList < > (); + helper(root, res); + return res; + } + + public void helper(TreeNode root, List res) { + + if (root != null) { + if (root.left != null) { + helper(root.left, res); + } + res.add(root.val); + if (root.right != null) { + helper(root.right, res); + } + } + } + +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_083/Leetcode1.java b/Week 02/id_083/Leetcode1.java new file mode 100644 index 000000000..c78377a25 --- /dev/null +++ b/Week 02/id_083/Leetcode1.java @@ -0,0 +1,37 @@ + +/* +1֮ +һ nums?һĿֵ target +ڸҳΪĿֵ +ǵ±ꡣ + + +ԼÿֻӦһ𰸡 +ǣ㲻ظͬԪء + + +ʾ: + + + nums = [2, 7, 11, 15], target = 9 + + +Ϊ nums[0] + nums[1] = 2 + 7 = 9 + +Է [0, 1] + + +*/ + +class Solution { + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(); + for(int i = 0; i< nums.length; i++) { + if(map.containsKey(target - nums[i])) { + return new int[] {map.get(target-nums[i]),i}; + } + map.put(nums[i], i); + } + throw new IllegalArgumentException("No two sum solution"); + } +} \ No newline at end of file diff --git a/Week 02/id_083/Leetcode242.java b/Week 02/id_083/Leetcode242.java new file mode 100644 index 000000000..22b9ac7a9 --- /dev/null +++ b/Week 02/id_083/Leetcode242.java @@ -0,0 +1,42 @@ +/* +242Чĸλ +ַ s t дһж t Ƿ s ĸλʡ + + +ʾ? +1: + +: s = "anagram", t = "nagaram" + +: true +ʾ +2: + +: s = "rat", t = "car" + +: false + + +*/ + + +class Solution { + public boolean isAnagram(String s, String t) { + Map map = new HashMap<>(); + for(int i =0;i> groupAnagrams(String[] strs) { + if (strs.length == 0) return new ArrayList(); + Map ans = new HashMap(); + for (String s : strs) { + char[] ca = s.toCharArray(); + Arrays.sort(ca); + String key = String.valueOf(ca); + if (!ans.containsKey(key)) ans.put(key, new ArrayList()); + ans.get(key).add(s); + } + return new ArrayList(ans.values()); + } +} diff --git a/Week 02/id_088/NOTE.md b/Week 02/id_088/NOTE.md index a6321d6e2..129e80782 100644 --- a/Week 02/id_088/NOTE.md +++ b/Week 02/id_088/NOTE.md @@ -1,4 +1,62 @@ -# NOTE +# 第二周总结 +第二周的学习让我意识到之前的学习方案是不对的。 +作业和总结是很好应付过去的东西, 每次只需要选择两道题,照着答案写好提交(碰到自己能够短时间,并且使用较优的解法几乎是不可能的)。总结一些简单的内容或者照着课堂的笔记记录一边,能够梳理成一个简洁有序的文档就算是一个漂亮的总结了。当然我也知道,这些要求只是一些软要求。其实我想说的是以现在学习计划并没有办法写出一篇像样子的总结。 +## Hash_Table + 这部分没有什么可说,只能用javascript重新实现一遍(肯定不是我自己实现的) + 哈希表 +> 一种根据关键字直接访问内存存储位置的数据解构, 通过哈希函数是数据元素存放位置和数据元素的关键字建立某种对应关系 +哈希函数三个特点 +``` + 1. 运算过程计量简单高效, 以提高he洗标的插入和检索效率 + 2. 具有较好的散列型,降低冲突几率 + 3. 具有较大压缩性,节省内存 +``` +几种方法实现哈希函数 + + 直接地址法 + 通过key的线性关系来定义的哈希函数 即: Hash_Fn(Key) = aKey + C + 优点:不会产生冲突 + 缺点:空间复杂度高 + + 除留余数法 + 就是key的ascii mode 一个常数 Hash_Fn(Key) = Key mode C + 关键在于 C 的选取 一般是最接近或等于哈希表本身长度的质数 + ```js + let divisor = 0 + const is_Print = (num) => { + for (let i = 0; i < num.length; i++) { + if (num % i === 0) { + return false + } + return true + } + } + while (hash_size > 2) { + if (is_Print(hash_size)) { + divisor = hash_size + break + } + hash_size-- + } + ``` + + 平方求和法 + 将key转化为 Unicode 之后 平方求和 + + 分段求和法 +哈希冲突的两个关键因素 + + 填装因子 + ```js + + ``` + +## 递归 + + 时间复杂度的计算 + + + 前序 + + + 中序 + + + 后续 + + 参考 + https://www.sohu.com/a/221334006_495695 diff --git a/Week 02/id_098/LeetCode_46_098.java b/Week 02/id_098/LeetCode_46_098.java new file mode 100644 index 000000000..7370aeb79 --- /dev/null +++ b/Week 02/id_098/LeetCode_46_098.java @@ -0,0 +1,35 @@ +// 不允许重复全排列 leetCode 46 +//思路:依次固定每个位置上的数字 不完全是回溯算法 目前最优解 DFS +class Solution { + + private Stack stack = new Stack(); + private List> res = new ArrayList>(); + + public List> permute(int[] nums) { + + arraySort(nums, nums.length, 0); + + return res; + } + + private void arraySort(int[] array, int length, int cur) { + + if(cur == length) { + res.add(new ArrayList(stack)); + return; + } + + for(int i=0;i> groupAnagrams(String[] strs) { + + List> res = new ArrayList>(); + + HashMap> map =new HashMap<>(); + + for (int i=0; i < strs.length; i++) { + char[] s = strs[i].toCharArray(); + Arrays.sort(s); + String key = String.valueOf(s); + + if (map.containsKey(key)) { + ArrayList tmpList = map.get(key); + tmpList.add(strs[i]); + map.put(key, tmpList); + } + else { + ArrayList newList = new ArrayList(); + newList.add(strs[i]); + map.put(key, newList); + } + + } + + for (List v : map.values()) { + res.add(v); + } + + return res; + } +} + +//相对最优解,代码精炼(思路一致) +class Solution { + public List> groupAnagrams(String[] strs) { + // 找到相同的字符串,主要是排序不一样 + HashMap> map = new HashMap<>(); + for (String str : strs) { + String key = sort(str); + if (!map.containsKey(key)) { + map.put(key, new ArrayList<>()); + } + map.get(key).add(str); + } + return new ArrayList<>(map.values()); + } + + String sort(String str) { + char[] c = str.toCharArray(); + Arrays.sort(c); + return String.valueOf(c); + } +} \ No newline at end of file diff --git a/Week 02/id_098/LeetCode_94_144_145_589_590_098.java b/Week 02/id_098/LeetCode_94_144_145_589_590_098.java new file mode 100644 index 000000000..c70219866 --- /dev/null +++ b/Week 02/id_098/LeetCode_94_144_145_589_590_098.java @@ -0,0 +1,161 @@ +//二叉树中序遍历(递归) LeetCode 94 +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List inorderTraversal(TreeNode root) { + List < Integer > res = new ArrayList < > (); + traversal(root, res); + return res; + } + + public void traversal(TreeNode root, List < Integer > res) { + if (root != null) { + if (root.left != null) { + traversal(root.left, res); + } + res.add(root.val); + if (root.right != null) { + traversal(root.right, res); + } + } + } + +} + +//二叉树前序遍历(递归) LeetCode 144 +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List preorderTraversal(TreeNode root) { + List < Integer > res = new ArrayList < > (); + traversal(root, res); + return res; + } + + public void traversal(TreeNode root, List < Integer > res) { + if (root != null) { + res.add(root.val); + if (root.left != null) { + traversal(root.left, res); + } + if (root.right != null) { + traversal(root.right, res); + } + } + } + +} + +//二叉树后序遍历(递归) LeetCode 145 +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List postorderTraversal(TreeNode root) { + List < Integer > res = new ArrayList < > (); + traversal(root, res); + return res; + } + + public void traversal(TreeNode root, List < Integer > res) { + if (root != null) { + if (root.left != null) { + traversal(root.left, res); + } + if (root.right != null) { + traversal(root.right, res); + } + res.add(root.val); + } + } +} + +//N叉树前序遍历(递归) LeetCode 589 +/* +// Definition for a Node. +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +}; +*/ +class Solution { + + public List preorder(Node root) { + List < Integer > res = new ArrayList < > (); + traversal(root, res); + return res; + } + + public void traversal(Node root, List < Integer > res) { + if (root != null) { + res.add(root.val); + + int s = root.children.size(); + for(int i = 0; i < s; i++) { + traversal(root.children.get(i), res); + } + } + } +} + +//N叉树后序遍历(递归) LeetCode 590 +/* +// Definition for a Node. +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +}; +*/ +class Solution { + + public List postorder(Node root) { + List < Integer > res = new ArrayList < > (); + traversal(root, res); + return res; + } + + public void traversal(Node root, List < Integer > res) { + if (root != null) { + + int s = root.children.size(); + for(int i = 0; i < s; i++) { + traversal(root.children.get(i), res); + } + res.add(root.val); + } + } +} diff --git a/Week 02/id_103/LeeCode_144_103.java b/Week 02/id_103/LeeCode_144_103.java new file mode 100644 index 000000000..683f75d2c --- /dev/null +++ b/Week 02/id_103/LeeCode_144_103.java @@ -0,0 +1,45 @@ +package com.homework.week2; +//给定一个二叉树,返回它的 前序 遍历。 +// +// 示例: +// +// 输入: [1,null,2,3] +// 1 +// \ +// 2 +// / +// 3 +// +//输出: [1,2,3] +// +// +// 进阶: 递归算法很简单,你可以通过迭代算法完成吗? +// Related Topics 栈 树 + + +import java.util.ArrayList; +import java.util.List; + +//class TreeNode { +// int val; +// TreeNode left; +// TreeNode right; +// TreeNode(int x) { val = x; } +//} + +public class LeeCode_144_103 { + public List preorderTraversal(TreeNode root) { + List result=new ArrayList<>(); + readPre(root,result); + return result; + } + + public void readPre(TreeNode root,List result) { + //条件返回语句 + if (root==null) { return;} + //执行逻辑 + result.add(root.val); + readPre(root.left,result); + readPre(root.right,result); + } +} diff --git a/Week 02/id_103/LeeCode_145_103.java b/Week 02/id_103/LeeCode_145_103.java new file mode 100644 index 000000000..4780be6f1 --- /dev/null +++ b/Week 02/id_103/LeeCode_145_103.java @@ -0,0 +1,43 @@ +package com.homework.week2; +//给定一个二叉树,返回它的 后序 遍历。 +// +// 示例: +// +// 输入: [1,null,2,3] +// 1 +// \ +// 2 +// / +// 3 +// +//输出: [3,2,1] +// +// 进阶: 递归算法很简单,你可以通过迭代算法完成吗? +// Related Topics 栈 树 + +//class TreeNode { +// int val; +// TreeNode left; +// TreeNode right; +// TreeNode(int x) { val = x; } +//} + +import java.util.ArrayList; +import java.util.List; + +public class LeeCode_145_103 { + public List postorderTraversal(TreeNode root) { + List result=new ArrayList<>(); + readLast(root,result); + return result; + } + + public void readLast(TreeNode root,List result) { + //条件返回 + if (root==null) {return;} + //逻辑处理 + readLast(root.left,result); + readLast(root.right,result); + result.add(root.val); + } +} diff --git a/Week 02/id_103/LeeCode_1_103.java b/Week 02/id_103/LeeCode_1_103.java new file mode 100644 index 000000000..3a6e2557e --- /dev/null +++ b/Week 02/id_103/LeeCode_1_103.java @@ -0,0 +1,31 @@ +package com.homework.week2; +//给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 +// +// 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 +// +// 示例: +// +// 给定 nums = [2, 7, 11, 15], target = 9 +// +//因为 nums[0] + nums[1] = 2 + 7 = 9 +//所以返回 [0, 1] +// +// Related Topics 数组 哈希表 + + +import java.util.HashMap; +import java.util.Map; + +public class LeeCode_1_103 { + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + int temp = target - nums[i]; + if (map.containsKey(temp)) { + return new int[]{map.get(temp), i}; + } + map.put(nums[i], i); + } + throw new IllegalArgumentException("no solution on this nums"); + } +} diff --git a/Week 02/id_103/LeeCode_242_103.java b/Week 02/id_103/LeeCode_242_103.java new file mode 100644 index 000000000..6b633ba77 --- /dev/null +++ b/Week 02/id_103/LeeCode_242_103.java @@ -0,0 +1,39 @@ +package com.homework.week2; +//给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 +// +// 示例 1: +// +// 输入: s = "anagram", t = "nagaram" +//输出: true +// +// +// 示例 2: +// +// 输入: s = "rat", t = "car" +//输出: false +// +// 说明: +//你可以假设字符串只包含小写字母。 +// +// 进阶: +//如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况? +// Related Topics 排序 哈希表 + +public class LeeCode_242_103 { + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + int[] counter = new int[26]; + for (int i = 0; i < s.length(); i++) { + counter[s.charAt(i) - 'a']++; + counter[t.charAt(i) - 'a']--; + } + for (int count : counter) { + if (count != 0) { + return false; + } + } + return true; + } +} diff --git a/Week 02/id_103/LeeCode_49_103.java b/Week 02/id_103/LeeCode_49_103.java new file mode 100644 index 000000000..6ae5065d7 --- /dev/null +++ b/Week 02/id_103/LeeCode_49_103.java @@ -0,0 +1,42 @@ +package com.homework.week2; + +//给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 +// +// 示例: +// +// 输入: ["eat", "tea", "tan", "ate", "nat", "bat"], +//输出: +//[ +// ["ate","eat","tea"], +// ["nat","tan"], +// ["bat"] +//] +// +// 说明: +// +// +// 所有输入均为小写字母。 +// 不考虑答案输出的顺序。 +// +// Related Topics 哈希表 字符串 + +import java.util.*; + +public class LeeCode_49_103 { + public List> groupAnagrams(String[] strs) { + Map> map = new HashMap<>(); + for (int i = 0; i < strs.length; i++) { + char[] chars = strs[i].toCharArray(); + Arrays.sort(chars); + String key = String.valueOf(chars); + if (map.containsKey(key)) { + map.get(key).add(strs[i]); + } else { + List array = new ArrayList<>(); + array.add(strs[i]); + map.put(key, array); + } + } + return new ArrayList>(map.values()); + } +} diff --git a/Week 02/id_103/LeeCode_589_103.java b/Week 02/id_103/LeeCode_589_103.java new file mode 100644 index 000000000..79cfe6d21 --- /dev/null +++ b/Week 02/id_103/LeeCode_589_103.java @@ -0,0 +1,57 @@ +package com.homework.week2; +//给定一个 N 叉树,返回其节点值的前序遍历。 +// +// 例如,给定一个 3叉树 : +// +// +// +// +// +// +// +// 返回其前序遍历: [1,3,5,6,2,4]。 +// +// +// +// 说明: 递归法很简单,你可以使用迭代法完成此题吗? Related Topics 树 + + +import java.util.ArrayList; +import java.util.List; + +//leetcode submit region begin(Prohibit modification and deletion) +/* +// Definition for a Node. +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +}; +*/ + +public class LeeCode_589_103 { + public List preorder(Node root) { + List result = new ArrayList<>(); + if (root == null) { + return result; + } + readPre(root, result); + return result; + } + + public void readPre(Node node, List result) { + if (node == null) { + return; + } + result.add(node.val); + for (Node n : node.children) { + readPre(n, result); + } + } +} diff --git a/Week 02/id_103/LeeCode_77_103.java b/Week 02/id_103/LeeCode_77_103.java new file mode 100644 index 000000000..ed9a52c4c --- /dev/null +++ b/Week 02/id_103/LeeCode_77_103.java @@ -0,0 +1,41 @@ +package com.homework.week2; +//给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 +// +// 示例: +// +// 输入: n = 4, k = 2 +//输出: +//[ +// [2,4], +// [3,4], +// [2,3], +// [1,2], +// [1,3], +// [1,4], +//] +// Related Topics 回溯算法 + +import java.util.ArrayList; +import java.util.List; + +public class LeeCode_77_103 { + public static List> combine(int n, int k) { + List> result = new ArrayList<>(); + List curr = new ArrayList<>(); + getArrs(result,curr,k,1,n); + return result; + } + + public static void getArrs(List> result, List curr, int k, int start,int n) { + if (curr.size() == k) { + result.add(new ArrayList(curr)); + return; + } + + for (int i = start; i <=n-(k-curr.size())+1; i++) { + curr.add(i); + getArrs(result,curr,k,i+1,n); + curr.remove(curr.size()-1); + } + } +} diff --git a/Week 02/id_103/LeeCode_94_103.java b/Week 02/id_103/LeeCode_94_103.java new file mode 100644 index 000000000..1d592d5d8 --- /dev/null +++ b/Week 02/id_103/LeeCode_94_103.java @@ -0,0 +1,47 @@ +package com.homework.week2; + +import java.util.ArrayList; +import java.util.List; + +//给定一个二叉树,返回它的中序 遍历。 +// +// 示例: +// +// 输入: [1,null,2,3] +// 1 +// \ +// 2 +// / +// 3 +// +//输出: [1,3,2] +// +// 进阶: 递归算法很简单,你可以通过迭代算法完成吗? +// Related Topics 栈 树 哈希表 + + +//class TreeNode { +// int val; +// TreeNode left; +// TreeNode right; +// TreeNode(int x) { val = x; } +//} + +public class LeeCode_94_103 { + public List inorderTraversal(TreeNode root) { + List list=new ArrayList<>(); + readMid(root,list); + return list; + } + + public void readMid(TreeNode root,List list){ + //条件语句 + if (root==null) { + return; + } + //执行逻辑 + readMid(root.left,list); + list.add(root.val); + readMid(root.right,list); + } +} diff --git a/Week 02/id_108/LeeCode_049_108.java b/Week 02/id_108/LeeCode_049_108.java new file mode 100644 index 000000000..3ab2249ad --- /dev/null +++ b/Week 02/id_108/LeeCode_049_108.java @@ -0,0 +1,51 @@ +package study; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; + +public class LeeCode_049_108 { + + public static List> groupAnagrams(String[] strs) { + if (strs == null || strs.length == 0) return new ArrayList>(); + + Map> map = new HashMap>(); + for (String s:strs){ + char[] ca = s.toCharArray(); + Arrays.sort(ca); //从小到大排序 + String keyStr = String.valueOf(ca); + if (!map.containsKey(keyStr)){ + map.put(keyStr,new ArrayList()); + } + map.get(keyStr).add(s); + } + return new ArrayList>(map.values()); + } + /*@Test + public static void main(String[] args) { + String[] strArra = {"eat", "tea", "tan", "ate", "nat", "bat"}; + List> groupStr = groupAnagrams(strArra); + + // 测试案例期望值 + List> expResult1 = new ArrayList<>(); + List list1 = new ArrayList<>(); + list1.add("ate"); + list1.add("eat"); + list1.add("tea"); + List list2 = new ArrayList<>(); + list2.add("nat"); + list2.add("tan"); + List list3 = new ArrayList<>(); + list3.add("bat"); + expResult1.add(list1); + expResult1.add(list2); + expResult1.add(list3); + // 判断期望值与实际值 + Assert.assertEquals(expResult1, groupStr); + }*/ +} diff --git a/Week 02/id_108/LeeCode_144_108.java b/Week 02/id_108/LeeCode_144_108.java new file mode 100644 index 000000000..2fb844710 --- /dev/null +++ b/Week 02/id_108/LeeCode_144_108.java @@ -0,0 +1,49 @@ +package study; + +import java.util.ArrayList; +import java.util.List; + + +//1.定义节点 +class Node { + int val; + Node left; + Node right; + + Node(int x) { + this.val = x; + } +} + +public class LeeCode_144_108 { + // 递归 + public static List preorderTraversal(Node root) { + ArrayList list = new ArrayList<>(); + // 递归终结者 + if (root == null) + return list; + // 递归实现-处理当前实现 + System.out.print(root.val + " "); + list.add(root.val); + // 下探下一层 + list.addAll(preorderTraversal(root.left)); + list.addAll(preorderTraversal(root.right)); + return list; + } + + //建立二叉树 + public static Node creatTree(int[] data, int i) { + if (i >= data.length || data[i] == -1) + return null; + Node temp = new Node(data[i]); + temp.left = creatTree(data, i * 2 + 1); + temp.right = creatTree(data, i * 2 + 2); + return temp; + } + + public static void main(String[] args) { + int[] array = { 1,4,2,3 }; + Node tree = creatTree(array, 0); + preorderTraversal(tree); + } +} diff --git a/Week 02/id_113/LeetCode144.py b/Week 02/id_113/LeetCode144.py new file mode 100644 index 000000000..3363894ce --- /dev/null +++ b/Week 02/id_113/LeetCode144.py @@ -0,0 +1,18 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + def preorderTraversal(self, root: TreeNode) -> List[int]: + traverse_path = [] + if root is None: + return [] + + traverse_path.append(root.val) + traverse_path.extend(self.preorderTraversal(root.left)) + traverse_path.extend(self.preorderTraversal(root.right)) + + return traverse_path diff --git a/Week 02/id_113/LeetCode24.py b/Week 02/id_113/LeetCode24.py new file mode 100644 index 000000000..52d09b781 --- /dev/null +++ b/Week 02/id_113/LeetCode24.py @@ -0,0 +1,31 @@ +class Solution: +# 排序暴力法 + def isAnagram(self, s: str, t: str) -> bool: + s_array = list(s) + t_array = list(t) + + if sorted(s_array) == sorted(t_array): + return True + else: + return False + +# 字典计数法 + def isAnagram(self, s: str, t: str) -> bool: + s_dict = {} + t_dict = {} + for i in s: + if s_dict.get(i,0) == 0: + s_dict[i] = 1 + else: + s_dict[i] += 1 +# + for j in t: + if t_dict.get(j,0) == 0: + t_dict[j] = 1 + else: + t_dict[j] += 1 + + if s_dict == t_dict: + return True + else: + return False diff --git a/Week 02/id_113/LeetCode49.py b/Week 02/id_113/LeetCode49.py new file mode 100644 index 000000000..b19e837c5 --- /dev/null +++ b/Week 02/id_113/LeetCode49.py @@ -0,0 +1,17 @@ +class Solution: + def groupAnagrams(self, strs): + word_dict = {} + for word in strs: + key = "".join(sorted(word)) + if word_dict.get(key, 0) == 0: + temp = [] + temp.append(word) + word_dict[key] = temp + else: + word_dict[key].append(word) + + group_list = [] + for key in word_dict: + group_list.append(word_dict[key]) + + return group_list diff --git a/Week 02/id_113/LeetCode51.py b/Week 02/id_113/LeetCode51.py new file mode 100644 index 000000000..fd08a09c1 --- /dev/null +++ b/Week 02/id_113/LeetCode51.py @@ -0,0 +1,37 @@ +class Solution: + def solveNQueens(self, n: int) -> List[List[str]]: + if n < 1: + return [] + + self.result = [] + self.cols = set() + self.pie = set() + self.na = set() + self.DFS(n, 0, []) + return self._generate_result(n) + + def DFS(self, n, row, cur_state): + if row >= n: + self.result.append(cur_state) + return + + for col in range(n): + if col in self.cols or col + row in self.pie or col - row in self.na: + continue + + self.cols.add(col) + self.pie.add(col + row) + self.na.add(col - row) + + self.DFS(n, row + 1, cur_state + [col]) + + self.cols.remove(col) + self.pie.remove(col + row) + self.na.remove(col - row) + + def _generate_result(self, n): + board = [] + for res in self.result: + for i in res: + board.append("." * i + "Q" + "." * (n - i - 1)) + return [board[i: i + n] for i in range(0, len(board), n)] \ No newline at end of file diff --git a/Week 02/id_113/LeetCode589.py b/Week 02/id_113/LeetCode589.py new file mode 100644 index 000000000..347487680 --- /dev/null +++ b/Week 02/id_113/LeetCode589.py @@ -0,0 +1,21 @@ +""" +# Definition for a Node. +class Node: + def __init__(self, val, children): + self.val = val + self.children = children +""" + + +class Solution: + def preorder(self, root: 'Node') -> List[int]: + traverse_path = [] + + if root is None: + return [] + + traverse_path.append(root.val) + for children in root.children: + traverse_path.extend(self.preorder(children)) + + return traverse_path \ No newline at end of file diff --git a/Week 02/id_113/LeetCode590.py b/Week 02/id_113/LeetCode590.py new file mode 100644 index 000000000..bf593a480 --- /dev/null +++ b/Week 02/id_113/LeetCode590.py @@ -0,0 +1,21 @@ +""" +# Definition for a Node. +class Node: + def __init__(self, val, children): + self.val = val + self.children = children +""" + + +class Solution: + def postorder(self, root: 'Node') -> List[int]: + traverse_path = [] + + if root is None: + return [] + + for children in root.children: + traverse_path.extend(self.postorder(children)) + traverse_path.append(root.val) + + return traverse_path \ No newline at end of file diff --git a/Week 02/id_113/LeetCode94.py b/Week 02/id_113/LeetCode94.py new file mode 100644 index 000000000..65b0400d9 --- /dev/null +++ b/Week 02/id_113/LeetCode94.py @@ -0,0 +1,18 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + def inorderTraversal(self, root: TreeNode) -> List[int]: + traverse_path = [] + if root is None: + return [] + + traverse_path += self.inorderTraversal(root.left) + traverse_path.append(root.val) + traverse_path += self.inorderTraversal(root.right) + + return traverse_path \ No newline at end of file diff --git a/Week 02/id_118/C++/LeetCode_17_118.cpp b/Week 02/id_118/C++/LeetCode_17_118.cpp new file mode 100644 index 000000000..10261651d --- /dev/null +++ b/Week 02/id_118/C++/LeetCode_17_118.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include + +using namespace std; + + +class Solution { +private: + + // mapping letter and number + const string letterMap[10] = { + " ", + "", + "abc", + "def", + "ghi", + "jkl", + "mno", + "pqrs", + "tuv", + "wxyz" + }; + + // res to store the result + vector res; + + // s is the current string from [0,...,index-1] + // at the current index level, just look for digits[index] => to get digits[0,...,index] + void findCombination(const string &digits, int index, const string &s) { + + // terminator + if (index == digits.size()) { + // here s is one solution + // save s to res + res.push_back(s); + return; + } + + // get the char of the digit + char c = digits[index]; + // make sure the char is between 0 and 9, also 1 doesn't have any char + assert(c >= '0' && c <= '9' && c != '1'); + // get letters based on the current number + string letters = letterMap[c - '0']; + for (int i = 0; i < letters.size(); ++i) { + // drill down + findCombination(digits, index + 1, s + letters[i]); + } + + return; + } + +public: + vector letterCombinations(string digits) { + // initialization + res.clear(); + + // edge case, empty string input => empty output, not empty string output + if (digits == "") { + return res; + } + findCombination(digits, 0, ""); + return res; + } +}; + +//leetcode submit region end(Prohibit modification and deletion) +int main() { + vector res = Solution().letterCombinations("4567"); + for (int i = 0; i < res.size(); ++i) { + cout << res[i] << endl; + } + return 0; +}; + diff --git a/Week 02/id_118/C++/LeetCode_46_118.cpp b/Week 02/id_118/C++/LeetCode_46_118.cpp new file mode 100644 index 000000000..b5c941521 --- /dev/null +++ b/Week 02/id_118/C++/LeetCode_46_118.cpp @@ -0,0 +1,76 @@ +// +// Created by Isaac Zhou on 2019-10-26. +// + +#include +#include +#include +#include + +using namespace std; + +class Solution { + +private: + // create a vector of vectors to store result + vector > res; + // create a helper vector to see if number is already used in permutation + vector used; + + // Recursion method + void genPerm(const &nums, int index, vector &p) { + // terminator + if (index == nums.size()) { + res.push_back(p); + return; + } + for (int i = 0; i < nums.size(); ++i) { + if (!used[i]) { + // process the current logic + // add nums[i] to p only if nums[i] is not used yet + p.push_back(nums[i]); + // update used[i] status + used[i] = true; + + // drill down + genPerm(nums, index + 1, p); + + // reverse, because we changed the p and used status + // reset them + p.pop_back(); + used[i] = false; + } + } + } + +public: + vector > permute(vector &nums) { + // initialize res with empty + res.clear(); + // edge case + if (nums.size() == 0) { + return res; + } + // create a vector p to store solution for each recursion + vector p; + // initialize the helper vector used with all false + used = vector(nums.size(), false); + // start recursion + genPerm(nums, 0, p); + // return result + return res; + } +}; + +// test function +int main() { + vector nums = vector < int > {1, 2, 3, 4, 7}; + vector > res = Solution().permute(nums); + for (int i = 0; i < res.size(); ++i) { + for (int j = 0; j < res[i].size(); ++j) { + cout << res[i][j] << " "; + } + cout << endl; + } + return 0; +} \ No newline at end of file diff --git a/Week 02/id_118/C++/LeetCode_51_118.cpp b/Week 02/id_118/C++/LeetCode_51_118.cpp new file mode 100644 index 000000000..5eb6e2743 --- /dev/null +++ b/Week 02/id_118/C++/LeetCode_51_118.cpp @@ -0,0 +1,114 @@ +// +// Created by Isaac Zhou on 2019-10-27. +// + +#include +#include +#include + +using namespace std; + +class Solution { +private: + // res stores the result + vector> res; + + // create 3 vectors to check conflicts on all 3 directions + // check conflict on col + vector col; + // check conflict on diagonal 1 + vector dia1; + // check conflict on diagonal 2 + vector dia2; + + // curr_level, current recursion level, a.k.a current row index + // n, number of Queens + // for curr_index, the + // row stores the Queen's position e.g. row[x]=y means on row x, + // Q is placed at col y + void genQueen(int curr_level, int n, vector &row) { + // step 1: Terminator + if (curr_level == n) { + // genBoard will print out based on the shape of a chess board + res.push_back(genBoard(n, row)); + return; + } + + // step 2: Process the current level logic + for (int i = 0; i < n; ++i) { + // try place curr_level row's Queen on col i + // if col[i] is false, then there's no conflict on horizontally + // for dia1, (curr_level, i) check curr_level + i, then there's no conflict on dia1 + // for dia2, (curr_level, i) check curr_level - i + (n - 1), then there's no conflict on dia2 + // for dia2, the reason use curr_level-i + (n-1) is to avoid negative index for vector + if (!col[i] && !dia1[curr_level + i] && !dia2[curr_level - i + n - 1]) { + // if there's no conflict, row[i] can place a queen + row.push_back(i); + col[i] = true; + dia1[curr_level + i] = true; + dia2[curr_level - i + n - 1] = true; + // step 3: drill down + genQueen(curr_level + 1, n, row); + + // step 4: reverse, clean and reset all the variables used on this level of recursion + // so that they can be used for next level recursion + row.pop_back(); + col[i] = false; + dia1[curr_level + i] = false; + dia2[curr_level - i + n - 1] = false; + } + } + // return from the recursion + return; + } + + // genBoard to print out based on the board shape + static vector genBoard(int n, vector &row) { + assert(row.size() == n); + vector res_board(n, string(n, '.')); + for (int i = 0; i < n; ++i) { + res_board[i][row[i]] = 'Q'; + } + return res_board; + } + +public: + vector> solveNQueens(int n) { + // initiate res + res.clear(); + // edge case if n == 1, return {{"Q"}} + if (n == 1) { + return vector>{{"Q"}}; + } + // edge cases, if n < 4 and n != 1, there's no solution + if (n < 4 && n != 1) { + return res; + } + // row to store Queen's position for the current level + vector row; + + // initialize the col conflict vector + // initially there's no Queen, so no conflict anywhere + col = vector(n, false); + // there are 2n - 1 diagonals for each direction + dia1 = vector(2 * n - 1, false); + dia2 = vector(2 * n - 1, false); + + // call the Recursion + genQueen(0, n, row); + return res; + } +}; + +int main() { + int n = 4; + vector> res = Solution().solveNQueens(n); + for (int i = 0; i < res.size(); ++i) { + cout << "Solution : " << i + 1 << endl; + for (int j = 0; j < n; ++j) { + cout << res[i][j] << endl; + } + cout << "------------------" << endl; + } + return 0; +} \ No newline at end of file diff --git a/Week 02/id_118/C++/LeetCode_77_118.cpp b/Week 02/id_118/C++/LeetCode_77_118.cpp new file mode 100644 index 000000000..fa5ea519a --- /dev/null +++ b/Week 02/id_118/C++/LeetCode_77_118.cpp @@ -0,0 +1,112 @@ +// +// Created by Isaac Zhou on 2019-10-26. +// + +#include +#include + +using namespace std; + + +class Solution { + +private: + vector > res; + + // To solve C(n,k) + // c stores the current solution + // start to begin searching new element + void genComb(int n, int k, int start, vector &c) { + // terminator + if (c.size() == k) { + res.push_back(c); + return; + } + // process the current level + for (int i = start; i <= n; ++i) { + c.push_back(i); + // drill down + genComb(n, k, i + 1, c); + // reverse and reset for the next recursion + c.pop_back(); + } + return; + } + +public: + vector > combine(int n, int k) { + res.clear(); + //edge cases: meaningless cases + if (n <= 0 || k <= 0 || k > n) { + return res; + } + vector c; + genComb(n, k, 1, c); + return res; + } +}; + + +class Solution2 { +/* + * This solution is a more optimized one, it removed the unnecessary branches + * */ +private: + vector > res; + + // To solve C(n,k) + // c stores the current solution + // start to begin searching new element + void genComb(int n, int k, int start, vector &c) { + // terminator + if (c.size() == k) { + res.push_back(c); + return; + } + // process the current level + // here's an optimized tweak, no need to go to all the way to n + // for the current level, there are only k - c.size() positions to be filled + // hence the stop point is n - (k-c.size()) + 1 + for (int i = start; i <= n - (k-c.size()) + 1; ++i) { + c.push_back(i); + // drill down + genComb(n, k, i + 1, c); + // reverse and reset for the next recursion + c.pop_back(); + } + return; + } + +public: + vector > combine(int n, int k) { + res.clear(); + //edge cases: meaningless cases + if (n <= 0 || k <= 0 || k > n) { + return res; + } + vector c; + genComb(n, k, 1, c); + return res; + } +}; + +int main() { + vector > res = Solution().combine(7, 4); + for (int i = 0; i < res.size(); ++i) { + cout << "Solution 1" <> res = Solution2().combine(7, 4); + for (int i = 0; i < res.size(); ++i) { + cout << "Solution 2" < optimal (time & space) +- Code +- Test + + +## Consolidate Good Solutions + +- Consolidate all the good solutions for each problem +- Review before interviews + + +# 2. Hash Table, Map and Set + + +## Hash Table + + +### Gist + +- Hash tables store key: value pairs +- Look up the element with time complexity of O(1) +- Map the key value into a position (index) in the table to speed up the lookup process + - this mapping function is called a hashing fuction + - this table storing records is called a hash table + + +### Real world implementation + +- Yello pages +- User lists +- LRU Cache +- Redis + + +### Hash Function + +- val => Hash Function => index +- Hash Colision: some values will generate the same index value after mapped by the hash function +- Solution in Java: use a Linked List, adding another dimension. The downside of this solution is as the linked list grows, it will increase the time complexity, approaching O(n) + + +### Big O + +1. Time + + - average: O(1) for lookup, insertion, deletion + - worst: O(n) => Hash Table is degraded to a Linked List + +2. Space + + - O(n) + + +### Python Implementation + +1. Dictionary + +2. Set + + +### LeetCode 242 Valid Anagram + +1. Thought Process + + - To determin if two strings s, t are anagram, they need to be: + - have the same unique letters + - the counts of each unique letters are the same + - use dictionary with key is the letter, value is the count + +2. Code 1 + + ```python + class Solution: + def isAnagram(self, s:str, t: str) -> bool: + s_d, t_d = {}, {} + + s_d = self._strTodict(s) + t_d = self._strTodict(t) + return s_d == t_d + + # def _compareDict(self, s_d:dict, t_d:dict) -> bool: + # for k in s_d.keys(): + # if k not in t_d: + # return False + # else: + # if s_d[k] != t_d[k]: + # return False + # return True + + def _strTodict(self, s:str) -> dict: + s_d = {} + for l in s: + if l in s_d: + s_d[l] += 1 + else: + s_d[l] = 1 + return s_d + + def test(): + s = "anagram" + t = "nagaram" + + sol = Solution() + print(sol.isAnagram(s, t)) + + def test2(): + s = "rat" + t = "car" + sol = Solution() + print(sol.isAnagram(s, t)) + + if __name__ == "__main__": + test() + test2() + + True + False + ``` + + +## Review HashMap in Java + + +### constructor + +1. constructor + + ```java + public HashMap() { + this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted + } + ``` + +2. default load factor + + ```java + /** + * The load factor used when none specified in constructor. + */ + static final float DEFAULT_LOAD_FACTOR = 0.75f; + ``` + +3. put + + ```java + public V put(K key, V value) { + return putVal(hash(key), key, value, false, true); + } + ``` + + 1. putVal + + - Initially, the table is empty, table length is 0, invoke the resize function + + ```java + final V putVal(int hash, K key, V value, boolean onlyIfAbsent, + boolean evict) { + Node[] tab; Node p; int n, i; + if ((tab = table) == null || (n = tab.length) == 0) + n = (tab = resize()).length; + ``` + + - resize will copy the old table and old table threshold + + ```java + final Node[] resize() { + Node[] oldTab = table; + int oldCap = (oldTab == null) ? 0 : oldTab.length; + int oldThr = threshold; + ``` + + - Assign new Cap and new Threshold + + ```java + else { // zero initial threshold signifies using defaults + newCap = DEFAULT_INITIAL_CAPACITY; + newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); + } + ``` + + - What's the value of newCap? + + - move 1 to the left by 4 digits => 16 + + ```java + static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 + ``` + + - What's the value of new Threshold? + + `DEFAULT_LOAD_FACTOR` \* `DEFAULT_INITIAL_CAPACITY` = 16 \* 0.75 = 12 + + - Why n is the power of 2 + + ```java + if ((p = tab[i = (n - 1) & hash]) == null) + ``` + + It will guarantee the `&` operation here, if it's not the power of 2, it will generate meaningless result + + - Create a new table, which is an array of Nodes + + ```java + Node[] newTab = (Node[])new Node[newCap]; + ``` + + - Node should be a node of a linked list + + ```java + static class Node implements Map.Entry { + final int hash; + final K key; + V value; + Node next; + ``` + + +# 3. Tree, Graph, Binary Tree, BST + + +## 2-D Data Structure + +- The biggest issue with linear data structure is its lookup performance. Time complexity is usually O(n) +- The key to improve the speed is to create an additional dimension +- The most common 2-D data structures are Tree and Graph + + +## Tree + + +### Gist + +- When a Linked List has multiple next pointers => it becomes a Tree +- Representation + ![img](https://www.tutorialspoint.com/data_structures_algorithms/images/binary_tree.jpg) +- Key elements + - Root + - Sub-tree + - Parent + - Child: left, right (for Binary Tree) + - Leaf: node without child + - Siblings + + +### Binary Tree + +- The most common trees are Binary Trees +- A binary tree only has 2 children: left and right + + +### Code + +1. Python + + ```python + class TreeNode: + def __init__(self, val): + self.val = val + self.left, self.right = None, None + ``` + + +### Traversal + +- One of the most important Tree operations is traversal + + +## Graph + + +### Gist + +- The biggest difference between a Tree and a Graph is the Tree doesn't circle backwards + - A Linked List is a special type of Tree + - Tree is a Graph without loops + + +# 3. Generic Recursion + + +## Template + + +### Python Code + +```python +def recursion(level, p1, p2, ...): + # recursion terminator: must have to avoid stack overflow + if level > MAX_LEVEL: + process_result + return + + # process the logic in the current level + process(level, data...) + + # drill down + self.recursion(level+1, p1, p2,...) + + # reverse the current level if needed +``` + + +### Key takeaway + +- Don't perform recursions mannually +- Find the most recent, repeatable sub-problems (because all of our codes can be converted only to: if…else, loop and recursion) +- Math induction and synthesis + - prove the base case, e.g. n = 1, n = 2 + - prove n + 1 given n +- MECE: Mutually exclusive; completely exhaustive + + +## Generate Parentheses + + +### Step 1 + +- generate all the permutation of n pairs +- don't think about "valid" at this point +- terminator + + +### Code Generate Parentheses without validity check + +```python +class Solution: + def generateParenthesis(self, n): + # terminator level is 0 + # total number of string is 2*n, which is the max level + # initial state is an empty string "" + return self._generate(0, 2*n, "") + + def _generate(self, level, max_level, curr_state): + # terminator + if level >= max_level: + print(curr_state) + return + + # process current level + s1 = curr_state + "(" + s2 = curr_state + ")" + + # drill down + self._generate(level + 1, max_level, s1) + self._generate(level + 1, max_level, s2) + + # reverse state + # no need as s1, s2 are all local variables + # didn't use global var + +def test(): + sol = Solution() + sol.generateParenthesis(2) +``` + + +​ + +```python +if __name__ == "__main__": + test() + +(((( +((() +(()( +(()) +()(( +()() +())( +())) +)((( +)(() +)()( +)()) +))(( +))() +)))( +)))) +``` + + +### Check validity + +- left can be added anytime as long as its number is <= n + +- right: + + - cannot start + - must follow a left, and the number of left must be greater than the number of right + + ```python + class Solution: + def __init__(self): + self.res = [] + + def generate_parentheses(self, n): + self._gen_par(0, 0, n, "") + return self.res + + def _gen_par(self, left, right, n, curr_state): + # terminator + if left == n and right == n: + self.res.append(curr_state) + return + # process the current level + if left < n: + self._gen_par(left+1, right, n, curr_state+"(") + + if right < left: + self._gen_par(left, right + 1, n, curr_state+")") + ``` + + ```python + def test(): + sol = Solution() + print(sol.generate_parentheses(3)) + + + if __name__ == "__main__": + test() + + ['((()))', '(()())', '(())()', '()(())', '()()()'] + ``` + + +# 4. Divide & Conquer + +- D&C is a type of recursion +- It breaks down the target problem into several sub-problems + - solve each sub-problem + - merge solutions + + +## Generic Recursion + +- Terminator +- Process logic of the current level +- Drill down +- Reverse + + +### Recursion template code + +```python +def gen_recursion(level, max_level, p1, p2,...): + # 1. Terminator + if level >= max_level: + process_result + return + # 2. Process the current logic + # do something + process(level, data...) + # 3. Drill down + self.gen_recursion(level+1, max_level, p1, p2,...) + + # 4. Reverse +``` + + +## Generic D&C + +- Compare to generic recursion, generic D&C need merge the sub-solutions into a general solution + + +### D&C template code + +```python +def divide_conquer(self, problem, p1, p2, ...): + # terminator + if problem is None: + # there's no subproblems to solve + do_something + return + + # prepare data + data = pre_data(problem) + # split target problme into subproblems + subproblems = split_problem(problem, data) + + # conquer sub problems + sol1 = self.divide_conquer(subproblems[0], p1,...) + sol2 = self.divide_conquer(subproblems[1], p1,...) + sol3 = self.divide_conquer(subproblems[2], p1,...) + + # merge the sub-solutions into the final solution + sol = merge_sol(sol1, sol2, sol3, ...) + + # revert the current level states +``` + + +# 5. Backtracking +- A type of recursion +- Trial and error on each level diff --git a/Week 02/id_118/Python/LeetCode_114_118.py b/Week 02/id_118/Python/LeetCode_114_118.py new file mode 100644 index 000000000..1f93de73d --- /dev/null +++ b/Week 02/id_118/Python/LeetCode_114_118.py @@ -0,0 +1,41 @@ +from typing import List + + +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution1: + """ + This is the recursive solution + """ + + def pre_order_traverse(self, root: TreeNode) -> List[int]: + res = [] + self._pre_order(root, res) + return res + + def _pre_order(self, root, res): + if root: + res.append(root.val) + self._pre_order(root.left, res) + self._pre_order(root.right, res) + + +class Solution2: + """ + This is the iterative solution + """ + + def pre_order_traverse(self, root: TreeNode) -> List[int]: + stack, res = [root], [] + while stack: + node = stack.pop() + if node: + res.append(node.val) + stack.append(node.right) + stack.append(node.left) + return res diff --git a/Week 02/id_118/Python/LeetCode_169_118.py b/Week 02/id_118/Python/LeetCode_169_118.py new file mode 100644 index 000000000..6c3a396af --- /dev/null +++ b/Week 02/id_118/Python/LeetCode_169_118.py @@ -0,0 +1,44 @@ +from typing import List + + +class Solution1: + """ + Solution 1 use hashmap + Time Complexity: O(n) + Space Complexity: O(n) + """ + + def majorityElement(self, nums: List[int]) -> int: + count = {} + for num in nums: + if num in count: + count[num] += 1 + else: + count[num] = 1 + + for num in count: + if count[num] > (len(nums) / 2): + return num + + +class Solution2: + """ + Solution 2 use hashmap but with only 1 iteration + """ + + def majorityElement(self, nums: List[int]) -> int: + count = {} + for num in nums: + if num in count: + count[num] += 1 + if count[num] > (len(nums) // 2): + return num + else: + count[num] = 1 + + +if __name__ == '__main__': + sol1 = Solution1() + sol2 = Solution2() + print(sol1.majorityElement([2, 2, 1, 1, 1, 2, 2])) + print(sol2.majorityElement([1, 1, 1, 2, 2])) diff --git a/Week 02/id_118/Python/LeetCode_17_118.py b/Week 02/id_118/Python/LeetCode_17_118.py new file mode 100644 index 000000000..170ea0ba9 --- /dev/null +++ b/Week 02/id_118/Python/LeetCode_17_118.py @@ -0,0 +1,82 @@ +from typing import List + + +class Solution: + """ + This solution strictly follows the recursion template + Time complexity is O(3^n), exponential + """ + + def __init__(self): + """ + initiate with a result to store + letter_map dict to store the mapping between number and letters + """ + self.res = [] + self.letter_map = { + 0: " ", + 1: "", + 2: "abc", + 3: "def", + 4: "ghi", + 5: "jkl", + 6: "mno", + 7: "pqrs", + 8: "tuv", + 9: "wxyz" + } + + def letterCombinations(self, digits: str) -> List[str]: + if len(digits) == 0: + return self.res + self._letter_combo(0, len(digits), digits, "") + return self.res + + def _letter_combo(self, curr_level, max_level, digits, curr_str): + """ + + :param curr_level: current digit + :param max_level: length of the digits + :param digits: digits + :param curr_str: current letter string + :return: + """ + # for each curr_level, print out the curr_str + print("current level is {a} : current str is {b}".format(a=curr_level, + b=curr_str)) + + # terminator + if curr_level >= max_level: + self.res.append(curr_str) + return + + # process the current logic + # get the current digit + digit = digits[curr_level] + # make sure the digit is valid + assert (0 <= int(digit) <= 9) and (int(digit) != 1) + + # get the letters of the digit + letters = self.letter_map[int(digit)] + + # drill down for each letter + for letter in letters: + # print out the details for each recursion + print("digits[{a}] = {b}, letter {c} is used".format(a=curr_level, + b=digit, + c=letter)) + self._letter_combo(curr_level + 1, max_level, digits, + curr_str + letter) + + # after finishing recursion at each level, revert to the last level + return + + +def test(): + sol = Solution() + res = sol.letterCombinations("5678") + print(res) + + +if __name__ == '__main__': + test() diff --git a/Week 02/id_118/Python/LeetCode_1_118.py b/Week 02/id_118/Python/LeetCode_1_118.py new file mode 100644 index 000000000..79e2f1a4a --- /dev/null +++ b/Week 02/id_118/Python/LeetCode_1_118.py @@ -0,0 +1,73 @@ +from typing import List + + +class Solution1: + """ + Brute force + """ + + def twoSum(self, nums: List[int], target: int) -> List[int]: + for i in range(len(nums) - 1): + for j in range(i + 1, len(nums)): + if nums[i] + nums[j] == target: + return [i, j] + + +class Solution2: + """ + Hashmap with two loops + """ + + def twoSum(self, nums: List[int], target: int) -> List[int]: + # store all numbers in a hashtable + temp = {} + for num in nums: + temp[num] = target - num + # for each num in temp, if they exist in nums + # and they are not used twice + for i in range(len(nums)): + if temp[nums[i]] in nums and i != nums.index(temp[nums[i]]): + return [i, nums.index(temp[nums[i]])] + + +class Solution3: + """ + Hashmap with only one loop + """ + + def twoSum(self, nums: List[int], target: int) -> List[int]: + # store all numbers in a hashtable + temp = {} + # use enumerate function to iterate both index and num simultaneously + for idx, num in enumerate(nums): + if num in temp: + return [temp[num], idx] + else: + temp[target - num] = idx + + +def test1(): + sol = Solution1() + nums = [2, 8, 11, 15, 7] + target = 9 + print(sol.twoSum(nums, target)) + + +def test2(): + sol = Solution2() + nums = [2, 8, 11, 15, 7] + target = 9 + print(sol.twoSum(nums, target)) + + +def test3(): + sol = Solution3() + nums = [2, 8, 11, 15, 7] + target = 9 + print(sol.twoSum(nums, target)) + + +if __name__ == '__main__': + test1() + test2() + test3() diff --git a/Week 02/id_118/Python/LeetCode_242_118.py b/Week 02/id_118/Python/LeetCode_242_118.py new file mode 100644 index 000000000..4f14bef80 --- /dev/null +++ b/Week 02/id_118/Python/LeetCode_242_118.py @@ -0,0 +1,66 @@ +class Solution1: + """ + Solution 1 Use sorted function + """ + + def isAnagram(self, s: str, t: str) -> bool: + return sorted(s) == sorted(t) + + +class Solution2: + """ + Solution 2 Use hashmap + """ + + def isAnagram(self, s: str, t: str) -> bool: + dict_s, dict_t = self._str_dict(s), self._str_dict(t) + return dict_s == dict_t + + def _str_dict(self, s: str) -> dict: + res = {} + for c in s: + if c in res: + res[c] += 1 + else: + res[c] = 1 + return res + + +class Solution3: + """ + Use dict get method, equivalent to Solution2 + """ + + def isAnagram(self, s: str, t: str) -> bool: + dict_s, dict_t = self._str_dict(s), self._str_dict(t) + return dict_s == dict_t + + def _str_dict(self, s: str) -> dict: + res = {} + for c in s: + res[c] = res.get(c, 0) + 1 + return res + + +def test1(): + sol = Solution1() + s, t = "anagram", "nagaram" + print(sol.isAnagram(s, t)) + + +def test2(): + sol = Solution2() + s, t = "a", "b" + print(sol.isAnagram(s, t)) + + +def test3(): + sol = Solution3() + s, t = "anagram", "nagaram" + print(sol.isAnagram(s, t)) + + +if __name__ == '__main__': + test1() + test2() + test3() diff --git a/Week 02/id_118/Python/LeetCode_429_118.py b/Week 02/id_118/Python/LeetCode_429_118.py new file mode 100644 index 000000000..aacbd3994 --- /dev/null +++ b/Week 02/id_118/Python/LeetCode_429_118.py @@ -0,0 +1,13 @@ +class Node: + def __init__(self, val, children): + self.val, self.children = val, children + + +class Solution: + def level_order(self, root): + if not root: return [] + queue, res = [root], [] + while queue: + res.append([node.val for node in queue]) + queue = [child for node in queue for child in node.children] + return res diff --git a/Week 02/id_118/Python/LeetCode_46_118.py b/Week 02/id_118/Python/LeetCode_46_118.py new file mode 100644 index 000000000..5e8662e53 --- /dev/null +++ b/Week 02/id_118/Python/LeetCode_46_118.py @@ -0,0 +1,54 @@ +from typing import List + + +class Solution: + """ + This solution follows strictly the generic recursion solution from the class + Time Complexity: O(n^n) exponential + Space Complexity: O(n) + """ + + def __init__(self): + # initialize result in res, as a list of list + self.res = [] + # initialize a helper vector + self.used = [] + + def _gen_perm(self, curr_level, max_level, nums, curr_p): + # terminator + if curr_level == max_level: + # this is important to add a copy method, + # otherwise it will be affected by the pop later + # Assignment statements in Python do not copy objects, + # they create bindings between a target and an object. + self.res.append(curr_p.copy()) + return + # process the current level + for i in range(len(nums)): + if not self.used[i]: + # if nums[i] is not used + curr_p.append(nums[i]) + self.used[i] = True + + # drill down + self._gen_perm(curr_level + 1, max_level, nums, curr_p) + + # really important to reverse and reset global variables here + curr_p.pop() + self.used[i] = False + return + + def permute(self, nums: List[int]) -> List[List[int]]: + if len(nums) == 0: + return self.res + + self.used = [False] * len(nums) + # current solution is [] + self._gen_perm(0, len(nums), nums, []) + + return self.res + + +if __name__ == '__main__': + sol = Solution() + print(sol.permute([1, 2, 3])) diff --git a/Week 02/id_118/Python/LeetCode_47_118.py b/Week 02/id_118/Python/LeetCode_47_118.py new file mode 100644 index 000000000..acce45b1f --- /dev/null +++ b/Week 02/id_118/Python/LeetCode_47_118.py @@ -0,0 +1,62 @@ +from typing import List + + +class Solution: + """ + This solution follows strictly the generic recursion solution from the class + Time Complexity: O(n^n) exponential + Space Complexity: O(n) + """ + + def __init__(self): + # initialize result in res, as a list of list + self.res = [] + # initialize a helper vector + self.used = [] + + def _gen_perm(self, curr_level, max_level, nums, curr_p): + # terminator + if curr_level == max_level: + # this is important to add a copy method, + # otherwise it will be affected by the pop later + # Assignment statements in Python do not copy objects, + # they create bindings between a target and an object. + self.res.append(curr_p.copy()) + return + # process the current level + for i in range(len(nums)): + # if nums[i] is not used + + if not self.used[i]: + # check if nums[i] is duplicated + if (i > 0) and (not self.used[i - 1]) and ( + nums[i] == nums[i - 1]): + continue + + curr_p.append(nums[i]) + self.used[i] = True + + # drill down + self._gen_perm(curr_level + 1, max_level, nums, curr_p) + + # really important to reverse and reset global variables here + curr_p.pop() + self.used[i] = False + return + + def permute(self, nums: List[int]) -> List[List[int]]: + if len(nums) == 0: + return self.res + + self.used = [False] * len(nums) + # current solution is [] + self._gen_perm(0, len(nums), sorted(nums), []) + + return self.res + + +if __name__ == '__main__': + sol = Solution() + res = sol.permute([3, 3, 0, 3]) + print(res) + print(len(res)) \ No newline at end of file diff --git a/Week 02/id_118/Python/LeetCode_49_118.py b/Week 02/id_118/Python/LeetCode_49_118.py new file mode 100644 index 000000000..890655386 --- /dev/null +++ b/Week 02/id_118/Python/LeetCode_49_118.py @@ -0,0 +1,28 @@ +from typing import List + + +class Solution1: + """ + This solution 1 using hashmap + """ + + def group_anagram(self, strs: List[str]) -> List[List[str]]: + res = {} + for s in strs: + # note here need to sort the string to keep the key the same + # reason to use tuple is because str is not hashable in Python + key = tuple(sorted(s)) + if key not in res: + res[key] = [s] + else: + res[key] += [s] + return res.values() + + +def test1(): + sol = Solution1() + print(sol.group_anagram(["eat", "tea", "tan", "ate", "nat", "bat"])) + + +if __name__ == '__main__': + test1() diff --git a/Week 02/id_118/Python/LeetCode_51_118.py b/Week 02/id_118/Python/LeetCode_51_118.py new file mode 100644 index 000000000..ad1ddf2f7 --- /dev/null +++ b/Week 02/id_118/Python/LeetCode_51_118.py @@ -0,0 +1,59 @@ +from typing import List + + +class Solution: + """ + This method strictly follow the generic backtracking template + """ + + def __init__(self): + self.res = [] + # conflict check lists + self.col, self.dia1, self.dia2 = [], [], [] + + def solveNQueens(self, n: int) -> List[List[str]]: + # Edge case + if n == 1: + return [["Q"]] + if n < 4 and n != 1: + return self.res + row = [] + self.col = [False] * n + self.dia1 = [False] * (2 * n - 1) + self.dia2 = [False] * (2 * n - 1) + self._gen_queen(0, n, row) + return self.res + + def _gen_queen(self, curr_level, n, row): + # Step 1: Terminator + if curr_level == n: + self.res.append(self._gen_board(n, row)) + return + + # Step 2: Process the current level logic + for i in range(n): + if (not self.col[i]) and (not self.dia1[curr_level + i]) and ( + not self.dia2[curr_level - i]): + row.append(i) + self.col[i] = True + self.dia1[curr_level + i] = True + self.dia2[curr_level - i] = True + + # Step 3: drill down + self._gen_queen(curr_level + 1, n, row) + + # Step 4: Reverse + row.pop() + self.col[i] = False + self.dia1[curr_level + i] = False + self.dia2[curr_level - i] = False + return + + def _gen_board(self, n, row): + return ["." * i + "Q" + "." * (n - i - 1) for i in row] + + +if __name__ == '__main__': + sol = Solution() + res = sol.solveNQueens(4) + print(res) diff --git a/Week 02/id_118/Python/LeetCode_589_118.py b/Week 02/id_118/Python/LeetCode_589_118.py new file mode 100644 index 000000000..62cc70bcc --- /dev/null +++ b/Week 02/id_118/Python/LeetCode_589_118.py @@ -0,0 +1,43 @@ +from typing import List + + +class Node: + def __init__(self, val, children): + self.val = val + self.children = children + + +class Solution1: + """ + This is a recursive solution + """ + + def preorder(self, root: Node) -> List[int]: + # edge case + if not root: return [] + res = [] + self._preorder(root, res) + return res + + def _preorder(self, root, res): + res.append(root.val) + if root.children: + for node in root.children: + self._preorder(node, res) + + +class Solution2: + """ + This is an iterative solution + """ + + def preorder(self, root): + stack, res = [root], [] + while stack: + node = stack.pop() + if node: + res.append(node.val) + # reverse the children order + for child in node.children[::-1]: + stack.append(child) + return res diff --git a/Week 02/id_118/Python/LeetCode_590_118.py b/Week 02/id_118/Python/LeetCode_590_118.py new file mode 100644 index 000000000..68cf6e7ce --- /dev/null +++ b/Week 02/id_118/Python/LeetCode_590_118.py @@ -0,0 +1,44 @@ +from typing import List + + +class Node: + def __init__(self, val, children): + self.val = val + self.children = children + + +class Solution1: + """ + This is a recursive solution + """ + + def postorder(self, root: Node) -> List[int]: + res = [] + self._postorder(root, res) + return res + + def _postorder(self, root: Node, res: List[int]) -> List[int]: + if root: + # first visit all the children + for node in root.children: + self._postorder(node, res) + # finally visit root + res.append(root.val) + + +class Solution2: + """ + This is an iterative solution + """ + + def postorder(self, root: Node) -> List[int]: + # edge case root is empty + if not root: return [] + stack, res = [root], [] + while stack: + node = stack.pop() + if node.children: + for child in node.children: + stack.append(child) + res.insert(0, node.val) + return res diff --git a/Week 02/id_118/Python/LeetCode_77_118.py b/Week 02/id_118/Python/LeetCode_77_118.py new file mode 100644 index 000000000..09060492b --- /dev/null +++ b/Week 02/id_118/Python/LeetCode_77_118.py @@ -0,0 +1,78 @@ +from typing import List + + +class Solution: + """ + res is C(n,k) + Follow strictly the generic recursion template + """ + + def __init__(self): + self.res = [] + + def _gen_comb(self, n, k, start, curr_c): + # terminator + if len(curr_c) == k: + self.res.append(curr_c.copy()) + return + # process the current level logic + for i in range(start, n + 1): + curr_c.append(i) + # drill down + self._gen_comb(n, k, i + 1, curr_c) + + # reverse to reset for the next recursion + curr_c.pop() + return + + def combine(self, n: int, k: int) -> List[List[int]]: + # edge cases + if n <= 0 or k <= 0 or k > n: + return self.res + curr_c = [] + self._gen_comb(n, k, 1, curr_c) + return self.res + + +class Solution2: + """ + A more optimized solution by removing unnecessary branches + """ + + def __init__(self): + self.res = [] + + def _gen_comb(self, n, k, start, curr_c): + # terminator + if len(curr_c) == k: + self.res.append(curr_c.copy()) + return + # process the current level logic + # here no need to go to all the way to n (n+1 is not included in py) + # for any given state, the available slots are k - len(curr_c) + # so the stop point is n + 1 - (k - len(curr_c)) + 1 + for i in range(start, n - (k - len(curr_c)) + 2): + curr_c.append(i) + # drill down + self._gen_comb(n, k, i + 1, curr_c) + + # reverse to reset for the next recursion + curr_c.pop() + return + + def combine(self, n: int, k: int) -> List[List[int]]: + # edge cases + if n <= 0 or k <= 0 or k > n: + return self.res + curr_c = [] + self._gen_comb(n, k, 1, curr_c) + return self.res + + +if __name__ == '__main__': + sol = Solution() + res = sol.combine(7, 4) + print(res) + sol2 = Solution2() + res2 = sol2.combine(7, 4) + print(res2) diff --git a/Week 02/id_118/Python/LeetCode_94_118.py b/Week 02/id_118/Python/LeetCode_94_118.py new file mode 100644 index 000000000..6e73a8c0d --- /dev/null +++ b/Week 02/id_118/Python/LeetCode_94_118.py @@ -0,0 +1,47 @@ +from typing import List + + +class TreeNode: + def __init__(self, x: int): + self.val = x + self.left, self.right = None, None + + +class Solution1: + """ + This solution is recursive + """ + + def inorderTraversal(self, root: TreeNode) -> List[int]: + res = [] + if root: + self._inorder_tra(root, res) + return res + + def _inorder_tra(self, root: TreeNode, res: List[int]) -> List[int]: + if root: + if root.left: + self._inorder_tra(root.left, res) + res.append(root.val) + if root.right: + self._inorder_tra(root.right, res) + return res + + +class Solution2: + """ + This solution is iterative + """ + + def inorderTraversal(self, root: TreeNode) -> List[int]: + res, stack = [], [] + while True: + while root: + # this reflects in-order, as FILO + stack.append(root) + root = root.left + if not stack: + return res + node = stack.pop() + res.append(node.val) + root = node.right diff --git a/Week 02/id_123/LeetCode_1_123.c b/Week 02/id_123/LeetCode_1_123.c new file mode 100644 index 000000000..e00ef2154 --- /dev/null +++ b/Week 02/id_123/LeetCode_1_123.c @@ -0,0 +1,33 @@ +/** + * Note: The returned array must be malloced, assume caller calls free(). + */ +//c中哈希表uthash +typedef struct hash_node{ + int id;//key + int index; + UT_hash_handle hh; +}hash_node; +int* twoSum(int* nums, int numsSize, int target, int* returnSize){ + int *two_nums = (int*)malloc(sizeof(int)*2); + hash_node *hash_table = NULL, *hash_item1=NULL, *hash_item2=NULL; + for(int i=0; iindex; + two_nums[1]=i; + * returnSize = 2; + return two_nums; + } + //哈希表中添加元素, + hash_item2 = (hash_node*)malloc(sizeof(hash_node)); + hash_item2->id = *(nums+i); + hash_item2->index = i; + HASH_ADD_INT(hash_table, id, hash_item2); + + } + return two_nums; + +} + diff --git a/Week 02/id_123/LeetCode_242_123.c b/Week 02/id_123/LeetCode_242_123.c new file mode 100644 index 000000000..2f1f844e2 --- /dev/null +++ b/Week 02/id_123/LeetCode_242_123.c @@ -0,0 +1,44 @@ +//˼·һ s ĸƵʣ t ټеÿĸļȻǷص㡣 +// bool isAnagram(char * s, char * t){ +// int n = strlen(s); +// int m = strlen(t); +// if(n!=m) +// return false; +// int map[26]={0}; +// for(int i=0; i PreorderTraversal(TreeNode root) + { + var result = new List(); + _ProOrderTraversal(root, result); + return result; + } + + private void _ProOrderTraversal(TreeNode node, IList result) + { + if (node is null) + { + return; + } + result.Add(node.val); + _ProOrderTraversal(node.left, result); + _ProOrderTraversal(node.right, result); + + } +} + diff --git a/Week 02/id_128/LeetCode_17_128.cs b/Week 02/id_128/LeetCode_17_128.cs new file mode 100644 index 000000000..577da2257 --- /dev/null +++ b/Week 02/id_128/LeetCode_17_128.cs @@ -0,0 +1,89 @@ +//dfs +public class Solution +{ + public IList LetterCombinations(string digits) + { + var result = new List(); + if (digits.Length == 0) + return result; + + var map = new Dictionary>(10); + + map['2'] = new List() { 'a', 'b', 'c' }; + map['3'] = new List() { 'd', 'e', 'f' }; + map['4'] = new List() { 'g', 'h', 'i' }; + map['5'] = new List() { 'j', 'k', 'l' }; + map['6'] = new List() { 'm', 'n', 'o' }; + map['7'] = new List() { 'p', 'q', 'r', 's' }; + map['8'] = new List() { 't', 'u', 'v' }; + map['9'] = new List() { 'w', 'x', 'y', 'z' }; + + + Search(digits, map, 0, new char[digits.Length], result); + return result; + } + + private void Search(string digits, IDictionary> map, int index, char[] temp, IList result) + { + + if (index == digits.Count()) + { + result.Add(new String(temp)); + return; + } + foreach (char letter in map[digits[index]]) + { + temp[index] = letter; + Search(digits, map, index + 1, temp, result); + } + } +} + + +//bfs +public class Solution +{ + public IList LetterCombinations(string digits) + { + var result = new List(); + if (digits.Length == 0) + return result; + + var map = new Dictionary>(10); + + map['2'] = new List() { "a", "b", "c" }; + map['3'] = new List() { "d", "e", "f" }; + map['4'] = new List() { "g", "h", "i" }; + map['5'] = new List() { "j", "k", "l" }; + map['6'] = new List() { "m", "n", "o" }; + map['7'] = new List() { "p", "q", "r", "s" }; + map['8'] = new List() { "t", "u", "v" }; + map['9'] = new List() { "w", "x", "y", "z" }; + + var queue = new Queue(); + foreach (var letter in map[digits[0]]) + { + queue.Enqueue(letter); + } + var idx = 1; + while (queue.Count() > 0) + { + var size = queue.Count(); + while (size-- > 0) + { + var curString = queue.Dequeue(); + if (idx == digits.Length) + { + result.Add(curString); + continue; + } + foreach (var letter in map[digits[idx]]) + { + queue.Enqueue(curString + letter); + } + } + idx++; + } + return result; + } +} \ No newline at end of file diff --git a/Week 02/id_128/LeetCode_1_128.cs b/Week 02/id_128/LeetCode_1_128.cs new file mode 100644 index 000000000..1fa785ae3 --- /dev/null +++ b/Week 02/id_128/LeetCode_1_128.cs @@ -0,0 +1,18 @@ + +//One loop, build mapping for each element, meanwhile check against the mapping for target - element +public class Solution { + public int[] TwoSum(int[] nums, int target) { + + //key: element value: index + Dictionary map = new Dictionary(nums.Length); + for (int i = 0; i < nums.Length; i++) { + if (map.ContainsKey(target - nums[i])) { + return new int[] {map[target - nums[i]], i}; + } + if (!map.ContainsKey(nums[i])) { + map.Add(nums[i], i); + } + } + throw new ArgumentException(); + } +} \ No newline at end of file diff --git a/Week 02/id_128/LeetCode_22_128.cs b/Week 02/id_128/LeetCode_22_128.cs new file mode 100644 index 000000000..da1a2df7d --- /dev/null +++ b/Week 02/id_128/LeetCode_22_128.cs @@ -0,0 +1,29 @@ +public class Solution +{ + public IList GenerateParenthesis(int n) + { + var result = new List(); + _GenerateParentheis(0, 0, n, "", result); + return result; + } + + + private void _GenerateParentheis(int i, int j, int n, string tempStr, IList result) + { + if (j >= n) + { + result.Add(tempStr); + return; + } + + if (i < n) + { + _GenerateParentheis(i + 1, j, n, tempStr + '(', result); + } + if (j < i) + { + _GenerateParentheis(i, j + 1, n, tempStr + ')', result); + } + } +} + diff --git a/Week 02/id_128/LeetCode_242_128.cs b/Week 02/id_128/LeetCode_242_128.cs new file mode 100644 index 000000000..8ed610023 --- /dev/null +++ b/Week 02/id_128/LeetCode_242_128.cs @@ -0,0 +1,60 @@ +//One loop, log occurences by ++ for string1 and by -- for string2. +public class Solution1 { + public bool IsAnagram(string s, string t) { + + if (s.Length != t.Length) { + return false; + } + Dictionary map = new Dictionary(s.Length); + + for (int i = 0; i< s.Length;i++) { + if (!map.ContainsKey(s[i])) { + map[s[i]] = 0; + } + ++map[s[i]]; + + if (!map.ContainsKey(t[i])) { + map[t[i]] = 0; + } + --map[t[i]]; + } + + foreach (var value in map.Values) { + if (value != 0) { + return false; + } + } + + return true; + } +} + + +//Loop string1 to build mapping, then loop string2 +public class Solution2 { + public bool IsAnagram(string s, string t) { + + if (s.Length != t.Length) { + return false; + } + Dictionary map = new Dictionary(s.Length); + + foreach (var c in s) { + if (!map.ContainsKey(c)) { + map[c] = 0; + } + ++map[c]; + } + + foreach (var c in t) { + if (!map.ContainsKey(c)) { + return false; + } + if (--map[c] <= 0) { + map.Remove(c); + } + } + + return true; + } +} diff --git a/Week 02/id_128/LeetCode_429_128.cs b/Week 02/id_128/LeetCode_429_128.cs new file mode 100644 index 000000000..fe746ee21 --- /dev/null +++ b/Week 02/id_128/LeetCode_429_128.cs @@ -0,0 +1,33 @@ +public class Solution +{ + + public IList> LevelOrder(Node root) + { + var result = new List>(); + _LevelOrder(root, 0, result); + return result; + } + + private void _LevelOrder(Node node, int level, IList> result) + { + if (node is null) + { + return; + } + + if (level + 1 > result.Count()) + { + result.Add(new List()); + } + + result[level++].Add(node.val); + + foreach (var child in node.children) + { + _LevelOrder(child, level, result); + } + } + + +} + diff --git a/Week 02/id_128/LeetCode_49_128.cs b/Week 02/id_128/LeetCode_49_128.cs new file mode 100644 index 000000000..ee730dde8 --- /dev/null +++ b/Week 02/id_128/LeetCode_49_128.cs @@ -0,0 +1,41 @@ + +//categorize str by chararacter occurence count +//Time complexity O(nm) +public class Solution +{ + public IList> GroupAnagrams(string[] strs) + { + var map = new Dictionary>(strs.Length); + foreach (var str in strs) + { + var key = GetKey(str); + if (!map.ContainsKey(key)) + { + map[key] = new List(); + } + map[key].Add(str); + } + + return map.Values.ToList(); + } + + private string GetKey(string str) + { + int[] temp = new int[26]; + foreach (var c in str) + { + temp[c - 'a']++; + } + + var sb = new StringBuilder(); + for (int i = 0; i < 26; i++) + { + if (temp[i] > 0) + { + sb.Append((char)i); + sb.Append(temp[i]); + } + } + return sb.ToString(); + } +} \ No newline at end of file diff --git a/Week 02/id_128/LeetCode_590_128.cs b/Week 02/id_128/LeetCode_590_128.cs new file mode 100644 index 000000000..67114a79c --- /dev/null +++ b/Week 02/id_128/LeetCode_590_128.cs @@ -0,0 +1,22 @@ +public class Solution +{ + public IList Postorder(Node root) + { + var result = new List(); + _Postorder(root, result); + return result; + } + + private void _Postorder(Node node, IList result) + { + if (node is null) + return; + + foreach (var child in node.children) + { + _Postorder(child, result); + } + result.Add(node.val); + } +} + diff --git a/Week 02/id_128/LeetCode_70_128.cs b/Week 02/id_128/LeetCode_70_128.cs new file mode 100644 index 000000000..b5a4f0fe8 --- /dev/null +++ b/Week 02/id_128/LeetCode_70_128.cs @@ -0,0 +1,24 @@ +//practice the recursive template +public class Solution +{ + public int ClimbStairs(int n) + { + var temp = new Dictionary(n); + temp[1] = 1; + temp[2] = 2; + return _ClimbStairs(n, temp); + } + + + private int _ClimbStairs(int n, IDictionary temp) + { + if (temp.ContainsKey(n)) + { + return temp[n]; + } + + temp[n] = _ClimbStairs(n - 1, temp) + _ClimbStairs(n - 2, temp); + return temp[n]; + } +} +// @lc code diff --git a/Week 02/id_128/LeetCode_94_128.cs b/Week 02/id_128/LeetCode_94_128.cs new file mode 100644 index 000000000..4f40ba88c --- /dev/null +++ b/Week 02/id_128/LeetCode_94_128.cs @@ -0,0 +1,21 @@ + +public class Solution +{ + public IList InorderTraversal(TreeNode root) + { + var result = new List(); + _InorderTraversal(root, result); + return result; + } + + private void _InorderTraversal(TreeNode node, IList result) + { + if (node is null) + return; + _InorderTraversal(node.left, result); + result.Add(node.val); + _InorderTraversal(node.right, result); + } + +} + diff --git a/Week 02/id_128/NOTE.md b/Week 02/id_128/NOTE.md index a6321d6e2..b1ffaa62f 100644 --- a/Week 02/id_128/NOTE.md +++ b/Week 02/id_128/NOTE.md @@ -1,4 +1,92 @@ -# NOTE - +Hash table (abstract) + +一定要设计好的哈希函数 - 将key均匀映射以尽量避免哈希碰撞 + +time complexity +average insert, delete, search O(1) +Worst case (bad hash function) O(n) + + +binary search tree (sorted binary tree) +左子树所有节点值均小于根节点值 +右子树所有节点值均大于根节点值 +左右子树也是二叉搜索树 + +中序遍历二叉搜索树即得到升序排列 + +递归四步 +1. 中介条件 +2. 处理当前层 +3. 下探 +4. 清理当前层变量 + +解题四步 +1. clarification + a. 不认识 不明白的词 + b. 边缘情况 + c. case sensitive? + d。 etc。 +2. possible solutions --> optimal (time&space) +3. code +4. test cases + + +思维要点: +1. 抵制人肉递归 +2. 找到最近最贱的方法, 将其拆解成可重复解决的问题 - 重复子问题 +3. 数学归纳法的思维 + +递归模版 +public void recur(int level, int param) { + + // terminator + if (level > MAX_LEVEL) { + // process result + return; + } + + // process current logic + process(level, param); + + // drill down + recur( level: level + 1, newParam); + + // restore current status + +} + + +divide and conquer +理念 - 将原问题拆分成n个规模较小的且与原问题相似的子问题,是的每个子问题都易于求解,然后将子问题结果归并,从而得到原问题的解 +递归的变体 - 不是顺序递归, 需要多一步来合并结果 + +def divide_conquer(problem, param1, param2, ...): + # recursion terminator + if problem is None: + print_result + return + + # prepare data + data = prepare_data(problem) + subproblems = split_problem(problem, data) + + # conquer subproblems + subresult1 = self.divide_conquer(subproblems[0], p1, ...) + subresult2 = self.divide_conquer(subproblems[1], p1, ...) + subresult3 = self.divide_conquer(subproblems[2], p1, ...) + … + + # process and generate the final result + result = process_result(subresult1, subresult2, subresult3, …) + + # revert the current level states + +back tracking 回溯 +是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标 +采用试错思想,当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为 回溯点 + +本周学习时间较短,课程较多。 分治的习题还需要继续练习。 +本周贯彻五毒神掌做题法 (第三毒), 将第一周的题目全部再做了一遍。发现大部分题目已经忘记了具体答案,无法向上一次重复时,直接(默)写出来。但是按照四步解题法,在得出暴力答案之后,很快就能得出最优解思路并且写出解题算法。但是对于每种方法的复杂度分析还拿不准,仍然需要参照题解。 + diff --git a/Week 02/id_133/leetcode_105_133.java b/Week 02/id_133/leetcode_105_133.java new file mode 100644 index 000000000..751ae937e --- /dev/null +++ b/Week 02/id_133/leetcode_105_133.java @@ -0,0 +1,36 @@ + +//leetcode 题号105 从前序和中序遍历构造二叉树 + +import java.io.*; +import java.util.Arrays; +import javax.swing.tree.TreeNode; + +public class Solution { + + public TreeNode buildTree(int[] preorder, int[] inorder) { + return buildTreeHelper(preorder, 0, preorder.length, inorder, 0, inorder.length); + } + + private TreeNode buildTreeHelper(int[] preorder, int p_start, int p_end, int[] inorder, int i_start, int i_end) { + // preorder 为空,直接返回 null + if (p_start == p_end) { + return null; + } + int root_val = preorder[p_start]; + TreeNode root = new TreeNode(root_val); + //在中序遍历中找到根节点的位置 + int i_root_index = 0; + for (int i = i_start; i < i_end; i++) { + if (root_val == inorder[i]) { + i_root_index = i; + break; + } + } + int leftNum = i_root_index - i_start; + //递归的构造左子树 + root.left = buildTreeHelper(preorder, p_start + 1, p_start + leftNum + 1, inorder, i_start, i_root_index); + //递归的构造右子树 + root.right = buildTreeHelper(preorder, p_start + leftNum + 1, p_end, inorder, i_root_index + 1, i_end); + return root; + } +} \ No newline at end of file diff --git a/Week 02/id_133/leetcode_94_133.java b/Week 02/id_133/leetcode_94_133.java new file mode 100644 index 000000000..663d34fe4 --- /dev/null +++ b/Week 02/id_133/leetcode_94_133.java @@ -0,0 +1,30 @@ + +//leetcode 题号94 二叉树的中序遍历 +import java.io.*; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.List; + +import javax.swing.tree.TreeNode; + +public class Solution { + + + public List < Integer > inorderTraversal(TreeNode root) { + List < Integer > res = new ArrayList < > (); + helper(root, res); + return res; + } + + public void helper(TreeNode root, List < Integer > res) { + if (root != null) { + if (root.left != null) { + helper(root.left, res); + } + res.add(root.val); + if (root.right != null) { + helper(root.right, res); + } + } + } +} \ No newline at end of file diff --git a/Week 02/id_138/LeetCode_105_138.java b/Week 02/id_138/LeetCode_105_138.java new file mode 100644 index 000000000..692d95323 --- /dev/null +++ b/Week 02/id_138/LeetCode_105_138.java @@ -0,0 +1,96 @@ +import java.util.HashMap; +import java.util.Map; + +/** + * 从前序和中序遍历序列构造二叉树 + * @author L + * + */ +public class LeetCode_105_138 { + /** + * + * @param preorder 前序遍历:父节点 -左子节点-右子节点 + * @param inorder 中序遍历: 左子节点-父节点-右子节点 + * @return + * 思路: + * 前序遍历的第一个元素为根节点;中序遍历中,根节点所在位置的左侧为左子树,右侧为右子树 + * 1 找到各个子树的根节点 + * 2 构建该根节点的左子树 + * 3 构建该根节点的右子树 + * 4 重复1-3的问题 + * + */ + private MapinMap = null; + public TreeNode buildTree(int[] preorder, int[] inorder) { + inMap = mapIndex(inorder); + return buildTreeHelper(preorder,0,preorder.length, inorder,0,inorder.length); + } + public TreeNode buildTreeHelper(int[] preorder,int ps,int pe, int[] inorder,int is,int ie) { + /** + * recursion terminator :递归终止条件 + */ + if(ps == pe) + /* + * process result:处理结果 + */ + return null; + /** + * process logic in current level :当前层处理逻辑 + */ + //前序遍历第一个值为根节点,构造根节点 + TreeNode root = new TreeNode(preorder[ps]); + //找到当前根节点在中序遍历数组里的index + int mid = inMap.get(preorder[ps]); + int left_num = mid -is; + int pLeft = ps+left_num+1; + + /** + * drill down: 向下层探索递归调用 根据数学归纳法,转化为可重复解决的问题[重复子问题] + */ + + //构建左子树 + root.left = buildTreeHelper(preorder,ps+1,pLeft,inorder,is,mid); + + //构建右子树 + root.right = buildTreeHelper(preorder, pLeft, pe, inorder, mid+1, ie); + + //reverse or restore the current level status if needed:必要时返回或者保存当前层的状态 + + return root; + } + + /** + * 存放元素与位置的对应关系 + * @param array + * @return + */ + private Map mapIndex(int[] array) { + Map map = new HashMap(); + for(int i=0;iinMap = new LeetCode_105_138().mapIndex(inorder); + //找到当前根节点在中序遍历数组里的index + int mid = inMap.get(preorder[0]); + System.out.println(mid); + + + } + + +} diff --git a/Week 02/id_138/LeetCode_144_138.java b/Week 02/id_138/LeetCode_144_138.java new file mode 100644 index 000000000..82333679b --- /dev/null +++ b/Week 02/id_138/LeetCode_144_138.java @@ -0,0 +1,34 @@ +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; +/** + * 前序遍历 + * @author L + * + */ +public class LeetCode_144_138 { + /** + * 非递归方式-树的前序遍历:从根节点开始,每次迭代弹出当前栈顶元素,并将其孩子节点压入栈中,先压右孩子再压左孩子。 + * 输出的最终结果顺序按照Top->Bottom和left->right,符合前序遍历的顺序。 + * @param root + * @return + */ + public List preorderTraversal(TreeNode root) { + Stack stack = new Stack(); + List list = new ArrayList(); + if(root == null) { + return list; + } + stack.add(root); + while(!stack.isEmpty()) { + TreeNode node = stack.pop(); + list.add(node.val); + if(node.right != null) + stack.push(node.right); + if(node.left != null) + stack.push(node.left); + } + + return list; + } +} diff --git a/Week 02/id_138/LeetCode_169_138.java b/Week 02/id_138/LeetCode_169_138.java new file mode 100644 index 000000000..6b9510f00 --- /dev/null +++ b/Week 02/id_138/LeetCode_169_138.java @@ -0,0 +1,56 @@ +import java.util.Arrays; + +/** + * 求众数:求一个数组中出现次数大于n/2的元素 + * @author L + * + */ +public class LeetCode_169_138 { + /** + * 暴力方式 + * @param nums + * @return + */ + public int majorityElement(int[] nums) { + int count = 0; + for(int i=0;inums.length/2) { + return nums[i]; + } + } + return -1; + } + + /** + * 简化方式 + * @param nums + * @return + */ + public int majorityElement2(int[] nums) { + //排序 + Arrays.sort(nums); + //如果存在众数,众数必定在nums.length/2的位置 + return nums[nums.length/2]; + } + /** + * 投票方法 + * @param nums + * @return + */ + public int majorityElement3(int[] nums) { + int count = 0; + int flag = nums[0]; + for(int num:nums) { + if(count == 0) { + flag = num; + } + count += (num == flag)?1:-1; + } + return flag; + } +} diff --git a/Week 02/id_138/LeetCode_1_138.java b/Week 02/id_138/LeetCode_1_138.java new file mode 100644 index 000000000..40adcecdb --- /dev/null +++ b/Week 02/id_138/LeetCode_1_138.java @@ -0,0 +1,59 @@ +import java.util.HashMap; +import java.util.Map; + +/** + * 两数之和 + * 1.审题,确认搞懂题意 + * 2.整理思路,分析算法时间、空间复杂度,找出最优解 + * 3.code完成编码实现 + * 4.test完成测试用例 + * @author Lukas + * @since 2019/10/21 22:28 + **/ +public class LeetCode_1_138 { + + /** + * 暴力法求解,从左到右遍历数组,对于任意一个元素,判断该元素右侧是否存在满足条件的其他元素 + * 时间复杂度:O(n^2) + * 空间复杂度:0(1) + * @param nums + * @param target + * @return + */ + public int[] twoSums_solution1(int[] nums, int target) { + for (int i = 0; i < nums.length-1; i++) {//外层循环最右侧为length-2 + for (int j = i+1; j < nums.length; j++) {//内层循环从i+1开始到length-1 + if (nums[i] + nums[j] == target) {//找到了 + return new int[]{i,j}; + } + } + } + return null; + } + + /** + * 使用map优化 + * 时间复杂度:O(n) + * 空间复杂度:O(n) + * @param nums + * @param target + * @return + */ + public int[] twoSums_solution2(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) {//初始化map + map.put(nums[i], i);//元素为key,下标为value + } + int key = 0; + for (int i = 0; i < nums.length; i++) { + key = target-nums[i]; + if (map.containsKey(key) //代表找到了 + && i!=map.get(key)){//代表找到的不是自己 + return new int[]{i, map.get(key)}; + } + } + return null; + } + + +} diff --git a/Week 02/id_138/LeetCode_236_138.java b/Week 02/id_138/LeetCode_236_138.java new file mode 100644 index 000000000..ffeaeab3a --- /dev/null +++ b/Week 02/id_138/LeetCode_236_138.java @@ -0,0 +1,39 @@ +/** + * 二叉树的最近公共祖先 + * @author L + * + */ +public class LeetCode_236_138 { + + private TreeNode ancestor = null; + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + findLowestAncestor(root, p, q); + return ancestor; + } + /** + * 从pre节点向下寻找是否存在子节点p和q + * @param pre + * @param p + * @param q + * @return + */ + private boolean findLowestAncestor(TreeNode pre, TreeNode p, TreeNode q) { + //没有找到 + if(pre == null) + return false; + //左子树递归,如果返回true则设置left=1 + int left = findLowestAncestor(pre.left, p, q)?1:0; + //右子树递归,如果返回true则设置right=1 + int right = findLowestAncestor(pre.right, p, q)?1:0; + + int mid = (pre == p || pre == q)?1:0; + + int sum = mid+left+right; + if(sum >= 2) {//当前pre就是最近祖先节点 + this.ancestor = pre; + } + return sum>0;//当前pre是p或者q的祖先 + + } + +} diff --git a/Week 02/id_138/LeetCode_242_138.java b/Week 02/id_138/LeetCode_242_138.java new file mode 100644 index 000000000..89586bd22 --- /dev/null +++ b/Week 02/id_138/LeetCode_242_138.java @@ -0,0 +1,96 @@ +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * 有效的字母异位词 + * @author Lukas + * @since 2019/10/21 22:47 + **/ +public class LeetCode_242_138 { + + /** + * 暴力法:先排序后转换为string比较 + * 时间复杂度:O(nlogn) + * 空间复杂度:O(n) + * @param s + * @param t + * @return + */ + public boolean isAnagram_solution1(String s, String t) { + char[]ss = s.toCharArray(); + char[]tt = t.toCharArray(); + Arrays.sort(ss); + Arrays.sort(tt); + /** + s = new String(ss); + t = new String(tt); + if (s.equals(t)) { + return true; + } + return false; + **/ + //以上代码可优化为 + return Arrays.equals(ss,tt); + } + + /** + * hash表 + * 时间复杂度: O(n) + * 空间复杂度: O(1) + * @param s + * @param t + * @return + */ + public boolean isAnagram_solution2(String s, String t) { + Map map = new HashMap<>(); + char ss[] = s.toCharArray(); + for (int i = 0; i < ss.length; i++) { + if (map.containsKey(ss[i])) { + map.put(ss[i],map.get(ss[i])+1); + }else + map.put(ss[i],1); + } + + char[] tt = t.toCharArray(); + for (int i = 0; i < tt.length; i++) { + if (map.containsKey(tt[i])) { + map.put(tt[i],map.get(tt[i])-1); + }else + return false; + } + + for (Integer value : map.values()) { + if (value!=0) + return false; + } + return true; + } + + + /** + * 最优题解 + * @param s + * @param t + * @return + */ + public boolean isAnagram_solution3(String s, String t) { + if (s.length() != t.length()) { + return false; + } + + int[] table = new int[26];//代表26个字母个数的数组从a->z排列 table[0]存放的是a的个数,默认是0 + for (int i = 0; i < s.length(); i++) { + table[s.charAt(i)-'a']++;//字母的个数保存到table中 + } + + for (int i = 0; i < t.length(); i++) { + table[t.charAt(i)-'a']--;//将相同字母的个数减一 + if (table[t.charAt(i) - 'a'] < 0) {//如果存在小于0的直接false + return false; + } + } + return true; + } + +} diff --git a/Week 02/id_138/LeetCode_46_138.java b/Week 02/id_138/LeetCode_46_138.java new file mode 100644 index 000000000..205ea7750 --- /dev/null +++ b/Week 02/id_138/LeetCode_46_138.java @@ -0,0 +1,39 @@ +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 数字的全排列 + * “回溯搜索”算法即“深度优先遍历 + 状态重置 + 剪枝” + * @author L + * + */ +public class LeetCode_46_138 { + private List> res = new ArrayList>(); + public List> permute(int[] nums) { + int size = nums.length; + List list = new ArrayList(); + for(int num:nums) {//数组转为list + list.add(num); + } + permuteHelper(list,size,0); + + return res; + } + /** + * + * @param list 元素集合 + * @param size 原数组的长度 + * @param start 第一个整数的索引位置 + */ + private void permuteHelper(List list, int size, int start) { + if(size == start) { + res.add(new ArrayList(list)); + } + for(int i=start;i> agganumGroup_solution1 (String[] words){ + List> result = new ArrayList>(); + Map> map = new HashMap(); + for(String word: words) { + char[] array = word.toCharArray(); + Arrays.sort(array); + String sortStr = new String(array); + if (map.containsKey(sortStr)) { + List temp = map.get(sortStr); + temp.add(word); + map.put(sortStr, temp); + }else { + List temp = new ArrayList(); + temp.add(word); + map.put(sortStr,temp); + } + } + result.addAll(map.values()); + return result; + } + + /** + * 慢 + * @param words + * @return + */ + public List> agganumGroup_solution2 (String[] words){ + List> result = new ArrayList>(); + Set set = new HashSet(); + for(int i=0;i ls = new ArrayList(); + ls.add(words[i]); + set.add(i); + for(int j=i+1;j> res = new ArrayList(); + public List> solveNQueens(int n) { + if (n == 0) { + return res; + } + + // 初始化数组 + int[] nums = new int[n]; + for (int i = 0; i < n; i++) { + nums[i] = i; + } + + Set col = new HashSet();// 行/列 + Set master = new HashSet();// 主对角线 纵坐标+横坐标固定 + Set slave = new HashSet();// 从对角线 纵坐标-横坐标固定 + Stack stack = new Stack(); + + backHelper(nums, 0, n, col, master, slave, stack); + + return res; + } + + private void backHelper(int[] nums, int row, int n, + Set col, + Set master, + Set slave, + Stack stack) { + // TODO Auto-generated method stub + if (row == n) {// 按行放置queen,遍历完所有行 + List queen = findQueen(stack, n); + res.add(queen); + return; + } + // 每一行测试是否可以放置queen + for (int i = 0; i < n; i++) { + if (!col.contains(i) && !master.contains(row + i) && !slave.contains(row - i)) { + stack.add(nums[i]); + col.add(i); + master.add(row + i); + slave.add(row - i); + + backHelper(nums, row + 1, n, col, master, slave, stack); + + slave.remove(row - i); + master.remove(row + i); + col.remove(i); + stack.pop(); + } + } + + } + + /** + * 打印该行皇后位置信息 + * @param stack + * @param n + * @return + */ + private List findQueen(Stack stack, int n) { + List row = new ArrayList(); + for (Integer num : stack) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < n; i++) { + sb.append("."); + } + sb.replace(num, num + 1, "Q"); + row.add(sb.toString()); + } + return row; + } + + public static void main(String[] args) { + int n = 4; + LeetCode_51_138 solution = new LeetCode_51_138(); + List> res = solution.solveNQueens(n); + System.out.println(res); + } + +} diff --git a/Week 02/id_138/LeetCode_590_138.java b/Week 02/id_138/LeetCode_590_138.java new file mode 100644 index 000000000..821b47442 --- /dev/null +++ b/Week 02/id_138/LeetCode_590_138.java @@ -0,0 +1,65 @@ +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * n叉树的后序遍历 + * @author L + * + */ +public class LeetCode_590_138 { + + /** + * 递归法-1 + * @param root + * @return + */ + public List postorder_solution1(Node root) { + List list = new ArrayList(); + if(root == null) + return list; + postorderNode(root, list); + return null; + } + + private void postorderNode(Node root, List list) { + if(root == null) { + return ; + } + for(Node node: root.children) { + postorderNode(node, list); + } + list.add(root.val); + } + + /** + * + * @param root + * @return + */ + public List postorder_solution2(Node root) { + List list = new ArrayList(); + if(root == null) + return list; + Stack stack = new Stack(); + stack.push(root);//根节点最先入栈 + //上次处理的节点 + Node pre = null; + while(!stack.isEmpty()) { + Node cur = stack.peek(); + if((cur.children.size()==0) || (pre!=null && cur.children.contains(pre))) { + //加入结果集 + list.add(cur.val); + stack.pop(); + //更新pre节点 + pre = cur; + }else { + List nodes = cur.children; + for(int i=nodes.size()-1;i>0;i--) {//从右到左入栈 + stack.push(nodes.get(i)); + } + } + } + return list; + } +} diff --git a/Week 02/id_138/LeetCode_70_138.java b/Week 02/id_138/LeetCode_70_138.java new file mode 100644 index 000000000..7a6016ca5 --- /dev/null +++ b/Week 02/id_138/LeetCode_70_138.java @@ -0,0 +1,59 @@ +import java.util.HashMap; +import java.util.Map; + +/** + * 爬楼梯 + * @author L + * + */ +public class LeetCode_70_138 { + + /** + * 递归求解爬楼梯-n最多45,不加缓存会导致超时 + * @param n + * @return + */ + private Map map = new HashMap(); + public int climbStairs(int n) { + if(n<=2) { + map.put(n,n); + return n; + } + int n_1 = 0; + if(map.containsKey(n-1)){ + n_1 = map.get(n-1); + }else{ + n_1 = climbStairs(n-1); + map.put(n-1,n_1); + } + int n_2 = 0; + if(map.containsKey(n-2)){ + n_2 = map.get(n-2); + }else{ + n_2 = climbStairs(n-2); + map.put(n-2,n_2); + } + + return n_1+n_2; + } + + /** + * 非递归方法 + * @param n + * @return + */ + public int climbStairs2(int n) { + if(n<=2) + return n; + int i =3; + int sum=0,one = 1,two = 2; + while(i++<=n) { + sum = two+one; + one = two; + two = sum; + } + return sum; + } + + +} diff --git a/Week 02/id_138/LeetCode_77_138.java b/Week 02/id_138/LeetCode_77_138.java new file mode 100644 index 000000000..b6cae912e --- /dev/null +++ b/Week 02/id_138/LeetCode_77_138.java @@ -0,0 +1,36 @@ +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 + * @author L + * + */ +public class LeetCode_77_138 { + private List> result = new ArrayList(); + public List> combine(int n, int k) { + if(n<0 || k<0 || n stack = new Stack(); + //从1到n开始,第一个元素是1 + combineHelper(n,k,1,stack); + + return result; + } + private void combineHelper(int n, int k, int start, Stack stack) { + // TODO Auto-generated method stub + if(stack.size() == k) {//从n个元素已经收集元素的个数=k + result.add(new ArrayList(stack)); + return; + } + for(int i=start;i<=n;i++) {//从start开始继续收集元素 + stack.add(i); + combineHelper(n, k, i+1, stack); + stack.pop();//最后加入的一个元素已经找不到符合的结果了 + } + } + + +} diff --git a/Week 02/id_138/LeetCode_94_138.java b/Week 02/id_138/LeetCode_94_138.java new file mode 100644 index 000000000..727459d23 --- /dev/null +++ b/Week 02/id_138/LeetCode_94_138.java @@ -0,0 +1,70 @@ +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Stack; + +/** + * 二叉树的中序遍历 + * @author L + * + */ +public class LeetCode_94_138 { + /** + * 递归方法 + * 时间复杂度O(n) + * @param root + * @return + */ + public List inorderTraversal_solution1(TreeNode root) { + List list = new ArrayList(); + inorderFindNode(root,list); + return list; + } + + private void inorderFindNode(TreeNode root, List list) { + if(root == null) { + return ; + } + inorderFindNode(root.left, list); + list.add(root.val); + inorderFindNode(root.right, list); + + /** + * 前序遍历 + */ +// list.add(root.val); +// inorderFindNode(root.left, list); +// inorderFindNode(root.right, list); + + /** + * 后序遍历 + */ +// inorderFindNode(root.left, list); +// inorderFindNode(root.right, list); +// list.add(root.val); + } + + /** + * 非递归方式 + * @param root + * @return + */ + public List inorderTraversal_solution2(TreeNode root) { + List list = new ArrayList(); + Stack stack = new Stack(); + stack.add(root); + TreeNode current = root; + int i=0; + while(current !=null || !stack.isEmpty()) { + while(current != null) {//当前树的所有左孩子入栈 + stack.push(current); + current = current.left; + } + current = stack.pop();//左孩子出栈 + list.add(current.val);//左孩子也是某个中间(根)节点 + current = current.right; + } + + return list; + } +} diff --git a/Week 02/id_138/NOTE.md b/Week 02/id_138/NOTE.md index a6321d6e2..8c1c2ac6c 100644 --- a/Week 02/id_138/NOTE.md +++ b/Week 02/id_138/NOTE.md @@ -1,4 +1,172 @@ # NOTE +PART A Week2学习总结 +1 相关数据结构: + map/hashtable(哈希表) : 根据key直接进行访问的数据结构,通过散列函数将关键码和值映射到散列表的一个位置。insert/delete/search:平均时间复杂度O(1) + set:底层是通过hashmap实现,不允许重复元素。 + 树: 树是没有环的图,链表是特殊的树 + 二叉树:前序遍历(根-左节点-右节点);中序遍历(左节点-根-右节点);后序遍历(左节点--右节点-根),通过递归或者栈循环遍历。 + 二叉搜索树: 有序的二叉树,左子树的所有节点的值均小于根节点的值;右子树所有节点的值均大于根节点的值;其左右子树也都是有序的二叉 树。insert/delete/search:平均时间复杂度O(log(n)),最坏O(n) + 树的相关面试题一般都是通过递归解决,主要原因是节点的定义存在左右子树与问题重复性(自相似性)。 + 2 递归 + 递归的模板: + 1)process terminater:递归的终止条件 + 2)process logic in current level:当前逻辑处理 + 3)drill down:递归调用处理 + 4)reverse current level status :当前层状态回归 + 递归的思想: + 1)不要人肉进行递归,搞懂题意,多动手多画图,一步步分析。 + 2)找到规律,即最近最简方法,拆解成可重复解决的问题。(自相似性重复子问题) + 3)数学归纳法思维。多写多锻炼。 + 3 分治/回溯 + 分治:将问题分解为若干子问题,类似的思想例如java的forkjoin,haddop的mapreduce + 分治模板: + function divider(params...){ + + #recursion terminator + if(..){ + ... + return + } + + #prepare data + ..to do... + + #conquer sumproblems + subresult1 = divider(...) + subresult2 = divider(...) + + #process and generate final result + result = process_result(subresult1,subresult2,...) + + # revert the current level states + ..to do + } + + 回溯 + 回溯的思想是通过不断的试错,尝试分步骤的解决问题。在此过程中一般会涉及到递归与剪枝 ,“回溯搜索”算法即“深度优先遍历 + 状态重置 + 剪枝”。通过画图,把回溯问题转换为树形结构。 + 参考链接:https://leetcode-cn.com/problems/permutations/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liweiw/,多阅读几次有助于自己理解该类问题。 + +PART B 我所理解的hashmap +1.hashmap定义及说明:public class HashMap extends AbstractMap implements Map, Cloneable, Serializable +由类定义可知,hashmap和AbstracMap都是map接口的实现,而同时hashmap又继承了抽象类AbstractMap。这个模式在jdk或者spring的世界中处处可见,抽象类与子类实现同样的接口。 +hashmap是键值映射的一种实现,允许null键或者null值,但是非线程安全的。在多线程环境下调用put方法的扩张过程可能会形成环形链表的问题。初始值默认为16.每次扩展都是最接近当前size的下一个2的n次方值。 +当然hashmap内部是通过一系列的位运算操作得到每次扩展的值的。如果要求在线程安全的环境下使用hashmap,推荐使用ConcurrentHashmap或者Collections.synchorizedMap(new HashMap())。此外,任何作为Hashmap键的对象,必须重写 +equals()与hashcode()方法;像我们平常使用的Integer/String都已经重写了该方法,因为key键是唯一的。 +2.HashMap方法说明: + 1)构造函数 + HashMap() + 使用默认的初始容量(16)和默认的加载因子(0.75)构造一个空的HashMap。 + HashMap(int initialCapacity) + 构造一个具有指定初始容量和默认负载因子(0.75)的空HashMap。 + HashMap(int initialCapacity, float loadFactor) + 构造一个具有指定初始容量和负载因子的空HashMap。 + HashMap(Map m) + 构造一个具有与指定Map相同的映射关系的新HashMap + 2)方法总结 + void clear() + 删除所有映射 + boolean containsKey(Object key) + 如果此映射包含指定键的映射,则返回true。 + boolean containsValue(Object value) + 如果此映射将一个或多个键映射到指定值,则返回true。 + Set> entrySet() + 返回Set此映射中包含的映射的视图。 + V get(Object key) + 返回指定键所映射的值,或者null此映射不包含该键的映射 + boolean isEmpty() + 如果此映射不包含键值映射,则返回true。 + Set keySet() + 返回Set此映射中包含的键的视图。 + V merge(K key, V value, BiFunction remappingFunction) + 如果指定的键尚未与值关联或与null关联,请将其与给定的非null值关联。 + V put(K key, V value) + 将指定值与该映射中的指定键相关联。 + void putAll(Map m) + 将所有映射从指定映射复制到此映射。 + V putIfAbsent(K key, V value) + 如果指定键尚未与值关联(或映射到null),则将其与给定值关联并返回 null,否则返回当前值。 + V remove(Object key) + 如果存在,则从此映射中删除指定键的映射。 + boolean remove(Object key, Object value) + 仅当当前映射到指定值时,才删除指定键的条目。 + + 3)使用说明 + a)HashMap的遍历方式 + Map map = new HashMap(); + //遍历方式1 + Set> set = map.entrySet(); + for(Entry entry: set) { + System.out.println(entry.getKey()+entry.getValue()); + } + + //遍历方式2 + Set keys = map.keySet(); + for(Integer key : keys) { + System.out.println(key+" "+map.get(key)); + } + //遍历方式3 + Collection values = map.values(); + for(String value: values) { + System.out.println(value); + } + //遍历方式4 jdk1.8+ + map.forEach((key,value)->{ + System.out.println(key+value); + }); +4) put/get方法原理分析 + 首先需要说明的是,HashMap底层是一个Entry类型的数组,Entry可以是链表结构也可以是红黑树结构,取决于链表的长度。 + V put(K key,V value)该方法会返回key对应的原值,如果原值存在的话。 + put方法调用的是putVal,该方法是final方法,不可被重写。key的hash是通过key.hashCode^(hashCode>>>16)得到的,因为数组长度有限, + 为了让key的hashcode高位也参加运算所以先右移16bit,再与原hash值异或,这样能更好的发散hash映射。 + 通过key定位到桶的位置(数组的下标):index = (n-1)&hash; + static final int hash(Object key) { + int h; + return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); + } + 得到index后,如果没有碰撞,就直接放入桶对应的位置;if ((p = tab[i = (n - 1) & hash]) == null) + tab[i] = newNode(hash, key, value, null); + 如果有碰撞,(jdk8)就遍历链表插入=到链表尾部。在此过程中,会记录步长,如果步长=8就会转换为红黑树。 + if (p instanceof TreeNode)//如果已经是红黑树节点 + e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); + else {//记录步长 + for (int binCount = 0; ; ++binCount) { + if ((e = p.next) == null) { + p.next = newNode(hash, key, value, null); + if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st + treeifyBin(tab, hash); + break; + } + if (e.hash == hash && + ((k = e.key) == key || (key != null && key.equals(k)))) + break; + p = e; + } + } + 如果size超过factor * capacity,就会resize() + if (++size > threshold) + resize(); + resize()是map的size触发阀值时自动扩容到原容量的2倍。newCap = oldCap<<1;因为扩容是扩充的 2 倍,n-1 转换为二进制也就是高位变成了1,那么根据(n - 1) & hash 计算,如果 hash 高位是 1 那么新的 index 位置就是 oldIndex + 16, + 如果hash 的高位 是 0 ,那么 index 的位置就是原来的 oldIndex 的位置,这样直接判断高位就可以了,省去了重新计算hash。 + + get方法原理分析 + public V get(Object key) { + Node e; + return (e = getNode(hash(key), key)) == null ? null : e.value; + } + getNode方法: + 根据key的hash值和key与桶对应位置的节点进行遍历匹配,key值相等hash值也相等的节点就是要找的节点。 + if (first.hash == hash && // always check first node + ((k = first.key) == key || (key != null && key.equals(k)))) + return first; + if ((e = first.next) != null) { + if (first instanceof TreeNode) + return ((TreeNode)first).getTreeNode(hash, key); + do { + if (e.hash == hash && + ((k = e.key) == key || (key != null && key.equals(k)))) + return e; + } while ((e = e.next) != null); + } diff --git a/Week 02/id_138/Node.java b/Week 02/id_138/Node.java new file mode 100644 index 000000000..786356126 --- /dev/null +++ b/Week 02/id_138/Node.java @@ -0,0 +1,13 @@ +import java.util.List; + +public class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +} diff --git a/Week 02/id_138/TreeNode.java b/Week 02/id_138/TreeNode.java new file mode 100644 index 000000000..fc6b2a2b6 --- /dev/null +++ b/Week 02/id_138/TreeNode.java @@ -0,0 +1,6 @@ +public class TreeNode { + int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { val = x; } +} diff --git a/Week 02/id_143/LeetCode_105_143.java b/Week 02/id_143/LeetCode_105_143.java new file mode 100644 index 000000000..5f440024b --- /dev/null +++ b/Week 02/id_143/LeetCode_105_143.java @@ -0,0 +1,83 @@ +/* + * @lc app=leetcode.cn id=105 lang=java + * + * [105] 从前序与中序遍历序列构造二叉树 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + +/** + * 分治,通过前序列表第一个元素,找到其在中序数组中位置K, + * 将中序分解成以K为分界点的左,右两个数组, + * 同步将 前序列表,从第二个元素至最后一个元素 分解为前后两个数组,分别对应中序左右两个数组长度 + */ +class Solution { + public TreeNode buildTree(int[] preorder, int[] inorder) { + if(preorder.length ==0 || inorder.length ==0 ){ + return null; + } + return findSon(preorder,inorder,0,preorder.length-1,0,inorder.length-1); + } + + public TreeNode findSon( + int[] preorder, + int[] inorder, + int preorderS, + int preorderE, + int inorderS, + int inorderE + ){ + //退出条件 + TreeNode currentNode = null; + if(preorderS == preorderE){ + currentNode = new TreeNode(preorder[preorderS]); + //加速收敛,可多打败18% + }else if(inorderS == inorderE){ + currentNode = new TreeNode(inorder[inorderE]); + }else{ + currentNode = new TreeNode(preorder[preorderS]); + //找到inorder中该节点下标 + int k = 0; + for(int i=0; (i+inorderS) <=inorderE;i++){ + if(inorder[i+inorderS] == preorder[preorderS]){ + k = i; + break; + } + } + if(k==0){ + currentNode.left = null; + } else { + currentNode.left = findSon(preorder,inorder,preorderS+1,preorderS+k,inorderS,inorderS+k-1); + } + + + + + if(inorderS+k == inorderE){ + currentNode.right = null; + }else{ + currentNode.right = findSon(preorder,inorder,preorderS+k+1,preorderS,inorderS+k+1,inorderE); + } + + + + } + return currentNode; + + } + +} +// @lc code=end + + + +//方法二,在上面的基础上,因为每次需要查某元素在中序中的位置取K值,推想是否用可以缓存元素在中序中的位置,利用位置差代置每次循环计算K值。 diff --git a/Week 02/id_143/LeetCode_144_143.java b/Week 02/id_143/LeetCode_144_143.java new file mode 100644 index 000000000..9e6e94a46 --- /dev/null +++ b/Week 02/id_143/LeetCode_144_143.java @@ -0,0 +1,62 @@ +/* + * @lc app=leetcode.cn id=144 lang=java + * + * [144] 二叉树的前序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List preorderTraversal(TreeNode root) { + List result = new ArrayList(); + if(root !=null ){ + result = getSon(root,result); + } + + return result; + } + + public List getSon(TreeNode currentNode,List result){ + if(currentNode.left == null && currentNode.right == null){ + result.add(currentNode.val); + return result; + } + result.add(currentNode.val); + if(currentNode.left != null){ + result = getSon(currentNode.left, result); + } + if(currentNode.right != null){ + result = getSon(currentNode.right, result); + } + return result; + } +} +// @lc code=end +//基于栈的实现 +class Solution { + public List preorderTraversal(TreeNode root) { + List result = new ArrayList(); + Stack stack = new Stack<>(); + TreeNode current = root; + while(!stack.isEmpty() || current!=null){ + while(current!=null ){ + stack.push(current); + result.add(current.val); + current = current.left; + } + current = stack.pop(); + current = current.right; + + + } + return result; + } +} \ No newline at end of file diff --git a/Week 02/id_143/LeetCode_145_143.java b/Week 02/id_143/LeetCode_145_143.java new file mode 100644 index 000000000..e8ef5b53a --- /dev/null +++ b/Week 02/id_143/LeetCode_145_143.java @@ -0,0 +1,68 @@ +/* + * @lc app=leetcode.cn id=145 lang=java + * + * [145] 二叉树的后序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List postorderTraversal(TreeNode root) { + List result = new ArrayList(); + if(root !=null ){ + result = getSon(root,result); + } + + return result; + } + + public List getSon(TreeNode currentNode,List result){ + if(currentNode.left == null && currentNode.right == null){ + result.add(currentNode.val); + return result; + } + + if(currentNode.left != null){ + result = getSon(currentNode.left, result); + } + if(currentNode.right != null){ + result = getSon(currentNode.right, result); + } + result.add(currentNode.val); + return result; + + } +} +// @lc code=end +// 比较难想到,或者说陷入 前序与中序的套路里了。 + //主要思路: 宽度优先,然后逆序输出 + public List postorderTraversal(TreeNode root) { + LinkedList result = new LinkedList(); + + if (root == null) { + return result; + } + LinkedList stack = new LinkedList(); + stack.push(root); + while( !stack.isEmpty() ){ + + TreeNode current = stack.pollLast(); + result.addFirst(current.val); + + if(current.left != null){ + stack.add(current.left); + } + if(current.right != null){ + stack.add(current.right); + } + } + return result; + } \ No newline at end of file diff --git a/Week 02/id_143/LeetCode_1_143.java b/Week 02/id_143/LeetCode_1_143.java new file mode 100644 index 000000000..06ebc0a22 --- /dev/null +++ b/Week 02/id_143/LeetCode_1_143.java @@ -0,0 +1,39 @@ +import java.util.Arrays; +/* + * @lc app=leetcode.cn id=1 lang=java + * + * [1] 两数之和 + * 双循环+哈希 5ms + */ + +// @lc code=start +class Solution { + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap(); + for (int i = 0; i < nums.length; i++) { + map.put(new Integer(nums[i]), i); + + } + for (int j = 0; j < nums.length; j++) { + if (map.containsKey(new Integer(target - nums[j])) && (j != map.get(new Integer(target - nums[j])))) { + return new int[]{j, map.get(new Integer(target - nums[j]))}; + } + } + return null; + } +} +// @lc code=end + +// 单循环 3ms +class Solution { + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap(); + for (int j = 0; j < nums.length; j++) { + if (map.containsKey(new Integer(target - nums[j])) && (j != map.get(new Integer(target - nums[j])))) { + return new int[]{j, map.get(new Integer(target - nums[j]))}; + } + map.put(new Integer(nums[j]), j); + } + return null; + } +} diff --git a/Week 02/id_143/LeetCode_20_143.java b/Week 02/id_143/LeetCode_20_143.java new file mode 100644 index 000000000..45ccb5d8a --- /dev/null +++ b/Week 02/id_143/LeetCode_20_143.java @@ -0,0 +1,38 @@ +/* + * @lc app=leetcode.cn id=22 lang=java + * + * [22] 括号生成 + */ + +// @lc code=start +class Solution { + public List generateParenthesis(int n) { + List result = new ArrayList<>(); + + generateP(0,0,n,"",result); + + return result; + } + + private static void generateP(int leftNum, int rightNum,int n, String s, List result) { + //terminator + if(leftNum == n && rightNum == n){ + result.add(s); + } + // process current level logic + //dill down + if(leftNum < n){ + generateP(leftNum+1,rightNum,n,s+"(",result); + + } + if(rightNum < leftNum ){ + generateP(leftNum,rightNum+1,n,s+")",result); + + } + + + //reverse states + } +} +// @lc code=end + diff --git a/Week 02/id_143/LeetCode_236_143.java b/Week 02/id_143/LeetCode_236_143.java new file mode 100644 index 000000000..593130a70 --- /dev/null +++ b/Week 02/id_143/LeetCode_236_143.java @@ -0,0 +1,79 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +//1、暴力法。 +//首先:遍历两次树,找出p与q的父列表pPath与qPath。 +//然后:循环比较pPath与qPath,找出最后一个相同的节点即为最近祖先节点 +//---居然:超时了,应该是10000节点那个用例。其次,自我感觉中间变量太多 + +class Solution { + private List pPath = new ArrayList(); + private List qPath = new ArrayList(); + + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + List pTempPath = new ArrayList(); + List qTempPath = new ArrayList(); + getPath(root,p,pTempPath,pPath); + getPath(root,q,qTempPath,qPath); + TreeNode parentNode = pPath.get(0); + for(int i=0;i currentPath = new ArrayList(); + currentPath.addAll(path); + getPath(currentNode.left,searchedNode,currentPath,resultPath); + } + if(currentNode.right !=null){ + List currentPath = new ArrayList(); + currentPath.addAll(path); + getPath(currentNode.right,searchedNode,currentPath,resultPath); + } + } +} +//借鉴官网 深度优先+回溯,比官方少一层遍历 +class Solution { + + public TreeNode ans; + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + + findMark(root,p,q); + + return ans; + } + + public boolean findMark(TreeNode currentNode,TreeNode p,TreeNode q){ + + int currentMark = (currentNode ==p || currentNode ==q )?1:0 ; + + int leftMark = (currentNode.left == null) ? 0 : (findMark(currentNode.left,p,q)? 1: 0 ); + + int rightMark = (currentNode.right == null) ? 0 : (findMark(currentNode.right,p,q)? 1: 0) ; + // + if((currentMark + leftMark+ rightMark) == 2){ + this.ans = currentNode ; + } + // + return (currentMark + leftMark+ rightMark) > 0 ; + + + } +} diff --git a/Week 02/id_143/LeetCode_242_143.java b/Week 02/id_143/LeetCode_242_143.java new file mode 100644 index 000000000..6665be34b --- /dev/null +++ b/Week 02/id_143/LeetCode_242_143.java @@ -0,0 +1,54 @@ +import java.util.Arrays; + +/* + * @lc app=leetcode.cn id=242 lang=java + * + * [242] 有效的字母异位词 + * 暴力法 + */ + +// @lc code=start +class Solution { + public boolean isAnagram(String s, String t) { + if(s.length()!= t.length() ){ + return false; + } + char[] ss = s.toCharArray(); + char[] tt = t.toCharArray(); + Arrays.sort(ss); Arrays.sort(tt); + for(int i=0;i> groupAnagrams(String[] strs) { + Map> a = new HashMap>(); + + for(String str : strs){ + char[] aa = str.toCharArray(); + Arrays.sort(aa); + + StringBuffer aaaa = new StringBuffer(); + for(int i =0;i bb = (a.get(aaa) == null)? (new ArrayList()) : a.get(aaa); + bb.add(str); + a.put(aaa, bb); + } + List result = new ArrayList>(); + for(String key : a.keySet()){ + result.add(a.get(key)); + } + return result; + } +} +// 最后四行可以用ArrayList 实现空间换时间,减少3M的内存,提高1ms +// @lc code=end + diff --git a/Week 02/id_143/LeetCode_94_143.java b/Week 02/id_143/LeetCode_94_143.java new file mode 100644 index 000000000..4e88d05c4 --- /dev/null +++ b/Week 02/id_143/LeetCode_94_143.java @@ -0,0 +1,70 @@ +/* + * @lc app=leetcode.cn id=94 lang=java + * + * [94] 二叉树的中序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + * 经典递归算法 + */ +class Solution { + public List inorderTraversal(TreeNode root) { + List result = new ArrayList(); + if(root == null){ + return result; + } + + result = getSon(root,result); + return result; + } + + public List getSon(TreeNode currentNode,List result){ + if(currentNode.left == null && currentNode.right == null){ + result.add(currentNode.val); + return result; + } + if(currentNode.left !=null){ + result = getSon(currentNode.left,result); + } + result.add(currentNode.val); + if(currentNode.right != null ){ + result = getSon(currentNode.right, result); + } + return result; + } + +} +// @lc code=end +//尝试加深理解基于栈的算法 + +class Solution { + public List inorderTraversal(TreeNode root) { + List result = new ArrayList(); + + + Stack stack = new Stack<>(); + TreeNode current = root; + while((!stack.isEmpty()) || (current != null)){ + while(current != null){ + stack.push(current); + current = current.left; + } + current = stack.pop(); + result.add(current.val); + current = current.right; + + } + return result; + } + + + +} diff --git a/Week 02/id_143/NOTE.md b/Week 02/id_143/NOTE.md index a6321d6e2..39bb394f2 100644 --- a/Week 02/id_143/NOTE.md +++ b/Week 02/id_143/NOTE.md @@ -1,4 +1,419 @@ -# NOTE +# 算法训练营学习 +# 第二周 +## 第五课 +### 哈希表、映射、集合的实现与特征 +1. 哈希表是根据关键码值进行直接访问的数据结构。平均时间复杂度为O(1) +2. 映射函数又叫散列函数,把关键码值映射到一个访问位置。 + +- 哈希表源码学习总结 HashSet、HashTable. + - HashSet底层基于HashMap来实现。 + - 非同步,支持 null 为key,key唯一。 + - 内部为数组+链表组合的数据结构,链表长>8时,转为红黑树存储 + - 主成员分析 + ``` + /** + * HashMap采用lazy-load机制,也就是我们通过构造函数初始化了一个HashMap后,仅仅是赋予了该容器负载因子,并没有创建容器。 + * 也可以通过指定负载因子和初始容量进行创建,但是即便指定了长度,HashMap也会将其转换为相近的2的幂次 + /** + * The default initial capacity - MUST be a power of two. + */ + static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 + + /** + * The maximum capacity, used if a higher value is implicitly specified + * by either of the constructors with arguments. + * MUST be a power of two <= 1<<30. + */ + static final int MAXIMUM_CAPACITY = 1 << 30; + + /** + * The load factor used when none specified in constructor. + */ + static final float DEFAULT_LOAD_FACTOR = 0.75f; + + /** + * The bin count threshold for using a tree rather than list for a + * bin. Bins are converted to trees when adding an element to a + * bin with at least this many nodes. The value must be greater + * than 2 and should be at least 8 to mesh with assumptions in + * tree removal about conversion back to plain bins upon + * shrinkage. + */ + static final int TREEIFY_THRESHOLD = 8; + + /** + * The bin count threshold for untreeifying a (split) bin during a + * resize operation. Should be less than TREEIFY_THRESHOLD, and at + * most 6 to mesh with shrinkage detection under removal. + */ + static final int UNTREEIFY_THRESHOLD = 6; + + /** + * The smallest table capacity for which bins may be treeified. + * (Otherwise the table is resized if too many nodes in a bin.) + * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts + * between resizing and treeification thresholds. + */ + static final int MIN_TREEIFY_CAPACITY = 64; + /** + * Basic hash bin node, used for most entries. (See below for + * TreeNode subclass, and in LinkedHashMap for its Entry subclass.) + */ + static class Node implements Map.Entry { + final int hash; + final K key; + V value; + Node next; + + Node(int hash, K key, V value, Node next) { + this.hash = hash; + this.key = key; + this.value = value; + this.next = next; + } + + public final K getKey() { return key; } + public final V getValue() { return value; } + public final String toString() { return key + "=" + value; } + + public final int hashCode() { + return Objects.hashCode(key) ^ Objects.hashCode(value); + } + + public final V setValue(V newValue) { + V oldValue = value; + value = newValue; + return oldValue; + } + + public final boolean equals(Object o) { + if (o == this) + return true; + if (o instanceof Map.Entry) { + Map.Entry e = (Map.Entry)o; + if (Objects.equals(key, e.getKey()) && + Objects.equals(value, e.getValue())) + return true; + } + return false; + } + } + + /* ---------------- Static utilities -------------- */ + + /** + * Computes key.hashCode() and spreads (XORs) higher bits of hash + * to lower. Because the table uses power-of-two masking, sets of + * hashes that vary only in bits above the current mask will + * always collide. (Among known examples are sets of Float keys + * holding consecutive whole numbers in small tables.) So we + * apply a transform that spreads the impact of higher bits + * downward. There is a tradeoff between speed, utility, and + * quality of bit-spreading. Because many common sets of hashes + * are already reasonably distributed (so don't benefit from + * spreading), and because we use trees to handle large sets of + * collisions in bins, we just XOR some shifted bits in the + * cheapest possible way to reduce systematic lossage, as well as + * to incorporate impact of the highest bits that would otherwise + * never be used in index calculations because of table bounds. + */ + static final int hash(Object key) { + int h; + return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); + } + + /** + * Returns x's Class if it is of the form "class C implements + * Comparable", else null. + */ + static Class comparableClassFor(Object x) { + if (x instanceof Comparable) { + Class c; Type[] ts, as; Type t; ParameterizedType p; + if ((c = x.getClass()) == String.class) // bypass checks + return c; + if ((ts = c.getGenericInterfaces()) != null) { + for (int i = 0; i < ts.length; ++i) { + if (((t = ts[i]) instanceof ParameterizedType) && + ((p = (ParameterizedType)t).getRawType() == + Comparable.class) && + (as = p.getActualTypeArguments()) != null && + as.length == 1 && as[0] == c) // type arg is c + return c; + } + } + } + return null; + } + + /** + * Returns k.compareTo(x) if x matches kc (k's screened comparable + * class), else 0. + */ + @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable + static int compareComparables(Class kc, Object k, Object x) { + return (x == null || x.getClass() != kc ? 0 : + ((Comparable)k).compareTo(x)); + } + + /** + * 逻辑右移,返回第一个比指定数大的2^n次方整数.为何要如此做,是因为Hash运算是 hash&(n-1)。 + * 其实用hash%length,更为科学,但是反而采用hash&(n-1)运算效率更高效。 + */ + static final int tableSizeFor(int cap) { + int n = cap - 1; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; + } + + /* ---------------- Fields -------------- */ + + /** + * The table, initialized on first use, and resized as + * necessary. When allocated, length is always a power of two. + * (We also tolerate length zero in some operations to allow + * bootstrapping mechanics that are currently not needed.) + */ + transient Node[] table; + + /** + * Holds cached entrySet(). Note that AbstractMap fields are used + * for keySet() and values(). + */ + transient Set> entrySet; + + /** + * The number of key-value mappings contained in this map. + */ + transient int size; + + /** + * The number of times this HashMap has been structurally modified + * Structural modifications are those that change the number of mappings in + * the HashMap or otherwise modify its internal structure (e.g., + * rehash). This field is used to make iterators on Collection-views of + * the HashMap fail-fast. (See ConcurrentModificationException). + */ + transient int modCount; + + /** + * The next size value at which to resize (capacity * load factor). + * + * @serial + */ + // (The javadoc description is true upon serialization. + // Additionally, if the table array has not been allocated, this + // field holds the initial array capacity, or zero signifying + // DEFAULT_INITIAL_CAPACITY.) + int threshold; + + /** + * The load factor for the hash table. + * + * @serial + */ + final float loadFactor; + ``` + - 构造方法 + ``` + /** + * Constructs an empty HashMap with the specified initial + * capacity and load factor. + * + * @param initialCapacity the initial capacity + * @param loadFactor the load factor + * @throws IllegalArgumentException if the initial capacity is negative + * or the load factor is nonpositive + */ + public HashMap(int initialCapacity, float loadFactor) { + if (initialCapacity < 0) + throw new IllegalArgumentException("Illegal initial capacity: " + + initialCapacity); + if (initialCapacity > MAXIMUM_CAPACITY) + initialCapacity = MAXIMUM_CAPACITY; + if (loadFactor <= 0 || Float.isNaN(loadFactor)) + throw new IllegalArgumentException("Illegal load factor: " + + loadFactor); + this.loadFactor = loadFactor; + this.threshold = tableSizeFor(initialCapacity); + } + + /** + * Constructs an empty HashMap with the specified initial + * capacity and the default load factor (0.75). + * + * @param initialCapacity the initial capacity. + * @throws IllegalArgumentException if the initial capacity is negative. + */ + public HashMap(int initialCapacity) { + this(initialCapacity, DEFAULT_LOAD_FACTOR); + } + + /** + * Constructs an empty HashMap with the default initial capacity + * (16) and the default load factor (0.75). + */ + public HashMap() { + this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted + } + + /** + * Constructs a new HashMap with the same mappings as the + * specified Map. The HashMap is created with + * default load factor (0.75) and an initial capacity sufficient to + * hold the mappings in the specified Map. + * + * @param m the map whose mappings are to be placed in this map + * @throws NullPointerException if the specified map is null + */ + public HashMap(Map m) { + this.loadFactor = DEFAULT_LOAD_FACTOR; + putMapEntries(m, false); + } + ``` + - get 方法 + ``` + 如果是TreeNode,直接查树,否则查链表 + public V get(Object key) { + Node e; + return (e = getNode(hash(key), key)) == null ? null : e.value; + } + + /** + * Implements Map.get and related methods + * + * @param hash hash for key + * @param key the key + * @return the node, or null if none + */ + final Node getNode(int hash, Object key) { + Node[] tab; Node first, e; int n; K k; + if ((tab = table) != null && (n = tab.length) > 0 && + (first = tab[(n - 1) & hash]) != null) { + if (first.hash == hash && // always check first node + ((k = first.key) == key || (key != null && key.equals(k)))) + return first; + if ((e = first.next) != null) { + if (first instanceof TreeNode) + return ((TreeNode)first).getTreeNode(hash, key); + do { + if (e.hash == hash && + ((k = e.key) == key || (key != null && key.equals(k)))) + return e; + } while ((e = e.next) != null); + } + } + return null; + } + ``` + - set 方法 + ```` + /** + * HashMap的存储节点的树化本质是一个安全问题。在现实环境下,构建冲突的数据比较容易,恶意代码就可以利用点,构造大量数据与服务端进行交互,导致服务端CPU大量被占用,这就构成了hash碰撞拒接服务攻击。树化可以一定程度上减少碰撞攻击带来的性能损失 + * + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @return the previous value associated with key, or + * null if there was no mapping for key. + * (A null return can also indicate that the map + * previously associated null with key.) + */ + public V put(K key, V value) { + return putVal(hash(key), key, value, false, true); + } + + /** + * Implements Map.put and related methods + * + * @param hash hash for key + * @param key the key + * @param value the value to put + * @param onlyIfAbsent if true, don't change existing value + * @param evict if false, the table is in creation mode. + * @return previous value, or null if none + */ + final V putVal(int hash, K key, V value, boolean onlyIfAbsent, + boolean evict) { + Node[] tab; + Node p;//存放待插入node + int n, i; + //若桶数组为空的话,通过resize方法创建一个桶数组 + if ((tab = table) == null || (n = tab.length) == 0) + n = (tab = resize()).length; + //通过hash运算得到的位置没有元素,则在该位置插入node + if ((p = tab[i = (n - 1) & hash]) == null) + tab[i] = newNode(hash, key, value, null); + else { + Node e; + K k; + //若出现hash碰撞,并且链表上第一个节点的key值与插入元素的key相同 + //会在方法结尾进行相同key的value替换 + if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) + e = p; + //判断当前位置是否是树化的节点,若树化了则按照红黑树插入方式插入元素 + else if (p instanceof TreeNode) + e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); + else {//若不是树化节点 + //遍历链表节点 + for (int binCount = 0; ; ++binCount) { + //若next节点为空,则插入node + if ((e = p.next) == null) { + p.next = newNode(hash, key, value, null); + //判断插入node后是否需要树化 + if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st + treeifyBin(tab, hash); + break; + } + //若next节点的key和插入node.key相同,退出循环 + if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) + break; + p = e; + } + } + //替换value + if (e != null) { + V oldValue = e.value; + if (!onlyIfAbsent || oldValue == null) + e.value = value; + afterNodeAccess(e); + return oldValue; + } + } + ++modCount; + //若桶数组有效长度大于阈值(当前容量*负载因子0.75),则调用resize方法进行扩容 + //扩容到原来的两倍后重排 + if (++size > threshold) + resize(); + afterNodeInsertion(evict); + + return null; + } + ```` +## 第六课 +### 树、二叉树、二叉搜索树 + - 树与图的区别在于: 下级节点不会指向之前出现过的节点。 + - 二叉搜索树 **左子树**节点全部小于 自己,**右子树**节点全部大于自己。因此中序遍历下,数据是有序排列。 + - 二叉搜索树的 添加、删除、查询的平均复杂度均为O(log N),最坏情况退化到n。 + - 二叉搜索树的删除:如果删除叶子节点,直接删除。删除非叶子节点时,需选其它节点替代其位置(可以是其左子树最右节点,或右子树最左节点,但我们一般选右子树最左节点) +## 第七课 +### 泛型递归、树的递归 + - 泛型递归:4步 + 1. recursion terminator. + 2. process logic int current level. + 3. drill down. + 4. reverse the current level states if needed + - 泛型递归思维要点 + 1. 不要人肉递归。 + 2. 找最近最简方法,将其折解成为可重复解决的问题(重复子问题)。 + 3. 数学归纳法。 + - 树的递归(前中后序,只需切换代码) + +## 个人感悟 + - **课程学习计划问题**: 本周与上周的安程数以及练习题数有所有不,应该按原先定好的时间计划执行,需要先看课程结合自己的时间,规划好学习计划再执行。 + - **练习问题**: 本周的计划与上周的重温,时间上还有一些冲突。 + - 团队问题: 定期"**叫醒**"服务。我应该天天催一催,要有套路和切入点。 - diff --git a/Week 02/id_148/LeetCode_17_148.java b/Week 02/id_148/LeetCode_17_148.java new file mode 100644 index 000000000..ca8f5ea8f --- /dev/null +++ b/Week 02/id_148/LeetCode_17_148.java @@ -0,0 +1,48 @@ +package com.ning.test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 出口条件 + * 逻辑处理 + * drill down + * 状态保存 + * + * @author shaoyu + * @create 2019-11-03 下午11:01 + */ +public class LeetCode_17_148 { + private Map phone = new HashMap() {{ + put("2", "abc"); + put("3", "def"); + put("4", "ghi"); + put("5", "jkl"); + put("6", "mno"); + put("7", "pqrs"); + put("8", "tuv"); + put("9", "wxyz"); + }}; + private List output = new ArrayList(); + + public void recursion(String combination, String next_digits) { + if (next_digits.length() == 0) { + output.add(combination); + } else { + String digit = next_digits.substring(0, 1); + String letters = phone.get(digit); + for (int i = 0; i < letters.length(); i++) { + String letter = phone.get(digit).substring(i, i + 1); + recursion(combination + letter, next_digits.substring(1)); + } + } + } + + public List letterCombinations(String digits) { + if (digits.length() != 0) + recursion("", digits); + return output; + } +} diff --git a/Week 02/id_148/LeetCode_242_148.java b/Week 02/id_148/LeetCode_242_148.java new file mode 100644 index 000000000..189660098 --- /dev/null +++ b/Week 02/id_148/LeetCode_242_148.java @@ -0,0 +1,61 @@ +package com.ning.test; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +/** + * 描述: + * + * @author shaoyu + * @create 2019-10-24 下午6:32 + */ +public class LeetCode_242_148 { + public boolean isAnagram(String s, String t) { + Map map = new HashMap<>(); + + if(s == null) { + return false; + } + + if(t == null) { + return false; + } + + if(s.length() != t.length()) { + return false; + } + + char[] arrS = s.toCharArray(); + for(int i = 0; i < s.length(); i++) { + if(map.get(arrS[i]) == null) { + map.put(arrS[i], 1); + } else { + map.put(arrS[i], map.get(arrS[i]) + 1); + } + } + + char[] arrT = t.toCharArray(); + for(int i = 0; i < t.length(); i++) { + if(map.get(arrT[i]) == null) { + return false; + } else if(map.get(arrT[i]) == 1) { + map.remove(arrT[i]); + } else { + map.put(arrT[i], map.get(arrT[i]) - 1); + } + } + + if(map.isEmpty()) { + return true; + } + + return false; + } + + @Test + public void test() { + System.out.println(isAnagram("anagram", "Aagaram")); + } +} diff --git a/Week 02/id_148/LeetCode_77_148.java b/Week 02/id_148/LeetCode_77_148.java new file mode 100644 index 000000000..42f01ee5f --- /dev/null +++ b/Week 02/id_148/LeetCode_77_148.java @@ -0,0 +1,34 @@ +package com.ning.test; + +import java.util.LinkedList; +import java.util.List; + +/** + * 描述: + * + * @author shaoyu + * @create 2019-11-02 下午1:36 + */ +public class LeetCode_77_148 { + List> output = new LinkedList(); + int n; + int k; + + public void backtrack(int first, LinkedList curr) { + if (curr.size() == k) + output.add(new LinkedList(curr)); + + for (int i = first; i < n + 1; ++i) { + curr.add(i); + backtrack(i + 1, curr); + curr.removeLast(); + } + } + + public List> combine(int n, int k) { + this.n = n; + this.k = k; + backtrack(1, new LinkedList()); + return output; + } +} diff --git a/Week 02/id_153/LeetCode_144_153.js b/Week 02/id_153/LeetCode_144_153.js new file mode 100644 index 000000000..dffeeeff5 --- /dev/null +++ b/Week 02/id_153/LeetCode_144_153.js @@ -0,0 +1,17 @@ +var preorderTraversal = function(root) { + let result = []; + const preorderTree = root => { + if (root) { + result.push(root.val); + if (root.left) { + preorderTree(root.left); + } + if (root.right) { + preorderTree(root.right); + } + } + }; + + preorderTree(root); + return result; +}; diff --git a/Week 02/id_153/LeetCode_242_153.java b/Week 02/id_153/LeetCode_242_153.java new file mode 100644 index 000000000..13c790ddf --- /dev/null +++ b/Week 02/id_153/LeetCode_242_153.java @@ -0,0 +1,44 @@ +import java.util.Arrays; + +/** + * LeetCode_242_153 https://leetcode-cn.com/problems/valid-anagram/ + */ +public class LeetCode_242_153 { + + /** + * 如果两个字符串长度不相等,一定不是异或词。 将字符串排序,排序后的结果相等,则为异或词 + * + * @param s + * @param t + * @return + */ + public boolean isAnagram1(String s, String t) { + if (s.length() != t.length()) { + return false; + } + char[] str1 = s.toCharArray(); + char[] str2 = t.toCharArray(); + Arrays.sort(str1); + Arrays.sort(str2); + return Arrays.equals(str1, str2); + } + + public boolean isAnagram2(String s, String t) { + if (s.length() != t.length()) { + return false; + } + + int[] alpha = new int[26]; + for (int i = 0; i < s.length(); i++) { + alpha[s.charAt(i) - 'a']++; + alpha[t.charAt(i) - 'a']--; + } + + for (int i = 0; i < alpha.length; i++) { + if (alpha[i] != 0) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/Week 02/id_153/LeetCode_242_153.js b/Week 02/id_153/LeetCode_242_153.js new file mode 100644 index 000000000..43063986d --- /dev/null +++ b/Week 02/id_153/LeetCode_242_153.js @@ -0,0 +1,34 @@ +const isAnagram1 = (s, t) => { + let sArr = s.split(""); + let tArr = t.split(""); + sArr.sort(); + tArr.sort(); + return sArr + "" === tArr + ""; +}; + +const isAnagram2 = (s, t) => { + const map = new Map(); + for (let i = 0; i < s.length; i++) { + if (map.has(s[i])) { + map.set(s[i], map.get(s[i]) + 1); + } else { + map.set(s[i], 1); + } + } + + for (let i = 0; i < t.length; i++) { + if (map.has(t[i])) { + map.set(t[i], map.get(t[i]) - 1); + } else { + map.set(t[i], 1); + } + + if (map.get(t[i]) === 0) { + map.delete(t[i]); + } + } + if (map.size !== 0) { + return false; + } + return true; +}; diff --git a/Week 02/id_153/LeetCode_94_153.js b/Week 02/id_153/LeetCode_94_153.js new file mode 100644 index 000000000..8cb1f55cc --- /dev/null +++ b/Week 02/id_153/LeetCode_94_153.js @@ -0,0 +1,17 @@ +var inorderTraversal = function(root) { + let result = []; + const inorderTree = root => { + if (root) { + if (root.left) { + inorderTree(root.left); + } + result.push(root.val); + if (root.right) { + inorderTree(root.right); + } + } + }; + + inorderTree(root); + return result; +}; diff --git a/Week 02/id_153/NOTE.md b/Week 02/id_153/NOTE.md index a6321d6e2..f87204d60 100644 --- a/Week 02/id_153/NOTE.md +++ b/Week 02/id_153/NOTE.md @@ -1,4 +1,47 @@ -# NOTE +# 总结 +1. 哈希表 + 也叫散列表,是一种 key-value 形式的数据结构,可以根据哈希函数直接计算得出其位置,所以时间复杂度是 O(1) 的。 + 最坏情况下,哈希函数不好或者空间太小,导致不停的发生碰撞,而发生碰撞后,哈希表会以链表的形式来存储相同位置的元素,这也就导致了其时间复杂度退化成了 O(n) + 因为时间都耗在了链表的查询上了。 +2. 树、二叉树 + 二叉树,根节点、左节点、右节点。遍历的时间复杂度 O(n)。 + 二叉搜索树,其左节点都小于跟节点,而其又节点大于根节点。时间复杂度 O(logn)。 + 前序遍历: 根-左-右 + 中序遍历: 左-根-右 + 后序遍历: 左-右-根 + 一般记忆可以根据根节点的访问顺序来记忆。 + 写熟练三种遍历的递归写法 + 模板代码 + ```python + def preorder(self, root): + if root: + self.traverse_path.append(root.val) + self.preorder(root.left) + self.preorder(root.right) - + def inorder(self, root): + if root: + self.preorder(root.left) + self.traverse_path.append(root.val) + self.preorder(root.right) + def postorder(self, root): + if root: + self.preorder(root.left) + self.preorder(root.right) + self.traverse_path.append(root.val) + ``` +3. 递归的模板代码,记住,然后写递归的时候就可以套用此模板 + ```java + public void recur(int level, int param) { + // 1. terminator 递归结束条件 + if (level > MAX_LEVEL) { // process result + return; + } + // 2. process current logic 当前逻辑 + process(level, param); + // 3. drill down 递归调用 + recur( level: level + 1, newParam); + // 4. restore current status 定义了全局变量记得清空 + } + ``` \ No newline at end of file diff --git "a/Week 02/id_158/144.\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.cs" "b/Week 02/id_158/144.\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.cs" new file mode 100644 index 000000000..be1a17401 --- /dev/null +++ "b/Week 02/id_158/144.\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.cs" @@ -0,0 +1,55 @@ +/* + * @lc app=leetcode.cn id=144 lang=csharp + * + * [144] 二叉树的前序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int x) { val = x; } + * } + */ +public class Solution { + public IList PreorderTraversal(TreeNode root) { + IList res = new List(); + Preorder(root,res); + return res; + } + + private void Preorder(TreeNode treeNode,IList res){ + if(treeNode==null) return; + res.Add(treeNode.val); + Preorder(treeNode.left,res); + Preorder(treeNode.right,res); + } + + public IList PreorderTraversalWithStack(TreeNode root){ + IList res = new List(); + if(root==null){ + return res; + } + TreeNode treeNode = root; + Stack stack = new Stack(); + while (treeNode!=null || stack.Count>0) + { + while (treeNode!=null) + { + res.Add(treeNode.val); + stack.Push(treeNode); + treeNode = treeNode.left; + } + if(stack.Count>0){ + treeNode=stack.Pop(); + treeNode = treeNode.right; + } + } + return res; + } +} +// @lc code=end + diff --git "a/Week 02/id_158/22.\346\213\254\345\217\267\347\224\237\346\210\220.cs" "b/Week 02/id_158/22.\346\213\254\345\217\267\347\224\237\346\210\220.cs" new file mode 100644 index 000000000..e80667f74 --- /dev/null +++ "b/Week 02/id_158/22.\346\213\254\345\217\267\347\224\237\346\210\220.cs" @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode.cn id=22 lang=csharp + * + * [22] 括号生成 + */ + +// @lc code=start +public class Solution { + IList result = new List(); + public IList GenerateParenthesis(int n) { + Generate(0,0,n,""); + return result; + } + private void Generate(int left,int right,int max,string str) + { + if(left ==max && right==max){ + result.Add(str); + return; + } + + if(left> GroupAnagrams(string[] strs) + { + int len = strs.Length; + IList> result = new List>(); + Dictionary> map = new Dictionary>(); + foreach (var str in strs) + { + char[] arrStr = str.ToCharArray(); + Array.Sort(arrStr); + string strSort = new string(arrStr); + if (map.ContainsKey(strSort)) + { + map[strSort].Add(str); + } + else + { + map.Add(strSort, new List { str }); + } + } + foreach (var item in map) + { + result.Add(item.Value); + } + return result; + } + + public IList> GroupAnagrams(string[] strs) + { + int len = strs.Length; + IList> result = new List>(); + Dictionary> map = new Dictionary>(); + foreach (var str in strs) + { + char[] arrStr = str.ToCharArray(); + Array.Sort(arrStr); + string strSort = new string(arrStr); + if (!map.ContainsKey(strSort)) + { + map.Add(strSort, new List()); + + } + map[strSort].Add(str); + } + foreach (var item in map) + { + result.Add(item.Value); + } + return result; + } +} +// @lc code=end + diff --git "a/Week 02/id_158/70.\347\210\254\346\245\274\346\242\257.cs" "b/Week 02/id_158/70.\347\210\254\346\245\274\346\242\257.cs" new file mode 100644 index 000000000..e263e9b1a --- /dev/null +++ "b/Week 02/id_158/70.\347\210\254\346\245\274\346\242\257.cs" @@ -0,0 +1,24 @@ +/* + * @lc app=leetcode.cn id=70 lang=csharp + * + * [70] 爬楼梯 + */ + +// @lc code=start +using System.Collections.Generic; + +public class Solution5 { + private Dictionary dict = new Dictionary(); + public int ClimbStairs(int n) { + if(n==1) return 1; + if(n==2) return 2; + if(dict.ContainsKey(n)){ + return dict[n]; + } + var fn= ClimbStairs(n-1)+ClimbStairs(n-2); + dict.Add(n,fn); + return fn; + } +} +// @lc code=end + diff --git "a/Week 02/id_158/78.\345\255\220\351\233\206.cs" "b/Week 02/id_158/78.\345\255\220\351\233\206.cs" new file mode 100644 index 000000000..b45281853 --- /dev/null +++ "b/Week 02/id_158/78.\345\255\220\351\233\206.cs" @@ -0,0 +1,62 @@ +/* + * @lc app=leetcode.cn id=78 lang=csharp + * + * [78] 子集 + * + * https://leetcode-cn.com/problems/subsets/description/ + * + * algorithms + * Medium (75.00%) + * Likes: 368 + * Dislikes: 0 + * Total Accepted: 42.1K + * Total Submissions: 56.1K + * Testcase Example: '[1,2,3]' + * + * 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。 + * + * 说明:解集不能包含重复的子集。 + * + * 示例: + * + * 输入: nums = [1,2,3] + * 输出: + * [ + * ⁠ [3], + * [1], + * [2], + * [1,2,3], + * [1,3], + * [2,3], + * [1,2], + * [] + * ] + * + */ + +// @lc code=start +public class Solution { + public IList> Subsets(int[] nums) { + IList> ans = new List>(); + IList list = new List(); + dfs(ans,list,nums,0); + return ans; + } + + private void dfs(IList> ans,IList list,int[] nums,int index){ + if(index == nums.Length){ + ans.Add(list.ToList()); + return; + } + + dfs(ans,list,nums,index+1); + + list.Add(nums[index]); + + dfs(ans,list,nums,index+1); + + list.RemoveAt(list.Count-1); + } +} +// @lc code=end + diff --git "a/Week 02/id_158/94.\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.cs" "b/Week 02/id_158/94.\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.cs" new file mode 100644 index 000000000..39a22bf18 --- /dev/null +++ "b/Week 02/id_158/94.\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.cs" @@ -0,0 +1,61 @@ +/* + * @lc app=leetcode.cn id=94 lang=csharp + * + * [94] 二叉树的中序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int x) { val = x; } + * } + */ +public class Solution { + public IList InorderTraversal(TreeNode root) { + List res = new List(); + if(root==null){ + return res; + } + IList left = InorderTraversal(root.left); + if(left!=null && left.Any()){ + res.AddRange(left); + } + res.Add(root.val); + IList right= InorderTraversal(root.right); + if(right!=null && right.Any()){ + res.AddRange(right); + } + return res; + } + + public IList InorderTraversal(TreeNode root){ + List res = new List(); + if(root==null){ + return res; + } + Stack stack = new Stack(); + TreeNode treeNode = root; + while (treeNode!=null || stack.Count>0) + { + while (treeNode!=null) + { + stack.Push(treeNode); + treeNode = treeNode.left; + } + + if (stack.Count>0) + { + treeNode=stack.Pop(); + res.Add(treeNode.val); + treeNode = treeNode.right; + } + } + return res; + } +} +// @lc code=end + diff --git a/Week 02/id_173/LeetCode_144_173.cpp b/Week 02/id_173/LeetCode_144_173.cpp new file mode 100644 index 000000000..728e95f96 --- /dev/null +++ b/Week 02/id_173/LeetCode_144_173.cpp @@ -0,0 +1,27 @@ +/* + * 144. 二叉树的前序遍历 + */ + +class Solution { +public: + vector preorderTraversal(TreeNode* root) { + vector res; + stack s; + TreeNode* curNode = root; + + while(curNode || !s.empty()) { + while(curNode) { + res.push_back(curNode->val); + s.push(curNode); + curNode = curNode->left; + } + + TreeNode* top = s.top(); + s.pop(); + + curNode = top->right; + } + + return res; + } +}; \ No newline at end of file diff --git a/Week 02/id_173/LeetCode_1_173.cpp b/Week 02/id_173/LeetCode_1_173.cpp new file mode 100644 index 000000000..effe5bd2d --- /dev/null +++ b/Week 02/id_173/LeetCode_1_173.cpp @@ -0,0 +1,22 @@ +/* + * 1. 两数之和 + */ + +class Solution { +public: + vector twoSum(vector& nums, int target) { + vector res; + unordered_map hashTable; + + for(int i=0; i> levelOrder(Node* root) { + vector> res; + + if(root == NULL) + return res; + + queue q; + q.push(root); + + while(!q.empty()) { + vector temp; + int size = q.size(); + + for(int i=0; ival); + for(auto child : curNode->children) + q.push(child); + } + + res.push_back(temp); + } + + return res; + } +}; \ No newline at end of file diff --git a/Week 02/id_173/LeetCode_49_173.cpp b/Week 02/id_173/LeetCode_49_173.cpp new file mode 100644 index 000000000..53825e9f9 --- /dev/null +++ b/Week 02/id_173/LeetCode_49_173.cpp @@ -0,0 +1,25 @@ +/* + * 49. 字母异位词分组 + */ + +class Solution { +public: + vector> groupAnagrams(vector& strs) { + vector> res; + unordered_map hashTable; + + for(auto str : strs) { + string temp = str; + sort(temp.begin(), temp.end()); + + if(hashTable.count(temp)) + res[hashTable[temp]].push_back(str); + else { + hashTable[temp] = res.size(); + res.push_back(vector({str})); + } + } + + return res; + } +}; \ No newline at end of file diff --git a/Week 02/id_173/LeetCode_589_173.cpp b/Week 02/id_173/LeetCode_589_173.cpp new file mode 100644 index 000000000..d304efe25 --- /dev/null +++ b/Week 02/id_173/LeetCode_589_173.cpp @@ -0,0 +1,29 @@ +/* + * 589. N叉树的前序遍历 + */ + +class Solution { +public: + vector preorder(Node* root) { + vector res; + + if(root == NULL) + return res; + + stack s; + s.push(root); + + while(!s.empty()) { + Node* top = s.top(); + s.pop(); + res.push_back(top->val); + + for(int i=top->children.size()-1; i>=0; --i) { + if(top->children[i]) + s.push(top->children[i]); + } + } + + return res; + } +}; \ No newline at end of file diff --git a/Week 02/id_173/LeetCode_590_173.cpp b/Week 02/id_173/LeetCode_590_173.cpp new file mode 100644 index 000000000..9fc570ae0 --- /dev/null +++ b/Week 02/id_173/LeetCode_590_173.cpp @@ -0,0 +1,33 @@ +/* + * 590. N叉树的后序遍历 + */ + +class Solution { +public: + vector postorder(Node* root) { + vector res; + + if(root == NULL) + return res; + + stack> s; + s.push(make_pair(root, 0)); + + while(!s.empty()) { + pair top = s.top(); + + if(top.second == 1) { + s.pop(); + res.push_back(top.first->val); + } + else { + s.top().second = 1; + for(int i=(top.first->children.size())-1; i>=0; --i) { + s.push(make_pair(top.first->children[i], 0)); + } + } + } + + return res; + } +}; \ No newline at end of file diff --git a/Week 02/id_173/LeetCode_94_173.cpp b/Week 02/id_173/LeetCode_94_173.cpp new file mode 100644 index 000000000..f0a0dc1d1 --- /dev/null +++ b/Week 02/id_173/LeetCode_94_173.cpp @@ -0,0 +1,27 @@ +/* + * 94. 二叉树的中序遍历 + */ + +class Solution { +public: + vector inorderTraversal(TreeNode* root) { + vector res; + stack s; + TreeNode* curNode = root; + + while(curNode || !s.empty()) { + while(curNode) { + s.push(curNode); + curNode = curNode->left; + } + + TreeNode* top = s.top(); + s.pop(); + res.push_back(top->val); + + curNode = top->right; + } + + return res; + } +}; \ No newline at end of file diff --git a/Week 02/id_173/NOTE.md b/Week 02/id_173/NOTE.md index a6321d6e2..bd59b6502 100644 --- a/Week 02/id_173/NOTE.md +++ b/Week 02/id_173/NOTE.md @@ -1,4 +1,71 @@ -# NOTE +## 一、关联式容器:map +  map 作为一个关联式容器,它的所有元素类型都是`pair`,其中`pair`的第一个元素被视为**键值**(key),第二个元素被视为**实值**(value)。 +### 1. map +- 在 map 中,不允许任何两个元素拥有相同的键值。 +- map 底层由**红黑树**来实现,所以它的所有元素都会根据元素的键值自动被排序。 - +### 2. multimap +- 在 multimap 中,允许键值重复。 +- multimap 底层由**红黑树**来实现,所以它的所有元素都会根据元素的键值自动被排序。 +### 3. unordered_map +- 在 unordered_map 中,不允许任何两个元素拥有相同的键值。 +- unordered_map 底层由**哈希表**来实现,所以在 unordered_map 中查找一个元素的平均时间复杂度为 `O(1)` 。 + +### 4. unordered_multimap +- 在 unordered_multimap 中,允许键值重复。 +- unordered_multimap 底层由**哈希表**来实现,所以在 unordered_map 中查找一个元素的平均时间复杂度为 `O(1)` 。 + +## 二、关联式容器:set +  set 作为一个关联式容器,它的元素不像 map 那样可以同时拥有键值(key)和实值(value),但是在 set 中元素的键值其实就是实值,而实值其实也是键值。 +### 1. set +- 在 set 中,不允许任何两个元素拥有相同的键值。 +- set 底层由**红黑树**来实现,所以它的所有元素都会根据元素的键值自动被排序。 + +### 2. multiset +- 在 multiset 中,允许键值重复。 +- multiset 底层由**红黑树**来实现,所以它的所有元素都会根据元素的键值自动被排序。 + +### 3. unordered_set +- 在 unordered_set 中,不允许任何两个元素拥有相同的键值。 +- unordered_set 底层由**哈希表**来实现,所以在 unordered_set 中查找一个元素的平均时间复杂度为 `O(1)` 。 + +### 4. unordered_multiset +- 在 unordered_multiset 中,允许键值重复。 +- unordered_multiset 底层由**哈希表**来实现,所以在 unordered_set 中查找一个元素的平均时间复杂度为 `O(1)` 。 + +## 三、二叉树 +### 1. 遍历方式 +- 深度优先搜索 + - 先序遍历(根,左,右) + - 中序遍历(左,根,右) + - 后序遍历(左,右,根) +- 广度优先搜索 + - 层序遍历 + +### 2. 二叉搜索树 +- 性质 + - 若它的左子树不为空,则左子树上所有结点的值均小于它的根结点的值; + - 若它的右子树不为空,则右子树上所有结点的值均大于它的根结点的值; + - 它的左、右子树也分别为二叉搜索树。 +- 特点 + - 中序遍历一棵二叉搜索树,其结果是有序的。 + +## 四、递归 +### 1. 思维要点 +- 不要人肉进行递归(最大误区) +- 找到最近最简方法,将其拆解成可重复解决的问题(重复子问题) +- 数学归纳法思维 + +### 2. 模板 +> 第一步:递归终止条件
+> 第二步:处理当前层
+> 第三步:下探到下一层
+> 第四步:清理当前层的状态(可能需要)
+ +## 五、分治和回溯 +  分治和回溯本质上其实都是递归,只是在递归基础之上又做了一些不同的技术改进。 +### 1. 分治 +  先将大问题划分为多个子问题,然后逐个解决每个子问题,最后再将所有子问题的结果进行合并,组成一个最终结果。 +### 2. 回溯 +  在分步解决一个问题时,当尝试后发现现有的分步解法不能得到有效的正确的答案的时候,它将取消上一步甚至是上几步的计算,再通过其它的可能的分步解法,再次尝试寻找问题的答案。 diff --git a/Week 02/id_183/Leetcode_144_183.cpp b/Week 02/id_183/Leetcode_144_183.cpp new file mode 100644 index 000000000..cf972fddd --- /dev/null +++ b/Week 02/id_183/Leetcode_144_183.cpp @@ -0,0 +1,50 @@ +/* + * @lc app=leetcode id=144 lang=cpp + * + * [144] Binary Tree Preorder Traversal + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ +class Solution { +public: + vector preorderTraversal(TreeNode* root) { + vector res; + if(!root){ + return res; + } + stack bucket; + + bucket.push(root); + while(!bucket.empty()) + { + TreeNode* cur = bucket.top(); + res.push_back(cur->val); + bucket.pop(); + if(cur->right) + { + bucket.push(cur->right); + } + + + if(cur->left) + { + bucket.push(cur->left); + } + + + } + + return res; + } +}; +// @lc code=end + diff --git a/Week 02/id_183/Leetcode_146_183.cpp b/Week 02/id_183/Leetcode_146_183.cpp new file mode 100644 index 000000000..eec738bc1 --- /dev/null +++ b/Week 02/id_183/Leetcode_146_183.cpp @@ -0,0 +1,69 @@ +/* + * @lc app=leetcode id=146 lang=cpp + * + * [146] LRU Cache + */ + +// @lc code=start +class LRUCache { +private: + int cap; + //double linked list: key & val + list> cache; + //hash table: position that key --> (key , cal)in cache + unordered_map>::iterator> map; +public: + LRUCache(int capacity) { + this->cap = capacity; + } + int get(int key) { + auto it = map.find(key); + //key not exist + if(it == map.end()) return -1; + //key exist, (k,v) in font of queue + pair kv = *map[key]; + cache.erase(map[key]); + cache.push_front(kv); + //update cache + map[key] = cache.begin(); + return kv.second; //value + } + + void put(int key, int value) { + //whether the key exist + auto it = map.find(key); + if(it == map.end()) + { + /*key not exust, cache is full or not*/ + if(cache.size() == cap) + { + //cahce is full , erase rear + //erase comparable elements in map and cache + auto lastPair = cache.back(); + int lastKey = lastPair.first; + map.erase(lastKey); + cache.pop_back(); + } + //cache is not full + cache.push_front(make_pair(key,value)); + map[key] = cache.begin(); + } + else + { + //key exists,change value and put it in the front + cache.erase(map[key]); + cache.push_front(make_pair(key,value)); + map[key] = cache.begin(); + } + + } +}; + +/** + * Your LRUCache object will be instantiated and called as such: + * LRUCache* obj = new LRUCache(capacity); + * int param_1 = obj->get(key); + * obj->put(key,value); + */ +// @lc code=end + diff --git a/Week 02/id_183/Leetcode_169_183.cpp b/Week 02/id_183/Leetcode_169_183.cpp new file mode 100644 index 000000000..f52a92f53 --- /dev/null +++ b/Week 02/id_183/Leetcode_169_183.cpp @@ -0,0 +1,28 @@ +/* + * @lc app=leetcode id=169 lang=cpp + * + * [169] Majority Element + */ + +// @lc code=start +class Solution { +public: + int majorityElement(vector& nums) { + int target = nums[0]; + int count = 1; + for(int i = 1;i twoSum(vector& nums, int target) { + // vector res(2,0); + // for(int i=0; i a; //one to one hash + vector b(2,-1);//vector for result; + for(int i =0; i0) + { + b[0] = a[target - nums[i]]; + b[1]=i; + break; + } + a[nums[i]] = i; + } + return b; + } +}; +// @lc code=end + diff --git a/Week 02/id_183/Leetcode_236_183.cpp b/Week 02/id_183/Leetcode_236_183.cpp new file mode 100644 index 000000000..15c19b2b3 --- /dev/null +++ b/Week 02/id_183/Leetcode_236_183.cpp @@ -0,0 +1,28 @@ +/* + * @lc app=leetcode id=236 lang=cpp + * + * [236] Lowest Common Ancestor of a Binary Tree + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ +class Solution { +public: + TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { + if (!root || root == p || root == q) return root; + TreeNode* left = lowestCommonAncestor(root->left, p, q); + TreeNode* right = lowestCommonAncestor(root->right, p, q); + if (left && right) return root; + return left ? left : right; + } +}; +// @lc code=end + diff --git a/Week 02/id_183/Leetcode_242_183.cpp b/Week 02/id_183/Leetcode_242_183.cpp new file mode 100644 index 000000000..dd3cb8e78 --- /dev/null +++ b/Week 02/id_183/Leetcode_242_183.cpp @@ -0,0 +1,30 @@ +/* + * @lc app=leetcode id=242 lang=cpp + * + * [242] Valid Anagram + */ + +// @lc code=start +class Solution { +public: + bool isAnagram(string s, string t) { + if(s.length() != t.length()) + { + + return false; + } + vector alpha (26,0); + for(int i = 0;i children; + + Node() {} + + Node(int _val, vector _children) { + val = _val; + children = _children; + } +}; +*/ +class Solution { +public: + vector> levelOrder(Node* root) { + if(!root) return {}; + vector> ans; + queue que; + que.push(root); + while(!que.empty()) + { + vector v; + for(int i=que.size();i;i--) + { + //压入当前层 + Node* curr=que.front(); + que.pop(); + v.push_back(curr->val); + for(Node* it:curr->children) + que.push(it); + } + ans.push_back(v); + } + return ans; + } +}; +// @lc code=end + diff --git a/Week 02/id_183/Leetcode_47_183.cpp b/Week 02/id_183/Leetcode_47_183.cpp new file mode 100644 index 000000000..2ebbe6e85 --- /dev/null +++ b/Week 02/id_183/Leetcode_47_183.cpp @@ -0,0 +1,41 @@ +/* + * @lc app=leetcode id=47 lang=cpp + * + * [47] Permutations II + */ + +// @lc code=start +class Solution { +public: + vector nums; + vector> res; + vector path; + + void DFS(int level,vector& visitor){ + if(level < 0){ + res.push_back(path); + return; + } + for(int i = 0;i0 && nums[i] == nums[i-1]&&!visitor[i-1]) + continue; + visitor[i] = true; + path.push_back(nums[i]); + DFS(level-1,visitor); + path.pop_back(); + visitor[i] = false; + } + } + + vector> permuteUnique(vector& nums) { + sort(nums.begin(),nums.end()); + vector visitor(nums.size(),false); + this->nums = nums; + DFS(nums.size()-1,visitor); + + return res; + + } +}; +// @lc code=end + diff --git a/Week 02/id_183/Leetcode_49_183.cpp b/Week 02/id_183/Leetcode_49_183.cpp new file mode 100644 index 000000000..ca60d9deb --- /dev/null +++ b/Week 02/id_183/Leetcode_49_183.cpp @@ -0,0 +1,30 @@ +/* + * @lc app=leetcode id=49 lang=cpp + * + * [49] Group Anagrams + */ + +// @lc code=start +class Solution { +public: + vector> groupAnagrams(vector& strs) { + unordered_map> hashMap; + for(auto s: strs) + { + string temp = s; + sort(temp.begin(),temp.end()); + hashMap[temp].push_back(s); + } + int len = hashMap.size(); + vector> ans(len); + int index = 0; + for(auto i :hashMap) + { + ans[index] = i.second; + ++ index; + } + return ans; + } +}; +// @lc code=end + diff --git a/Week 02/id_183/Leetcode_589_183.cpp b/Week 02/id_183/Leetcode_589_183.cpp new file mode 100644 index 000000000..d6dbc21d9 --- /dev/null +++ b/Week 02/id_183/Leetcode_589_183.cpp @@ -0,0 +1,42 @@ +/* + * @lc app=leetcode id=589 lang=cpp + * + * [589] N-ary Tree Preorder Traversal + */ + +// @lc code=start +/* +// Definition for a Node. +class Node { +public: + int val; + vector children; + + Node() {} + + Node(int _val, vector _children) { + val = _val; + children = _children; + } +}; +*/ +class Solution { +public: + vector preorder(Node* root) { + vector ret; + stack s; + s.push(root); + while(!s.empty()){ + root = s.top();s.pop(); + if(!root) continue; + ret.push_back(root->val); + for(int i = root->children.size()-1;i>=0;--i){ + s.push(root->children[i]); + } + } + return ret; + + } +}; +// @lc code=end + diff --git a/Week 02/id_183/Leetcode_590_183.cpp b/Week 02/id_183/Leetcode_590_183.cpp new file mode 100644 index 000000000..849121312 --- /dev/null +++ b/Week 02/id_183/Leetcode_590_183.cpp @@ -0,0 +1,41 @@ +/* + * @lc app=leetcode id=590 lang=cpp + * + * [590] N-ary Tree Postorder Traversal + */ + +// @lc code=start +/* +// Definition for a Node. +class Node { +public: + int val; + vector children; + + Node() {} + + Node(int _val, vector _children) { + val = _val; + children = _children; + } +}; +*/ +class Solution { +public: + vector postorder(Node* root) { + vector ans; + pso(ans, root); + return ans; + } + void pso(vector& ans,Node* root) + { + if (root) + { + for (int i = 0; i < root->children.size(); i++) + pso(ans, root->children[i]); + ans.push_back(root->val); + } + } +}; +// @lc code=end + diff --git a/Week 02/id_183/Leetcode_77_183.cpp b/Week 02/id_183/Leetcode_77_183.cpp new file mode 100644 index 000000000..cad97971c --- /dev/null +++ b/Week 02/id_183/Leetcode_77_183.cpp @@ -0,0 +1,38 @@ +/* + * @lc app=leetcode id=77 lang=cpp + * + * [77] Combinations + */ + +// @lc code=start +class Solution { +private: + vector> res; + + void dfs(int n, int k, int start, vector &path) { + if (path.size() == k) { + res.push_back(path); + return; + } + + for (int i = start; i <= n - (k - path.size()) + 1; ++i) { + path.push_back(i); + dfs(n, k, i + 1, path); + path.pop_back(); + + } + } + +public: + vector> combine(int n, int k) { + if (n <= 0 || k <= 0 || k > n) { + return res; + } + + vector path; + dfs(n, k, 1, path); + return res; + } +}; +// @lc code=end + diff --git a/Week 02/id_183/Leetcode_94_183.cpp b/Week 02/id_183/Leetcode_94_183.cpp new file mode 100644 index 000000000..61ef1297a --- /dev/null +++ b/Week 02/id_183/Leetcode_94_183.cpp @@ -0,0 +1,41 @@ +/* + * @lc app=leetcode id=94 lang=cpp + * + * [94] Binary Tree Inorder Traversal + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ +class Solution { +public: + vector inorderTraversal(TreeNode* root) { + stack s; + vector ans; + TreeNode* t = root; + while(t || !s.empty()) + { + while(t) + { + s.push(*t); + t = t->left; + } + if(!s.empty()) + { + ans.push_back(s.top().val); + t = s.top().right; + s.pop(); + } + } + return ans; + } +}; +// @lc code=end + diff --git a/Week 02/id_188/LeetCode_49_188.go b/Week 02/id_188/LeetCode_49_188.go new file mode 100644 index 000000000..85aef3eaa --- /dev/null +++ b/Week 02/id_188/LeetCode_49_188.go @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode.cn id=49 lang=golang + * + * [49] 字母异位词分组 + */ + +// @lc code=start +package leetCode + +import ( + "sort" + "strings" +) + +func groupAnagrams(strs []string) [][]string { + if len(strs) == 0 { + return [][]string{} + } + ret, cache := make([][]string, 0, len(strs)), make(map[string][]string, len(strs)) + for _, value := range strs { + arr := strings.Split(value, "") + sort.Strings(arr) + key := strings.Join(arr, "") + cache[key] = append(cache[key], value) + } + for _, value := range cache { + ret = append(ret, value) + } + return ret +} + +// @lc code=end diff --git a/Week 02/id_188/LeetCodt_144_188.go b/Week 02/id_188/LeetCodt_144_188.go new file mode 100644 index 000000000..a23bdbd71 --- /dev/null +++ b/Week 02/id_188/LeetCodt_144_188.go @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode.cn id=144 lang=golang + * + * [144] 二叉树的前序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +package leetCode + +func preorderTraversal(root *TreeNode) []int { + if root == nil { + return nil + } + leftRoot := preorderTraversal(root.Left) + rightRoot := preorderTraversal(root.Right) + return append(append([]int{root.Val}, leftRoot...), rightRoot...) +} + +// @lc code=end diff --git a/Week 02/id_188/LeetCodt_242_188.go b/Week 02/id_188/LeetCodt_242_188.go new file mode 100644 index 000000000..3f23db67a --- /dev/null +++ b/Week 02/id_188/LeetCodt_242_188.go @@ -0,0 +1,28 @@ +/* + * @lc app=leetcode.cn id=242 lang=golang + * + * [242] 有效的字母异位词 + */ + +// @lc code=start +package leetCode + +func isAnagram(s string, t string) bool { + if len(s) != len(t) { + return false + } + var counter [26]int + sRuneArray, tRuneArray := []rune(s), []rune(t) + for index := 0; index < len(sRuneArray); index++ { + counter[sRuneArray[index]-'a']++ + counter[tRuneArray[index]-'a']-- + } + for _, value := range counter { + if value != 0 { + return false + } + } + return true +} + +// @lc code=end diff --git a/Week 02/id_188/LeetCodt_50_188.go b/Week 02/id_188/LeetCodt_50_188.go new file mode 100644 index 000000000..4a89feda3 --- /dev/null +++ b/Week 02/id_188/LeetCodt_50_188.go @@ -0,0 +1,29 @@ +/* + * @lc app=leetcode.cn id=50 lang=golang + * + * [50] Pow(x, n) + */ + +// @lc code=start +package leetCode + +func myPow(x float64, n int) float64 { + if x == 0 { + return 0 + } + if n == 0 || x == 1 { + return 1 + } + if n < 0 { + x = 1 / x + n = -n + } + helf := myPow(x, n/2) + if n%2 == 1 { + return helf * helf * x + } else { + return helf * helf + } +} + +// @lc code=end diff --git a/Week 02/id_188/LeetCodt_589_188.cs b/Week 02/id_188/LeetCodt_589_188.cs new file mode 100644 index 000000000..4f3dbce64 --- /dev/null +++ b/Week 02/id_188/LeetCodt_589_188.cs @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode.cn id=589 lang=csharp + * + * [589] N叉树的前序遍历 + */ + +// @lc code=start +/* +// Definition for a Node. +public class Node { + public int val; + public IList children; + + public Node(){} + public Node(int _val,IList _children) { + val = _val; + children = _children; + } +} +*/ +public class Solution { + public IList Preorder(Node root) { + var ret = new List(); + if(root == null){ + return ret ; + } + ret.Add(root.val); + foreach (var node in root.children){ + ret.AddRange(Preorder(node)); + } + return ret; + } +} +// @lc code=end + diff --git a/Week 02/id_188/LeetCodt_78_188.go b/Week 02/id_188/LeetCodt_78_188.go new file mode 100644 index 000000000..b91009a63 --- /dev/null +++ b/Week 02/id_188/LeetCodt_78_188.go @@ -0,0 +1,30 @@ +/* + * @lc app=leetcode.cn id=78 lang=golang + * + * [78] 子集 + */ + +// @lc code=start +package leetCode + +func subsets(nums []int) [][]int { + ret := [][]int{} + if nums == nil || len(nums) == 0 { + return ret + } + dfs(&ret, nums, []int{}, 0) + return ret +} + +func dfs(ret *[][]int, nums []int, arr []int, index int) { + if len(nums) == index { + *ret = append(*ret, arr) + return + } + dfs(ret, nums, arr, index+1) + arr = append(arr, nums[index]) + dfs(ret, nums, arr, index+1) + arr = arr[:0] +} + +// @lc code=end diff --git a/Week 02/id_188/LeetCodt_94_188.go b/Week 02/id_188/LeetCodt_94_188.go new file mode 100644 index 000000000..521ccb8f1 --- /dev/null +++ b/Week 02/id_188/LeetCodt_94_188.go @@ -0,0 +1,28 @@ +/* + * @lc app=leetcode.cn id=94 lang=golang + * + * [94] 二叉树的中序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +package leetCode + +func inorderTraversal(root *TreeNode) []int { + if root == nil { + return nil + } + + left := inorderTraversal(root.Left) + right := inorderTraversal(root.Right) + return append(append(left, root.Val), right...) +} + +// @lc code=end diff --git a/Week 02/id_198/LeetCode_144_198.go b/Week 02/id_198/LeetCode_144_198.go new file mode 100644 index 000000000..bf4fe2776 --- /dev/null +++ b/Week 02/id_198/LeetCode_144_198.go @@ -0,0 +1,34 @@ +package leetcode + +//TreeNode TreeNode +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func preorderTraversal(root *TreeNode) []int { + + rt := make([]int, 0) + if root == nil { + + return rt + } + rt = append(rt, root.Val) + if root.Left != nil { + + for _, v := range preorderTraversal(root.Left) { + + rt = append(rt, v) + } + } + if root.Right != nil { + + for _, v := range preorderTraversal(root.Right) { + + rt = append(rt, v) + } + } + + return rt +} diff --git a/Week 02/id_198/LeetCode_144_198_test.go b/Week 02/id_198/LeetCode_144_198_test.go new file mode 100644 index 000000000..46d29d013 --- /dev/null +++ b/Week 02/id_198/LeetCode_144_198_test.go @@ -0,0 +1,26 @@ +package leetcode + +import ( + "reflect" + "testing" +) + +func Test_preorderTraversal(t *testing.T) { + type args struct { + root *TreeNode + } + tests := []struct { + name string + args args + want []int + }{ + {name: "simple", args: args{root: &TreeNode{Val: 1, Left: &TreeNode{Val: 2}, Right: &TreeNode{Val: 3}}}, want: []int{1, 2, 3}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := preorderTraversal(tt.args.root); !reflect.DeepEqual(got, tt.want) { + t.Errorf("inorderTraversal() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/Week 02/id_198/LeetCode_1_198.go b/Week 02/id_198/LeetCode_1_198.go new file mode 100644 index 000000000..7a71461e4 --- /dev/null +++ b/Week 02/id_198/LeetCode_1_198.go @@ -0,0 +1,19 @@ +package leetcode + +func twoSum(nums []int, target int) []int { + + m := make(map[int]int) + rt := make([]int, 2) + for i := 0; i < len(nums); i++ { + + num := nums[i] + if index, ok := m[num]; ok { + + rt[0] = index + rt[1] = i + return rt + } + m[target-num] = i + } + return rt +} diff --git a/Week 02/id_198/LeetCode_242_198.go b/Week 02/id_198/LeetCode_242_198.go new file mode 100644 index 000000000..0217b78af --- /dev/null +++ b/Week 02/id_198/LeetCode_242_198.go @@ -0,0 +1,27 @@ +package leetcode + +func isAnagram(s string, t string) bool { + + if len(s) != len(t) { + + return false + } + sr := []rune(s) + tr := []rune(t) + + counter := [26]int{} + for i := 0; i < len(sr); i++ { + + counter[sr[i]-'a'] = counter[sr[i]-'a'] + 1 + counter[tr[i]-'a'] = counter[tr[i]-'a'] - 1 + } + for i := 0; i < len(counter); i++ { + + if counter[i] != 0 { + + return false + } + } + + return true +} diff --git a/Week 02/id_198/LeetCode_429_198.py b/Week 02/id_198/LeetCode_429_198.py new file mode 100644 index 000000000..811c4f963 --- /dev/null +++ b/Week 02/id_198/LeetCode_429_198.py @@ -0,0 +1,21 @@ +class Node: + def __init__(self, val, children): + self.val = val + self.children = children + + +class Solution(object): + def levelOrder(self, root): + rt = [] + if not root: + return rt + rt.append([root.val]) + nodes = root.children + while nodes and len(nodes) > 0: + rt.append([n.val for n in nodes]) + next = [] + for node in nodes: + if node.children: + next.extend(node.children) + nodes = next + return rt \ No newline at end of file diff --git a/Week 02/id_198/LeetCode_49_198.go b/Week 02/id_198/LeetCode_49_198.go new file mode 100644 index 000000000..eeedbc09c --- /dev/null +++ b/Week 02/id_198/LeetCode_49_198.go @@ -0,0 +1,24 @@ +package leetcode + +import "sort" + +func groupAnagrams(strs []string) [][]string { + + m := make(map[string][]string) + for _, v := range strs { + + r := []rune(v) + sort.SliceStable(r, func(i int, j int) bool { + + return r[i] > r[j] + }) + order := string(r) + m[order] = append(m[order], v) + } + var rt = [][]string{} + for _, value := range m { + + rt = append(rt, value) + } + return rt +} diff --git a/Week 02/id_198/LeetCode_51_198.go b/Week 02/id_198/LeetCode_51_198.go new file mode 100644 index 000000000..433f1acbe --- /dev/null +++ b/Week 02/id_198/LeetCode_51_198.go @@ -0,0 +1,45 @@ +package leetcode + +import "bytes" + +//该解决方案基于其他改造,后续重新实现和优化 +func solveNQueens(n int) [][]string { + var res [][]string + find(&res, n, []int{}) + res.for + return res +} + +func find(res *[][]string, n int, path []int) { + if len(path) == n { + var queenPrint []string + for _, q := range path { + var line bytes.Buffer + for i := 0; i < n; i++ { + if i+1 == q { + + line.WriteString("Q") + } else { + line.WriteString(".") + } + } + queenPrint = append(queenPrint, line.String()) + } + *res = append(*res, queenPrint) + return + } + + var pos = len(path) + 1 +next: + for step := 1; step <= n; step++ { + for i, existStep := range path { + existPos := i + 1 + if existPos+existStep == pos+step || existStep == step || existPos-existStep == pos-step { + continue next + } + } + path = append(path, step) + find(res, n, path) + path = path[:len(path)-1] + } +} diff --git a/Week 02/id_198/LeetCode_589_198.py b/Week 02/id_198/LeetCode_589_198.py new file mode 100644 index 000000000..b9671aa3b --- /dev/null +++ b/Week 02/id_198/LeetCode_589_198.py @@ -0,0 +1,16 @@ +class Node: + def __init__(self, val, children): + self.val = val + self.children = children + + +class Solution: + def preorder(self, root: 'Node') -> List[int]: + + rt = list([]) + if not root: + return rt + rt.append(root.val) + for cr in root.children: + rt.extend(self.postorder(cr)) + return rt diff --git a/Week 02/id_198/LeetCode_590_198.py b/Week 02/id_198/LeetCode_590_198.py new file mode 100644 index 000000000..b291832d2 --- /dev/null +++ b/Week 02/id_198/LeetCode_590_198.py @@ -0,0 +1,16 @@ +class Node: + def __init__(self, val, children): + self.val = val + self.children = children + + +class Solution: + def postorder(self, root: 'Node') -> List[int]: + + rt = list([]) + if not root: + return rt + for cr in root.children: + rt.extend(self.postorder(cr)) + rt.append(root.val) + return rt diff --git a/Week 02/id_198/LeetCode_94_198.go b/Week 02/id_198/LeetCode_94_198.go new file mode 100644 index 000000000..7f17d5343 --- /dev/null +++ b/Week 02/id_198/LeetCode_94_198.go @@ -0,0 +1,34 @@ +package leetcode + +//TreeNode TreeNode +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func inorderTraversal(root *TreeNode) []int { + + rt := make([]int, 0) + if root == nil { + + return rt + } + if root.Left != nil { + + for _, v := range inorderTraversal(root.Left) { + + rt = append(rt, v) + } + } + rt = append(rt, root.Val) + if root.Right != nil { + + for _, v := range inorderTraversal(root.Right) { + + rt = append(rt, v) + } + } + + return rt +} diff --git a/Week 02/id_198/LeetCode_94_198_test.go b/Week 02/id_198/LeetCode_94_198_test.go new file mode 100644 index 000000000..acdd46a05 --- /dev/null +++ b/Week 02/id_198/LeetCode_94_198_test.go @@ -0,0 +1,32 @@ +package leetcode + +import ( + "reflect" + "testing" +) + +func Test_inorderTraversal(t *testing.T) { + type args struct { + root *TreeNode + } + tests := []struct { + name string + args args + want []int + }{ + {name: "simple", args: args{root: &TreeNode{Val: 1, Left: &TreeNode{Val: 2}, Right: &TreeNode{Val: 3}}}, want: []int{1, 3, 2}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := inorderTraversal(tt.args.root); !reflect.DeepEqual(got, tt.want) { + t.Errorf("inorderTraversal() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_T(t *testing.T) { + + dfv := [10]rune{'.'} + t.Logf("%v", dfv) +} diff --git a/Week 02/id_198/NOTE.md b/Week 02/id_198/NOTE.md index a6321d6e2..ab8fb6a7d 100644 --- a/Week 02/id_198/NOTE.md +++ b/Week 02/id_198/NOTE.md @@ -1,4 +1,18 @@ # NOTE - +## 基本概念 +### 递归(Recursion) + +若函数在其自己的定义中调用自己,那该函数被称为递归函数。其基本思想是把规模大的问题转化为相似规模小的的子问题来解决。 + +### 分治(divide and conquer) + +基于多项分支的一种很重要的算法范式。分而治之,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。 + + +### 回溯(backtracking) + +用于查找某些计算问题的所有(或部分)解决方案通用方法,特别是约束补偿问题([CSPs](https://en.wikipedia.org/wiki/Constraint_satisfaction_problem),一组对象,其状态必须满足许多约束或限制。),它递增地为解决方案构建候选方案,并在确定候选方案不可能完成有效解决方案时放弃候选方案(“回溯”)。 + +回溯主要用于寻找可能存在的解空间。 \ No newline at end of file diff --git a/Week 02/id_203/LeetCode_144_203.go b/Week 02/id_203/LeetCode_144_203.go new file mode 100644 index 000000000..56c78a1a1 --- /dev/null +++ b/Week 02/id_203/LeetCode_144_203.go @@ -0,0 +1,50 @@ +package week02 + +/** +第二周 第6课作业 + +给定一个二叉树,返回它的 前序 遍历。 + + 示例: + +输入: [1,null,2,3] + 1 + \ + 2 + / + 3 + +输出: [1,2,3] + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/binary-tree-preorder-traversal +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func preorderTraversal(root *TreeNode) []int { + var res []int + + preorder(root, &res) + + return res +} + +func preorder(root *TreeNode, res *[]int) { + if root != nil { + *res = append(*res, root.Val) + + if root.Left != nil { + preorder(root.Left, res) + } + + if root.Right != nil { + preorder(root.Right, res) + } + } +} \ No newline at end of file diff --git a/Week 02/id_203/LeetCode_236_203.go b/Week 02/id_203/LeetCode_236_203.go new file mode 100644 index 000000000..ba3aa8ac4 --- /dev/null +++ b/Week 02/id_203/LeetCode_236_203.go @@ -0,0 +1,57 @@ +package week02 + +/** +第二周 第7课作业 + +236. 二叉树的最近公共祖先 + +给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 + +百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” + +例如,给定如下二叉树:  root = [3,5,1,6,2,0,8,null,null,7,4] + +示例 1: + +输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 +输出: 3 +解释: 节点 5 和节点 1 的最近公共祖先是节点 3。 +示例 2: + +输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4 +输出: 5 +解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。 +  + +说明: + +所有节点的值都是唯一的。 +p、q 为不同节点且均存在于给定的二叉树中。 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { + if root == nil || root == p || root == q { + return root + } + + left := lowestCommonAncestor(root.Left, p, q) + right := lowestCommonAncestor(root.Right, p, q) + + if left == nil { + return right + } else if right == nil { + return left + } + + return root +} diff --git a/Week 02/id_203/LeetCode_94_203.go b/Week 02/id_203/LeetCode_94_203.go new file mode 100644 index 000000000..1b5c3ca48 --- /dev/null +++ b/Week 02/id_203/LeetCode_94_203.go @@ -0,0 +1,50 @@ +package week02 + +/** +第二周 第6课作业 + +给定一个二叉树,返回它的中序 遍历。 + +示例: + +输入: [1,null,2,3] + 1 + \ + 2 + / + 3 + +输出: [1,3,2] + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/binary-tree-inorder-traversal +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func inorderTraversal(root *TreeNode) []int { + var res []int + + inorder(root, &res) + + return res +} + +func inorder(root *TreeNode, res *[]int) { + if root != nil { + if root.Left != nil { + inorder(root.Left, res) + } + + *res = append(*res, root.Val) + + if root.Right != nil { + inorder(root.Right, res) + } + } +} \ No newline at end of file diff --git "a/Week 02/id_203/\346\200\273\347\273\223.md" "b/Week 02/id_203/\346\200\273\347\273\223.md" new file mode 100644 index 000000000..1c64ee897 --- /dev/null +++ "b/Week 02/id_203/\346\200\273\347\273\223.md" @@ -0,0 +1,56 @@ +### HashMap总结 + +对Java不熟,就不分析源码了,不过可以总结一下HashMap的实现原理,HashMap实现 `k,v` 的存储方式,底层是一个数组,在存入数据的时候,用Hash函数算出Key对应的下标,然后将元素放到数组的对应下标中。 + +在取出元素的时候,照样用同样的Hash函数算出Key的下标,然后到对应下标去取出元素即可,查找的时间复杂度是O(1) + +但是会存在哈希碰撞的情况,即不同的Key经过Hash出来的下标是一样的,那么在同一个下标下的元素将会形成一个链表,取出数据的时候,在找到对应下标之后,还会去搜索链表,找到相应的key然后返回该元素,所以HashMap在最差的情况下,查找时间复杂度是O(n) + +### 二叉树的遍历 + +前序遍历(preorder):根->左->右 +中序遍历(inorder):左->根->右 +后序遍历(postorder):左->右->根 + +遍历顺序其实很好记,前中后序指的是根节点在子树的遍历位置,然后左节点一定在右节点前面,这样只要看根节点的位置就知道是那一种遍历顺序了 + +树的面试题解法一般都是用递归解决(为什么?) +- 结点和树本身的数据结构定义就是用递归的方式定义的 +- 算法特性也是由所谓的重复性,即之前所说的最近重复子问题,二叉树都可以看出一个一个的子树构建起来的具有重复性 + +### 递归代码模板 + +1、递归的终止条件 +2、处理当前层的逻辑 +3、下探到下一层(递归) +4、有全局变量的话,需要清理当前层的状态 + +代码模板: +```cgo +public void recursion(int level, Object... params) { + // 递归终止条件 + if (level > MAX_LEVEL) { + // 逻辑处理 + return; + } + + // 处理当前层的逻辑 + process(level, params); + + // 下探(递归) + this.recursion(level + 1, params); + + // 有全局变量的话,需要清理当前层的状态 +} +``` + +### 递归的思维要点 + +- 不要人肉递归(最大的误区) +- 找到最近最简的方法,将其拆解成可重复解决的问题 +- 数学归纳法思维 + +### 分治&回溯 + +分治和回溯其实也是递归,分治是把一个大问题分解成各个小问题,然后合并结果,其代码模板跟递归的一样,只是在下探一层之后再加一个合并结果 + diff --git a/Week 02/id_213/213_week02/Anagram.java b/Week 02/id_213/213_week02/Anagram.java new file mode 100644 index 000000000..de4ce53e5 --- /dev/null +++ b/Week 02/id_213/213_week02/Anagram.java @@ -0,0 +1,27 @@ +public class Solution{ + +//这个题目解法颇多。 +//思路1.可将字符串转为字符数组,然后排序。最后使用数组的比较方法即可求得。 + + public boolean isAnagram(String s,String t) { + char[] c = s.toCharArray(); + char[] c1 = t.toCharArray(); + Arrays.sort(c); + Arrays.sort(c1); + return Arrays.equals(c,c1); + + } +//思路2.使用字符串的ASC值做下标,记录26个字母出现的次数,两者相同返回true,否则返回false。 + public boolean isAnagram2(String s, String t) { + int[] counter = new int[26]; + for (int i = 0; i < s.length; i++) { + counter[s.charAt[i] - 'a']++; + counter[t.charAt[i] - 'a']--; + } + for(int count : counter) { + if(count !=0) return false; + } + return true; + } + +} \ No newline at end of file diff --git a/Week 02/id_213/213_week02/GroupAnagrams.java b/Week 02/id_213/213_week02/GroupAnagrams.java new file mode 100644 index 000000000..4ce57bea7 --- /dev/null +++ b/Week 02/id_213/213_week02/GroupAnagrams.java @@ -0,0 +1,19 @@ +class Solution { +//主要思路是:将字符串变成字符数组,并且排序。 +//让互为异位词的字符串以数组形式存入,然后以某一键值存入。 +//遍历结束,分类完毕。 +//获取map的值对象,得到的应该是一系列的值,存入ArrayList即可。 + public List> groupAnagrams(String[] strs) { + HashMap> map = new HashMap(); + for(int i = 0; i < strs.length; i++) { + char[] a = strs[i].toCharArray(); + Arrays.sort(a); + String key = String.valueOf(a); + if(!map.containsKey(key)) { + map.put(key,new ArrayList()); + }map.get(key).add(strs[i]); + } + return new ArrayList(map.values()); + } + +} \ No newline at end of file diff --git a/Week 02/id_213/213_week02/InorderTraversal.java b/Week 02/id_213/213_week02/InorderTraversal.java new file mode 100644 index 000000000..709e41e2e --- /dev/null +++ b/Week 02/id_213/213_week02/InorderTraversal.java @@ -0,0 +1,33 @@ +class Solution { +//二叉树的中序遍历,就是左根右遍历。 + public List inorderTraveesal(TreeNode root) { + List list = new ArrayList(); + sortt(list, root); + return list; + } + public void sortt(List list, TreeNode root) { + if (root != null) { + if(root.left != null) + sortt(list, root.left); + list.add(root.val); + if(root.right != null) + sortt(list, root.right); + } + } +//前序遍历,顾名思义就是根左右。 + public List inorderTraveesal(TreeNode root) { + List list = new ArrayList(); + sortt(list, root); + return list; + } + public void sortt(List list, TreeNode root) { + if (root != null) { + list.add(root.val); + if(root.left != null) + sortt(list, root.left); + if(root.right != null) + sortt(list, root.right); + } + } + +} \ No newline at end of file diff --git a/Week 02/id_213/213_week02/MajorityElement.java b/Week 02/id_213/213_week02/MajorityElement.java new file mode 100644 index 000000000..aaaf443fb --- /dev/null +++ b/Week 02/id_213/213_week02/MajorityElement.java @@ -0,0 +1,20 @@ +class Solution { +//个人理解的众数和题目中给出的众数是不一样的, +//个人理解是在一组树中出现次数最多的一个数。 +//思路:对于数组中出现的任何一个数,都应该有一个计数器。 +// 故得用键值对的形式的做题,以元素键,计数器为值。 + public int majorityElement(int[] nums) { + HashMap map = new HashMap(); + int num = 0; + int maxNum = 0; + for(int i = 0; i < nums.length; i++) { + int count = map.getOrDefault(nums[i],0) + 1; + if (count > num) { + num = count; + maxNum = nums[i]; + } + map.put(nums[i],count); + } + return maxNum; + } +} \ No newline at end of file diff --git a/Week 02/id_213/213_week02/N-Ary-Tree.java b/Week 02/id_213/213_week02/N-Ary-Tree.java new file mode 100644 index 000000000..ba6b1fa5f --- /dev/null +++ b/Week 02/id_213/213_week02/N-Ary-Tree.java @@ -0,0 +1,34 @@ +class Solution{ +// n叉树后续遍历,这个可以利用二叉树的遍历的思路来解这个题。 + public List postorder(Node root) { + List list = new ArrayList(); + sortt(list, root); + return list; + } + public void sortt(List list, Node root) { + if(root != null ) { + if(root.children != null) { + for(int i = 0; i < root.children.size(); i++){ + sortt(list,root.children.get(i)); + } + } + list.add(root.val); + } + } +// n叉树前序续遍历,如上 +// n叉树不不存在中序遍历的。 + public List preorder(Node root) { + List list = new ArrayList(); + sortt1(list, root); + return list; + } + public void sortt1(List list, Node root) { + if (root != null) { + list.add(root.val); + if (root.children != null) { + for(int i = 0; i < root.children.size(); i++) + sortt1(list, root.children.get(i)); + } + } + } +} \ No newline at end of file diff --git a/Week 02/id_218/LeetCode_144_218.java b/Week 02/id_218/LeetCode_144_218.java new file mode 100644 index 000000000..99a087e43 --- /dev/null +++ b/Week 02/id_218/LeetCode_144_218.java @@ -0,0 +1,62 @@ +package leetcode.week2; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * https://leetcode-cn.com/problems/binary-tree-preorder-traversal/ + * + * @author eason.feng at 2019/10/23/0023 20:33 + **/ +public class LeetCode_144_218 { + + public List preorderTraversalWithoutLoop(TreeNode root) { + List list = new ArrayList(); + Stack stack = new Stack(); + TreeNode curr = root; + while (curr != null || !stack.isEmpty()) { + while (curr != null) { + stack.push(curr); + curr = curr.left; + } + curr = stack.pop(); + list.add(curr.val); + curr = curr.right; + } + return list; + } + + public List preorderTraversalWithLoop(TreeNode root) { + List list = new ArrayList(); + loops(root, list); + return list; + } + + private void loops(TreeNode root, List list) { + if (root == null) { + return; + } + if (root.left != null) { + loops(root.left, list); + } + list.add(root.val); + if (root.right != null) { + loops(root.right, list); + } + } + + public static void main(String[] args) { + LeetCode_144_218 l = new LeetCode_144_218(); + } + +} + +class TreeNode { + int val; + TreeNode left; + TreeNode right; + TreeNode (int x) { + this.val = x; + } +} diff --git a/Week 02/id_218/LeetCode_17_218.java b/Week 02/id_218/LeetCode_17_218.java new file mode 100644 index 000000000..d186ffaa8 --- /dev/null +++ b/Week 02/id_218/LeetCode_17_218.java @@ -0,0 +1,54 @@ +package leetcode.week2; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/ + * + * @author eason.feng at 2019/10/27/0027 15:12 + **/ +public class LeetCode_17_218 { + + public static void main(String[] args) { + LeetCode_17_218 leetCode_17_218 = new LeetCode_17_218(); + List list = leetCode_17_218.letterCombinations("23"); + System.out.println(list); + } + + public List letterCombinations(String digits) { + List res = new ArrayList(); + if (digits == null || digits.length() == 0) { + return res; + } + Map map = new HashMap(); + map.put('2', "abc"); + map.put('3', "def"); + map.put('4', "ghi"); + map.put('5', "jkl"); + map.put('6', "mno"); + map.put('7', "pqrs"); + map.put('8', "tuv"); + map.put('9', "wxyz"); + search("", digits, 0, res, map); + return res; + } + + private void search(String s, String digits, int level, List res, Map map) { + //terminator + if (level == digits.length()) { + res.add(s); + return; + } + //process + String letters = map.get(digits.charAt(level)); + for (int i = 0; i < letters.length(); i++) { + //drill down + search(s + letters.charAt(i), digits, level + 1, res, map); + } + //reverse state + } + +} diff --git a/Week 02/id_218/LeetCode_1_218.java b/Week 02/id_218/LeetCode_1_218.java new file mode 100644 index 000000000..f1037721d --- /dev/null +++ b/Week 02/id_218/LeetCode_1_218.java @@ -0,0 +1,56 @@ +package leetcode.week2; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * https://leetcode-cn.com/problems/two-sum/description/ + * + * @author eason.feng at 2019/10/22/0022 19:15 + **/ +public class LeetCode_1_218 { + + public static void main(String[] args) { + int[] nums = {3, 2, 4}; + int target = 6; + LeetCode_1_218 leetCode_1_218 = new LeetCode_1_218(); + int[] res = leetCode_1_218.twoSum(nums, target); + System.out.println(res); + } + + public int[] twoSum(int[] nums, int target) { + if (nums == null || nums.length <= 0) { + return new int[]{}; + } + Map map = new HashMap(nums.length); + for (int i = 0; i < nums.length; i++) { + int b = target - nums[i]; + if (map.containsKey(b)) { + return new int[]{i, map.get(b)}; + } + map.put(nums[i], i); + } + return new int[]{}; + } + + public int[] twoSumWith2On(int[] nums, int target) { + if (nums == null || nums.length <= 0) { + return new int[]{}; + } + Map map = new HashMap(nums.length); + for (int i = 0; i < nums.length; i++) { + map.put(nums[i], i); + } + + for (int i = 0; i < nums.length; i++) { + int b = target - nums[i]; + if (map.containsKey(b) && map.get(b) != i) { + return new int[]{i, map.get(b)}; + } + } + return new int[]{}; + } + +} diff --git a/Week 02/id_218/LeetCode_22_218.java b/Week 02/id_218/LeetCode_22_218.java new file mode 100644 index 000000000..6871c4363 --- /dev/null +++ b/Week 02/id_218/LeetCode_22_218.java @@ -0,0 +1,44 @@ +package leetcode.week2; + +import java.util.ArrayList; +import java.util.List; + +/** + * https://leetcode-cn.com/problems/generate-parentheses/ + * + * @author eason.feng at 2019/10/27/0027 12:03 + **/ +public class LeetCode_22_218 { + + public List generateParenthesis(int n) { + List list = new ArrayList(); + generate(0, 0, n, "", list); + return list; + } + + private void generate(int left, int right, int n, String s, List list) { + // terminator + if (left == n && right == n) { + list.add(s); + return; + } + // process + + // drill down + if (left < n) { + generate(left + 1, right, n, s + "(", list); + } + if (left > right) { + generate(left, right + 1, n, s + ")", list); + } + + // reverse states + } + + public static void main(String[] args) { + LeetCode_22_218 leetCode_22_218 = new LeetCode_22_218(); + List list = leetCode_22_218.generateParenthesis(3); + System.out.println(list); + } + +} diff --git a/Week 02/id_218/LeetCode_242_218.java b/Week 02/id_218/LeetCode_242_218.java new file mode 100644 index 000000000..6e9d8ea3d --- /dev/null +++ b/Week 02/id_218/LeetCode_242_218.java @@ -0,0 +1,109 @@ +package leetcode.week2; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * + * https://leetcode-cn.com/problems/valid-anagram/description/ + * + * //TODO 要熟悉Arrays里的常见函数。 + * + * @author eason.feng at 2019/10/22/0022 08:49 + **/ +public class LeetCode_242_218 { + + public boolean isAnagramBestWay(String s, String t) { + if (s == null || t == null || s.length() != t.length()) { + return false; + } + int[] table = new int[26]; + for(int i = 0; i < s.length(); i++) { + table[s.charAt(i) - 'a']++; + } + for(int i = 0; i < s.length(); i++) { + int pointer = t.charAt(i) - 'a'; + table[pointer]--; + if(table[pointer] < 0) { + return false; + } + } + return true; + } + + public static boolean isAnagram(String s, String t) { + if (s == null || t == null) { + return false; + } + if (s.length() != t.length()) { + return false; + } + int len = s.length(); + int[] counter = new int[26]; + for (int i = 0 ; i < len; i++) { + counter[s.charAt(i) - 'a']++; + counter[t.charAt(i) - 'a']--; + } + for (int i = 0; i < 26; i++) { + if (counter[i] != 0) { + return false; + } + } + return true; + } + + public static boolean isAnagramByHashMap(String s, String t) { + if (s == null || t == null) { + return false; + } + if (s.length() != t.length()) { + return false; + } + int len = s.length(); + Map map = new HashMap(len); + for (int i = 0; i < len; i++) { + char x = s.charAt(i); + map.put(x, ((map.get(x) == null) ? 0 : map.get(x)) + 1); + char y = t.charAt(i); + map.put(y, ((map.get(y) == null) ? 0 : map.get(y)) - 1); + } + Set> set = map.entrySet(); + for(Map.Entry entry : set) { + if (entry.getValue() != 0) { + return false; + } + } + return true; + } + + public static boolean isAnagramBruceForce(String s, String t) { + if (s == null || t == null) { + return false; + } + if (s.length() != t.length()) { + return false; + } + byte[] sBytes = s.getBytes(); + byte[] tBytes = t.getBytes(); + Arrays.sort(sBytes); + Arrays.sort(tBytes); + s = new String(sBytes); + t = new String(tBytes); + int len = s.length(); + for (int i = 0; i < len; i++) { + if (s.charAt(i) != t.charAt(i)) { + return false; + } + } + return true; + } + + public static void main(String[] args) { + String s = "anagram"; + String t = "nagaram"; + System.out.println(isAnagram(s, t)); + } + +} diff --git a/Week 02/id_218/LeetCode_49_218.java b/Week 02/id_218/LeetCode_49_218.java new file mode 100644 index 000000000..22b241eff --- /dev/null +++ b/Week 02/id_218/LeetCode_49_218.java @@ -0,0 +1,31 @@ +package leetcode.week2; + +import java.util.*; + +/** + * https://leetcode-cn.com/problems/group-anagrams/ + * + * @author eason.feng at 2019/10/22/0022 09:55 + **/ +public class LeetCode_49_218 { + + public List> groupAnagrams(String[] strs) { + if (strs == null || strs.length == 0) { + return new ArrayList(); + } + Map> map = new HashMap>(strs.length); + for (int i = 0; i < strs.length; i++) { + char[] chars = strs[i].toCharArray(); + Arrays.sort(chars); + String sorted = new String(chars); + List list = map.get(sorted); + if (list == null) { + list = new ArrayList(); + } + list.add(strs[i]); + map.put(sorted, list); + } + return new ArrayList>(map.values()); + } + +} diff --git a/Week 02/id_218/LeetCode_50_218.java b/Week 02/id_218/LeetCode_50_218.java new file mode 100644 index 000000000..d5436fcae --- /dev/null +++ b/Week 02/id_218/LeetCode_50_218.java @@ -0,0 +1,39 @@ +package leetcode.week2; + +/** + * https://leetcode-cn.com/problems/powx-n/ + * + * @author eason.feng at 2019/10/27/0027 14:02 + **/ +public class LeetCode_50_218 { + + public static void main(String[] args) { + LeetCode_50_218 leetCode_50_218 = new LeetCode_50_218(); + double result = leetCode_50_218.myPow(10.0, 2); + System.out.println(result); + } + public double myPow(double x, int n) { + if (n == 0) { + return 0; + } + if (n == 1) { + return x; + } + + double half = this.myPow(x, n / 2); + if (n % 2 == 1) { + return half * half * x; + } + else { + return half * half; + } + } + + public double myPowBruceForce(double x, int n) { + double res = 1.0; + for (int i = 1; i <= n; i++) { + res = res * x; + } + return res; + } +} diff --git a/Week 02/id_218/LeetCode_51_218.java b/Week 02/id_218/LeetCode_51_218.java new file mode 100644 index 000000000..6a591115d --- /dev/null +++ b/Week 02/id_218/LeetCode_51_218.java @@ -0,0 +1,75 @@ +package leetcode.week2; + +import java.util.*; + +/** + * https://blog.csdn.net/piyongduo3393/article/details/86497081 + * + * https://leetcode.com/problems/n-queens/ + * https://leetcode-cn.com/problems/n-queens/ + * + * 撇坐标和恒等 + * 捺坐标差(列-行)恒等( > 0) + * + * @author eason.feng at 2019/10/27/0027 15:32 + **/ +public class LeetCode_51_218 { + + public static void main(String[] args) { + LeetCode_51_218 leetCode_51_218 = new LeetCode_51_218(); + List> res = leetCode_51_218.solveNQueens(4); + System.out.println(res); + } + + public List> solveNQueens(int n) { + List> res = new ArrayList>(); + if (n == 0) { + return res; + } + char[][] chs = new char[n][n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + chs[i][j] = '.'; + } + } + backTracking(chs, 0, n, res); + return res; + } + + private void backTracking(char[][] chs, int row, int n, List> res) { + if (row == n) { + res.add(chsToList(chs)); + return; + } + + for (int col = 0; col < n; col++) { + if (isValid(chs, row, col)) { + chs[row][col] = 'Q'; + //递归 + backTracking(chs, row + 1, n, res); + //回溯 + chs[row][col] = '.'; + } + } + } + + private boolean isValid(char[][] chs, int row, int col) { + for (int i = 0; i < row; i++) { + for (int j = 0; j < chs[0].length; j++) { + //遍历当前为皇后,坐标为(i, j) 域 (row, col) 的横纵坐标之差相等即为在一条斜线上。 + if (chs[i][j] == 'Q' && (j == col || Math.abs(row - i) == Math.abs(col -j))) { + return false; + } + } + } + return true; + } + + private List chsToList(char[][] chs) { + List list = new ArrayList(); + for (int i = 0; i HashMap with the default initial capacity +* (16) and the default load factor (0.75). +*/ +public HashMap() {    + this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted +} +/** +* Constructs an empty HashMap with the specified initial +* capacity and load factor. +* +* @param  initialCapacity the initial capacity +* @param  loadFactor      the load factor +* @throws IllegalArgumentException if the initial capacity is negative +*         or the load factor is nonpositive +*/ +public HashMap(int initialCapacity, float loadFactor) {    + if (initialCapacity < 0)        + throw new IllegalArgumentException("Illegal initial capacity: "                                           + initialCapacity);    + if (initialCapacity > MAXIMUM_CAPACITY)        + initialCapacity = MAXIMUM_CAPACITY;    + if (loadFactor <= 0 || Float.isNaN(loadFactor))        + throw new IllegalArgumentException("Illegal load factor: "                                             + loadFactor);    + this.loadFactor = loadFactor;    + this.threshold = tableSizeFor(initialCapacity);} +``` + +`tableSizeFor()` 的主要功能是返回一个比给定整数大且最接近的2的幂次方整数,如给定10,返回2的4次方16. +``` +/** +* Returns a power of two size for the given target capacity. +*/ +static final int tableSizeFor(int cap) {    + int n = cap - 1;    + n |= n >>> 1;    + n |= n >>> 2;    + n |= n >>> 4;    + n |= n >>> 8;    + n |= n >>> 16;    + return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; +} +``` + +首先,`int n = cap -1`是为了防止cap已经是2的幂时,执行完后面的几条无符号右移操作之后,返回的capacity是这个cap的2倍,因为cap已经是2的幂了,就已经满足条件了。 如果不懂可以往下看完几个无符号移位后再回来看。 +![e05b0e70196d0d9d005fb9fd8a42f5d6.png](en-resource://database/2351:1) +``` +public HashMap(Map m) {    + this.loadFactor = DEFAULT_LOAD_FACTOR;    + putMapEntries(m, false); +} +/** +* Implements Map.putAll and Map constructor +* +* @param m the map +* @param evict false when initially constructing this map, else +* true (relayed to method afterNodeInsertion). +*/ +final void putMapEntries(Map m, boolean evict) {    + int s = m.size(); + //当m中有元素时,则需将map中元素放入本HashMap实例对象里。 + if (s > 0) {        + //判断table是否已经初始化,未初始化,则先初识创建一些变量。 + if (table == null) { // pre-size + //根据待插入的map的size计算需要创建的HashMap的容量。 + float ft = ((float)s / loadFactor) + 1.0F;            + int t = ((ft < (float)MAXIMUM_CAPACITY) ? + (int)ft : MAXIMUM_CAPACITY);  + //把要创建的HashMap的容量存在threshold中 + if (t > threshold)                + threshold = tableSizeFor(t); + } + //如果table初始化过,因为别的函数也会调用它,所以,有可能HashMap已经被初始化了。 + //判断待插入的map的size,如size大于threshold,则先进性resize(),进行扩容。 + else if (s > threshold) + resize(); + //然后遍历带插入的map,将每一个Entry插入到本HashMap中。 + for (Map.Entry e : m.entrySet()) {            + K key = e.getKey();            + V value = e.getValue(); + //put(k, v)也是调用 putVal函数进行元素的插入。 + putVal(hash(key), key, value, false, evict);        + } + }} + +//实际存储key,value的数组,只不过key,value被封装成Node了 +transient Node[] table; + +//用于判断链表转到红黑树的阈值 +static final int TREEIFY_THRESHOLD = 8; +//用于判断红黑树转到链表的阈值 +static final int UNTREEIFY_THRESHOLD = 6; +``` +如下为HashMap的内部结构图: +![f9207cb04c2356611aca73dc703b7406.png](en-resource://database/2353:1) +再回到putMapEntries函数中,如果table为null,那么这时就设置合适的threshold,如果不为空并且指定的map的size>threshold,那么就resize()。然后把指定的map的所有Key,Value,通过putVal添加到我们创建的新的map中。putVal中传入了个hash(key),那我们就先来看看hash(key): 其中key 的 hash值的计算是通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^ (h >>> 16) + +``` +static final int hash(Object key) {    + int h;    + return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); +} +``` + +异或运算:(h = key.hashCode()) ^ (h >>> 16)原 来 的 hashCode : 1111 1111 1111 1111 0100 1100 0000 1010 + +移位后的hashCode: 0000 0000 0000 0000 1111 1111 1111 1111 +进行异或运算 结果:1111 1111 1111 1111 1011 0011 1111 0101 +这样做的好处是,可以将hashcode高位和低位的值进行混合做异或运算,而且混合后,低位的信息中加入了高位的信息,这样高位的信息被变相的保留了下来。掺杂的元素多了,那么生成的hash值的随机性会增大。 + +``` + final Node[] resize() { + // 保存当前table + Node[] oldTab = table; + // 保存当前table的容量 + int oldCap = (oldTab == null) ? 0 : oldTab.length; + // 保存当前阈值 + int oldThr = threshold; + // 初始化新的table容量和阈值 + int newCap, newThr = 0; + /* + 1. resize()函数在size > threshold时被调用。oldCap大于 0 代表原来的 table 表非空, + oldCap 为原表的大小,oldThr(threshold) 为 oldCap × load_factor + */ + if (oldCap > 0) { + // 若旧table容量已超过最大容量,更新阈值为Integer.MAX_VALUE(最大整形值),这样以后就不会自动扩容了。 + if (oldCap >= MAXIMUM_CAPACITY) { + threshold = Integer.MAX_VALUE; + return oldTab; + } + // 容量翻倍,使用左移,效率更高 + else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && + oldCap >= DEFAULT_INITIAL_CAPACITY) + // 阈值翻倍 + newThr = oldThr << 1; // double threshold + } + /* + 2. resize()函数在table为空被调用。oldCap 小于等于 0 且 oldThr 大于0,代表用户创建了一个 HashMap,但是使用的构造函数为 + HashMap(int initialCapacity, float loadFactor) 或 HashMap(int initialCapacity) + 或 HashMap(Map m),导致 oldTab 为 null,oldCap 为0, oldThr 为用户指定的 HashMap的初始容量。 +   */ + else if (oldThr > 0) // initial capacity was placed in threshold + //当table没初始化时,threshold持有初始容量。还记得threshold = tableSizeFor(t)么; + newCap = oldThr; + /* + 3. resize()函数在table为空被调用。oldCap 小于等于 0 且 oldThr 等于0,用户调用 HashMap()构造函数创建的 HashMap,所有值均采用默认值,oldTab(Table)表为空,oldCap为0,oldThr等于0, + */ + else { // zero initial threshold signifies using defaults + newCap = DEFAULT_INITIAL_CAPACITY; + newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); + } + // 新阈值为0 + if (newThr == 0) { + float ft = (float)newCap * loadFactor; + newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? + (int)ft : Integer.MAX_VALUE); + } + threshold = newThr; + @SuppressWarnings({"rawtypes","unchecked"}) + // 初始化table + Node[] newTab = (Node[])new Node[newCap]; + table = newTab; + if (oldTab != null) { + // 把 oldTab 中的节点 reHash 到 newTab 中去 + for (int j = 0; j < oldCap; ++j) { + Node e; + if ((e = oldTab[j]) != null) { + oldTab[j] = null; + // 若节点是单个节点,直接在 newTab 中进行重定位 + if (e.next == null) + newTab[e.hash & (newCap - 1)] = e; + // 若节点是 TreeNode 节点,要进行 红黑树的 rehash 操作 + else if (e instanceof TreeNode) + ((TreeNode)e).split(this, newTab, j, oldCap); + // 若是链表,进行链表的 rehash 操作 + else { // preserve order + Node loHead = null, loTail = null; + Node hiHead = null, hiTail = null; + Node next; + // 将同一桶中的元素根据(e.hash & oldCap)是否为0进行分割(代码后有图解,可以回过头再来看),分成两个不同的链表,完成rehash + do { + next = e.next; + // 根据算法 e.hash & oldCap 判断节点位置rehash 后是否发生改变 + //最高位==0,这是索引不变的链表。 + if ((e.hash & oldCap) == 0) { + if (loTail == null) + loHead = e; + else + loTail.next = e; + loTail = e; + } + //最高位==1 (这是索引发生改变的链表) + else { + if (hiTail == null) + hiHead = e; + else + hiTail.next = e; + hiTail = e; + } + } while ((e = next) != null); + if (loTail != null) { // 原bucket位置的尾指针不为空(即还有node) + loTail.next = null; // 链表最后得有个null + newTab[j] = loHead; // 链表头指针放在新桶的相同下标(j)处 + } + if (hiTail != null) { + hiTail.next = null; + // rehash 后节点新的位置一定为原来基础上加上 oldCap,具体解释看下图 + newTab[j + oldCap] = hiHead; + } + } + } + } + } + return newTab; + }} +``` +因为使用的是2次幂的扩展(指长度扩为原来2倍),所以,元素的位置要么是在原位置,要么是在原位置再移动2次幂的位置。 + +看下图可以明白这句话的意思,n为table的长度,图(a)表示扩容前的key1和key2两种key确定索引位置的示例,图(b)表示扩容后key1和key2两种key确定索引位置的示例,其中hash1是key1对应的哈希与高位运算结果。 +![e39c893e654ce686c17c395a0da981c6.png](en-resource://database/2355:1) +元素在重新计算hash之后,因为n变为2倍,那么n-1的mask范围在高位多1bit(红色),因此新的index就会发生这样的变化: +![da8767e7ddec419cd2b89599acdd9329.png](en-resource://database/2357:1) + +因此,我们在扩充HashMap的时候,只需要看看原来的hash值新增的那个bit是1还是0就好了,是0的话索引没变,是1的话索引变成“原索引+oldCap”,可以看看下图为16扩充为32的resize示意图 : +![30c9ecb3a2e5a72d40a78eb6ce696db1.png](en-resource://database/2359:1) + +* 什么时候扩容: + * 通过HashMap源码可以看到是在put操作时,即向容器中添加元素时,判断当前容器中元素的个数是否达到阈值(当前数组长度乘以加载因子的值)的时候,就要自动扩容了。 +* 扩容(resize): + * 其实就是重新计算容量;而这个扩容是计算出所需容器的大小之后重新定义一个新的容器,将原来容器中的元素放入其中。 + +``` + + //实现put和相关方法。 + final V putVal(int hash, K key, V value, boolean onlyIfAbsent, + boolean evict) { + Node[] tab; Node p; int n, i; + //如果table为空或者长度为0,则resize() + if ((tab = table) == null || (n = tab.length) == 0) + n = (tab = resize()).length; + //确定插入table的位置,算法是(n - 1) & hash,在n为2的幂时,相当于取摸操作。 + ////找到key值对应的槽并且是第一个,直接加入 + if ((p = tab[i = (n - 1) & hash]) == null) + tab[i] = newNode(hash, key, value, null); + //在table的i位置发生碰撞,有两种情况,1、key值是一样的,替换value值, + //2、key值不一样的有两种处理方式:2.1、存储在i位置的链表;2.2、存储在红黑树中 + else { + Node e; K k; + //第一个node的hash值即为要加入元素的hash + if (p.hash == hash && + ((k = p.key) == key || (key != null && key.equals(k)))) + e = p; + //2.2 + else if (p instanceof TreeNode) + e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); + //2.1 + else { + //不是TreeNode,即为链表,遍历链表 + for (int binCount = 0; ; ++binCount) { + ///链表的尾端也没有找到key值相同的节点,则生成一个新的Node, + //并且判断链表的节点个数是不是到达转换成红黑树的上界达到,则转换成红黑树。 + if ((e = p.next) == null) { + // 创建链表节点并插入尾部 + p.next = newNode(hash, key, value, null); + ////超过了链表的设置长度8就转换成红黑树 + if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st + treeifyBin(tab, hash); + break; + } + if (e.hash == hash && + ((k = e.key) == key || (key != null && key.equals(k)))) + break; + p = e; + } + } + //如果e不为空就替换旧的oldValue值 + if (e != null) { // existing mapping for key + V oldValue = e.value; + if (!onlyIfAbsent || oldValue == null) + e.value = value; + afterNodeAccess(e); + return oldValue; + } + } + ++modCount; + if (++size > threshold) + resize(); + afterNodeInsertion(evict); + return null; + } +``` + +* 注:hash 冲突发生的几种情况: + * 1.两节点key 值相同(hash值一定相同),导致冲突; + * 2.两节点key 值不同,由于 hash 函数的局限性导致hash 值相同,冲突; + * 3.两节点key 值不同,hash 值不同,但 hash 值对数组长度取模后相同,冲突; + +* 1.7和1.8的HashMap的不同点 + * JDK1.7用的是头插法,而JDK1.8及之后使用的都是尾插法 + + JDK1.7是用单链表进行的纵向延伸,当采用头插法就是能够提高插入的效率,但是也会容易出现逆序且环形链表死循环问题。但是在JDK1.8之后是因为加入了红黑树使用尾插法,能够避免出现逆序且链表死循环的问题。 + + * 扩容后数据存储位置的计算方式也不一样: + + 1:在JDK1.7的时候是直接用hash值和需要扩容的二进制数进行&(这里就是为什么扩容的时候为啥一定必须是2的多少次幂的原因所在,因为如果只有2的n次幂的情况时最后一位二进制数才一定是1,这样能最大程度减少hash碰撞)(hash值 & length-1) 。 + 2:而在JDK1.8的时候直接用了JDK1.7的时候计算的规律,也就是扩容前的原始位置+扩容的大小值=JDK1.8的计算方式,而不再是JDK1.7的那种异或的方法。但是这种方式就相当于只需要判断Hash值的新增参与运算的位是0还是1就直接迅速计算出了扩容后的储存方式。 + 3:JDK1.7的时候使用的是数组+ 单链表的数据结构。但是在JDK1.8及之后时,使用的是数组+链表+红黑树的数据结构(当链表的深度达到8的时候,也就是默认阈值,就会自动扩容把链表转成红黑树的数据结构来把时间复杂度从O(N)变成O(logN)提高了效率)。 + +* HashMap为什么是线程不安全的? + * put的时候导致的多线程数据不一致 + + 比如有两个线程A和B,首先A希望插入一个key-value对到HashMap中,首先计算记录所要落到的 hash桶的索引坐标,然后获取到该桶里面的链表头结点,此时线程A的时间片用完了,而此时线程B被调度得以执行,和线程A一样执行,只不过线程B成功将记录插到了桶里面,假设线程A插入的记录计算出来的 hash桶索引和线程B要插入的记录计算出来的 hash桶索引是一样的,那么当线程B成功插入之后,线程A再次被调度运行时,它依然持有过期的链表头但是它对此一无所知,以至于它认为它应该这样做,如此一来就覆盖了线程B插入的记录,这样线程B插入的记录就凭空消失了,造成了数据不一致的行为。 + + * resize而引起死循环 + + 这种情况发生在HashMap自动扩容时,当2个线程同时检测到元素个数超过 数组大小 × 负载因子。此时2个线程会在put()方法中调用了resize(),两个线程同时修改一个链表结构会产生一个循环链表(JDK1.7中,会出现resize前后元素顺序倒置的情况)。接下来再想通过get()获取某一个元素,就会出现死循环。 + +* HashMap和HashTable的区别:HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。 + * HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。 + * HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。 + * 另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。 + * 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。 + * HashMap不能保证随着时间的推移Map中的元素次序是不变的。 + +待学习:https://tech.meituan.com/2016/06/24/java-hashmap.html + +#### 树 +* 源于对一维数据结构的升维而产生了树。BST的中序遍历是递增的。 +* 二叉树:常见的BST,红黑树,AVL + * 二叉搜索树: + * 左子树的所有节点均小于它的根的值 + * 右子树的所有节点均大于它的根的值 + * 常见操作:https://visualgo.net/zh/bst?slide=1 + * 输的面试题解法一般都是递归,主要是因为树的数据结构决定,树只有左右节点。 +* 遍历:前序,中序、后序(根在前 中 后的位置来决定) +* 树的操作:使用递归来实现。 + +#### 递归 + +* 向下进入到不同的层;向上又回到原来的一层; +* 递归栈,类似剥洋葱 +* 递归模板: + * 优先确定递归结束条件; + * 处理当前层逻辑; + * 调用到下一层(level + 1) + * 可能清理当前层数据 + +* 思维要点 + * 不要人肉递归(最大误区) + * 找最近重复子问题:找到最近最简单的方法,将其拆解成可重复解决的问题 + * 数学归纳法思维 + +#### 分治 回溯 + +分治、回溯是一种特殊或较为复杂的递归。 + +* 找重复性: + * 最近重复性:分治/回溯 + * 最优重复性:即为动态规划 + +* 分治(Divide & Conquer):在递归时,将当前的一个问题化解成多个子问题。 +* 回溯(BackTracking): 采用试错的思想,它尝试分步的去解决问题。如果不能解决,则取消上一步,甚至是上几步的计算。 +##### 分治模板 + +``` +def divide_conquer(problem, param1, param2, ...):  + # recursion terminator  + if problem is None:  + print_result  + return  + + # prepare data  + data = prepare_data(problem)  + subproblems = split_problem(problem, data)  + + # conquer subproblems  + subresult1 = self.divide_conquer(subproblems[0], p1, ...)  + subresult2 = self.divide_conquer(subproblems[1], p1, ...)  + subresult3 = self.divide_conquer(subproblems[2], p1, ...)  + … + + # process and generate the final result  + result = process_result(subresult1, subresult2, subresult3, …) + + # revert the current level states +``` + +#### 总结 +通过第二周的学习,使我了解和学习了如下基本技能,也在不断的训练自己使用: +* 递归解题大法: + * 1 terminator 结束条件 + * 2 process 当前循环的逻辑 + * 3 drill down 进入到下一层(可能只需要一个,也可能需要两次进入,也可能是for循环进入) + * 4 reverse states更新清理当前状态(一般回溯算法需要用到。) +* 不要人肉递归(最大误区),找到最小重复单元 diff --git a/Week 02/id_218/RecursionTemplate.java b/Week 02/id_218/RecursionTemplate.java new file mode 100644 index 000000000..e6d9766d0 --- /dev/null +++ b/Week 02/id_218/RecursionTemplate.java @@ -0,0 +1,25 @@ +package leetcode.week2; + +/** + * @author eason.feng at 2019/10/27/0027 11:21 + **/ +public class RecursionTemplate { + + public void recur(int level, int param) { + //terminator + int maxLevel = Integer.MAX_VALUE; + if (level > maxLevel) { + //process result + return; + } + + //process current logic; + int newParam = param; + + //drill down + recur(level + 1, newParam); + + //restore current status + } + +} diff --git a/Week 02/id_223/LeetCode_144_223.py b/Week 02/id_223/LeetCode_144_223.py new file mode 100644 index 000000000..e9b6a7954 --- /dev/null +++ b/Week 02/id_223/LeetCode_144_223.py @@ -0,0 +1,9 @@ +class Solution: + def __init__(self): + self.traverse_path = [] + def preorderTraversal(self, root: TreeNode) -> List[int]: + if root: + self.traverse_path.append(root.val) + self.preorderTraversal(root.left) + self.preorderTraversal(root.right) + return self.traverse_path \ No newline at end of file diff --git a/Week 02/id_223/LeetCode_49_223.java b/Week 02/id_223/LeetCode_49_223.java new file mode 100644 index 000000000..d6b0cf4ea --- /dev/null +++ b/Week 02/id_223/LeetCode_49_223.java @@ -0,0 +1,14 @@ +class Solution { + public List> groupAnagrams(String[] strs) { + Map> map = new HashMap<>(); + for (String s: strs) { + char[] ca = s.toCharArray(); + Arrays.sort(ca); + // String key = new String(ca); + String key = String.valueOf(ca); + if (!map.containsKey(key)) map.put(key, new ArrayList()); + map.get(key).add(s); + } + return new ArrayList(map.values()); + } +} \ No newline at end of file diff --git a/Week 02/id_223/LeetCode_94_223.py b/Week 02/id_223/LeetCode_94_223.py new file mode 100644 index 000000000..8dd097d18 --- /dev/null +++ b/Week 02/id_223/LeetCode_94_223.py @@ -0,0 +1,9 @@ +class Solution: + def __init__(self): + self.traverse_path = [] + def inorderTraversal(self, root: TreeNode) -> List[int]: + if root: + self.inorderTraversal(root.left); + self.traverse_path.append(root.val) + self.inorderTraversal(root.right) + return self.traverse_path \ No newline at end of file diff --git a/Week 02/id_223/NOTE.md b/Week 02/id_223/NOTE.md index a6321d6e2..b0346bdd5 100644 --- a/Week 02/id_223/NOTE.md +++ b/Week 02/id_223/NOTE.md @@ -1,4 +1,158 @@ -# NOTE +# 学习总结 +## Week 2 +### 哈希表、映射、集合 +#### Part1: +> 哈希表(Hash table),也叫散列表,是根据关键码值(Key value)而直接进行访问的数据结构。 它通过把关键码值映射到表中一个位置来访问记录,以加快查找的 速度。 +> 这个映射函数叫作散列函数(Hash Function),存放记录的数组 叫作哈希表(或散列表)。 +##### 哈希表 +- 哈希表以时间换空间,记录每个key的位置 +- 访问、查询和增删的时间复杂度为O(1) +- 当发生哈希冲突时(hash值相同),退化为链表记录这些冲突值(拉链式) + +##### 映射、集合 +- Map + - 键值对数据 + - Key不能重复,Value可以 + - 支持 null键和 null值 + - new HashMap() / new TreeMap() + - map.set(key, value) + - map.get(key) + - map.has(key) + - map.size() + - map.clear() +- Set + - 数据是无序的(hashcode决定位置) + - 数据不能重复 + - new HashSet() / new TreeSet() + - set.add(value) + - set.delete(value) + - set.hash(value) + +#### Part2: +1. 利用Hash表解题:WordCount,字母异位词,2sum等 + + +### 树、二叉树、二叉搜索树 +#### Part1: +- 链表就是特殊化的树 +- 树就是特殊化的图 + +###### 树结点(TreeNode)模板代码 +``` +Java +public class TreeNode { + public int val; + public TreeNode left, right; + public TreeNode(int val) { +this.val = val; this.left = null; this.right = null; +} } + +Python +class TreeNode: +def __init__(self, val): +self.val = val +self.left, self.right = None, None +``` +###### 二叉树遍历 +> 前中后是根据根的位置,然后左右 +1. 前序(Pre-order):根-左-右 +1. 中序(In-order):左-根-右 +1. 后序(Post-order):左-右-根 + +##### 二叉搜索树-BST +1. 左子树上所有结点的值均小于它的根结点的值; +2. 右子树上所有结点的值均大于它的根结点的值; +3. 以此类推:左、右子树也分别为二叉搜索树。 (这就是重复性!) +4. 中序遍历后->升序排列 + +``` +def preorder(self, root): + if root: + self.traverse_path.append(root.val) + self.preorder(root.left) + self.preorder(root.right) +def inorder(self, root): + if root: + self.inorder(root.left) + self.traverse_path.append(root.val) + self.inorder(root.right) +def postorder(self, root): + if root: + self.postorder(root.left) + self.postorder(root.right) + self.traverse_path.append(root.val) +``` + + + +### 泛型递归、树的递归 +#### Part1 +- 递归 Recursion + 1. 从前有个山 + 2. 山里有个庙 + 3. 庙里有个和尚讲故事 + 4. 返回1 +- 盗梦空间 + 1. 向下进入到不同梦境中;向上又回到原来一层 + 1. 通过声音同步回到上一层 + 1. 每一层的环境和周围的人都是一份拷贝、 + 1. 主角等几个人穿越不同层级的梦境(发生和携带变化) +- 递归模板 +1. terminator - 递归终结条件 +1. process logic in current level - 处理这一层逻辑 +1. drill down - 处理下一层(找出重复子问题) +1. restore current stauts - 清理当前层留下的状态 + +``` +Python 代码模版 +def recursion(level, param1, param2, ...): + # recursion terminator + if level > MAX_LEVEL: + process_result + return + # process logic in current level + process(level, data...) + # drill down + self.recursion(level + 1, p1, ...) + # reverse the current level status if needed +``` + +``` +Java 代码模版 +public void recur(int level, int param) { + // terminator +if (level > MAX_LEVEL) { + // process result + return; } + // process current logic + process(level, param); + // drill down + recur( level: level + 1, newParam); + // restore current status +} +``` +- 思维要点 + 1. 不要人肉进行递归(最大误区) + 2. 找到最近最简方法,将其拆解成可重复解决的问题(重复子问题) + 3. 数学归纳法思维 + +### 分治、回溯 +#### 分治 Divide & Conquer + +- 分治也是一种递归,不过在它递归的过程中,把一个问题划分为好几个子问题 +- 代码模板 + - terminator + - process(split your big problem) + - drill down(subproblmes) + - reverse states + +#### 回溯 Backtracking + +- 回溯法采用试错的思想,它尝试分步的去解决一个问题.在分步解决问题的过程中,当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至上几步的计算,再通过其他的可能的分步解答再次尝试寻找问题的答案. +- 回溯法通常用最简单的递归方法来实现,在反复重复上述步骤后可能出现2种情况 + - 找到一种可能存在的正确答案 + - 在尝试了所有可能的分步方法后宣告该方法没有解 +- 在最坏的情况下,回溯法会导致一次复杂度为指数时间的计算 diff --git a/Week 02/id_228/LeetCode_105_228.js b/Week 02/id_228/LeetCode_105_228.js new file mode 100644 index 000000000..24afea19b --- /dev/null +++ b/Week 02/id_228/LeetCode_105_228.js @@ -0,0 +1,29 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {number[]} preorder + * @param {number[]} inorder + * @return {TreeNode} + */ +var buildTree = function(preorder, inorder) { + if (!preorder.length || !inorder.length) { + return null + } + let root = preorder[0] + let inorderRootPosition = inorder.indexOf(root) + let inorderLeft = inorder.slice(0, inorderRootPosition) + let inorderRight = inorder.slice(inorderRootPosition + 1) + let preorderLeft = preorder.slice(1, inorderRootPosition + 1) + let preorderRight = preorder.slice(inorderRootPosition + 1) + + let tree = new TreeNode(root) + tree.left = buildTree(preorderLeft,inorderLeft) + tree.right = buildTree(preorderRight,inorderRight) + return tree + +}; \ No newline at end of file diff --git a/Week 02/id_228/LeetCode_144_228.js b/Week 02/id_228/LeetCode_144_228.js new file mode 100644 index 000000000..404a6fb4c --- /dev/null +++ b/Week 02/id_228/LeetCode_144_228.js @@ -0,0 +1,27 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {number[]} + */ +var preorderTraversal = function(root) { + let result = []; + let pushRoot = function(root) { + if (root) { + result.push(root.val); + if (root.left) { + pushRoot(root.left); + } + if (root.right) { + pushRoot(root.right); + } + } + }; + pushRoot(root); + return result; +}; diff --git a/Week 02/id_228/LeetCode_169_288.js b/Week 02/id_228/LeetCode_169_288.js new file mode 100644 index 000000000..6b3fb20cf --- /dev/null +++ b/Week 02/id_228/LeetCode_169_288.js @@ -0,0 +1,14 @@ + +// 解法一,排序后去中间值 +/** + * @param {number[]} nums + * @return {number} + */ +var majorityElement = function(nums) { + if (nums.length === 0) { + return null + } + let halfPositin = Math.floor(nums.length / 2) + let sortNums = nums.sort() + return sortNums[halfPositin] +}; diff --git a/Week 02/id_228/LeetCode_17_228.js b/Week 02/id_228/LeetCode_17_228.js new file mode 100644 index 000000000..7d890507a --- /dev/null +++ b/Week 02/id_228/LeetCode_17_228.js @@ -0,0 +1,34 @@ +/** + * @param {string} digits + * @return {string[]} + */ +var letterCombinations = function(digits) { + if (digits.length === 0) { + return []; + } + let hashMap = { + "2": "abc", + "3": "def", + "4": "ghi", + "5": "jkl", + "6": "mno", + "7": "pqrs", + "8": "tuv", + "9": "wxyz" + }; + let result = []; + let backtrack = function(combination, nextDigits) { + if (nextDigits.length === 0) { + result.push(combination); + } else { + let digit = nextDigits.substring(0, 1); + let letters = hashMap[digit]; + for (let i = 0; i < letters.length; i++) { + let letter = hashMap[digit].substring(i, i + 1); + backtrack(combination + letter, nextDigits.substring(1)); + } + } + }; + backtrack("", digits); + return result; +}; diff --git a/Week 02/id_228/LeetCode_1_228.js b/Week 02/id_228/LeetCode_1_228.js new file mode 100644 index 000000000..63132aadb --- /dev/null +++ b/Week 02/id_228/LeetCode_1_228.js @@ -0,0 +1,14 @@ +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = function(nums, target) { + for(let i = 0; i < nums.length - 1; i++) { + for(let j = i + 1; j < nums.length; j++) { + if (nums[i] + nums[j] === target) { + return [i, j] + } + } + } +}; \ No newline at end of file diff --git a/Week 02/id_228/LeetCode_236_228.js b/Week 02/id_228/LeetCode_236_228.js new file mode 100644 index 000000000..c5f9a1721 --- /dev/null +++ b/Week 02/id_228/LeetCode_236_228.js @@ -0,0 +1,30 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @param {TreeNode} p + * @param {TreeNode} q + * @return {TreeNode} + */ +var lowestCommonAncestor = function(root, p, q) { + if(root === null || p === null || q === null) { + return null + } + if (root === p || root === q) { + return root + } + let left = lowestCommonAncestor(root.left, p, q) + let right = lowestCommonAncestor(root.right, p, q) + if (left === null) { + return right + } + if (right === null) { + return left + } + return root +}; \ No newline at end of file diff --git a/Week 02/id_228/LeetCode_242_228.js b/Week 02/id_228/LeetCode_242_228.js new file mode 100644 index 000000000..91c31fc65 --- /dev/null +++ b/Week 02/id_228/LeetCode_242_228.js @@ -0,0 +1,38 @@ +// 排序比较法 +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function(s, t) { + let sortStringS= s.split('').sort((a, b) => a - b).join('') + let sortStringT = t.split('').sort((a, b) => a - b).join('') + return sortStringS === sortStringT +}; + +// 哈希表解法 +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function(s, t) { + if (s.length !== t.length) { + return false + } + let hashMap = new Array(26) + for(let i = 0; i < hashMap.length; i++) { + hashMap[i] = 0 + } + let firstCode = 'a'.charCodeAt() + for(let j = 0; j < s.length; j++) { + hashMap[s[i].charCodeAt() - firstCode]++ + hashMap[t[i].charCodeAt() - firstCode]-- + } + for(let i = 0; i < hashMap.length; i++) { + if (hashMap[i] !== 0) { + return false + } + } + return true +}; \ No newline at end of file diff --git a/Week 02/id_228/LeetCode_429_228.js b/Week 02/id_228/LeetCode_429_228.js new file mode 100644 index 000000000..c18dab442 --- /dev/null +++ b/Week 02/id_228/LeetCode_429_228.js @@ -0,0 +1,28 @@ +/** + * // Definition for a Node. + * function Node(val,children) { + * this.val = val; + * this.children = children; + * }; + */ +/** + * @param {Node} root + * @return {number[][]} + */ +let levelOrder = function(root) { + if (!root) { + return [] + } + let result = [] + let helper = function(root, depth, result) { + if (depth + 1 > result.length) { + result[depth] = [] + } + result[depth].push(root.val) + for (let i = 0; i < root.children.length; i++) { + helper(root.children[i], depth + 1, result) + } + } + helper(root, 0, result) + return result +}; \ No newline at end of file diff --git a/Week 02/id_228/LeetCode_46_228.js b/Week 02/id_228/LeetCode_46_228.js new file mode 100644 index 000000000..362f9cd61 --- /dev/null +++ b/Week 02/id_228/LeetCode_46_228.js @@ -0,0 +1,27 @@ + +// 写法错误,待更新 +/** + * @param {number[]} nums + * @return {number[][]} + */ +var permute = function(nums) { + let result = [] + let swap = function(a, b) { + let temp + temp = a + a = b + b = temp + } + let backtrack = function(nums, result, i) { + if (i === nums.length) { + result.push(nums) + } + for (let j = i; j < nums.length; j++) { + swap(nums[i], nums[j]) + backtrack(nums, result, i+1) + swap(nums[i], nums[j]) + } + } + backtrack(nums, result, 0) + return result +}; \ No newline at end of file diff --git a/Week 02/id_228/LeetCode_49_228.js b/Week 02/id_228/LeetCode_49_228.js new file mode 100644 index 000000000..701854444 --- /dev/null +++ b/Week 02/id_228/LeetCode_49_228.js @@ -0,0 +1,21 @@ +// 暴力排序 +/** + * @param {string[]} strs + * @return {string[][]} + */ +var groupAnagrams = function(strs) { + let tempCode = '' + let hashMap = {} + let result = [] + for (let i = 0; i < strs.length; i++) { + tempCode = strs[i].split('').sort() + if (!hashMap[tempCode]) { + hashMap[tempCode] = [] + } + hashMap[tempCode].push(strs[i]) + } + Object.keys(hashMap).forEach(key => { + result.push(hashMap[key]) + }) + return result +}; \ No newline at end of file diff --git a/Week 02/id_228/LeetCode_51_228.js b/Week 02/id_228/LeetCode_51_228.js new file mode 100644 index 000000000..e69de29bb diff --git a/Week 02/id_228/LeetCode_589_228.js b/Week 02/id_228/LeetCode_589_228.js new file mode 100644 index 000000000..d2aaae4ed --- /dev/null +++ b/Week 02/id_228/LeetCode_589_228.js @@ -0,0 +1,25 @@ +/** + * // Definition for a Node. + * function Node(val,children) { + * this.val = val; + * this.children = children; + * }; + */ +/** + * @param {Node} root + * @return {number[]} + */ +var preorder = function(root) { + let result = [] + let helper = function(root) { + if (root) { + result.push(root.val) + let len = root.children.length + for (let i = 0; i < len; i++) { + helper(root.children[i]); + } + } + } + helper(root) + return result +}; \ No newline at end of file diff --git a/Week 02/id_228/LeetCode_590_228.js b/Week 02/id_228/LeetCode_590_228.js new file mode 100644 index 000000000..da591815e --- /dev/null +++ b/Week 02/id_228/LeetCode_590_228.js @@ -0,0 +1,26 @@ +/** + * // Definition for a Node. + * function Node(val,children) { + * this.val = val; + * this.children = children; + * }; + */ +/** + * @param {Node} root + * @return {number[]} + */ +var postorder = function(root) { + let result = []; + let helper = function(root) { + if (root) { + if (root.children && root.children.length > 0) { + for (let i = 0; i < root.children.length; i++) { + helper(root.children[i]); + } + } + result.push(root.val); + } + }; + helper(root); + return result; +}; diff --git a/Week 02/id_228/LeetCode_94-228.js b/Week 02/id_228/LeetCode_94-228.js new file mode 100644 index 000000000..f1ee2ba69 --- /dev/null +++ b/Week 02/id_228/LeetCode_94-228.js @@ -0,0 +1,28 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ + +/** + * @param {TreeNode} root + * @return {number[]} + */ +var inorderTraversal = function(root) { + let result = [] + var pushRoot = function(root) { + if (root) { + if (root.left) { + pushRoot(root.left) + } + result.push(root.val) + if (root.right) { + pushRoot(root.right) + } + } + } + pushRoot(root) + return result +}; \ No newline at end of file diff --git a/Week 02/id_233/LeetCode_17_233.java b/Week 02/id_233/LeetCode_17_233.java new file mode 100644 index 000000000..002529f4a --- /dev/null +++ b/Week 02/id_233/LeetCode_17_233.java @@ -0,0 +1,42 @@ +public class LeetCode_17_233{ + public List letterCombinations(String digits) { + + if(null==digits||digits.equals("")){ + return new ArrayList<>(); + } + + Map dict = new HashMap<>(); + dict.put('2',"abc".toCharArray()); + dict.put('3',"def".toCharArray()); + dict.put('4',"ghi".toCharArray()); + dict.put('5',"jkl".toCharArray()); + dict.put('6',"mno".toCharArray()); + dict.put('7',"pqrs".toCharArray()); + dict.put('8',"tuv".toCharArray()); + dict.put('9',"wxyz".toCharArray()); + + char[] chars = digits.toCharArray(); + List source = new ArrayList<>(); + for (char c:chars){ + if(dict.containsKey(c)){ + source.add(dict.get(c)); + } + } + List result = new ArrayList<>(); + _generate("",source,0,result); + + return result; + } + + private void _generate(String s, List source, int level, List result) { + if(level==source.size()){ + result.add(s); + return; + } + + char[] chars = source.get(level); + for (int i = 0; i > groupAnagrams(String[] strs) { + + List> result = new ArrayList<>(); + + List group = null; + + if (null != strs && strs.length <= 1) { + group = new ArrayList<>(); + group.add(strs[0]); + result.add(group); + return result; + } + + List newStr = null; + int gat = 0; //组计数器 + int nst = 0; //新计数器 + + f: + for (int i = strs.length - 1; i > 0; i--) { + if (isAnagram(strs[0], strs[i])) { + if (gat++ == 0) { + group = null; + group = new ArrayList<>(); + group.add(strs[0]); + } + group.add(strs[i]); + } else { + if (nst++ == 0) { + newStr = null; + newStr = new ArrayList<>(); + } + newStr.add(strs[i]); + } + if (i == 1) { + if (gat > 0) + result.add(group); + if (gat == 0) { + group = new ArrayList<>(); + group.add(strs[0]); + result.add(group); + } + if (nst == 1) + result.add(newStr); + + if (nst > 1) { + strs = newStr.toArray(new String[nst]); + i = nst; + gat = 0; + nst = 0; + continue f; + } + } + } + return result; + } + + + private boolean isAnagram(String str, String str1) { + if (str.length() != str1.length()) + return false; + int[] words = new int[26]; + for (int i = 0; i < str.length(); ++i) { + words[str.charAt(i) - 'a']++; + words[str1.charAt(i) - 'a']--; + } + for (int n : words) { + if (n != 0) + return false; + } + return true; + } + + /** + * 第二种题解 + * 将每个字符串按照字母顺序排序,这样的话就可以把 eat,tea,ate 都映射到 aet。其他的类似 + * 时间复杂度:排序的话算作 O(Klog(K)) + * + * @param strs + * @return + */ + public List> groupAnagrams2(String[] strs) { + if (strs.length == 0) return new ArrayList(); + Map ans = new HashMap(); + for (String s : strs) { + char[] ca = s.toCharArray(); + Arrays.sort(ca); + String key = String.valueOf(ca); + if (!ans.containsKey(key)) ans.put(key, new ArrayList()); + ans.get(key).add(s); + } + return new ArrayList(ans.values()); + } + + public List> groupAnagrams3(String[] strs) { + HashMap> hash = new HashMap<>(); + //每个字母对应一个质数 + int[] prime = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103}; + for (int i = 0; i < strs.length; i++) { + int key = 1; + //累乘得到 key + for (int j = 0; j < strs[i].length(); j++) { + key *= prime[strs[i].charAt(j) - 'a']; + } + if (hash.containsKey(key)) { + hash.get(key).add(strs[i]); + } else { + List temp = new ArrayList(); + temp.add(strs[i]); + hash.put(key, temp); + } + + } + return new ArrayList>(hash.values()); + } +} \ No newline at end of file diff --git a/Week 02/id_233/NOTE.md b/Week 02/id_233/NOTE.md index a6321d6e2..172e5b1dc 100644 --- a/Week 02/id_233/NOTE.md +++ b/Week 02/id_233/NOTE.md @@ -1,4 +1,29 @@ -# NOTE +# 233-Week 02 学习总结 - +## 本周知识点 +- **数据结构:** 哈希表、树 +- **算法:** 递归、分治、回溯 +## 递归阶梯技巧 四步骤 +- terminator 终止条件 +- process current logic 处理当前层逻辑 +- drill down 进入下一层 +- restore current status 清理当前层状态 +``` +// Java 递归模版 +public void recur(int level, int param) { + // terminator + if (level > MAX_LEVEL) { + // process result + return; + } + // process current logic + process(level, param); + // drill down + recur( level: level + 1, newParam); + // restore current status +} +``` +[HashMap 源码分析参考文章1](https://blog.csdn.net/zxt0601/article/details/77413921) +[HashMap 源码分析参考文章2](https://juejin.im/post/5a7719456fb9a0633e51ae14) +## !!分治 回溯的代码还没搞明白,搞定后更新总结 diff --git "a/Week 02/id_243/HashMap\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.png" "b/Week 02/id_243/HashMap\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.png" new file mode 100644 index 000000000..72e0e759e Binary files /dev/null and "b/Week 02/id_243/HashMap\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.png" differ diff --git a/Week 02/id_243/LeetCode_105_243.java b/Week 02/id_243/LeetCode_105_243.java new file mode 100644 index 000000000..7c8bb730e --- /dev/null +++ b/Week 02/id_243/LeetCode_105_243.java @@ -0,0 +1,17 @@ +/** + * @author eazonshaw + * @date 2019/10/27 18:09 + * 题目:105. 从前序与中序遍历序列构造二叉树 + * + * 描述:根据一棵树的前序遍历与中序遍历构造二叉树。 + * + * 注意: + * 你可以假设树中没有重复的元素 + * + * 链接:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ + */ +public class LeetCode_105_243 { + + //TODO + +} diff --git a/Week 02/id_243/LeetCode_144_243.java b/Week 02/id_243/LeetCode_144_243.java new file mode 100644 index 000000000..95e223f10 --- /dev/null +++ b/Week 02/id_243/LeetCode_144_243.java @@ -0,0 +1,30 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * @author eazonshaw + * @date 2019/10/24 21:22 + * + * 题目:144. 二叉树的前序遍历 + * 描述:给定一个二叉树,返回它的 前序 遍历。 + * 链接:https://leetcode-cn.com/problems/binary-tree-preorder-traversal/ + */ +public class LeetCode_144_243 { + + class Solution { + public List preorderTraversal(TreeNode root) { + List list = new ArrayList<>(); + preOrder(root,list); + return list; + } + //递归函数 + private void preOrder(TreeNode root,List list){ + if(root==null) return; + list.add(root.val); + preOrder(root.left,list); + preOrder(root.right,list); + } + + } + +} diff --git a/Week 02/id_243/LeetCode_1_243.java b/Week 02/id_243/LeetCode_1_243.java new file mode 100644 index 000000000..4cd04b44b --- /dev/null +++ b/Week 02/id_243/LeetCode_1_243.java @@ -0,0 +1,56 @@ +import java.util.HashMap; +import java.util.Map; + +/** + * @author eazonshaw + * @date 2019/10/23 16:02 + * + * 题目:1. 两数之和 + * + * 描述:给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。 + * 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 + * + * 示例:给定 nums = [2, 7, 11, 15], target = 9 + * + * 因为 nums[0] + nums[1] = 2 + 7 = 9 + * 所以返回 [0, 1] + * + * 链接:https://leetcode-cn.com/problems/two-sum/ + */ +public class LeetCode_1_243 { + + /** + * 暴力法:冒泡遍历,两数和符合要求则返回 + */ + public int[] twoSum1(int[] nums, int target) { + + for(int i=0;i map = new HashMap<>(); + for(int i=0;i=2){ + tn = root; + } + + //只要含有两个节点则返回真 + return left + right + mid >0; + } + + +} diff --git a/Week 02/id_243/LeetCode_242_243.java b/Week 02/id_243/LeetCode_242_243.java new file mode 100644 index 000000000..9c09c83d6 --- /dev/null +++ b/Week 02/id_243/LeetCode_242_243.java @@ -0,0 +1,76 @@ +import java.util.Arrays; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +/** + * @author eazonshaw + * @date 2019/10/22 20:21 + * + * 题目:242. 有效的字母异位词 + * 描述:给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 + * 链接:https://leetcode-cn.com/problems/valid-anagram/ + */ +public class LeetCode_242_243 { + + /** + * 暴力法 + * 直接对两个字符进行排序,再比较是否相等 + */ + public boolean isAnagram1(String s, String t) { + char[] sChars = s.toCharArray(); + char[] tChars = t.toCharArray(); + Arrays.sort(sChars); + Arrays.sort(tChars); + return Arrays.equals(sChars,tChars); + } + + /** + * 散列表 + * 将字符串的每一项都存入同一个散列表中做计数 + * 遍历散列表,查看散列值是否都为0 + */ + public boolean isAnagram2(String s, String t){ + + Map table = new HashMap<>(); + char[] sChars = s.toCharArray(); + char[] tChars = t.toCharArray(); + if(sChars.length != tChars.length){ + return false; + } + for(int i=0;i entry:table.entrySet()){ + if (entry.getValue()!=0){ + return false; + } + } + return true; + } + + /** + * 同样是散列表,抛弃高级数据结构,直接用数组解决 + */ + public boolean isAnagram3(String s, String t) { + if(s.length()!=t.length()){ + return false; + } + int[] counts = new int[26]; + for(int i=0;i> levelOrder(Node root) { + List> list = new ArrayList<>(); + order(root,0,list); + return list; + } + + //递归函数 + private void order(Node root,Integer depth,List> list){ + if(root==null) return; + //判断是否为新的一层 + if(depth + 1 >list.size()){ + list.add(new ArrayList<>()); + } + list.get(depth).add(root.val); + + //处理子节点 + for(Node node:root.children){ + order(node,depth+1,list); + } + } + +} diff --git a/Week 02/id_243/LeetCode_49_243.java b/Week 02/id_243/LeetCode_49_243.java new file mode 100644 index 000000000..8fd12cbd1 --- /dev/null +++ b/Week 02/id_243/LeetCode_49_243.java @@ -0,0 +1,50 @@ +import java.util.*; + +/** + * @author eazonshaw + * @date 2019/10/22 21:36 + * + * 题目:49. 字母异位词分组 + * 描述:给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串 + * 示例: + * 输入: ["eat", "tea", "tan", "ate", "nat", "bat"], + * 输出: + * [ + * ["ate","eat","tea"], + * ["nat","tan"], + * ["bat"] + * ] + * 链接:https://leetcode-cn.com/problems/group-anagrams/ + */ +public class LeetCode_49_243 { + + /** + * 新建一个hash表,将strs中元素的排序后值作为key,List作为value,返回hash表 + */ + public List> groupAnagrams(String[] strs) { + + Map> hash = new HashMap<>(); + + for(String str:strs){ + char[] chars = str.toCharArray(); + //排序 + Arrays.sort(chars); + String key = String.valueOf(chars); + //初始 + if(!hash.containsKey(key)){ + hash.put(key,new ArrayList<>()); + } + List list = hash.get(key); + list.add(str); + hash.put(key,list); + } + + List> result = new ArrayList<>(); + for(Map.Entry> entry:hash.entrySet()){ + result.add(entry.getValue()); + } + + return result; + } + +} diff --git a/Week 02/id_243/LeetCode_590_243.java b/Week 02/id_243/LeetCode_590_243.java new file mode 100644 index 000000000..42d2d5b23 --- /dev/null +++ b/Week 02/id_243/LeetCode_590_243.java @@ -0,0 +1,27 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * @author eazonshaw + * @date 2019/10/24 21:32 + * 题目:590. N叉树的后序遍历 + * 描述:给定一个 N 叉树,返回其节点值的后序遍历。 + * 链接:https://leetcode-cn.com/problems/n-ary-tree-postorder-traversal/ + */ +public class LeetCode_590_243 { + + public List postorder(Node root) { + List list = new ArrayList<>(); + post(list,root); + return list; + } + //递归函数 + private void post(List list,Node root){ + if(root==null)return; + for(Node node:root.children){ + post(list,node); + } + list.add(root.val); + } + +} diff --git a/Week 02/id_243/LeetCode_94_243.java b/Week 02/id_243/LeetCode_94_243.java new file mode 100644 index 000000000..3c9209daf --- /dev/null +++ b/Week 02/id_243/LeetCode_94_243.java @@ -0,0 +1,29 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * @author eazonshaw + * @date 2019/10/24 21:12 + * + * 题目:94. 二叉树的中序遍历 + * 描述:给定一个二叉树,返回它的中序 遍历。 + * 链接:https://leetcode-cn.com/problems/binary-tree-inorder-traversal/ + */ +public class LeetCode_94_243 { + class Solution { + List list = new ArrayList<>(); + public List inorderTraversal(TreeNode root) { + inorder(root); + return list; + } + //递归函数 + private void inorder(TreeNode root){ + if(root == null) return; + inorderTraversal(root.left); + list.add(root.val); + inorderTraversal(root.right); + } + + } + +} diff --git a/Week 02/id_243/NOTE.md b/Week 02/id_243/NOTE.md index a6321d6e2..20793dbd1 100644 --- a/Week 02/id_243/NOTE.md +++ b/Week 02/id_243/NOTE.md @@ -1,4 +1,96 @@ -# NOTE +# 学习总结 +## 散列表 +1. 散列表的定义 +* 散列表,又称哈希表。 +* 散列表用的是**数组**支持按照下标随机访问数据的特性,所以散列表其实就是数组的一种扩展,由数组演化而来。可以说,**如果没有数组,就没有散列表**。 +2. 散列函数的设计原则 +* 散列函数计算得到的散列值是一个非负整数; +* 如果 key1 = key2,那 hash(key1) == hash(key2); +* 如果 key1 ≠ key2,那 hash(key1) ≠ hash(key2)。 +> 设计再好的散列函数也没有办法完全避免散列冲突(hash寻址重复) +3. 散列冲突的解决 +* 开放寻址法 - 小规模数据、装载因子不高的散列表 +* 链表法/拉链法 - 普遍适用 +> Java中的HashMap的实现就是利用链法表,而LinkedHashMap利用的是双向链表+散列表。 +### HashMap小结 +[HashMap脑图](https://github.com/eazonshaw/algorithm004-03/tree/master/Week%2002/id_243/HashMap的实现原理.png) + +## 树 +1. 树 +> 非线性结构 +* 高度:节点到叶子节点的最大路径 +* 深度:根节点到某个节点所经历的路径数 +* 层数:节点的深度+1 +* 树的高度:根节点的高度 +2. 二叉树 +> 每个节点最多有两个节点(左节点、右节点)的树 +* 完全二叉树,最后一层的叶子节点靠左排列,其它层完整 +* 满二叉树,每个节点都有左右两个叶子节点 +3. 二叉树的遍历 +* 前序遍历(根左右) +* 中序遍历(左根右) +* 后续遍历(左右根) +> 以上遍历的时间复杂度均为O(n),n为节点的个数 +4. 二叉搜索树 +* 中序遍历二叉查找树,可以输出有序的数据序列,所以二叉搜索树又称二叉排序树 +> 参考java中优先队列的实现 +* 最坏情况下,退化成链表,时间复杂度为O(N) +* 最好情况下,满二叉树,时间复杂度为O(logN) +### 重点掌握 +三种遍历方式的实现,二叉搜索树的查找、插入、**删除** + +# 递归 +### 四部曲 +- 终止条件 +- 处理当前层逻辑 +- 进入到下一层 +- 清理当前层的环境 +### python模板 +```python +def recursion(level, param1, param2, ...): + # recursion terminator + if level > MAX_LEVEL: + process_result + return + + # process logic in current level + process(level, data...) + + # drill down + self.recursion(level + 1, p1, ...) + + # reverse the current level status if needed +``` +### java模板 +```java +public void recur(int level, int param) { + + // terminator + if (level > MAX_LEVEL) { + // process result + return; + } + + // process current logic + process(level, param); + + // drill down + recur( level: level + 1, newParam); + + // restore current status + +} +``` +### 重点,拒绝人肉递归,多练多记 + + + + + + + + + diff --git a/Week 02/id_243/Node.java b/Week 02/id_243/Node.java new file mode 100644 index 000000000..771044b9c --- /dev/null +++ b/Week 02/id_243/Node.java @@ -0,0 +1,20 @@ +import java.util.List; + +/** + * @author eazonshaw + * @date 2019/10/24 21:33 + * + * N叉树的节点类 + */ +public class Node { + + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +} diff --git a/Week 02/id_243/TreeNode.java b/Week 02/id_243/TreeNode.java new file mode 100644 index 000000000..9ce61dfa7 --- /dev/null +++ b/Week 02/id_243/TreeNode.java @@ -0,0 +1,14 @@ +/** + * @author eazonshaw + * @date 2019/10/24 21:13 + * + * 树节点类 + */ +public class TreeNode { + + int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { val = x; } + +} diff --git a/Week 02/id_253/LeetCode_105_253.java b/Week 02/id_253/LeetCode_105_253.java new file mode 100644 index 000000000..885fa86d9 --- /dev/null +++ b/Week 02/id_253/LeetCode_105_253.java @@ -0,0 +1,25 @@ +class Solution { + int pre_idx=0; + int[] preorder; + int[] inorder; + HashMap map = new HashMap(); + public TreeNode helper(int left ,int right){ + if(left == right) return null; + int pre = preorder[pre_idx]; + TreeNode root = new TreeNode(pre); + pre_idx++; + int index= map.get(pre); + root.left=helper(left,index); + root.right=helper(index+1,right); + return root; + } + public TreeNode buildTree(int[] preorder, int[] inorder) { + this.preorder=preorder; + this.inorder=inorder; + int idx=0; + for(Integer a : inorder){ + map.put(a,idx++); + } + return helper(0,preorder.length); + } +} diff --git a/Week 02/id_253/LeetCode_144_253.java b/Week 02/id_253/LeetCode_144_253.java new file mode 100644 index 000000000..a5ad9595e --- /dev/null +++ b/Week 02/id_253/LeetCode_144_253.java @@ -0,0 +1,31 @@ +class Solution { + public List preorderTraversal(TreeNode root) { + Stack stack = new Stack<>(); + List ans = new ArrayList<>(); + TreeNode cur=root; + while(cur != null || !stack.isEmpty()){ + while(cur != null) { + ans.add(cur.val); + stack.push(cur); + cur=cur.left; + } + cur=stack.pop(); + cur=cur.right; + } + return ans; + } +} +// class Solution { +// public List preorderTraversal(TreeNode root) { +// List ans = new ArrayList<>(); +// a(root,ans); +// return ans; +// } +// public void a(TreeNode root , List ans){ +// if(root != null){ +// ans.add(root.val); +// if(root.left != null) a(root.left , ans); +// if(root.right != null) a(root.right , ans ); +// } +// } +// } diff --git a/Week 02/id_253/LeetCode_169_253.java b/Week 02/id_253/LeetCode_169_253.java new file mode 100644 index 000000000..98ae1a259 --- /dev/null +++ b/Week 02/id_253/LeetCode_169_253.java @@ -0,0 +1,15 @@ +class Solution { + public int majorityElement(int[] nums) { + Map map = new HashMap<>(); + int maxnum=0; int maxcount=0; + for(int num : nums){ + int count = map.getOrDefault(num,0) + 1; + map.put(num,count); + if(count > maxcount){ + maxcount=count; + maxnum=num; + } + } + return maxnum; + } +} diff --git a/Week 02/id_253/LeetCode_17_253.java b/Week 02/id_253/LeetCode_17_253.java new file mode 100644 index 000000000..0fe280a5a --- /dev/null +++ b/Week 02/id_253/LeetCode_17_253.java @@ -0,0 +1,27 @@ +class Solution { + List ans = new LinkedList(); + public void search(String s ,String digits,int i,Map map){ + if(i == digits.length()){ + ans.add(s); + return; + } + String letters = map.get(digits.charAt(i)); + for(int j=0;j letterCombinations(String digits) { + if(digits.length()==0 || digits == null) return new ArrayList(); + Map map = new HashMap<>(); + map.put('2',"abc"); + map.put('3',"def"); + map.put('4',"ghi"); + map.put('5',"jkl"); + map.put('6',"mno"); + map.put('7',"pqrs"); + map.put('8',"tuv"); + map.put('9',"wxyz"); + search("",digits,0,map); + return ans; + } +} diff --git a/Week 02/id_253/LeetCode_1_253.java b/Week 02/id_253/LeetCode_1_253.java new file mode 100644 index 000000000..e1b8608ca --- /dev/null +++ b/Week 02/id_253/LeetCode_1_253.java @@ -0,0 +1,13 @@ +class Solution { + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(); + for(int i=0;i=2) {ans=root;} + if(left + right + mid>0) return true; + return false; + } + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + this.helper(root ,p,q); + return this.ans; + } +} diff --git a/Week 02/id_253/LeetCode_242_253.java b/Week 02/id_253/LeetCode_242_253.java new file mode 100644 index 000000000..571adc2d1 --- /dev/null +++ b/Week 02/id_253/LeetCode_242_253.java @@ -0,0 +1,15 @@ +class Solution { +public: + bool isAnagram(string s, string t) { + if(s.length() != t.length()) return false; + int a[26]={0}; + for(int i=0;i> levelOrder(Node root) { + List> ans = new ArrayList<>(); + if(root == null) return ans; + ans.add(new ArrayList(root.val)); + a(root,0,ans); + return ans; + } + public void a(Node root ,int depth, List> ans){ + if(root == null) return ; + if(depth +1 >ans.size()) ans.add(new ArrayList<>()); + ans.get(depth).add(root.val); + for(Node node : root.children){ + a(node,depth+1,ans); + } + } +} diff --git a/Week 02/id_253/LeetCode_46_253.java b/Week 02/id_253/LeetCode_46_253.java new file mode 100644 index 000000000..8b2736eaa --- /dev/null +++ b/Week 02/id_253/LeetCode_46_253.java @@ -0,0 +1,19 @@ +class Solution { + List> ans = new LinkedList>(); + public void helper(int[] nums , LinkedList curr,int[] visited){ + if(curr.size() == nums.length) {ans.add(new LinkedList(curr)); return;} + for(int i=0; i> permute(int[] nums) { + int[] visited = new int[nums.length]; + helper(nums , new LinkedList(),visited); + return ans; + } +} diff --git a/Week 02/id_253/LeetCode_47_253.java b/Week 02/id_253/LeetCode_47_253.java new file mode 100644 index 000000000..7e7c1b361 --- /dev/null +++ b/Week 02/id_253/LeetCode_47_253.java @@ -0,0 +1,29 @@ +class Solution { + private List> ans = new LinkedList<>(); + private boolean[] used; + public void backtrack(int[] nums, LinkedList curr){ + if(curr.size() == nums.length) { + ans.add(new LinkedList(curr)); + return; + } + for(int i=0;i 0 && nums[i] == nums[i - 1] && !used[i - 1]) { + continue; + } + if(used[i] == true) continue; + used[i] = true; + curr.add(nums[i]); + backtrack(nums,curr); + used[i]=false; + curr.removeLast(); + } + } + public List> permuteUnique(int[] nums) { + if(nums.length == 0) return ans; + Arrays.sort(nums); + used=new boolean[nums.length]; + backtrack(nums,new LinkedList()); + return ans; + } +} + diff --git a/Week 02/id_253/LeetCode_49_253.java b/Week 02/id_253/LeetCode_49_253.java new file mode 100644 index 000000000..be09ca39c --- /dev/null +++ b/Week 02/id_253/LeetCode_49_253.java @@ -0,0 +1,21 @@ +class Solution { + public List> groupAnagrams(String[] strs) { + int[] count = new int[26]; + if(strs.length == 0) return new ArrayList(); + Map ans = new HashMap(); + for(String s : strs){ + Arrays.fill(count,0); + for(char c : s.toCharArray()){ + count[c - 'a']++; + } + StringBuilder sb = new StringBuilder(""); + for(int i=0;i<26;i++){ + sb.append(count[i]); + } + String key = sb.toString(); + if(!ans.containsKey(key)) ans.put(key,new ArrayList()); + ans.get(key).add(s); + } + return new ArrayList(ans.values()); + } +} diff --git a/Week 02/id_253/LeetCode_50_253.java b/Week 02/id_253/LeetCode_50_253.java new file mode 100644 index 000000000..ac23e0b16 --- /dev/null +++ b/Week 02/id_253/LeetCode_50_253.java @@ -0,0 +1,24 @@ +class Solution { + private double helper(double x,long N){ + if(N == 0){ + return 1.0; + } + double half = helper(x,N/2); + if(N%2==1){ + return half*half*x; + } + if(N%2==0){ + return half*half; + } + return x; + } + public double myPow(double x, int n) { + long N = n; + if(N < 0){ + x= 1/x; + N=-N; + } + return helper(x,N); + } +} + diff --git a/Week 02/id_253/LeetCode_589_253.java b/Week 02/id_253/LeetCode_589_253.java new file mode 100644 index 000000000..0b670b9c3 --- /dev/null +++ b/Week 02/id_253/LeetCode_589_253.java @@ -0,0 +1,16 @@ +class Solution { + public List preorder(Node root) { + List ans = new ArrayList(); + helper(root,ans); + return ans; + } + public void helper(Node root , List ans){ + if(root != null){ + ans.add(root.val); + for(Node temp : root.children){ + helper(temp,ans); + } + } + return; + } +} diff --git a/Week 02/id_253/LeetCode_590_253.java b/Week 02/id_253/LeetCode_590_253.java new file mode 100644 index 000000000..419b65a0e --- /dev/null +++ b/Week 02/id_253/LeetCode_590_253.java @@ -0,0 +1,16 @@ +class Solution { + public List postorder(Node root) { + List ans = new ArrayList<>(); + if(root == null) return ans; + a(root ,ans); + return ans; + } + public void a(Node root,List ans){ + if(root != null){ + for(Node node : root.children){ + a(node,ans); + } + ans.add(root.val); + } + } +} diff --git a/Week 02/id_253/LeetCode_77_253.java b/Week 02/id_253/LeetCode_77_253.java new file mode 100644 index 000000000..7d41fd490 --- /dev/null +++ b/Week 02/id_253/LeetCode_77_253.java @@ -0,0 +1,19 @@ +class Solution { + List> ans = new ArrayList>(); + int n; + int k; + public void backtrack(int first , LinkedList curr){ + if(curr.size()== k) ans.add(new LinkedList(curr)); + for(int i=first ; i<=n;i++){ + curr.add(i); + backtrack(i+1,curr); + curr.removeLast(); + } + } + public List> combine(int n, int k) { + this.n=n; + this.k=k; + backtrack(1, new LinkedList()); + return ans; + } +} diff --git a/Week 02/id_253/LeetCode_78_253.java b/Week 02/id_253/LeetCode_78_253.java new file mode 100644 index 000000000..dbd6df876 --- /dev/null +++ b/Week 02/id_253/LeetCode_78_253.java @@ -0,0 +1,17 @@ +class Solution { + List> ans = new LinkedList<>(); + public void dfs(int[] nums,int depth,LinkedList curr){ + if(depth == nums.length){ + ans.add(new LinkedList(curr)); + return; + } + dfs(nums,depth+1,curr); + curr.add(nums[depth]); + dfs(nums,depth+1,curr); + curr.removeLast(); + } + public List> subsets(int[] nums) { + dfs(nums,0,new LinkedList()); + return ans; + } +} diff --git a/Week 02/id_253/LeetCode_94_253.java b/Week 02/id_253/LeetCode_94_253.java new file mode 100644 index 000000000..5ad03076c --- /dev/null +++ b/Week 02/id_253/LeetCode_94_253.java @@ -0,0 +1,32 @@ +class Solution { + public List inorderTraversal(TreeNode root) { + Stack stack = new Stack<>(); + List ans = new ArrayList<>(); + TreeNode cur=root; + while(cur != null || !stack.isEmpty()){ + while(cur != null){ + stack.push(cur); + cur=cur.left; + } + cur = stack.pop(); + ans.add(cur.val); + cur=cur.right; + } + return ans; + } +} +// class Solution { +// public List inorderTraversal(TreeNode root) { +// List ans = new ArrayList<>(); +// a(root , ans); +// return ans; +// } +// public void a(TreeNode root , List ans){ +// if(root != null){ +// if(root.left != null) a(root.left , ans); +// ans.add(root.val); +// if(root.right !=null) a(root.right , ans); +// } + +// } +// } diff --git a/Week 02/id_258/LeetCode_144_258.js b/Week 02/id_258/LeetCode_144_258.js new file mode 100644 index 000000000..72fc96962 --- /dev/null +++ b/Week 02/id_258/LeetCode_144_258.js @@ -0,0 +1,42 @@ +/* + * @lc app=leetcode.cn id=144 lang=javascript + * + * [144] 二叉树的前序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {number[]} + */ +var preorderTraversal = function (root) { + let res = []; + + preorderProcess(root, res); + + return res; +}; + +var preorderProcess = function (root, res) { + if (root !== null) { + res.push(root.val); + + if (root.left !== null) { + preorderProcess(root.left, res); + } + + if (root.right !== null) { + preorderProcess(root.right, res); + } + + } +} + +// @lc code=end \ No newline at end of file diff --git a/Week 02/id_258/LeetCode_169_258.js b/Week 02/id_258/LeetCode_169_258.js new file mode 100644 index 000000000..3af4cba62 --- /dev/null +++ b/Week 02/id_258/LeetCode_169_258.js @@ -0,0 +1,19 @@ +/* + * @lc app=leetcode.cn id=169 lang=javascript + * + * [169] 求众数 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var majorityElement = function (nums) { + let map = {} + for (let i = 0; i < nums.length; i++) { + map[nums[i]] = map[nums[i]] + 1 || 1; + if (map[nums[i]] > nums.length / 2) return nums[i]; + } +}; +// @lc code=end \ No newline at end of file diff --git a/Week 02/id_258/LeetCode_1_258.js b/Week 02/id_258/LeetCode_1_258.js new file mode 100644 index 000000000..586615d68 --- /dev/null +++ b/Week 02/id_258/LeetCode_1_258.js @@ -0,0 +1,23 @@ +/* + * @lc app=leetcode.cn id=1 lang=javascript + * + * [1] 两数之和 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = function (nums, target) { + let map = new Map(); + + for (let i = 0; i < nums.length; i++) { + if (map.has(target - nums[i])) { + return [i, map.get(target - nums[i])] + } + map.set(nums[i], i) + } +}; +// @lc code=end \ No newline at end of file diff --git a/Week 02/id_258/LeetCode_242_258.js b/Week 02/id_258/LeetCode_242_258.js new file mode 100644 index 000000000..20ab0c7ad --- /dev/null +++ b/Week 02/id_258/LeetCode_242_258.js @@ -0,0 +1,33 @@ +/* + * @lc app=leetcode.cn id=242 lang=javascript + * + * [242] 有效的字母异位词 + */ + +// @lc code=start +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function (s, t) { + // let sList = s.split("").sort() + // let tList = t.split("").sort() + // if (sList.length !== tList.length) return false; + // return sList.every((ele, index, array) => { + // return ele === tList[index] + // }) + return s.split("").sort().join("") === t.split("").sort().join("") + +}; + +// var isAnagram = function (s, t) { +// const map = {}; +// s.split("").map(item => map[item] = map[item] ? map[item] + 1 : 1); +// t.split("").map(item => map[item] = map[item] ? map[item] - 1 : -1); + +// return Object.keys(map).every(key => map[key] === 0); +// }; + +console.log(isAnagram('a', 'ab')) +// @lc code=end \ No newline at end of file diff --git a/Week 02/id_258/LeetCode_290_258.js b/Week 02/id_258/LeetCode_290_258.js new file mode 100644 index 000000000..8092a10c7 --- /dev/null +++ b/Week 02/id_258/LeetCode_290_258.js @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode.cn id=590 lang=javascript + * + * [590] N叉树的后序遍历 + */ + +// @lc code=start +/** + * // Definition for a Node. + * function Node(val,children) { + * this.val = val; + * this.children = children; + * }; + */ +/** + * @param {Node} root + * @return {number[]} + */ +var postorder = function (root) { + let res = []; + + postorderProcess(root, res); + + return res; +}; + +var postorderProcess = function (root, res) { + if (root !== null) { + for (let i = 0; i < root.children.length; i++) { + postorderProcess(root.children[i], res); + } + res.push(root.val) + } +} +// @lc code=end \ No newline at end of file diff --git a/Week 02/id_258/LeetCode_589_258.js b/Week 02/id_258/LeetCode_589_258.js new file mode 100644 index 000000000..da4d7cc6c --- /dev/null +++ b/Week 02/id_258/LeetCode_589_258.js @@ -0,0 +1,37 @@ +/* + * @lc app=leetcode.cn id=589 lang=javascript + * + * [589] N叉树的前序遍历 + */ + +// @lc code=start +/** + * // Definition for a Node. + * function Node(val,children) { + * this.val = val; + * this.children = children; + * }; + */ +/** + * @param {Node} root + * @return {number[]} + */ +var preorder = function (root) { + let res = []; + + preorderProcess(root, res); + + return res; +}; + +var preorderProcess = function (root, res) { + if (root !== null) { + res.push(root.val); + + root.children.forEach(element => { + preorderProcess(element, res); + }); + } +} + +// @lc code=end \ No newline at end of file diff --git a/Week 02/id_258/LeetCode_94_258.js b/Week 02/id_258/LeetCode_94_258.js new file mode 100644 index 000000000..cf775d856 --- /dev/null +++ b/Week 02/id_258/LeetCode_94_258.js @@ -0,0 +1,34 @@ +/* + * @lc app=leetcode.cn id=94 lang=javascript + * + * [94] 二叉树的中序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {number[]} + */ +var inorderTraversal = function (root) { + let res = []; + + inOrder(root, res); + + return res; +}; + +var inOrder = function (root, res) { + if (root !== null) { + if (root.left !== null) inOrder(root.left, res); + res.push(root.val); + if (root.right !== null) inOrder(root.right, res); + } +}; +// @lc code=end \ No newline at end of file diff --git a/Week 02/id_273/LeetCode_104_273.java b/Week 02/id_273/LeetCode_104_273.java new file mode 100644 index 000000000..4d2b464d8 --- /dev/null +++ b/Week 02/id_273/LeetCode_104_273.java @@ -0,0 +1,32 @@ +//104. 二叉树最大深度 + +//解法1:递归 执行用时击败100% +//思路:每drill down一次, 深度+1, 最后返回左右子树的最大深度即可 +//时间复杂度:O(n) +//空间复杂度:O(n) +class Solution { + public int maxDepth(TreeNode root) { + return root == null ? 0 : Math.max(maxDepth(root.left) + 1, maxDepth(root.right) + 1); + } +} + +//解法2:BFS 执行用时击败89% +//思路:通过维护一个栈记录每一层的节点, 若当前层节点还存在孩子节点, 下探到下一层, level + 1 +//时间复杂度:O(n) +//空间复杂度:O(n) +public int maxDepth(TreeNode root) { + if (root == null) return 0; + Queue queue = new LinkedList<>(); + queue.offer(root); + int level = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + level++; + for (int i = 0; i < size; i++) { + TreeNode curr = queue.poll(); + if (curr.left != null) queue.offer(curr.left); + if (curr.right != null) queue.offer(curr.right); + } + } + return level; +} \ No newline at end of file diff --git a/Week 02/id_273/LeetCode_105_273.java b/Week 02/id_273/LeetCode_105_273.java new file mode 100644 index 000000000..0a6fb0111 --- /dev/null +++ b/Week 02/id_273/LeetCode_105_273.java @@ -0,0 +1,28 @@ +//105. 从前序与中序遍历构造二叉树 + +//解法1:递归 执行用时击败约54% +//思路:根据前序遍历根左右的特性, 可以确定前序遍历第一个节点为根节点。 +// 通过前序遍历获取到根节点后, 再通过中序遍历的特性, 可以寻找到该结点的左/右子节点 +// 中序遍历中:左节点位于数组上的坐标就是根节点的左半部分, 同理右节点 +//时间复杂度O(N) +//空间复杂度O(N) +public TreeNode buildTree(int[] preorder, int[] inorder) { + return recur(preorder, inorder, 0, 0, inorder.length - 1); +} + +private TreeNode recur(int[] preorder, int[] inorder, int preStart, int inStart, int inEnd) { + if (preStart > preorder.length - 1 || inStart > inEnd) { + return null; + } + TreeNode curr = new TreeNode(preorder[preStart]); + int index = 0; + for (int i = inStart; i <= inEnd; i++) { + if (inorder[i] == curr.val) { + index = i; + break; + } + } + curr.left = recur(preorder, inorder, preStart + 1, inStart, index - 1); + curr.right = recur(preorder, inorder, preStart + 1 + index - inStart, index + 1, inEnd); + return curr; +} \ No newline at end of file diff --git a/Week 02/id_273/LeetCode_111_273.java b/Week 02/id_273/LeetCode_111_273.java new file mode 100644 index 000000000..43777629f --- /dev/null +++ b/Week 02/id_273/LeetCode_111_273.java @@ -0,0 +1,25 @@ +//111. 二叉树的最小深度 + +//解法1:递归 执行时间击败100% +//思路:主要思路和"二叉树的最大深度"差不多, 区别在于该题是先drill down到底层, 返回到上层的时候Depth再 + 1, 而"最大深度"是drill down的同时, Depth + 1 +// 求最小深度可以分为三种情况 +// 1. currentNode == null return 0 +// 2. currentNode.left and right == null return 1 +// 3. currentNode.left or right == null 也就是只有一边存在深度的情况 则return leftDepth + rightDepth + 1即可, 因为此条件下, leftDepth和rightDepth必有一处 = 0 +// 4. 若两边都存在子节点 也就是两边都存在深度的情况 则return 左右子树的minDepth + 1即可 +//时间复杂度O(n) +//空间复杂度O(logN) 但最坏情况下二叉树为链式结构时,递归会调用N次,空间复杂度为O(N) +class Solution { + public int minDepth(TreeNode root) { + if (root == null) return 0; + if (root.left == null && root.right == null) return 1; + int leftDeep = minDepth(root.left); + int rightDeep = minDepth(root.right); + //若左右节点存在一个为null,那么必定有一个深度为0 + if (root.left == null || root.right == null) { + return leftDeep + rightDeep + 1; + } + //若左右节点都不为空,返回一个较小值 + return Math.min(leftDeep, rightDeep) + 1; + } +} \ No newline at end of file diff --git a/Week 02/id_273/LeetCode_144_273.java b/Week 02/id_273/LeetCode_144_273.java new file mode 100644 index 000000000..ead58cca1 --- /dev/null +++ b/Week 02/id_273/LeetCode_144_273.java @@ -0,0 +1,35 @@ +//144 二叉树前序遍历 + +//1. 递归解法 执行用时击败100% +//时间复杂度O(n) +class Solution { + List arraylist = new ArrayList<>(); + public List preorderTraversal(TreeNode root) { + if (root != null) { + arraylist.add(root.val); + preorderTraversal(root.left); + preorderTraversal(root.right); + } + return arraylist; + } +} + +//2. 辅助栈迭代 执行用时击败93% +//时间复杂度O(n) +class Solution { + List list = new ArrayList<>(); + public List preorderTraversal(TreeNode root) { + Stack stack = new Stack<>(); + TreeNode current = root; + while (!stack.isEmpty() || root != null) { + while (root != null) { + stack.push(root); + list.add(root.val); + root = root.left; + } + TreeNode curr = stack.pop(); + root = curr.right; + } + return list; + } +} \ No newline at end of file diff --git a/Week 02/id_273/LeetCode_169_273.java b/Week 02/id_273/LeetCode_169_273.java new file mode 100644 index 000000000..866b264b5 --- /dev/null +++ b/Week 02/id_273/LeetCode_169_273.java @@ -0,0 +1,84 @@ +//169. 求众数 + +//解法1:HashMap +//思路:参考了LeetCode第一题TwoSum的HashMap思想, 重复出现的数value+1,当判断某个数value > n/2 , return this num; +//时间复杂度O(n) +//空间复杂度O(n) +//总结:一看到这类问题立马就想到了HashMap...虽然效率不高, 但是真的好用... (估计时间浪费在HashMap的操作上了) +public int majorityElement(int[] nums) { + HashMap map = new HashMap<>(); + for (int i = 0; i (nums.length/2)) { + return nums[i]; + } + continue; + } + map.put(nums[i],1); + } + return nums[0]; +} + +//解法2:分治 执行用时击败100% +//思路:将数组递归折半划分,求出两个区域中的中位数,若左右两边中位数相同直接return , else求出不同的中位数在左/右半部分出现的次数, return次数多的那个 +//时间复杂度O(nlogN) +//空间复杂度O(logN) +//总结:和快速排序采用了同样的思想——分治, 由于大部分操作都基于指针而不是对数组本身操作, 因此效率要快上不少。 +public int majorityElement(int[] nums) { + return recur(nums, 0, nums.length - 1); +} + +private int recur(int[] nums, int low, int high){ + if (low >= high) return nums[low]; + int mid = (high - low)/2 + low; + int left = recur(nums, low, mid); + int right = recur(nums, mid + 1, high); + //左半区和右半区的众数相同 + if (left == right) return left; + //若不相同, 计算left和right在当前low~high出现的次数 + int leftCount = countMethod(nums, left, low, high); + int rightCount = countMethod(nums, right, low, high); + return leftCount < rightCount ? right : left; +} + +private int countMethod(int[] nums, int num, int low, int high) { + int count = 0; + for (int i = low; i <= high; i++) { + if (nums[i] == num) count++; + } + return count; + } +} + +//解法3:迭代计数法 执行用时击败100% +//思路:设置一个major用来记录当前的众数, count用于计数 +// 遍历数组, 每当遇到与major相同的元素时count++ +// 遇到与major不同的count-- +// 若count此时已经为0, 那么众数需要更换至当前nums[i], count++ +//时间复杂度O(n) +//空间复杂度O(1) +public int majorityElement(int[] nums) { + int major = nums[0]; + int count = 1; + for (int i = 1; i < nums.length; i++) { + if (count == 0) { + major = nums[i]; + count++; + } + else if (nums[i] == major) count++; + else count--; + } + return major; +} + +//解法4:排序取中 +//思路:根据题目要求, 众数出现次数必须大于数组长度N, 那么排序过后位于数组index = nums.length/2的元素必定就是众数 +//时间复杂度:O(nlogN) +//空间复杂度:O(1) +public int majorityElement(int[] nums) { + Arrays.sort(nums); + return nums[nums.length/2]; +} + diff --git a/Week 02/id_273/LeetCode_17_273.java b/Week 02/id_273/LeetCode_17_273.java new file mode 100644 index 000000000..8db2917aa --- /dev/null +++ b/Week 02/id_273/LeetCode_17_273.java @@ -0,0 +1,57 @@ +//17. 电话号码的字母组合 + +//解法1:递归组合字符串 执行用时击败约98% +//思路:在每一层递归函数中获取digits对应的letters,然后letters中的所有字母再组合下一层递归中的letters, 直到所有的digits对应的letters都组合完毕 +//时间复杂度O(3^N * 4^M) 3、4为letters的个数, N,M为digits的长度 +//空间复杂度O(3^N * 4^M) +private static final String[] KEYS = { "", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" }; + +public List letterCombinations2(String digits) { + List res = new ArrayList<>(); + if (digits == "" || digits == null) { + return res; + } + combine("", digits, 0, res); + return res; +} + +private void combine(String str, String digits, int offset, List list) { + if (offset >= digits.length()) { + list.add(str); + return; + } + int index = digits.charAt(offset) - '0'; + String letters = KEYS[index]; + for (int i = 0; i letterCombinations(String digits) { + if (digits == null || digits.equals("")) return new ArrayList<>(); + HashMap map = new HashMap<>(); + map.put('2', "abc"); + map.put('3', "def"); + map.put('4', "ghi"); + map.put('5', "jkl"); + map.put('6', "mno"); + map.put('7', "pqrs"); + map.put('8', "tuv"); + map.put('9', "wxyz"); + List list = new ArrayList<>(); + combination(map, 0, list, digits, ""); + return list; +} + +private void combination(HashMap map, int offset, List list, String digits, String res) { + if (offset == digits.length()) { + list.add(res); + return; + } + String letters = map.get(digits.charAt(offset)); + for (int i = 0; i < letters.length(); i++) { + combination(map, offset + 1, list, digits, res + letters.charAt(i)); + } +} diff --git a/Week 02/id_273/LeetCode_1_273.java b/Week 02/id_273/LeetCode_1_273.java new file mode 100644 index 000000000..2db09a5da --- /dev/null +++ b/Week 02/id_273/LeetCode_1_273.java @@ -0,0 +1,34 @@ +//1. 两数之和 + +//解法1:暴力解法 执行用时击败约53% +//思路:通过两次遍历获取到所有不同的组合,比较组合相加是否等于target +//时间复杂度O(n^2) +//空间复杂度O(1) +//总结:虽然是暴力解法,但是通过两次遍历获取到所有不同的组合的这种遍历方式细节要掌握。 +public int[] twoSum(int[] nums, int target) { + for (int i = 0; i < nums.length - 1; i++) { + for (int j = i + 1; j < nums.length; j++) { + if (nums[i] + nums[j] == target) { + return new int[]{i, j}; + } + } + } + return null; +} + + +//解法2:HashMap 执行用时击败约98% +//思路:遍历nums, 每次遍历判断map中是否存储了target-nums[i]的key, 若已经存在直接取出结果返回, 不存在则将当前nums[i]存入map +//时间复杂度O(n) +//空间复杂度O(n) +//总结:比较典型的空间换时间优化策略,利用了HashMap随机访问高效的特性 +public int[] twoSum(int[] nums, int target) { + HashMap map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + if (map.containsKey(target - nums[i])) { + return new int[]{i, map.get(target - nums[i])}; + } + map.put(nums[i], i); + } + return null; +} \ No newline at end of file diff --git a/Week 02/id_273/LeetCode_226_273.java b/Week 02/id_273/LeetCode_226_273.java new file mode 100644 index 000000000..626cff7e5 --- /dev/null +++ b/Week 02/id_273/LeetCode_226_273.java @@ -0,0 +1,16 @@ +//226. 翻转二叉树 + +//解法1:递归 执行用时击败100% +//思路:从根节点开始递归,将根节点的left指向递归后的右子树,根节点的right指向递归后的左子树 +//时间复杂度O(n) +//空间复杂度O(n) +class Solution { + public TreeNode invertTree(TreeNode root) { + if (root == null) return root; + TreeNode leftNode = root.left; + TreeNode rightNode = root.right; + root.right = invertTree(leftNode); + root.left = invertTree(rightNode); + return root; + } +} diff --git a/Week 02/id_273/LeetCode_22_273.java b/Week 02/id_273/LeetCode_22_273.java new file mode 100644 index 000000000..a9e5e83cf --- /dev/null +++ b/Week 02/id_273/LeetCode_22_273.java @@ -0,0 +1,67 @@ +//22. 括号生成 + +//解法1:回溯法 执行用时击败约99% +//思路:首先获取到所有可能的括号组合,然后添加约束条件,筛选出所有符合要求的str: +// 左括号可以出现在任意位置,而右括号只能出现在左括号的后面 +class Solution { + public List generateParenthesis(int n) { + List result = new ArrayList<>(); + recur("", result, 0, 0, n); + return result; + } + + private void recur(String str, List list, int left, int right, int n) { + if (str.length() == n*2) { + list.add(str); + return; + } + if (left < n) recur(str + "(", list, left + 1, right, n); + if (right < left) recur(str + ")", list, left, right + 1, n); + } +} + +//解法2:bfs解法 +//思路:创建一个队列用于维护每次添加的括号组合, 当poll的str长度为n*2时将其添加到result +class Solution { + public List generateParenthesis(int n) { + List result = new ArrayList<>(); + Queue queue = new LinkedList<>(); + queue.offer("("); + + while (!queue.isEmpty()) { + String str = queue.poll(); + if (str.length() == n*2) result.add(str); + + int left = countMehodLeft(str);//获取str中的左括号数 + int right = countMethodRight(str);//获取str中的右括号数 + + if (left < n) { + queue.offer(str + "("); + } + if (right < left) { + queue.offer(str + ")"); + } + } + return result; + } + + private int countMehodLeft(String str) { + int count = 0; + for (int i = 0; i list = new LinkedList<>(Arrays.asList(values)); + return recurDe(list); + } + private TreeNode recurDe(List list) { + if (list.get(0).equals("null")) { + list.remove(0); + return null; + } + TreeNode curr = new TreeNode(Integer.valueOf(list.remove(0))); + curr.left = recurDe(list); + curr.right = recurDe(list); + return curr; + } +} + +//解法2:BFS解法 执行用时击败61% +public class Codec { + // Encodes a tree to a single string. + public String serialize(TreeNode root) { + if (root == null) return ""; + StringBuilder sb = new StringBuilder(); + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + TreeNode curr = queue.poll(); + if (curr == null) { + sb.append("null,"); + continue; + } + sb.append(curr.val+","); + queue.offer(curr.left); + queue.offer(curr.right); + } + return sb.toString(); + } + + // Decodes your encoded data to tree. + public TreeNode deserialize(String data) { + if (data == "") return null; + Queue queue = new LinkedList<>(); + String[] values = data.split(","); + TreeNode root = new TreeNode(Integer.parseInt(values[0])); + queue.offer(root); + for (int i = 1; i < values.length; i++) { + TreeNode parent = queue.poll(); + if (!values[i].equals("null")) { + TreeNode left = new TreeNode(Integer.parseInt(values[i])); + parent.left = left; + queue.offer(left); + } + if (!values[++i].equals("null")) { + TreeNode right = new TreeNode(Integer.parseInt(values[i])); + parent.right = right; + queue.offer(right); + } + } + return root; + } +} \ No newline at end of file diff --git a/Week 02/id_273/LeetCode_429_237.java b/Week 02/id_273/LeetCode_429_237.java new file mode 100644 index 000000000..70cfc40aa --- /dev/null +++ b/Week 02/id_273/LeetCode_429_237.java @@ -0,0 +1,28 @@ +//429 N叉树的层序遍历 + +//解法1:辅助队列迭代法: 执行用时击败约78% +//思路:每次迭代获取当前node的孩子节点, 并将它们依次入队, 按层序遍历的逻辑处理并将结果暂存到temp, 每当该层逻辑处理完毕后在结尾处将temp追加到list +//总结:在学习了层序遍历后, 只要牢记层序遍历模版, 这类题也就驾轻就熟了。 +public class NTreeLevelOrderTraversal { + List> list = new ArrayList<>(); + public List> levelOrder(Node root) { + if (root == null) return list; + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + int size = queue.size (); + List temp = new ArrayList<>(); + while (size-- > 0) { + Node curr = queue.poll(); + temp.add(curr.val); + if (curr.children != null) { + for (Node n : curr.children) { + queue.offer(n); + } + } + } + list.add(temp); + } + return list; + } +} diff --git a/Week 02/id_273/LeetCode_46_273.java b/Week 02/id_273/LeetCode_46_273.java new file mode 100644 index 000000000..2a56d79ea --- /dev/null +++ b/Week 02/id_273/LeetCode_46_273.java @@ -0,0 +1,53 @@ +//46. 全排列 + +//解法1:递归交换 提交时间击败100% +public List> permute(int[] nums) { + List> result = new ArrayList<>(); + List numsList = new ArrayList<>(); + for (int num : nums) { + numsList.add(num); + } + backtrackMethod(nums.length, numsList, result, 0); + return result; +} + +private void backtrackMethod(int length, List numsList, List> result, int start) { + //terminate: 当标志位start = length, 将当前numsList的结果添加到result + if (start == length) { + result.add(new ArrayList<>(numsList)); + return; + } + for (int i = start; i < length; i++) { + //将第i个数与当前递归层的第1个数交换位置 + Collections.swap(numsList, start, i); + //drill down + backtrackMethod(length, numsList, result, start + 1); + //回溯到交换之前的状态 + Collections.swap(numsList, start, i); + } +} + + +//解法2:回溯去重 执行用时击败约65 +//思路:深度优先递归, 判断当前遍历获取的nums[i]之前是否出现过, +// 若出现过, continue跳过当前循环 +// 若未出现, list.add(nums[i]) +public List> permute(int[] nums) { + List> result = new ArrayList<>(); + List list = new ArrayList<>(); + permutations(nums, result, list); + return result; +} + +private void permutations(int[] nums, List> result, List list) { + if (list.size() == nums.length) { + result.add(new ArrayList(list)); + return; + } + for (int i = 0; i < nums.length; i++) { + if(list.contains(nums[i])) continue; + list.add(nums[i]); + permutations(nums, result, list); + list.remove(list.size() - 1); + } +} \ No newline at end of file diff --git a/Week 02/id_273/LeetCode_47_273.java b/Week 02/id_273/LeetCode_47_273.java new file mode 100644 index 000000000..ae7340d56 --- /dev/null +++ b/Week 02/id_273/LeetCode_47_273.java @@ -0,0 +1,27 @@ +//47. 全排列II + +//解法1:dfs 执行用时击败约98% +public List> permuteUnique(int[] nums) { + List> result = new ArrayList<>(); + List temp = new ArrayList<>(); + Arrays.sort(nums); + boolean[] flags = new boolean[nums.length]; + dfs(result, temp, flags, nums); + return result; +} + +private void dfs(List> result, List temp, boolean[] flags, int[] nums) { + if (temp.size() == nums.length) { + result.add(new ArrayList<>(temp)); + return; + } + for (int i = 0; i < nums.length; i++) { + if (flags[i]) continue; + if (i > 0 && nums[i - 1] == nums[i] && !flags[i - 1]) continue; + flags[i] = true; + temp.add(nums[i]); + dfs(result, temp, flags, nums); + flags[i] = false; + temp.remove(temp.size() - 1); + } +} \ No newline at end of file diff --git a/Week 02/id_273/LeetCode_49_273.java b/Week 02/id_273/LeetCode_49_273.java new file mode 100644 index 000000000..5182d3bfc --- /dev/null +++ b/Week 02/id_273/LeetCode_49_273.java @@ -0,0 +1,24 @@ +//49. 字母异位词 + +//解法1:HashMap 执行用时击败约99% +//思路:使用HashMap, KEY存储经过排序后的异位词字符串, VALUE存储异位词List +// 遍历字符串数组, 判断map中是否包含当前元素的异位词List, 若有直接追加即可, 若没有创建对应的异位词List +//时间复杂度O(KlogK * N), K为单个异位词字符串的长度 +//空间复杂度O(N * K) +public List> groupAnagrams2(String[] strs) { + Map> map = new HashMap<>(); + List> result = new ArrayList<>(); + for (int i = 0; i < strs.length; i++) { + char[] temp = strs[i].toCharArray(); + Arrays.sort(temp); + String val = String.valueOf(temp); + if (map.containsKey(val)) { + map.get(val).add(strs[i]); + } else { + List list = new ArrayList<>(); + list.add(strs[i]); + map.put(val, list); + } + } + return new ArrayList<>(map.values()); +} \ No newline at end of file diff --git a/Week 02/id_273/LeetCode_50_273.java b/Week 02/id_273/LeetCode_50_273.java new file mode 100644 index 000000000..5fccd0962 --- /dev/null +++ b/Week 02/id_273/LeetCode_50_273.java @@ -0,0 +1,23 @@ +//Pow(x, n) + +//解法1:分治递归 执行用时击败约99% +//思路:考虑两种情况:n为奇数/n为偶数 +// 若n为奇数, 递归(x*x, n/2)还需要补乘一个x +// 若n为偶数, 递归(x*x, n/2) +//时间复杂度:O(logN) +//空间复杂度:O(logN) +//总结:相比起暴力解法n有多少就需要计算N次, 分治递归每次计算后n减半, 只需要计算logn次 +public double myPow(double x, int n) { + if (x == -1) { + if ((n & 1) == 0) return 1; + else return -1; + } + if (n > 0) return recur(x, n); + else return 1/recur(x, n); +} + +private double recur(double x, int n) { + if (n == 0) return 1; + if (n % 2 == 0) return recur(x*x, n/2); + else return recur(x*x, n/2)*x; +} \ No newline at end of file diff --git a/Week 02/id_273/LeetCode_51_273.java b/Week 02/id_273/LeetCode_51_273.java new file mode 100644 index 000000000..8b439b943 --- /dev/null +++ b/Week 02/id_273/LeetCode_51_273.java @@ -0,0 +1,42 @@ +//51. N皇后 + + +//解法1:回溯 执行用时:16ms +//思路:由于皇后的攻击范围是所在的行/列/对角线 ,因此在某一行放置了一个皇后, 该行不可能再放置第二个皇后 +// 因此我们只需要对行数作记录, 判断每一列的位置是否available即可 +// 通过3个Set分别存放当前皇后所占据的列,左/右对角线, 下一个皇后在放置之前通过Set判断当前位置是否可以放置 +class { + private Set col = new HashSet<>(); + private Set diag1 = new HashSet<>(); + private Set diag2 = new HashSet<>(); + + public List> solveNQueens(int n) { + List> result = new ArrayList<>(); + dfs(result, new ArrayList(), n, 0); + return result; + } + + private void dfs(List> result, List list, int n, int row) { + if (row == n) { + result.add(new ArrayList<>(list)); + return; + } + for (int i = 0; i < n; i++) { + if (col.contains(i) || diag1.contains(row + i) || diag2.contains(row - i)) continue; + char[] temp = new char[n]; + Arrays.fill(temp, '.'); + temp[i] = 'Q'; + list.add(new String(temp)); + col.add(i); + diag1.add(row + i); + diag2.add(row - i); + //drill down + dfs(result, list, n, row + 1); + //reverse + list.remove(list.size() - 1); + col.remove(i); + diag1.remove(row + i); + diag2.remove(row - i); + } + } +} \ No newline at end of file diff --git a/Week 02/id_273/LeetCode_589_273.java b/Week 02/id_273/LeetCode_589_273.java new file mode 100644 index 000000000..3a2746a2c --- /dev/null +++ b/Week 02/id_273/LeetCode_589_273.java @@ -0,0 +1,37 @@ +//589. N叉树的前序遍历 + +//解法1:辅助栈迭代法 执行用时击败约35% +//思路:和后序遍历差不多, 按着根--> 左--> 右 的顺序压栈, 并每次pop()一个根节点, 添加到list即可 +class Solution { + List list = new ArrayList<>(); + public List preorder(Node root) { + if (root == null) return list; + Stack stack = new Stack<>(); + stack.push(root); + while (!stack.isEmpty()) { + Node curr = stack.pop(); + list.add(curr.val); + if (curr.children != null) { + for (int i = curr.children.size() - 1; i >= 0; i--) { + stack.push(curr.children.get(i)); + } + } + } + return list; + } +} + +//解法2:递归实现 执行用时击败100% +//思路:与二叉树的前序遍历递归思路相同, 只是这里需要遍历孩子节点List +class Solution { + List list = new ArrayList<>(); + public List preorder(Node root) { + if (root != null) { + list.add(root.val); + for (Node n : root.children) { + preorder(n); + } + } + return list; + } +} \ No newline at end of file diff --git a/Week 02/id_273/LeetCode_590_273.java b/Week 02/id_273/LeetCode_590_273.java new file mode 100644 index 000000000..27bfd5348 --- /dev/null +++ b/Week 02/id_273/LeetCode_590_273.java @@ -0,0 +1,37 @@ +//590. N叉树的后序遍历 + +//解法1:递归法 执行用时击败100% +//思路:深度优先遍历思想 +class Solution { + List list = new ArrayList<>(); + public List postorder(Node root) { + if (root != null) { + for (int i = 0;i中->右->根, 那么只需要不断从当前的node.right进行深度优先遍历, 每次压栈时把当前node.val存放到list头部即可, 这样逆序存放可以使"根-->右-->中-->左"的遍历顺序得到"左->中->右->根"的结果 +class Solution { + List list = new ArrayList<>(); + public List postorder(Node root) { + if (root == null) return list; + Stack stack = new Stack<>(); + stack.push(root); + while (!stack.isEmpty()) { + Node current = stack.pop(); + list.add(0, current.val); + if (current.children != null) { + for (Node n : current.children) { + stack.push(n); + } + } + } + return list; + } +} \ No newline at end of file diff --git a/Week 02/id_273/LeetCode_70_283.java b/Week 02/id_273/LeetCode_70_283.java new file mode 100644 index 000000000..4cbfcbbaa --- /dev/null +++ b/Week 02/id_273/LeetCode_70_283.java @@ -0,0 +1,67 @@ +//70. 爬楼梯 + +//通过规律可知该题的结果为斐波那契数列 +//假设n = 1 ,只能选择跨一步,F(1) = 1 +// n = 2 ,则只可能从台阶1跨1步,或者直接跨2步 F(2) = 2 +// n = 3 ,则只可能从台阶1跨2步,或者从台阶2跨1步 F(3) = F(2) + F(1) +// n = 4 ,则只可能从台阶2跨2步,或者从台阶3跨1步 F(4) = F(3) + F(2) +// .... + +//解法1:斐波那契数列递归 +//思路:... +//时间复杂度O(2^n) +public static int climbStairs(int n) { + if (n <= 2) return n; + return climbStairs(n-1) + climbStairs(n-2); +} + +//解法2:记忆化递归 执行用时:0ms +//思路:创建一个缓存数组用于存储每一次递归的结果, 避免了不必要的重复计算 +//时间复杂度O(n) +//空间复杂度O(n) +class Solution { + public int climbStairs(int n) { + if (n <= 2) return n; + int[] temp = new int[n - 2]; + return recur(n, n, temp); + } + + private int recur(int n, int k, int[] temp) { + if (k == 1 || k == 2) return k; + else if (temp[n - k] != 0) return temp[n - k]; + else return temp[n - k] = recur(n, k - 1, temp) + recur(n, k - 2, temp); + } +} + +//解法3:动态规划(一维数组版) 执行用时:0ms +//思路:可以根据重复子问题能够推导出动态规划的状态方程 dp[n] = dp[n - 1] + dp[n - 2], 有了状态方程, 动态规划就会非常简单 +// 只需要将每一级台阶要跨的步数采用数组存储, 下一次计算时从数组中获取即可 +//时间复杂度O(n) +//空间复杂度O(n) +public int climbStairs(int n) { + if (n <= 2) return n; + int[] dp = new int[n]; + dp[0] = 1; + dp[1] = 2; + for (int i = 2; i < n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n - 1]; +} + +//解法2.1 动态规划(变量暂存版) 执行用时:0ms +//思路:由于动态规划方法每次都只需要操作三个变量, 因此将数组赋值操作简化为变量之间的赋值操作 +//时间复杂度O(n) +//空间复杂度O(1) +public int climbStairs(int n) { + if (n <= 2) return n; + int temp = 2, prev = 1, curr = 0; + for (int i = 3; i <= n; i++) { + curr = prev + temp; + prev = temp; + temp = curr; + } + return curr; +} + + diff --git a/Week 02/id_273/LeetCode_77_273.java b/Week 02/id_273/LeetCode_77_273.java new file mode 100644 index 000000000..ee7dd1599 --- /dev/null +++ b/Week 02/id_273/LeetCode_77_273.java @@ -0,0 +1,23 @@ +//77. 组合 + +//解法1:递归 +//时间复杂度O(n^k) +//空间复杂度O(k) +class Solution { + public List> combine(int n, int k) { + List> combs = new ArrayList>(); + combine(combs, new ArrayList(), 1, n, k); + return combs; + } + public void combine(List> combs, List comb, int start, int n, int k) { + if (k == 0) { + combs.add(new ArrayList<>(comb)); + return; + } + for (int i = start; i <= n; i++) { + comb.add(i); + combine(combs, comb, i + 1, n, k - 1); + comb.remove(comb.size() - 1); + } + } +} \ No newline at end of file diff --git a/Week 02/id_273/LeetCode_78_273.java b/Week 02/id_273/LeetCode_78_273.java new file mode 100644 index 000000000..01ae0f988 --- /dev/null +++ b/Week 02/id_273/LeetCode_78_273.java @@ -0,0 +1,21 @@ +//78. 子集 + +//解法1:回溯 执行时间超越99% +//思路:参考77. 组合, for循环的结束条件就是递归的结束条件, 每次drill down都添加当前list中的元素到result +//总结:对于子集, 组合, 全排列这类题, 递归的思路都差不太多, 每次做完这种题都会忘, 希望再多做几遍后能够看到问题的本质。 +class Solution { + public List> subsets(int[] nums) { + List> res = new ArrayList<>(); + dfs(0, nums, res, new ArrayList()); + return res; + } + + private void dfs(int start, int[] nums, List> res, List list) { + res.add(new ArrayList(list)); + for (int i = start; i < nums.length; i++) { + list.add(nums[i]); + dfs(i + 1, nums, res, list); + list.remove(list.size() - 1); + } + } +} \ No newline at end of file diff --git a/Week 02/id_273/LeetCode_94_273.java b/Week 02/id_273/LeetCode_94_273.java new file mode 100644 index 000000000..613466b5b --- /dev/null +++ b/Week 02/id_273/LeetCode_94_273.java @@ -0,0 +1,36 @@ +//94 二叉树的中序遍历 + +//1. 递归解法 执行用时击败100% +//通过最简单的递归完成左中右的中序遍历 +//时间复杂度O(n) +class Solution { + ArrayList arrayList = new ArrayList<>(); + public List inorderTraversal(TreeNode root) { + if (root != null) { + inorderTraversal(root.left); + arrayList.add(root.val); + inorderTraversal(root.right); + } + return arrayList; + } +} + +//2. 辅助栈迭代 执行用时击败大约95% +//思路:递归的本质是栈 +//时间复杂度O(n) +class Solution { + List arrayList = new ArrayList<>(); + public List inorderTraversal(TreeNode root) { + Stack stack = new Stack<>(); + while (!stack.isEmpty() || root != null) { + while (root != null) { + stack.push(root); + root = root.left; + } + TreeNode curr = stack.pop(); + arrayList.add(curr.val); + root = curr.right; + } + return arrayList; + } +} \ No newline at end of file diff --git a/Week 02/id_273/LeetCode_98_273.java b/Week 02/id_273/LeetCode_98_273.java new file mode 100644 index 000000000..d273d032d --- /dev/null +++ b/Week 02/id_273/LeetCode_98_273.java @@ -0,0 +1,72 @@ +//98. 验证是否是BSTree + +//解法1:深度优先遍历 执行用时击败100% +//思路:优先深入左子树, 判断左节点与父节点的value大小关系, 然后再去右子树 +//时间复杂度O(n) 深度优先遍历每个节点, 且仅遍历一遍 +//空间复杂度O(n) +class Solution { + public boolean isValidBST(TreeNode root) { + return helper(root, Long.MAX_VALUE, Long.MIN_VALUE); + } + + private boolean helper(TreeNode root, long maxVal, long minVal) { + if (root == null) return true; + if (root.val >= maxVal || root.val <= minVal) return false; + return helper(root.left, root.val, minVal) && helper(root.right, maxVal, root.val); + } +} + + +//解法2:广度优先遍历 执行用时击败约30% +//思路:用队列保存节点的信息, 逐层验证 +//时间复杂度O(n) +//空间复杂度O(n) +class Solution { + Queue queue = new LinkedList(); + Queue uppers = new LinkedList(); + Queue lowers = new LinkedList(); + + public void update(TreeNode root, Integer lower, Integer upper) { + queue.add(root); + lowers.add(lower); + uppers.add(upper); + } + + public boolean isValidBST(TreeNode root) { + Integer lower = null, upper = null; + update(root, lower, upper); + while (!queue.isEmpty()) { + root = queue.poll(); + lower = lowers.poll(); + upper = uppers.poll(); + if (root != null) { + if (lower != null && root.val <= lower) return false; + if (upper != null && root.val >= upper) return false; + update(root.left, lower, root.val); + update(root.right, root.val, upper); + } + } + return true; + } +} + +//解法3:中序遍历观察是否有序 执行用时击败约40% +//思路:基于二叉树中序遍历为从小到大的特性,每当进行一次中序遍历时,就与上一个节点的val值比较 +//时间复杂度O(n) +//空间复杂度O(n) +//总结:活用辅助栈完成迭代式中序遍历 +public boolean isValidBST(TreeNode root) { + Stack stack = new Stack<>(); + TreeNode prev = null; + while (!stack.isEmpty() || root != null) { + while(root != null) { + stack.push(root); + root = root.left; + } + root = stack.pop(); + if (prev != null && root.val <= prev.val) return false; + prev = root; + root = root.right; + } + return true; +} \ No newline at end of file diff --git a/Week 02/id_273/NOTE.md b/Week 02/id_273/NOTE.md index a6321d6e2..7aa53519a 100644 --- a/Week 02/id_273/NOTE.md +++ b/Week 02/id_273/NOTE.md @@ -1,4 +1,36 @@ # NOTE - +### 第五课 哈希表、映射、集合的实现和特性 + +#### HashMap的特性: +1. 关于HashMap的特性以及源码分析放在了下面链接的博文中 +2. 一般情况下, HashMap的使用场景大多都是基于KEY-VALUE的, 例如身份证号key对应一个公民对象value +* HashMap源码分析:https://blog.csdn.net/weixin_43624024/article/details/101975959 + +### 第六课 树、二叉树、二叉搜索树的实现和特性 + +#### 树的特性: +1. 大多数情况下, 树的数据结构都采用链表实现, 常见的二叉树就包含了left, right指针用于指向其左孩子/右孩子 + +#### 二叉树的特性: +1. 二叉树在树的基础上,限制了每个结点只有left-child和right-child两个指针 +2. 判断一个数据结构是图还是树,可以通过是否存在环来判断 +3. 二叉树的遍历方式:前序遍历(根-左-右)、后序遍历(左-右-根)、中序遍历(左-根-右), 记忆二叉树的遍历递归实现非常重要 +* 为什么二叉树的大部分问题都采用递归的方式解决:这是因为一个完整的二叉树又可以拆分为多个子树, 因为大部分问题中进行的操作大多都是针对树的重复操作, 所以树的问题一般都能用递归方式解决 + +#### 二叉搜索树的特性: +1. 在二叉树的基础之上,添加这么一个条件:root.left.val < root.val < root.right.val +2. 查询平均时间复杂度O(logN),最坏情况下由树结构退化为链表结构, 复杂度降到O(n) +* BS-Tree源码分析:https://blog.csdn.net/weixin_43624024/article/details/101145066 + +### 第七课 递归的实现、特性、以及思维要点 +1. 递归本质上有点类似于循环,可以把递归理解为通过函数体进行的循环 +2. 递归必须包含:终止条件terminate、逻辑代码logic、递归调用drill down、(清理当前层clean) +3. 写递归时不要考虑人肉递归, 当前层只需要寻找问题的重复性并且考虑当前的逻辑即可, 这也是比较典型的自顶向下编程的思想 +* 常见的一种对于递归的描述是:递归效率低下,不要在程序中使用递归。这是一种错误的说法, 虽然递归的可读性较差, 但是递归的效率取决于程序本身, 我们可以通过缓存或者动态规划对递归代码进行优化, 且递归方法的效率在不存在特别深的递归的情况下, 因为不需要开辟太多的方法栈, 效率也可视为和循环差不多 + +### 第八课 分治、回溯 +1. 分治和回溯其实本质上还是递归, 可以将其理解为较为复杂的递归 +2. 分治可以理解为把一个问题不断拆分成一个个的子问题,就像二叉树的结构,处理最底层的子问题返回结果给上层 +3. 回溯可以理解为尝试获取所有可能的解决方案,若当前方案不通过则返回上层继续寻找下一个方案试错,典型的题目有八皇后问题 diff --git a/Week 02/id_278/Leetcode_242_278.js b/Week 02/id_278/Leetcode_242_278.js new file mode 100644 index 000000000..405808da8 --- /dev/null +++ b/Week 02/id_278/Leetcode_242_278.js @@ -0,0 +1,31 @@ +/** + * 242. Valid Anagram + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function(s, t) { + const count = {}; + + if (s.length !== t.length) { + return false; + } + + for (const ch of s) { + if (count[ch]) { + count[ch] ++; + } else { + count[ch] = 1; + } + } + + for (const ch of t) { + if (count[ch]) { + count[ch] --; + } else { + return false; + } + } + + return Object.values(count).every(value => value === 0); +}; diff --git a/Week 02/id_278/Leetcode_49_278.js b/Week 02/id_278/Leetcode_49_278.js new file mode 100644 index 000000000..eb10483fb --- /dev/null +++ b/Week 02/id_278/Leetcode_49_278.js @@ -0,0 +1,24 @@ +/** + * 49. Group Anagrams + * @param {string[]} strs + * @return {string[][]} + */ +var groupAnagrams = function(strs) { + const group = {}; + const result = []; + + for (const s of strs) { + const sorted = s.split('').sort().join(''); + if (group[sorted]) { + group[sorted].push(s); + } else { + group[sorted] = [s]; + } + } + + for (const values of Object.values(group)) { + result.push(values); + } + + return result; +}; diff --git a/Week 02/id_283/LeetCode_49_283.java b/Week 02/id_283/LeetCode_49_283.java new file mode 100644 index 000000000..8bac726cb --- /dev/null +++ b/Week 02/id_283/LeetCode_49_283.java @@ -0,0 +1,23 @@ +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +/* + * @lc app=leetcode id=49 lang=java + * + * [49] Group Anagrams + */ +class Solution { + public List> groupAnagrams(String[] strs) { + Map> sortedMap = new HashMap<>(); + for (String word : strs) { + char[] charWord = word.toCharArray(); + Arrays.sort(charWord); + String newStr = new String(charWord); + sortedMap.putIfAbsent(newStr,new ArrayList<>()); + sortedMap.get(newStr).add(word); + } + return new ArrayList<>(sortedMap.values()); + } +} + diff --git a/Week 02/id_283/Leetcode_46_283.java b/Week 02/id_283/Leetcode_46_283.java new file mode 100644 index 000000000..4da5fbd3c --- /dev/null +++ b/Week 02/id_283/Leetcode_46_283.java @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode id=46 lang=java + * + * [46] Permutations + */ +class Solution { + public List> permute(int[] nums) { + List> result = new ArrayList<>(); + backTrace(nums, result, new ArrayList<>()); + return result; + } + + public void backTrace(int[] nums, List> result, List tempList){ + if(tempList.size() == nums.length){ + result.add(new ArrayList(tempList)); + return; + } + + for(int i = 0; i< nums.length; i++){ + if(tempList.contains(nums[i])) continue; + tempList.add(nums[i]); + backTrace(nums, result, tempList); + tempList.remove(tempList.size()-1); + } + } +} + diff --git a/Week 02/id_298/binary-tree-inorder-traversal.py b/Week 02/id_298/binary-tree-inorder-traversal.py new file mode 100644 index 000000000..ad8842260 --- /dev/null +++ b/Week 02/id_298/binary-tree-inorder-traversal.py @@ -0,0 +1,18 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + def inorderTraversal(self, root: TreeNode) -> List[int]: + result = [] + def _inorderTraversal(root): + if root is None: + return + _inorderTraversal(root.left) + result.append(root.val) + _inorderTraversal(root.right) + _inorderTraversal(root) + return result diff --git a/Week 02/id_298/binary-tree-preorder-traversal.py b/Week 02/id_298/binary-tree-preorder-traversal.py new file mode 100644 index 000000000..91190d9d6 --- /dev/null +++ b/Week 02/id_298/binary-tree-preorder-traversal.py @@ -0,0 +1,18 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + def preorderTraversal(self, root: TreeNode) -> List[int]: + result = [] + def _preorderTraversal(root): + if root is None: + return + result.append(root.val) + _preorderTraversal(root.left) + _preorderTraversal(root.right) + _preorderTraversal(root) + return result diff --git a/Week 02/id_298/group-anagrams.py b/Week 02/id_298/group-anagrams.py new file mode 100644 index 000000000..4e6b7ea31 --- /dev/null +++ b/Week 02/id_298/group-anagrams.py @@ -0,0 +1,45 @@ +class Solution: + def groupAnagrams(self, strs: List[str]) -> List[List[str]]: + + # -----------Method 1--------------- + # strs_sorted = ["".join(sorted(item)) for item in strs] + # print(strs_sorted) + # results = [] + # results_idx = [] + # idxs = set() + # for i in range(len(strs)): + # result = [] + # if i in idxs: + # continue + # result.append(i) + # for j in range(1, len(strs)): + # if j in idxs: + # continue + # if strs[i] == strs[j] or "".join(sorted(strs[i])) == "".join(sorted(strs[j])): + # result.append(j) + # idxs.add(j) + # results.append(result) + # results_idx.append(result) + # for idx in results_idx: + # res = [] + # for lidx in set(idx): + # res.append(strs[lidx]) + # results.append(res) + + # -----------Method 2--------------- + # results = collections.defaultdict(list) + # for item in strs: + # results["".join(sorted(item))].append(item) + + # -----------Method 3--------------- + results = {} + sort_str = lambda x : "".join(sorted(x)) + for item in strs: + sorted_item = sort_str(item) + if sorted_item not in results: + results[sorted_item] = [] + results[sorted_item].append(item) # 如果没有包含在结果中要添加自身 + else: + results[sorted_item].append(item) + + return results.values() diff --git a/Week 02/id_298/n-ary-tree-postorder.py b/Week 02/id_298/n-ary-tree-postorder.py new file mode 100644 index 000000000..6141f23eb --- /dev/null +++ b/Week 02/id_298/n-ary-tree-postorder.py @@ -0,0 +1,33 @@ +""" +# Definition for a Node. +class Node: + def __init__(self, val, children): + self.val = val + self.children = children +""" +class Solution: + def postorder(self, root: 'Node') -> List[int]: + result = [] + if root is None: + return result + def _postorder(root): + if root is None: + return + for child in root.children: + _postorder(child) + result.append(child.val) + _postorder(root) + result.append(root.val) + return result + +""" +{"$id":"1","children":[{"$id":"2","children":[{"$id":"5","children":[],"val":5},{"$id":"6","children":[],"val":6}],"val":3},{"$id":"3","children":[],"val":2},{"$id":"4","children":[],"val":4}],"val":1} +递归访问完了children后才访问到val, val后序 +所以是 +children: _postorder(root) +val : root.val + +children是一个列表,里面包含{children, val} + +改进方法:列表递归的写法? +""" diff --git a/Week 02/id_298/n-ary-tree-preorder.py b/Week 02/id_298/n-ary-tree-preorder.py new file mode 100644 index 000000000..553c11c7a --- /dev/null +++ b/Week 02/id_298/n-ary-tree-preorder.py @@ -0,0 +1,19 @@ +""" +# Definition for a Node. +class Node: + def __init__(self, val, children): + self.val = val + self.children = children +""" +class Solution: + def preorder(self, root: 'Node') -> List[int]: + result = [] + if root is None: + return + def _preorder(root): + for child in root.children: + result.append(child.val) + _preorder(child) + result.append(root.val) + _preorder(root) + return result diff --git a/Week 02/id_298/two-sum.py b/Week 02/id_298/two-sum.py new file mode 100644 index 000000000..bbb0e7df0 --- /dev/null +++ b/Week 02/id_298/two-sum.py @@ -0,0 +1,13 @@ +class Solution: + def twoSum(self, nums: List[int], target: int) -> List[int]: + + results = {} + for idx, num in enumerate(nums): + if target - num in results: + return [results[target - num], idx] + results[num] = idx + + # target - num 查以前的数, num是当前的数,如果当前数对应的key不在,则添加索引 + # 如果 data = target - num 就是要找的数字 如果 num + # num * 2 == target的单个num + # 还有 num1 = num2 and target = num1 + num2class Solution: diff --git a/Week 02/id_298/valid-anagram.py b/Week 02/id_298/valid-anagram.py new file mode 100644 index 000000000..5f0177594 --- /dev/null +++ b/Week 02/id_298/valid-anagram.py @@ -0,0 +1,18 @@ +class Solution: + def isAnagram(self, s: str, t: str) -> bool: + if len(s) != len(t): + return False + s_dict = {} + for ch in s: + if ch not in s_dict: + s_dict[ch] = 1 + else: + s_dict[ch] += 1 + + t_dict = {} + for ch in t: + if ch not in t_dict: + t_dict[ch] = 1 + else: + t_dict[ch] += 1 + return s_dict == t_dict diff --git a/Week 02/id_303/Permutations.swift b/Week 02/id_303/Permutations.swift new file mode 100644 index 000000000..13526f771 --- /dev/null +++ b/Week 02/id_303/Permutations.swift @@ -0,0 +1,29 @@ +// +// Permutations.swift +// AlgorithmStudy + + +import Foundation +//46.全排列 +func permute(_ nums: [Int]) -> [[Int]] { + var array = nums + var result = [[Int]]() + helper(&array,0,&result) + return result +} + +func helper (_ arr: inout [Int], _ begin:Int, _ results: inout [[Int]]){ + if begin >= arr.count{ + results.append(arr) + return + } else { + for i in begin.. [String] { + let array = digits.map { dictionary[$0] ?? [] } + let counts = array.map({$0.count}) + if counts.contains(0) || counts.isEmpty { + return [] + } + var flags = [Int](repeating: 0, count: array.count) + var temperate = "" + var answers = [String]() + var affectedFields = [Int]() + loop: while true { + if temperate.isEmpty { + for j in 0.. [String] { + if digits.count == 0 { + return [] + } + var result: [String] = [] + let letter = ["2" : ["a","b","c"], "3" : ["d","e","f"], "4" : ["g","h","i"], "5" : ["j","k","l"], "6" : ["m","n","o"], "7" : ["p","q","r","s"], "8" : ["t","u","v"], "9" : ["w","x","y","z"]] + for a in digits { + let le = letter[String(a)]! + if result.count == 0 { + result = le + continue + } + let res = result + result = [] + for l in le { + for r in res { + result.append(r+l) + } + } + } + return result + } +*/ diff --git a/Week 02/id_303/Pown.swift b/Week 02/id_303/Pown.swift new file mode 100644 index 000000000..d95b9b381 --- /dev/null +++ b/Week 02/id_303/Pown.swift @@ -0,0 +1,26 @@ +// +// Pown.swift +// AlgorithmStudy + +import Foundation + +//50.Pow(x,n) 快速幂算法(递归) +func myPow(_ x: Double, _ n: Int) -> Double { + var x = x, n = n + if n<0{ + x = 1/x + n = -n + } + return fastPow(x, n) +} +private func fastPow(_ x: Double, _ n:Int)->Double{ + if n == 0{ + return 1.0 + } + let half:Double = fastPow(x, n/2) + if n%2 == 0 { + return half*half + }else{ + return half*half*x + } +} diff --git a/Week 02/id_303/PreorderTraversal.swift b/Week 02/id_303/PreorderTraversal.swift new file mode 100644 index 000000000..884426078 --- /dev/null +++ b/Week 02/id_303/PreorderTraversal.swift @@ -0,0 +1,24 @@ +// +// PreorderTraversal.swift +// AlgorithmStudy + + +import Foundation + +//144.二叉树的前序遍历 +func preorderTraversal(_ root: TreeNode?) -> [Int] { + guard let root = root else { return [] } + var stack = [root] + var processed = [Int]() + while let node = stack.popLast() { + processed.append(node.val) + if let right = node.right { + stack.append(right) + } + + if let left = node.left { + stack.append(left) + } + } + return processed +} diff --git a/Week 02/id_303/ValidAnagram.swift b/Week 02/id_303/ValidAnagram.swift new file mode 100644 index 000000000..8363bde0b --- /dev/null +++ b/Week 02/id_303/ValidAnagram.swift @@ -0,0 +1,24 @@ +// +// ValidAnagram.swift +// AlgorithmStudy + +import Foundation + +//242有效的字母异位词题解 +func isAnagram(_ s: String, _ t: String) -> Bool { + //创建一个含有26个为0的值的数组 + var sArr = Array(repeating: 0, count: 26) + for character in s.unicodeScalars { + let index = Int(character.value - 97) + //记录字符出现的次数 + sArr[index] = sArr[index] + 1 + } + //创建一个含有26个为0的值的数组 + var tArr = Array(repeating: 0, count: 26) + for character in t.unicodeScalars { + let index = Int(character.value - 97) + //记录字符出现的次数 + tArr[index] = tArr[index] + 1 + } + return sArr == tArr +} diff --git a/Week 02/id_308/LeedCode_1.js b/Week 02/id_308/LeedCode_1.js new file mode 100644 index 000000000..f76b6cc51 --- /dev/null +++ b/Week 02/id_308/LeedCode_1.js @@ -0,0 +1,25 @@ +/** + * 题目: 两数之和 + * 语言: JavaScript + * 执行结果: 打败了86%的用户 + * */ + + +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +const twoSum = function(nums, target) { + const map = new Map(); + nums.forEach((value,key)=>map.set(value,key)); + + for(let i = 0;i < nums.length;i++){ + const temp = target - nums[i]; + if(map.has(temp) && map.get(temp) !== i){ + return [i,map.get(temp)]; + } + } + + return []; +}; diff --git a/Week 02/id_308/LeedCode_144.js b/Week 02/id_308/LeedCode_144.js new file mode 100644 index 000000000..4c0dc66e9 --- /dev/null +++ b/Week 02/id_308/LeedCode_144.js @@ -0,0 +1,27 @@ +/** + * 题目: 二叉树的前序遍历 + * 语言: JavaScript + * 执行结果: 打败了87%的用户 + * 方法: 递归 + * */ + + +/** + * @param {TreeNode} root + * @return {number[]} + */ + +const preorderTraversal = function (root) { + const result = []; + + traversing(root,result); + return result; +} + +function traversing(node,arr) { + if(node === null) return; + arr.push(node.val); + + if(node.left !== null) arguments.callee(node.left,arr); + if(node.right != null) arguments.callee(node.right,arr); +} diff --git a/Week 02/id_308/LeedCode_242.js b/Week 02/id_308/LeedCode_242.js new file mode 100644 index 000000000..c19e01a8d --- /dev/null +++ b/Week 02/id_308/LeedCode_242.js @@ -0,0 +1,25 @@ +/** + * 题目: 有效的字母异位词 + * 语言: JavaScript + * 执行结果: 打败了100%的用户 + * */ + + +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +const isAnagram = function(s, t) { + if(s.length !== t.length) return false; + const set = new Set(s.split("")); + + for(let item of set.keys()) { + const reg = new RegExp(item,"g"); + const s1 = s.match(reg); + const s2 = t.match(reg); + if(!s2 || s1.length !== s2.length) return false; + } + + return true; +}; diff --git a/Week 02/id_308/LeedCode_429.js b/Week 02/id_308/LeedCode_429.js new file mode 100644 index 000000000..b85975d51 --- /dev/null +++ b/Week 02/id_308/LeedCode_429.js @@ -0,0 +1,35 @@ +/** + * 题目: N叉树的层序遍历 + * 语言: JavaScript + * 执行结果: 打败了89.57%的用户 + * 方法: 递归 + * */ + + + +/** + * @param {TreeNode} root + * @return {number[]} + */ + +const levelOrder = function(root) { + const result = []; + traversing(root,result,0) + return result; +}; + +function traversing(node,arr,index) { + if(node === null) return; + + const {val,children} = node; + const temp = arr[index] || []; + + temp.push(val); + arr[index++] = temp; + + if(children && children.length > 0) { + for (let i = 0; i < children.length; i++) { + arguments.callee(children[i],arr,index); + } + } +} diff --git a/Week 02/id_308/LeedCode_49.js b/Week 02/id_308/LeedCode_49.js new file mode 100644 index 000000000..9c7ac7596 --- /dev/null +++ b/Week 02/id_308/LeedCode_49.js @@ -0,0 +1,31 @@ +/** + * 题目: 字母异位词分组 + * 语言: JavaScript + * 执行结果: 打败了99.07%的用户 + * */ + +/** + * @param {string[]} strs + * @return {string[][]} + */ +const groupAnagrams = function(strs) { + let map = new Map(); + let count = 0; + let result = []; + + strs.forEach(value=>{ + const temp = value.split('').sort().join(''); + if(map.has(temp)) { + const count = map.get(temp); + const arr = result[count] || []; + arr.push(value); + result[count] = arr; + }else{ + map.set(temp,count); + result[count++] = [value]; + } + }); + + + return result; +}; diff --git a/Week 02/id_308/LeedCode_589.js b/Week 02/id_308/LeedCode_589.js new file mode 100644 index 000000000..8727abcbd --- /dev/null +++ b/Week 02/id_308/LeedCode_589.js @@ -0,0 +1,33 @@ +/** + * 题目: N叉树的前序遍历 + * 语言: JavaScript + * 执行结果: 打败了90%的用户 + * 方法: 递归 + * */ + + +/** + * @param {TreeNode} root + * @return {number[]} + */ +var preorder = function(root) { + const result = []; + traversing(root,result,0) + return result; +}; + +function traversing(node,arr,index) { + if(node === null) return; + + const {val,children} = node; + const temp = arr[index] || []; + + temp.push(val); + arr[index++] = temp; + + if(children && children.length > 0) { + for (let i = 0; i < children.length; i++) { + arguments.callee(children[i],arr,index); + } + } +} diff --git a/Week 02/id_308/LeedCode_590.js b/Week 02/id_308/LeedCode_590.js new file mode 100644 index 000000000..bf3e2a51c --- /dev/null +++ b/Week 02/id_308/LeedCode_590.js @@ -0,0 +1,31 @@ +/** + * 题目: N叉树的后序遍历 + * 语言: JavaScript + * 执行结果: 打败了94%的用户 + * 方法: 递归 + * */ + + +/** + * @param {TreeNode} root + * @return {number[]} + */ + +const postorder = function(root) { + const result = []; + traversing(root,result); + return result; +}; + +function traversing(node,arr) { + if(node === null) return; + + const {children} = node; + if(children && children.length > 0) { + for(let i = 0; i < children.length; i++) { + arguments.callee(children[i],arr); + } + } + + arr.push(node.val); +} diff --git a/Week 02/id_308/LeedCode_94.js b/Week 02/id_308/LeedCode_94.js new file mode 100644 index 000000000..23289cdc3 --- /dev/null +++ b/Week 02/id_308/LeedCode_94.js @@ -0,0 +1,31 @@ +/** + * 题目: 二叉树的中序遍历 + * 语言: JavaScript + * 执行结果: 打败了96%的用户 + * 方法: 递归 + * */ + + +/** + * @param {TreeNode} root + * @return {number[]} + */ + +const inorderTraversal = function(root) { + let result = []; + traversing(root,result); + + return result; +}; + + +function traversing(node,arr) { + if(node === null) return; + if(node.left !== null) { + arguments.callee(node.left,arr) + }; + arr.push(node.val); + if(node.right !== null) { + arguments.callee(node.right,arr); + }; +} diff --git a/Week 02/id_308/NOTE.md b/Week 02/id_308/NOTE.md index a6321d6e2..1a354a0ec 100644 --- a/Week 02/id_308/NOTE.md +++ b/Week 02/id_308/NOTE.md @@ -1,4 +1,73 @@ -# NOTE - +## 哈希表、映射、集合的实现与特性 + +#### 哈希表 Hash Table + +- 也叫散列表,是根据关键码值(key value)而直接进行访问的数据结构,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度. +- 这个映射函数叫做散列函数,存放记录的数组叫做哈希表(散列表) +- 哈希碰撞:不同的key经过哈希函数有可能产生一个相同的数值,即此数值要存储多个数组,可以降其用链表连接,这种方法叫做拉链式解决冲突法 +- 正常情况查询/添加/删除的时间复杂度:O(1) +- 最坏情况查询/添加/删除的时间复杂度: O(n) + + +## 树、二叉树、二叉搜索树的实现和特性 + +## 二叉树 + +- 树是二维结构 +- 由根节点、子树组成 +- 二叉树的子节点最多只有2个 +- Linked List 是特殊化的Tree,Tree是特殊化的Graph +- 二叉树遍历 + * 前序: 根-左-右 + * 中序: 左-根-右 + * 后序: 左-右-根 + + +## 二叉搜索树 Binary Search Tree + +- 二叉搜索树,也叫有序二叉树(Ordered Binary Tree)、排序二叉树(Sorted Binary Tree),是指一棵空树或者具有下列性质的二叉树: + * 左子树上所有结点的值均小于它的根结点的值 + * 右子树上所有结点的值均大于它的根结点的值 + * 以此类推:左、右子树也分别为二叉查找树 +- 中序遍历是升序遍历 +- 查询/插入的时间复杂度O(logN) + + +## 递归的实现、特性以及思维要点 + +- 树面试题解法一般都是递归的原因: + * ① 树的结点和树本身数据结构的定义就是用递归的方式来进行的 + * ② 二叉树以及搜索二叉树,它在定义它数据结构和它算法特性的时候,也是有它的重复性,也就是自相似性 +- 递归 + * 递归本质上就是循环,通过函数体来进行循环 +- 代码模板 + * 递归终结条件 + * 处理当前逻辑 + * 下探到下一层 + * 清理当前层 +- 思维要点 + * 抵制人肉递归 + * 找最近重复性 + * 数学归纳法思维 + +## 回溯的实现和特性 + +#### 分治 Divide & Conquer + +- 分治也是一种递归,不过在它递归的过程中,把一个问题划分为好几个子问题 +- 代码模板 + * terminator + * process(split your big problem) + * drill down(subproblmes) + * reverse states + +#### 回溯 Backtracking + +- 回溯法采用试错的思想,它尝试分步的去解决一个问题.在分步解决问题的过程中,当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至上几步的计算,再通过其他的可能的分步解答再次尝试寻找问题的答案. +- 回溯法通常用最简单的递归方法来实现,在反复重复上述步骤后可能出现2种情况 + * 找到一种可能存在的正确答案 + * 在尝试了所有可能的分步方法后宣告该方法没有解 +- 在最坏的情况下,回溯法会导致一次复杂度为指数时间的计算 + diff --git a/Week 02/id_313/LeetCode_105_313.go b/Week 02/id_313/LeetCode_105_313.go new file mode 100644 index 000000000..50e7def55 --- /dev/null +++ b/Week 02/id_313/LeetCode_105_313.go @@ -0,0 +1,32 @@ +package id313 + +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ + +// 主要是对 前序和中序遍历掌握够熟练 +// 通过前序定位根结点,在中序中通过根结点找到左右节点区分。递归重复 +func buildTree(preorder []int, inorder []int) *TreeNode { + root := tree(preorder, 0, len(preorder)-1, inorder, 0, len(inorder)-1) + return root +} + +func tree(preoder []int, preL, preR int, inorder []int, inL, inR int) *TreeNode { + if preL > preR || inL > inR { + return nil + } + pivot := preoder[preL] + root := &TreeNode{Val: pivot} + pivotIndex := inL + for pivotIndex < inR && inorder[pivotIndex] != pivot { + pivotIndex++ + } + root.Left = tree(preoder, preL+1, pivotIndex-inL+preL, inorder, inL, pivotIndex-1) + root.Right = tree(preoder, pivotIndex-inL+preL+1, preR, inorder, pivotIndex+1, inR) + return root +} diff --git a/Week 02/id_313/LeetCode_169_313.go b/Week 02/id_313/LeetCode_169_313.go new file mode 100644 index 000000000..e37c1cc81 --- /dev/null +++ b/Week 02/id_313/LeetCode_169_313.go @@ -0,0 +1,17 @@ +package id313 + +// 摩尔投票 +func majorityElement(nums []int) int { + count, candidate := 1, nums[0] + for _, v := range nums[1:] { + if count == 0 { + candidate = v + count = 1 + } else if v == candidate { + count++ + } else { + count-- + } + } + return candidate +} diff --git a/Week 02/id_313/LeetCode_17_313.go b/Week 02/id_313/LeetCode_17_313.go new file mode 100644 index 000000000..e7cf6884e --- /dev/null +++ b/Week 02/id_313/LeetCode_17_313.go @@ -0,0 +1,34 @@ +package id313 + +// 多练 没啥可注释的了 +func letterCombinations(digits string) []string { + if digits == "" { + return []string{} + } + s := map[uint8]string{ + 2: "abc", + 3: "def", + 4: "ghi", + 5: "jkl", + 6: "mno", + 7: "pqrs", + 8: "tuv", + 9: "wxyz", + } + ret := make([]string, 0) + var f func(string, string, int) + f = func(digits, next string, index int) { + if index == len(digits) { + ret = append(ret, next) + return + } + digit := digits[index] + letter := s[digit-'0'] + for i := 0; i < len(letter); i++ { + f(digits, next+string(letter[i]), index+1) + } + return + } + f(digits, "", 0) + return ret +} diff --git a/Week 02/id_313/LeetCode_1_313.go b/Week 02/id_313/LeetCode_1_313.go new file mode 100644 index 000000000..40fda890d --- /dev/null +++ b/Week 02/id_313/LeetCode_1_313.go @@ -0,0 +1,15 @@ +package id313 + +// 使用map方式存储遍历数据 +func twoSum(nums []int, target int) []int { + m := make(map[int]int, 0) + for k, num := range nums { + v := target - num + if vv, ok := m[v]; ok && vv != k { + return []int{k, vv} + } + m[num] = k + + } + return []int{} +} diff --git a/Week 02/id_313/LeetCode_236_313.go b/Week 02/id_313/LeetCode_236_313.go new file mode 100644 index 000000000..a9694573a --- /dev/null +++ b/Week 02/id_313/LeetCode_236_313.go @@ -0,0 +1,24 @@ +package id313 + +func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { + if root == nil || root == p || root == q { + return root + } + left := lowestCommonAncestor(root.Left, p, q) + right := lowestCommonAncestor(root.Right, p, q) + + // left == nil 表示 p 和 q 不在 left中 + // right == nil 表示 p he q 不在right中 + // left 和 right 不能同时为 nil + if left != nil && right != nil { + // p 和 q 在 root.Left 和 root.Right 中 + return root + } + // p 和 q 在 root.Right 中 + if left == nil { + return right + } + // p 和 q 在 root.Left 中 + return left + +} diff --git a/Week 02/id_313/LeetCode_242_313.go b/Week 02/id_313/LeetCode_242_313.go new file mode 100644 index 000000000..a224f8d3a --- /dev/null +++ b/Week 02/id_313/LeetCode_242_313.go @@ -0,0 +1,24 @@ +package id313 + +func isAnagram(s string, t string) bool { + if len(s) != len(t) { + return false + } + + // 直接支持unicode + sr := []rune(s) + tr := []rune(t) + + length := len(sr) + c := make(map[rune]int, length) + for k := range sr { + c[sr[k]]++ + c[tr[k]]-- + } + for _, v := range c { + if v != 0 { + return false + } + } + return true +} diff --git a/Week 02/id_313/LeetCode_429_313.php b/Week 02/id_313/LeetCode_429_313.php new file mode 100644 index 000000000..2a0bb03a1 --- /dev/null +++ b/Week 02/id_313/LeetCode_429_313.php @@ -0,0 +1,34 @@ + $children + function __construct($val, $children) { + $this->val = $val; + $this->children = $children; + } +} +*/ +class Solution { + public $ret = []; + /** + * @param Node $root + * @return Integer[][] + */ + function levelOrder($root) { + $this->traverse($root, 0); + return $this->ret; + } + + function traverse($root, $depth) { + if ($root == null) return; + $this->ret[$depth][] = $root->val; + foreach($root->children as $child) { + $this->traverse($child, $depth + 1); + } + } +} \ No newline at end of file diff --git a/Week 02/id_313/LeetCode_46_313.go b/Week 02/id_313/LeetCode_46_313.go new file mode 100644 index 000000000..f2f6d63f1 --- /dev/null +++ b/Week 02/id_313/LeetCode_46_313.go @@ -0,0 +1,42 @@ +package id313 + +// 回溯,状态重置 +func solveNQueens(n int) [][]string { + if n == 0 { + return [][]string{} + } + ret := make([][]string, 0) + cols := make([]bool, n) + // 主对角线 + master := make([]bool, 2*n) + // 副对角线 + slave := make([]bool, 2*n) + board := make([]string, n) + var f func(int, []bool, []bool, []bool, []string) + f = func(row int, cols []bool, master []bool, slave []bool, board []string) { + if row == n { + tmp := make([]string, n) + copy(tmp, board) + ret = append(ret, tmp) + } + + for col := 0; col < len(board); col++ { + masterIndex := n + row - col + slaveIndex := 2*n - row - col - 1 + if !cols[col] && !master[masterIndex] && !slave[slaveIndex] { + b := make([]byte, n) + for i := range b { + b[i] = '.' + } + b[col] = 'Q' + board[row] = string(b) + cols[col], master[masterIndex], slave[slaveIndex] = true, true, true + f(row+1, cols, master, slave, board) + cols[col], master[masterIndex], slave[slaveIndex] = false, false, false + } + + } + } + f(0, cols, master, slave, board) + return ret +} diff --git a/Week 02/id_313/LeetCode_49_313.go b/Week 02/id_313/LeetCode_49_313.go new file mode 100644 index 000000000..62edbe6ef --- /dev/null +++ b/Week 02/id_313/LeetCode_49_313.go @@ -0,0 +1,25 @@ +package id313 + +import ( + "sort" +) + +// 对数据进行排序然后再分组处理 +func groupAnagrams(strs []string) [][]string { + ret := make([][]string, 0) + strMap := make(map[string]int, 0) + for _, s := range strs { + bs := []byte(s) + sort.Slice(bs, func(i, j int) bool { + return bs[i] < bs[j] + }) + ss := string(bs) + if index, ok := strMap[ss]; ok { + ret[index] = append(ret[index], s) + } else { + strMap[ss] = len(ret) + ret = append(ret, []string{s}) + } + } + return ret +} diff --git a/Week 02/id_313/LeetCode_589_313.php b/Week 02/id_313/LeetCode_589_313.php new file mode 100644 index 000000000..a7307d65a --- /dev/null +++ b/Week 02/id_313/LeetCode_589_313.php @@ -0,0 +1,30 @@ + $children + function __construct($val, $children) { + $this->val = $val; + $this->children = $children; + } +} +*/ +class Solution { + public $ret = []; + /** + * @param Node $root + * @return Integer[] + */ + function preorder($root) { + if ($root == null ) return []; + $this->ret[] = $root->val; + foreach($root->children as $child) { + $this->preorder($child); + } + return $this->ret; + } +} \ No newline at end of file diff --git a/Week 02/id_313/LeetCode_590_313.php b/Week 02/id_313/LeetCode_590_313.php new file mode 100644 index 000000000..26c0836a2 --- /dev/null +++ b/Week 02/id_313/LeetCode_590_313.php @@ -0,0 +1,31 @@ + $children + function __construct($val, $children) { + $this->val = $val; + $this->children = $children; + } +} + +*/ +class Solution { + public $ret = []; + /** + * @param Node $root + * @return Integer[] + */ + function postorder($root) { + if ($root == null) return []; + foreach($root->children as $child) { + $this->postorder($child); + } + $this->ret[] = $root->val; + return $this->ret; + } +} \ No newline at end of file diff --git a/Week 02/id_313/LeetCode_77_313.go b/Week 02/id_313/LeetCode_77_313.go new file mode 100644 index 000000000..440c77073 --- /dev/null +++ b/Week 02/id_313/LeetCode_77_313.go @@ -0,0 +1,22 @@ +package id313 + +// 回溯,画图捋清回溯的过程 +func combine(n int, k int) [][]int { + combination := make([]int, k) + ret := make([][]int, 0) + var f func(int, int) + f = func(index, begin int) { + if index == k { + tmp := make([]int, k) + copy(tmp, combination) + ret = append(ret, tmp) + return + } + for i := begin; i <= n-(k-index)+1; i++ { + combination[index] = i + f(index+1, i+1) + } + } + f(0, 1) + return ret +} diff --git a/Week 02/id_318/LeetCode_242_318.py b/Week 02/id_318/LeetCode_242_318.py new file mode 100644 index 000000000..d77725c89 --- /dev/null +++ b/Week 02/id_318/LeetCode_242_318.py @@ -0,0 +1,27 @@ +# 242. Valid Anagram + +class Solution: + def isAnagram(self, s: str, t: str) -> bool: + dic = {} + for i in s: + if i in dic: + dic[i] += 1 + else: + dic[i] = 1 + for i in t: + if i not in dic or dic[i] <= 0: + return False + dic[i] -= 1 + for i in dic: + if dic[i] != 0: + return False + return True + +if __name__ == '__main__': + s = "anagram" + t = "nagaram" + sol = Solution() + res = sol.isAnagram(s, t) + print(res) + +# output: True diff --git a/Week 02/id_318/LeetCode_94_318.py b/Week 02/id_318/LeetCode_94_318.py new file mode 100644 index 000000000..f9097e654 --- /dev/null +++ b/Week 02/id_318/LeetCode_94_318.py @@ -0,0 +1,20 @@ +# 94. Binary Tree Inorder Traversal + +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + def inorderTraversal(self, root: TreeNode) -> List[int]: + res = [] + def wrapper(root): + if not root: + return + wrapper(root.left) + res.append(root.val) + wrapper(root.right) + wrapper(root) + return res diff --git a/Week 02/id_328/PreorderTravelsal.java b/Week 02/id_328/PreorderTravelsal.java new file mode 100644 index 000000000..5eb9955a6 --- /dev/null +++ b/Week 02/id_328/PreorderTravelsal.java @@ -0,0 +1,17 @@ +class PreorderTravelsal { + public List preorderTraversal(TreeNode root) { + List result = new ArrayList(); + preorderTraversal(root,result); + return result; + } + + public void preorderTraversal(TreeNode root, List result){ + if(null == root){ + return; + }else{ + result.add(root.val); + preorderTraversal(root.left,result); + preorderTraversal(root.right,result); + } + } +} \ No newline at end of file diff --git a/Week 02/id_328/ValidAnagram.java b/Week 02/id_328/ValidAnagram.java new file mode 100644 index 000000000..6d7822199 --- /dev/null +++ b/Week 02/id_328/ValidAnagram.java @@ -0,0 +1,27 @@ +class ValidAnagram { + public boolean isAnagram(String s, String t) { + Map charMap = new HashMap<>(); + char[] sChars = s.toCharArray(); + char[] tChars = t.toCharArray(); + for(int i = 0 ; i < sChars.length ; i++) { + if(charMap.containsKey(sChars[i])) { + charMap.put(sChars[i], charMap.get(sChars[i]) + 1); + }else { + charMap.put(sChars[i], 1); + } + } + for(int i = 0 ; i < tChars.length ; i++) { + if(charMap.containsKey(tChars[i])) { + charMap.put(tChars[i], charMap.get(tChars[i]) - 1); + }else { + return false; + } + } + + for(Character key : charMap.keySet()) { + if(charMap.get(key) != 0) return false; + } + + return true; + } +} \ No newline at end of file diff --git a/Week 02/id_333/LeetCode_46_333.java b/Week 02/id_333/LeetCode_46_333.java new file mode 100644 index 000000000..e8ac848cb --- /dev/null +++ b/Week 02/id_333/LeetCode_46_333.java @@ -0,0 +1,49 @@ +package week_02; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +public class LeetCode_46_333 { +// private static List> lists = new ArrayList>(); + + public static List> permute(int[] nums) { + List> lists = new ArrayList>(); + if (nums.length == 0) { + return lists; + } else { + allSort(lists, nums, new Stack()); + return lists; + } + } + + private static void allSort(List> lists, int[] nums, Stack stack) { + if (stack.size() == nums.length) { + lists.add(new ArrayList<>(stack)); + return; + } + + for (int num : nums) { + if (stack.contains(num)) { + continue; + } + stack.push(num); + allSort(lists, nums, stack); + stack.pop(); + } + } + + public static void main(String[] args) { + int[] nums = new int[]{1,2,3}; + List> result = permute(nums); + System.out.print("["); + for(List list : result) { + System.out.print("["); + for(Integer num : list) { + System.out.print("" + num + ","); + } + System.out.print("],"); + } + System.out.print("]"); + } +} diff --git a/Week 02/id_333/LeetCode_49_333.java b/Week 02/id_333/LeetCode_49_333.java new file mode 100644 index 000000000..0deb6113f --- /dev/null +++ b/Week 02/id_333/LeetCode_49_333.java @@ -0,0 +1,41 @@ +package week_02; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +public class LeetCode_49_333 { + public static List> groupAnagrams(String[] strs) { + HashMap> hash = new HashMap<>(); + for (int i = 0; i < strs.length; i++) { + char[] s_arr = strs[i].toCharArray(); + //排序 + Arrays.sort(s_arr); + //映射到 key + String key = String.valueOf(s_arr); + //添加到对应的类中 + if (hash.containsKey(key)) { + hash.get(key).add(strs[i]); + } else { + List temp = new ArrayList(); + temp.add(strs[i]); + hash.put(key, temp); + } + + } + return new ArrayList>(hash.values()); + } + + public static void main(String[] args) { + String[] words = new String[]{"eat", "tea", "tan", "ate", "nat", "bat"}; + List> result = groupAnagrams(words); + for(List list : result) { + System.out.print("["); + for(String word : list) { + System.out.print("\"" + word + "\""); + } + System.out.println("]"); + } + } +} diff --git a/Week 02/id_338/LeetCode_144_338.java b/Week 02/id_338/LeetCode_144_338.java new file mode 100644 index 000000000..47573fa58 --- /dev/null +++ b/Week 02/id_338/LeetCode_144_338.java @@ -0,0 +1,50 @@ +package id_338; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * User: liwei + * Date: 2019/10/24 11:03 + * Desc: + */ +public class LeetCode_144_338 { + List list = new ArrayList(); + + //递归, 时间复杂度 O(N) + //空间复杂度最坏 O(N), 平均O(log) + public List preorderTraversal1(TreeNode root) { + if (root == null) return new ArrayList(); + list.add(root.val); + if (root.left != null) { + preorderTraversal1(root.left); + } + if (root.right != null) { + preorderTraversal1(root.right); + } + return list; + } + + //思路:因为根-左-右,所以先记录根节点,再入栈左右子树,出栈的时候先出左边的 + //使用链表模拟递归过程, 时间控件复杂度都是O(N) + //时间空间复杂度都是 O(N) + public List preorderTraversal2(TreeNode root) { + if (root == null) return new ArrayList(); + LinkedList stack = new LinkedList<>(); + + stack.add(root); + while(!stack.isEmpty()){ + TreeNode node = stack.pollLast(); + list.add(node.val); + if (node.right != null) { + stack.add(node.right); + } + if (node.left != null) { + stack.add(node.left); + } + } + + return list; + } +} diff --git a/Week 02/id_338/LeetCode_1_338.java b/Week 02/id_338/LeetCode_1_338.java new file mode 100644 index 000000000..8ce06d830 --- /dev/null +++ b/Week 02/id_338/LeetCode_1_338.java @@ -0,0 +1,25 @@ +package id_338; + +import java.util.HashMap; +import java.util.Map; + +/** + * 两数之和 + * https://leetcode-cn.com/problems/two-sum/submissions/ + */ +public class LeetCode_1_338 { + // 将 a + b = target 问题转化为 b = target - a + // 一次遍历即可解决, O(N) + // 注意: new int[]{} 赋值的时候不能指定大小。map.containsKey + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + if (map.containsKey(nums[i])) { + return new int[]{map.get(nums[i]), i}; + } else { + map.put(target - nums[i], i); // b = target - a + } + } + return new int[]{0, 0}; + } +} diff --git a/Week 02/id_338/LeetCode_242_338.java b/Week 02/id_338/LeetCode_242_338.java new file mode 100644 index 000000000..a49cb1180 --- /dev/null +++ b/Week 02/id_338/LeetCode_242_338.java @@ -0,0 +1,50 @@ +package id_338; + +import java.util.Arrays; + +/** + * 有效的字母异位词 + * https://leetcode-cn.com/problems/valid-anagram + */ +public class LeetCode_242_338 { + public static void main(String[] args) { + String s = "abc"; + String t = "bca"; + boolean result1 = isAnagram1(s, t); + boolean result2 = isAnagram2(s, t); + } + + //方法1: 利用数据排序, O(nlogn), Arrays.sort底层双基快排算法 + //要注意Arrays.sort(), Arrays.equals()的写法 + public static boolean isAnagram1(String s, String t) { + if (s.length() != t.length()) { + return false; + } + + char[] sArray = s.toCharArray(); + char[] tArray = t.toCharArray(); + Arrays.sort(sArray); + Arrays.sort(tArray); + return Arrays.equals(sArray, tArray); + } + + //方法2: 利用哈希表, O(n)。 也可以先将s放入哈希表,再遍历t,小于0就返回false + //要注意s.charAt(i)的写法 + public static boolean isAnagram2(String s, String t) { + if (s.length() != t.length()) { + return false; + } + int[] counter = new int[26]; + for (int i = 0; i < s.length(); i++) { + counter[s.charAt(i) - 'a']++; + counter[t.charAt(i) - 'a']--; + } + for (int count : counter) { + if (count != 0) { + return false; + } + } + return true; + } + +} diff --git a/Week 02/id_338/LeetCode_33_338.java b/Week 02/id_338/LeetCode_33_338.java new file mode 100644 index 000000000..72a8a63de --- /dev/null +++ b/Week 02/id_338/LeetCode_33_338.java @@ -0,0 +1,39 @@ +package id_338; + +/** + * Created by leesen on 2019/10/30. + */ +public class LeetCode_33_338 { + //硬上二叉搜索, 只是夹逼判断条件变换一下, O(log + public int search(int[] nums, int target) { + int left = 0; + int right = nums.length - 1; + + while (left < right) { + int mid = left + (right - left) / 2; + if (nums[mid] == target) return mid; //****增加等值输出, 减少下面判断复杂度 + //左边单调,比最大的还大,继续往右找 + //****nums[left] <= nums[mid], 此处必须有=号 + // 否则当left和mid值相等又都不等于target的时候right = mid - 1可能正好跳过了值 + if (nums[left] <= nums[mid] && (target > nums[mid] || target < nums[left])) left = mid + 1; + else if (target < nums[left] && target > nums[mid]) left = mid + 1; //左边非单调, 但是比左边小,比mid大,继续往右找 + else right = mid - 1; + } + return left == right && nums[left] == target ? left : -1; + } + + //相关子问题练习, 寻找旋转数组中的旋转位置下标 + private static int getReverseIndex(int[] a) { + if (a.length == 0 || a.length == 1) return a.length; + if (a.length == 2) return 1; //防止下面每次都判断边界溢出 + int left = 0; + int right = a.length - 1; + while (left < right) { + int mid = left + (right - left) / 2; + if (a[mid] < a[mid - 1] && a[mid] < a[mid + 1]) return mid; + else if (a[mid] > a[mid - 1] && a[mid] < a[mid + 1]) right = mid - 1; + else left = mid + 1; + } + return left; + } +} diff --git a/Week 02/id_338/LeetCode_429_338.java b/Week 02/id_338/LeetCode_429_338.java new file mode 100644 index 000000000..cb4a2bcef --- /dev/null +++ b/Week 02/id_338/LeetCode_429_338.java @@ -0,0 +1,34 @@ +package id_338; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: liwei + * Date: 2019/10/24 09:51 + * Desc: + */ +public class LeetCode_429_338 { + public List> levelOrder(Node root) { + List> res = new ArrayList<>(); + if (root == null) return new ArrayList<>(); + help(root, 0, res); + return res; + } + + private void help (Node root, int depth, List> res) { + //退出条件 + if (root == null) return; + //判断是否是新的一层 + if (res.size() < depth + 1) { + res.add(new ArrayList()); + } + res.get(depth).add(root.val); + //处理子节点 + for (Node node : root.children) { + if (node != null) { //空节点判断别遗漏 + help(node, depth+1, res); + } + } + } +} \ No newline at end of file diff --git a/Week 02/id_338/LeetCode_49_338.java b/Week 02/id_338/LeetCode_49_338.java new file mode 100644 index 000000000..d09529d29 --- /dev/null +++ b/Week 02/id_338/LeetCode_49_338.java @@ -0,0 +1,29 @@ +package id_338; + +import java.util.*; + +/** + * 字母异位词分组 + * https://leetcode-cn.com/problems/group-anagrams + */ +public class LeetCode_49_338 { + //时间复杂度:O(NKlogK),其中N是strs的长度,而K是strs中字符串的最大长度。当我们遍历每个字符串时,外部循环具有的复杂度为O(N)。然后,我们在O(KlogK) 的时间内对每个字符串排序。 + //空间复杂度:O(NK),排序存储在map中的全部信息内容。 + //思路: 先将strs按字母排序,然后作为key存入hashmap + //注意这题有个思维误区,可能会选用charAt相加后的数作为hash key, 但这种是不对的,duh和ill的值就相同 + public List> groupAnagrams(String[] strs) { + if (strs == null || strs.length == 0) return new ArrayList>(); + Map> map = new HashMap(); + for (String str : strs) { + char[] chars = str.toCharArray(); + Arrays.sort(chars); + String key = String.valueOf(chars); + if (!map.containsKey(key)) { + map.put(key, new ArrayList()); + } + map.get(key).add(str); + } + + return new ArrayList>(map.values()); + } +} diff --git a/Week 02/id_338/LeetCode_589_338.java b/Week 02/id_338/LeetCode_589_338.java new file mode 100644 index 000000000..22f12747c --- /dev/null +++ b/Week 02/id_338/LeetCode_589_338.java @@ -0,0 +1,21 @@ +package id_338; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: liwei + * Date: 2019/10/24 11:04 + * Desc: + */ +public class LeetCode_589_338 { + List list = new ArrayList<>(); + public List preorder(Node root) { + if (root == null) return new ArrayList<>(); + for (int i=0; i list = new ArrayList<>(); + public List postorder(Node root) { + if (root == null) return new ArrayList<>(); + for (int i=0; i list = new ArrayList(); + + //递归, 时间复杂度 O(N) + //空间复杂度最坏 O(N), 平均O(log) + public List inorderTraversal1(TreeNode root) { + if (root == null) return new ArrayList(); + if (root.left != null) { + inorderTraversal1(root.left); + } + list.add(root.val); + if (root.right != null) { + inorderTraversal1(root.right); + } + return list; + } + + //使用栈模拟递归过程, 时间控件复杂度都是O(N) + //时间空间复杂度都是 O(N) + public List inorderTraversal2(TreeNode root) { + if (root == null) return new ArrayList(); + + List list = new ArrayList(); + Stack stack = new Stack(); + TreeNode cur = root; + + while(cur!=null || !stack.empty()){ + while(cur!=null){ + stack.add(cur); + cur = cur.left; + } + cur = stack.pop(); + list.add(cur.val); + cur = cur.right; + } + + return list; + } +} diff --git a/Week 02/id_343/LeetCode_1.go b/Week 02/id_343/LeetCode_1.go new file mode 100644 index 000000000..5911b4daa --- /dev/null +++ b/Week 02/id_343/LeetCode_1.go @@ -0,0 +1,13 @@ +func twoSum(nums []int, target int) []int { + m := make(map[int]int, len(nums)) + + for k,v := range nums { + diff := target - v + if idx, ok := m[diff]; ok { + return []int{idx, k} + } + m[v] = k + } + + return nil +} diff --git a/Week 02/id_343/LeetCode_144.go b/Week 02/id_343/LeetCode_144.go new file mode 100644 index 000000000..ff242f37e --- /dev/null +++ b/Week 02/id_343/LeetCode_144.go @@ -0,0 +1,18 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func preorderTraversal(root *TreeNode) []int { + if root == nil { + return nil + } + + l := preorderTraversal(root.Left) + r := preorderTraversal(root.Right) + + return append(append([]int{root.Val}, l...), r...) +} diff --git a/Week 02/id_343/LeetCode_145.go b/Week 02/id_343/LeetCode_145.go new file mode 100644 index 000000000..74171fc6d --- /dev/null +++ b/Week 02/id_343/LeetCode_145.go @@ -0,0 +1,18 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func postorderTraversal(root *TreeNode) []int { + if root == nil { + return nil + } + + l := postorderTraversal(root.Left) + r := postorderTraversal(root.Right) + + return append(append(l, r...), root.Val) +} diff --git a/Week 02/id_343/LeetCode_242.go b/Week 02/id_343/LeetCode_242.go new file mode 100644 index 000000000..b99d95550 --- /dev/null +++ b/Week 02/id_343/LeetCode_242.go @@ -0,0 +1,19 @@ +func isAnagram(s string, t string) bool { + if len(s) != len(t) { + return false + } + m := make(map[byte]int) + + for i := 0; i < len(s); i++ { + m[s[i]]++ + m[t[i]]-- + } + + for _, v := range m { + if v != 0 { + return false + } + } + + return true +} diff --git a/Week 02/id_343/LeetCode_94.go b/Week 02/id_343/LeetCode_94.go new file mode 100644 index 000000000..701041209 --- /dev/null +++ b/Week 02/id_343/LeetCode_94.go @@ -0,0 +1,16 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func inorderTraversal(root *TreeNode) []int { + if root == nil { + return nil + } + l := inorderTraversal(root.Left) + r := inorderTraversal(root.Right) + return append(append(l, root.Val), r...) +} diff --git a/Week 02/id_348/groupAnagrams.py b/Week 02/id_348/groupAnagrams.py new file mode 100644 index 000000000..55a78d1d2 --- /dev/null +++ b/Week 02/id_348/groupAnagrams.py @@ -0,0 +1,14 @@ +class Solution(object): + def groupAnagrams(self, strs): + """ + :type strs: List[str] + :rtype: List[List[str]] + """ + tmp_strs = {} + for s in strs: + tmp_s = "".join(sorted(s)) + if tmp_s not in tmp_strs.keys(): + tmp_strs[tmp_s] = [s] + else: + tmp_strs[tmp_s].append(s) + return tmp_strs.values() \ No newline at end of file diff --git a/Week 02/id_348/lowestCommonAncestor.py b/Week 02/id_348/lowestCommonAncestor.py new file mode 100644 index 000000000..ae4f584a7 --- /dev/null +++ b/Week 02/id_348/lowestCommonAncestor.py @@ -0,0 +1,24 @@ +class Solution: + + def __init__(self): + self.ans = None + + def lowerCommonAncestor(self, root, p, q): + + def recurse_tree(current_node): + if not current_node: + return False + + left = recurse_tree(current_node.left) + right = recurse_tree(current_node.right) + + mid = current_node == p or current_node == q + + if mid + left + right >= 2: + self.ans = current_node + + return mid or left or right + + recurse_tree(root) + return self.ans + \ No newline at end of file diff --git a/Week 02/id_353/Leetcode_242_353.cpp b/Week 02/id_353/Leetcode_242_353.cpp new file mode 100644 index 000000000..282ba664c --- /dev/null +++ b/Week 02/id_353/Leetcode_242_353.cpp @@ -0,0 +1,38 @@ +/* + * @lc app=leetcode.cn id=242 lang=cpp + * + * [242] 有效的字母异位词 + */ + +// @lc code=start +#include +class Solution { +public: + bool isAnagram(string s, string t) { + if (s.length() != t.length()) return false; + + std::map mapS, mapT; + for (char chs : s) { + mapS[chs]++; + } + for (char cht : t) { + mapT[cht]++; + } + return mapS == mapT; + + // const int LETTERS = 26; + // int arrs[LETTERS] = {0}; + // int arrt[LETTERS] = {0}; + // for (int i = 0; i < s.length(); i++) { + // arrs[s[i] - 'a']++; + // arrt[t[i] - 'a']++; + // } + // int j = 0; + // for (; j < LETTERS; j++) { + // if (arrs[j] != arrt[j]) break; + // } + // return j == LETTERS; + } +}; +// @lc code=end + diff --git a/Week 02/id_353/Leetcode_94_353.cpp b/Week 02/id_353/Leetcode_94_353.cpp new file mode 100644 index 000000000..a7c160be4 --- /dev/null +++ b/Week 02/id_353/Leetcode_94_353.cpp @@ -0,0 +1,40 @@ +/* + * @lc app=leetcode.cn id=94 lang=cpp + * + * [94] 二叉树的中序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ +class Solution { +public: + vector inorderTraversal(TreeNode* root) { + vector res; + DoActualWork(root, res); + return res; + } + + void DoActualWork(TreeNode* node, vector &save) { + if (node != NULL) { + if (node->left != NULL) + { + DoActualWork(node->left, save); + } + save.push_back(node->val); + if (node->right != NULL) { + DoActualWork(node->right, save); + } + } + } + +}; +// @lc code=end + diff --git a/Week 02/id_358/NOTE.md b/Week 02/id_358/NOTE.md index a6321d6e2..f6c6af35f 100644 --- a/Week 02/id_358/NOTE.md +++ b/Week 02/id_358/NOTE.md @@ -1,4 +1,164 @@ -# NOTE +# 第二周学习总结 - +## 本周第一课 + +学习了哈希表,映射,集合的基本实现和相关算法题 + +### 1. 哈希表,集合 + +哈希表实现中最重要的是哈希函数。数据量大时可能导致不同数据经过哈希函数得到相同的hash值,这叫做哈希碰撞。在工程实现上有通过“拉链表”的形式去解决相同key的问题。但如果这个拉出来的链表过长,就会退化到“链表”。 + +哈希表的结构特征是 key-value的形式。key不重复 + +set集合的结构特征是value不重复。 + +## 相关实战题解析 + +### 1. 有效的字母异位词 + +1. 暴力法 + + 先将字母排序,再比较。时间复杂度O(NlogN) + +2. hashmap + + 统计每个字母的频次再比较。进阶的方法是存每个字母的ASCII码 + +### 2. 两数之和 + +​ 利用hashmap。枚举元素的同时,从map中找targe-a; + +## 本周第二课 + + 学习了树,二叉树,二叉搜索树的基本结构和相关算法题 + +### 1. 树 + +三种遍历: + +​ 前序 : 根 ---》左 ---》右 + +​ 中序: 左 ---》根 ---》 右 + +​ 后序: 左 ---》右 ---》根 + +> 树的相关算法题一般可以使用递归解决。因为1:不便于循环;2:树有自重复性的特征 + +### 2. 二叉搜索树 + +特点: + +1. 左子树上节点值 < 根 +2. 右子树上节点值 > 根 +3. 左右子树也为二叉搜索树 + +> 二叉搜索树的中序遍历是升序排列。这在解决某些算法题时有用。 + +查询和操作都是O(logN) + +> 删除:如果删除的是非叶子节点需要提大于删除的节点的第一个节点上去。 + +## 相关实战题解析 + +### 1. 二叉树的中序遍历 + +1. 递归 +2. 通过栈。(使用两个while循环,先入栈左节点,然后出栈,访问根,访问右节点) + +> 注意:递归不存在效率高低的问题。效率低是因为代码的问题,写成了“傻递归”。所以不要刻意规避递归 + +## 本周第三课 + +学习了递归的实现,特性和思维要点 + +递归的代码模板 + +四个部分:1. 递归终止条件;2:处理当前逻辑;3:下探到下一层;4:清理当前层(如果有需要); + +本课重点介绍的关于递归的思维要点: + +1. 不要人肉递归 +2. 找到“最简最近”的方法;将问题拆解成可重复解决的问题(重复子问题) +3. 利用数学归纳法;比如爬楼梯,斐波那契; + +### 相关实战题解析 + +### 1. 爬楼梯问题 + +解法是利用递归。不要用“傻递归”。可以优化,使用缓存。 + +还可以使用动态规划。 + +### 2. 括号生成问题 + +1. 把问题看成是往2 * n个格子里放括号。每个格子可以放左或右括号。关键是在放的过程中,验证括号的合法性。需满足:1)left括号可以随时加,但不能超标;2)right括号必须之前有left括号,且left 个数 > right个。 + +### 3. 验证二叉搜索树 + +1. 中序遍历是有序的。 + +> 该问题需要注意的是左子树,右子树都是BST,容易写的时候忽略。 + +### 4. 二叉树的最大深度 + +比较简单,利用递归,深度遍历。O(N) + +## 本周第四课 + +学习了分治,回溯的实现和特性 + +老师给出来分治代码模板。 + +1. Recursion terminator +2. prepare data +3. Conquer sub-problems +4. Processss and generate the final result +5. Reverse the current level states + +> 回溯法近似于递归 + +## 相关实战题解析 + +### 1. 实现 pow(x, n) + +1. 暴力法 。累乘 +2. 分治法。分解子问题,使用递归解决。 + +思路:x^n ---> 2^10 ---> 2^5 * 2^5 ---> (2^2)*2 + +子问题: pow(x, n/2). 注意n < 0 的情况 ( 1/ pow(x, -n)) + +merge: + +``` +if n % 2 === 1 + then result = half * half * x; +else + result = half * half +``` + +### 2. 子集 + +1. 递归。问题看成n个格子,每个元素选或不选。 +2. 迭代。 + +### 3. 电话号码的字母组合 + +递归解决 + +#### 4. N皇后问题。 + +递归解决。 + +关键:循环column。使用set保存Col, pie,na 三条线上可以攻击的位置。 + + + + + + + + + +​ diff --git a/Week 02/id_358/leetcode#17.js b/Week 02/id_358/leetcode#17.js new file mode 100644 index 000000000..db7e6d319 --- /dev/null +++ b/Week 02/id_358/leetcode#17.js @@ -0,0 +1,37 @@ +/** + * @param {string} digits + * @return {string[]} + */ +var letterCombinations = function(digits) { + if(!digits) return []; + // 用一个map存储数字和对应的字母 + const map = { + '2': 'abc', + '3': 'def', + '4': 'ghi', + '5': 'jkl', + '6': 'mno', + '7': 'pqrs', + '8': 'tuv', + '9': 'wxyz' + } + const res = [] + let tempStr = ''; + search(tempStr, 0, digits); + + return res; + + function search(tempStr, index, inputStr,) { + if(index === inputStr.length) { // terminator + res.push(tempStr) + return; + } + // process + const letters = map[inputStr.charAt(index)] + for(let i = 0; i < letters.length; i++) { + search(tempStr + letters.charAt(i), index+1, inputStr) + } + + //reverse + } +}; \ No newline at end of file diff --git a/Week 02/id_358/leetcode#94.js b/Week 02/id_358/leetcode#94.js new file mode 100644 index 000000000..2cf3cc3d3 --- /dev/null +++ b/Week 02/id_358/leetcode#94.js @@ -0,0 +1,47 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** 递归 + * @param {TreeNode} root + * @return {number[]} + */ +// var inorderTraversal = function(root) { +// const res = []; +// function helper(root, res) { +// if(root){ +// if(root.left) { +// helper(root.left, res) +// } +// res.push(root.val) +// if(root.right) { +// helper(root.right,res) +// } +// } +// } +// helper(root, res); +// return res; + +// }; + +/** +*栈 +*/ +var inorderTraversal = function(root) { + let res = []; + let stack = []; + let curr = root; + while(curr !== null || stack.length > 0) { + while(curr!== null) { + stack.unshift(curr) + curr = curr.left; + } + curr = stack.shift(); + res.push(curr.val) + curr = curr.right; + } + return res; +} \ No newline at end of file diff --git a/Week 02/id_363/LeetCode_105_363.java b/Week 02/id_363/LeetCode_105_363.java new file mode 100644 index 000000000..45c284641 --- /dev/null +++ b/Week 02/id_363/LeetCode_105_363.java @@ -0,0 +1,88 @@ +package com.test.leetcode.week02; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + + +public class SolutionBuildTree105 { + + + @Test + public void test1() { + TreeNode node = buildTree(new int[]{3, 9, 20, 15, 7}, new int[]{9, 3, 15, 20, 7}); + TreeNode node2 = buildTree2(new int[]{3, 9, 20, 15, 7}, new int[]{9, 3, 15, 20, 7}); + System.out.println(node); + System.out.println(node2); + } + + + + public TreeNode buildTree2(int[] preorder, int[] inorder) { + return buildTreeHelper(preorder, inorder, (long)Integer.MAX_VALUE + 1); + } + + int pre = 0; + int in = 0; + + private TreeNode buildTreeHelper(int[] preorder, int[] inorder, long stop) { + //到达末尾返回 null + if(pre == preorder.length){ + return null; + } + //到达停止点返回 null + //当前停止点已经用了,in 后移 + if (inorder[in] == stop) { + in++; + return null; + } + int root_val = preorder[pre++]; + TreeNode root = new TreeNode(root_val); + //左子树的停止点是当前的根节点 + root.left = buildTreeHelper(preorder, inorder, root_val); + //右子树的停止点是当前树的停止点 + root.right = buildTreeHelper(preorder, inorder, stop); + return root; + } + + + + /** + * 重复子问题:获取中序遍历的第一个节点左右跟节点,然后使用这个跟节点切分中序遍历的数组 + * @param preorder + * @param inorder + * @return + */ + int preRootIndex = 0; + int[] preorder ; + int[] inorder; + Map inOrderMap = new HashMap<>(); + public TreeNode buildTree(int[] preorder, int[] inorder) { + this.preorder = preorder; + this.inorder = inorder; + + for (int i = 0; i < inorder.length; i ++) { + inOrderMap.put(inorder[i], i); + } + return helper(0, inorder.length); + } + + private TreeNode helper(int left, int right) { + // 递归终止条件 + if (left == right) { + return null; + } + // 处理当前层逻辑 + int rootVal = preorder[preRootIndex ++]; + TreeNode root = new TreeNode(rootVal); + Integer mid = inOrderMap.get(rootVal); + // 下探到下一层 + root.left = helper(left, mid); + root.right = helper(mid + 1, right); + // 清理当前层 + return root; + + } + +} diff --git a/Week 02/id_363/LeetCode_144_363.java b/Week 02/id_363/LeetCode_144_363.java new file mode 100644 index 000000000..2bf7709a4 --- /dev/null +++ b/Week 02/id_363/LeetCode_144_363.java @@ -0,0 +1,118 @@ +package com.test.leetcode.week02; + +import org.junit.Test; + +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; +import java.util.Stack; + + +public class SolutionPreOrdertraversal144 { + + + @Test + public void test1() { + TreeNode node1 = new TreeNode(1); + TreeNode node2 = new TreeNode(2); + TreeNode node3 = new TreeNode(3); + TreeNode node4 = new TreeNode(4); + TreeNode node5 = new TreeNode(5); + TreeNode node6 = new TreeNode(6); + TreeNode node7 = new TreeNode(7); + node1.right = node3; + node1.left = node2; + node2.left = node4; + node2.right = node5; + node3.left = node6; + node3.right = node7; + + System.out.println(preorderTraversal_20191026_1(node1)); + System.out.println(preorderTraversal_20191026_2(node1)); + System.out.println(preorderTraversal_20191026_3(node1)); + } + + + + /** + * 递归: 时间复杂度是O(n) 空间复杂度是O(n) + * 栈:时间复杂度是O(n) 空间复杂度是O(n) + * @param root + * @return + */ + public List preorderTraversal_20191026_1(TreeNode root) { + List result = new LinkedList<>(); + helper_20191026_1(root, result); + return result; + } + + private void helper_20191026_1(TreeNode root, List result) { + if (root == null) { + return; + } + result.add(root.val); + helper_20191026_1(root.left, result); + helper_20191026_1(root.right, result); + } + + public List preorderTraversal_20191026_2(TreeNode root) { + List result = new LinkedList<>(); + LinkedList stack = new LinkedList<>(); + if(root == null) { + return result; + } + stack.add(root); + while (!stack.isEmpty()) { + TreeNode node = stack.pollLast(); + result.add(node.val); + if (node.right != null) { + stack.add(node.right); + } + if (node.left != null) { + stack.add(node.left); + } + } + return result; + } + + + + // 循环左节点的时候 把中间和左边数据加入到结果中 + // 栈弹出的时候,取右边数据好了 + public List preorderTraversal_20191026_3(TreeNode root) { + List result = new LinkedList<>(); + Deque stack = new LinkedList<>(); // 保证加入和取出是在同一段 + TreeNode cur = root; + while(cur != null || !stack.isEmpty()) { + while (cur != null) { + result.add(cur.val); + stack.addFirst(cur); + cur = cur.left; + } + cur = stack.pollFirst().right; + } + return result; + } + + // 左中右: 因为要先取左边的数据 所以吧左边数据全部加入到栈内 然后再取出 + // 这样左边数据就是在前面 + // 再取出的就是中间的数据 + // pop : 左边数据 | 中间数据 切换右边数据 + public List inorderTraversal2(TreeNode root) { + List result = new LinkedList<>(); + Stack stack = new Stack(); + TreeNode cur = root; + while (cur != null || !stack.isEmpty()) { + // 处理左节点 所有的左节点入栈 + while(cur != null){ + stack.push(cur); + cur = cur.left; + } + cur = stack.pop(); + result.add(cur.val); + cur = cur.right; + } + return result; + } + +} diff --git a/Week 02/id_363/LeetCode_169_363.java b/Week 02/id_363/LeetCode_169_363.java new file mode 100644 index 000000000..14772b574 --- /dev/null +++ b/Week 02/id_363/LeetCode_169_363.java @@ -0,0 +1,148 @@ +package com.test.leetcode.week02; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + + +public class SolutionMajorityElement169 { + + + @Test + public void test1() { + System.out.println(majorityElement(new int[]{2,2,1,1,1,2,2})); + System.out.println(majorityElement(new int[]{3,2,3})); + + System.out.println(majorityElement2(new int[]{1})); + System.out.println(majorityElement2(new int[]{2,2,1,1,1,2,2})); + System.out.println(majorityElement2(new int[]{3,2,3})); + System.out.println("------------"); + System.out.println(majorityElement2Better(new int[]{2,2,1,1,1,2,2})); + System.out.println(majorityElement2Better(new int[]{3,2,3})); + + System.out.println("------------"); + System.out.println(majorityElement3DivideAndConquer(new int[]{2,2,1,1,1,2,2})); + System.out.println(majorityElement3DivideAndConquer(new int[]{3,2,3})); + + + System.out.println("------------"); + System.out.println(majorityElementBM(new int[]{2,2,1,1,1,2,2})); + System.out.println(majorityElementBM(new int[]{3,2,3})); + } + + /** + * 1. map 统计数量 时间复杂度是O(n) + * 2. 排序 记录每个元素的count 时间复杂度是O(nlogn) + * 3. 分治 + * 4.Booyer-More 算法 候选人 + * @param nums + * @return + */ + public int majorityElement(int[] nums) { + int len = nums.length; + Map map = new HashMap<>(len); + for (int i = 0; i < len; i ++) { + Integer count = map.get(nums[i]); + map.put(nums[i], count == null ? 1 : count + 1); + if (map.get(nums[i]) > len / 2) { + return nums[i]; + } + } + return nums[0]; + } + + + public int majorityElement2(int[] nums) { + int len = nums.length; + Arrays.sort(nums); + int count = 1; + for (int i = 1; i < len; i ++) { + if (nums[i] == nums[i - 1]) { + count ++; + } else { + count = 1; + } + if (count > len / 2) { + return nums[i]; + } + } + return nums[0]; + } + + + /** + * 数据排序后,众数因为占数据的一般以上,所以下标在len / 2位置的数字一定是众数 + * 如果众数是数组的最小值或者最大值,当数组数量是奇数/偶数,那么n/2 一定会覆盖到 + * @param nums + * @return + */ + public int majorityElement2Better(int[] nums) { + Arrays.sort(nums); + return nums[nums.length / 2]; + } + + + public int majorityElement3DivideAndConquer(int[] nums) { + return majorityDC(nums, 0, nums.length - 1); + } + + // [start, end) + private int majorityDC(int[] nums, int start, int end) { + // 终止条件 + if ( start == end) { + return nums[start]; + } + // 解决当前层 + // 下探到下一层 + int mid = (start + end) / 2; + int left = majorityDC(nums, start, mid); + int right = majorityDC(nums, mid + 1, end); + // 合并子问题 + if (left == right) { + return left; + } + int leftCount = countNums(nums, left, start, mid); + int rightCount = countNums(nums, right, mid + 1, end); + return leftCount > rightCount ? left : right; + // 清理当前层 + + } + + private int countNums(int[] nums, int num, int start, int end) { + int count = 0; + for (int i = start; i < end; i ++) { + if (nums[i] == num) { + count ++; + } + } + return count; + } + + + /** + * Booyer-More 算法 + * 其他数字出现次数的总和不会超过这个数字出现的次数 + * 替换 candidate 的时候,如果这个candidate 是众数,那么去掉了相同数量的众数和其他数字 + * 剩余的数组中,众数的数量还是大于其他数量的综合 + * + * 如果替换的candidate 不是众数,那么去掉这个数,去掉了x的众数和大于x的非众数 + * 剩下的数组中,众数的数量还是大于其他数量的综合 + * + * @param nums + * @return + */ + public int majorityElementBM(int[] nums) { + int count = 0; + Integer condidate = null; + for (int i = 0; i < nums.length; i ++) { + if (count == 0) { + condidate = nums[i]; + } + count += (nums[i] == condidate ? 1 : -1); + } + return condidate; + } + +} diff --git a/Week 02/id_363/LeetCode_17_363.java b/Week 02/id_363/LeetCode_17_363.java new file mode 100644 index 000000000..62fb45c3e --- /dev/null +++ b/Week 02/id_363/LeetCode_17_363.java @@ -0,0 +1,107 @@ +package com.test.leetcode.week02; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; + + +public class SolutionLetterCombination17 { + + + /** + * 1. 递归 + * 2. queue+ 2循环 + * 3. queue + 1循环 + */ + @Test + public void test1() { +// System.out.println(letterCombinationsQueue("")); + System.out.println(letterCombinationsQueue2("234")); + } + + + public List letterCombinationsQueue2(String digits) { + LinkedList queue = new LinkedList<>(); + if (digits == null || digits.length() == 0) { + return queue; + } + String[] buttons = {"0", "1", "abc", "def", "ghi", "jkl", "mno", "qprs", "tuv", "wxyz"}; + queue.offer(""); + // 以队列里面字母的长度来判断当前层有多少数据 + while (queue.peek().length() != digits.length()) { + String temp = queue.poll(); + String chars = buttons[temp.length() + 2]; + for (Character c : chars.toCharArray()) { + queue.offer(temp + c); + } + } + return queue; + } + + + public List letterCombinationsQueue(String digits) { + LinkedList queue = new LinkedList<>(); + if (digits == null || digits.length() == 0) { + return queue; + } + String[] buttons = {"0", "1", "abc", "def", "ghi", "jkl", "mno", "qprs", "tuv", "wxyz"}; + queue.offer(""); + for (int i = 0; i < digits.length(); i ++) { + String chars = buttons[digits.charAt(i) - '0']; + while(queue.peek().length() == i) { + String temp = queue.poll(); + for (Character c : chars.toCharArray()) { + queue.offer(temp + c); + } + } + } + return queue; + } + + + + + Map map = new HashMap(); + + public List letterCombinations(String digits) { + map.put("2", "abc"); + map.put("3", "def"); + map.put("4", "ghi"); + map.put("5", "jkl"); + map.put("6", "mno"); + map.put("7", "qprs"); + map.put("8", "tuv"); + map.put("9", "wxyz"); + // map + List result = new LinkedList<>(); + if (digits == null || digits.length() == 0) { + return result; + } + helper(digits, "", result); + // digit i + // combination + 可能的字母 + 继续递归 + return result; + } + + private void helper(String digits, String combination, List result) { + // 终止条件 + if (digits.length() == 0) { + result.add(combination); + return; + } + // 处理当前层 获取当前层的数字 获取对应的字母 吧字母加入到combination 后面 + String num = digits.substring(0, 1); + String chars = map.get(num); + // 下探到下一层 digits = digits.substring(1); + digits = digits.substring(1); + for (Character c : chars.toCharArray()) { + helper(digits, combination+ c , result); + } + // 清理当前层 + } +} diff --git a/Week 02/id_363/LeetCode_236_363.java b/Week 02/id_363/LeetCode_236_363.java new file mode 100644 index 000000000..8fd272208 --- /dev/null +++ b/Week 02/id_363/LeetCode_236_363.java @@ -0,0 +1,143 @@ +package com.test.leetcode.week02; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + + +public class SolutionLowestCommonAncestor236 { + + + @Test + public void test1() { + TreeNode node0 = new TreeNode(0); + TreeNode node1 = new TreeNode(1); + TreeNode node2 = new TreeNode(2); + TreeNode node3 = new TreeNode(3); + TreeNode node4 = new TreeNode(4); + TreeNode node5 = new TreeNode(5); + TreeNode node6 = new TreeNode(6); + TreeNode node7 = new TreeNode(7); + TreeNode node8 = new TreeNode(8); + + node3.left = node5; + node3.right = node1; + node5.left = node6; + node5.right = node2; + node1.left = node0; + node1.right = node8; + node2.left = node7; + node2.right = node4; + +// System.out.println(lowestCommonAncestor1(node3, node5, node4)); + System.out.println(lowestCommonAncestor2(node3, node5, node4)); + System.out.println(lowestCommonAncestor3(node3, node5, node4)); + } + + + /** + * 判断左子树中是否存在p/q + * 判断右子树中是否存在p/q + * 如果左子树没有,那么返回右子树的值 + * 如果左子树有p/q 并且右子树没有p/q 那么返回左节点 否则返回root + * @param root + * @param p + * @param q + * @return + */ + public TreeNode lowestCommonAncestor3(TreeNode root, TreeNode p, TreeNode q) { + if(root == null || root == p || root == q) return root; + TreeNode left = lowestCommonAncestor3(root.left, p, q); // 左子树中的返回值 + TreeNode right = lowestCommonAncestor3(root.right, p, q); // 右子树中的返回值 + // 对于每一个节点 + if (left != null && right != null) return root; // 说明根节点是最近公共祖先 + return left == null ? right : left; // 返回其中一个节点,表示p/q 存在 + } + + + /** + * 使用父指针 + * 思路: + * 记录每个节点以及父指针 + * 获取p的所有父节点 + * 获取q的父节点,当q的父节点在p的父节点集合中存在的时候,那么这个节点就是最近公共祖先 + * + * 优点:简单暴力,思路简单 + * 缺点:需要保留所有节点和父节点之间的关系,内存占用较大。 + * @param root + * @param p + * @param q + * @return + */ + public TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q) { + Stack stack = new Stack(); + stack.push(root); + Map parents = new HashMap<>(); + + while (!parents.containsKey(p) || !parents.containsKey(q)) { + TreeNode node = stack.pop(); + if (node == null) { + break; + } + if (node.left != null) { + stack.push(node.left); + parents.put(node.left, node); + } + if (node.right != null) { + stack.push(node.right); + parents.put(node.right, node); + } + } + if (!parents.containsKey(p) || !parents.containsKey(q)) { + return root; + } + + Set ancestors = new HashSet(); + while (p != null) { + ancestors.add(p); + p = parents.get(p); + } + + while (!ancestors.contains(q)) { + q = parents.get(q); + } + return q; + } + + + + /** + * 当树的根节点 左子树 或者 右子树中包含这个值的时候,那么根节点就是结果 + * @param root + * @param p + * @param q + * @return + */ + TreeNode result = null; + public TreeNode lowestCommonAncestor1(TreeNode root, TreeNode p, TreeNode q) { + recurseTree(root, p, q); + return result; + } + + private Boolean recurseTree(TreeNode root, TreeNode p, TreeNode q) { + // 递归终止条件 + if (root == null) return false; + // 处理当前层的问题 如果当前节点 和 左右节点中有两个节点 是p/q ,那么返回mid + int mid = (root == p || root == q) ? 1 : 0; + // 下探到下一层 + int left = recurseTree(root.left, p, q) ? 1 : 0; + int right = recurseTree(root.right, p, q) ? 1 : 0; + // 合并 + if (mid + left + right >= 2) { + this.result = root; + return true; + } + // 清理当前层 + return (mid + left + right) > 0; + } + +} diff --git a/Week 02/id_363/LeetCode_242_363.java b/Week 02/id_363/LeetCode_242_363.java new file mode 100644 index 000000000..4d24cde7a --- /dev/null +++ b/Week 02/id_363/LeetCode_242_363.java @@ -0,0 +1,162 @@ +package com.test.leetcode.week02; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +public class SolutionIsAnagram242 { + + + @Test + public void test1() { + System.out.println(isAnagram_20191024_1("anagram", "nagaram")); + System.out.println(isAnagram_20191024_2("anagram", "nagaram")); + System.out.println(isAnagram_20191024_3("anagram", "nagaram")); + } + + + /** + * 1. 排序 O(Nklogk) O(K) + * 2. int[] O(NK) O(N) + * 3. map O(NK) O(N) + * @param s + * @param t + * @return + */ + public boolean isAnagram_20191024_1(String s, String t) { + if (s == null && t == null) return true; + if (s == null || t == null) return false; + if (s.length() != t.length()) return false; + char[] sChars = s.toCharArray(); + char[] tChars = t.toCharArray(); + Arrays.sort(sChars); + Arrays.sort(tChars); + return Arrays.equals(sChars, tChars); + } + + public boolean isAnagram_20191024_2(String s, String t) { + if (s == null && t == null) return true; + if (s == null || t == null) return false; + if (s.length() != t.length()) return false; + int[] table = new int[26]; + for (Character c : s.toCharArray()) { + table[c - 'a'] ++; + } + for (Character c : t.toCharArray()) { + table[c - 'a'] --; + if (table[c - 'a'] < 0) { + return false; + } + } + return true; + } + + public boolean isAnagram_20191024_3(String s, String t) { + if (s == null && t == null) return true; + if (s == null || t == null) return false; + if (s.length() != t.length()) return false; + Map map = new HashMap<>(); + for (Character c: s.toCharArray()) { + Integer count = map.get(c); + if (count == null) { + map.put(c, 1); + } else { + map.put(c, count + 1); + } + } + for (Character c: t.toCharArray()) { + Integer cnt = map.get(c); + if (cnt == 0) { + return false; + } else { + map.put(c, cnt - 1); + } + } + return true; + } + + /** + * 1.排序 + * 2.map 为什么使用map这么慢 + * 3.数组 + * + * map int【】 本质是一样的 + * 第一个循环计数 第二个循环减数 如果小于0 return false 第三个循环判断是否大于0 + * + * 1.审题 2.思考所有的思路 3.code 4.testCase + * @param s + * @param t + * @return + */ + public boolean isAnagram_sort(String s, String t) { + if (s == null && t == null) return true; + if (s == null || t == null) return false; + char[] sChars = s.toCharArray(); + char[] tChars = t.toCharArray(); + Arrays.sort(sChars); + Arrays.sort(tChars); + return Arrays.equals(sChars, tChars); + } + + + /** + * 要确认大小字母是够一样对待 + * 第二个循环做判断,提早结束 + * best + * 数组 + * @param s + * @param t + * @return + */ + public boolean isAnagram_arr(String s, String t) { + if (s == null && t == null) return true; + if (s == null || t == null) return false; + if (s.length() != t.length()) return false; + + int[] counter = new int[26]; + for (int i = 0; i < s.length(); i ++) { + counter[s.charAt(i) - 'a'] ++; + } + for (int i = 0; i < t.length(); i ++) { + int index = t.charAt(i) - 'a'; + counter[index] --; + if (counter[index] < 0) { + return false; + } + } + for (int count : counter) { + if (count > 0) { + return false; + } + } + return true; + } + + // 更加通用 + public boolean isAnagram_map_better(String s, String t) { + if (s == null && t == null) return true; + if (s == null || t == null) return false; + if (s.length() != t.length()) return false; + + Map sMap = new HashMap<>(s.length()); + for (int i = 0; i < s.length(); i++) { + Character sc = s.charAt(i); + sMap.put(sc, sMap.get(sc) == null ? 1 : sMap.get(sc) + 1); + } + for (int i = 0; i < t.length(); i++) { + Character tc = t.charAt(i); + Integer count = sMap.get(tc); + if (count == null || count == 0) { + return false; + } + sMap.put(tc, count - 1); + } + return true; + } + + +} diff --git a/Week 02/id_363/LeetCode_429_363.java b/Week 02/id_363/LeetCode_429_363.java new file mode 100644 index 000000000..d68e81444 --- /dev/null +++ b/Week 02/id_363/LeetCode_429_363.java @@ -0,0 +1,120 @@ +package com.test.leetcode.week02; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + + +public class SolutionLevelOrder429 { + + + @Test + public void test1() { + Node node1 = new Node(); node1.val = 1; + Node node2 = new Node(); node2.val = 2; + Node node3 = new Node(); node3.val = 3; + Node node4 = new Node(); node4.val = 4; + Node node5 = new Node(); node5.val = 5; + Node node6 = new Node(); node6.val = 6; + Node node7 = new Node(); node7.val = 7; + node1.children = Arrays.asList(node3, node2, node4); + node3.children = Arrays.asList(node5, node6); + System.out.println(levelOrder(node1)); + System.out.println(levelOrder2(node1)); + System.out.println(levelOrderBetter(node1)); + } + + + /** + * 使用LinkedList是时候最好不适用i遍历 + * 最好使用poll这种 O(1)时间复杂度来遍历 + * @param root + * @return + */ + public List> levelOrder(Node root) { + List> result = new LinkedList<>(); + if (root == null) { + return result; + } + Queue levelNodes = new LinkedList<>(); + levelNodes.add(root); + while(levelNodes.size() > 0) { + List segResult = new LinkedList<>(); + int size = levelNodes.size(); + Queue childNodes = new LinkedList<>(); + while (size > 0) { + Node node = levelNodes.poll(); + segResult.add(node.val); + if (node.children != null && node.children.size() > 0 ) { + childNodes.addAll(node.children); + } + size --; + } + result.add(segResult); + levelNodes = childNodes; + } + return result; + } + + + // queue中可以使用count来表示当前层有哪些数据 + public List> levelOrderBetter(Node root) { + List> result = new LinkedList<>(); + if (root == null) { + return result; + } + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + List curLevel = new LinkedList<>(); + int len = queue.size(); + for (int i = 0; i < len; i ++) { + Node node = queue.poll(); + curLevel.add(node.val); + if (node.children != null) { + queue.addAll(node.children); + } + } + result.add(curLevel); + } + return result; + } + + + /** + * 重复子问题:获取每一层的list 增加当前节点的值到list中 + * @param root + * @return + */ + public List> levelOrder2(Node root) { + List> result = new ArrayList<>(); + helper(root, 0, result); + return result; + } + + private void helper(Node root, int depth, List> result) { + // 递归终止条件 + if (root == null) { + return; + } + // 处理当前层 + if (depth + 1 > result.size()) { + result.add(new LinkedList<>()); + } + result.get(depth).add(root.val); + // 下探到下一层 + List children = root.children; + if (children != null && children.size() > 0) { + for (Node child : children) { + helper(child, depth + 1, result); + } + } + // 清理当前层 + + } + +} diff --git a/Week 02/id_363/LeetCode_49_363.java b/Week 02/id_363/LeetCode_49_363.java new file mode 100644 index 000000000..8f47cc08f --- /dev/null +++ b/Week 02/id_363/LeetCode_49_363.java @@ -0,0 +1,103 @@ +package com.test.leetcode.week02; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + + +public class SolutionGroupAnagrams49 { + + + @Test + public void test1() { + System.out.println(groupAnagrams(new String[]{"eat", "tea", "tan", "ate", "nat", "bat"})); + System.out.println(groupAnagrams2(new String[]{"eat", "tea", "tan", "ate", "nat", "bat"})); + System.out.println(groupAnagrams3(new String[]{"eat", "tea", "tan", "ate", "nat", "bat"})); + } + + + /** + * 给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 + * ["eat", "tea", "tan", "ate", "nat", "bat"], + * [ + * ["ate","eat","tea"], + * ["nat","tan"], + * ["bat"] + * ] + * 排序: O(NMlogM) N是数组长度 M是数组内字符串的平均长度 空间复杂度O(N) + * @param strs + * @return + */ + public List> groupAnagrams(String[] strs) { + if (strs == null || strs.length == 0) return null; + Map> result = new HashMap<>(strs.length); + for(String str : strs) { + char[] charArr = str.toCharArray(); + Arrays.sort(charArr); + String orderStr = new String(charArr); + List charList = result.get(orderStr); + if ( charList == null) { + charList = new LinkedList<>(); + result.put(orderStr, charList); + } + charList.add(str); + } + return new ArrayList<>(result.values()); + } + + // 生成特殊字符 时间复杂度:O(NM) 空间复杂度 O(N) + public List> groupAnagrams2(String[] strs) { + if (strs == null || strs.length == 0) return null; + Map> result = new HashMap<>(strs.length); + int[] count = new int[26]; + for (String str : strs) { + Arrays.fill(count, 0); + for (Character c : str.toCharArray()) { + count[c - 'a'] ++; + } + StringBuffer keyBuf = new StringBuffer(); + for (int j = 0; j < count.length; j ++) { + keyBuf.append("#" + count[j]); + } + List keyList = result.get(keyBuf.toString()); + if (keyList == null) { + keyList = new LinkedList<>(); + result.put(keyBuf.toString(), keyList); + } + keyList.add(str); + } + return new ArrayList<>(result.values()); + } + + /** + * 算术基本定理(正整数唯一分解定理):每一个大于1的自然数,要么本身是质数, + * 要么可以写成两个以上质数的积,而且这些质因子按大小排列好之后,写法仅有一种方式 + * 缺陷: key 是累计相乘,可能会导致int溢出 + * 时间复杂度:O(NM) 空间复杂度 O(N) + * @param strs + * @return + */ + public List> groupAnagrams3(String[] strs) { + if (strs == null || strs.length == 0) return null; + Map> result = new HashMap<>(strs.length); + int[] prime = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103 }; + for (String str : strs) { + Integer key = 1; + for (Character c: str.toCharArray()) { + key *= prime[c - 'a']; + } + List keyList = result.get(key); + if (keyList == null) { + keyList = new ArrayList<>(); + result.put(key, keyList); + } + keyList.add(str); + } + return new ArrayList<>(result.values()); + } +} diff --git a/Week 02/id_363/LeetCode_51_363.java b/Week 02/id_363/LeetCode_51_363.java new file mode 100644 index 000000000..c9cecf222 --- /dev/null +++ b/Week 02/id_363/LeetCode_51_363.java @@ -0,0 +1,93 @@ +package com.test.leetcode.week02; + +import org.junit.Test; + +import java.util.LinkedList; +import java.util.List; + + +public class SolutionNQueens51 { + + + @Test + public void test1() { + System.out.println(solveNQueens(4)); + } + + /** + * 1. 回溯 + * DFS + * int[] 第一个数放在第一个 第二个数放在第二个 + * @param n + * @return + */ + + + int[] cols; // 表示那一列已经有值了 + int[] hill; + int[] dale; + int[] queens; + int n = 0; + List> result = new LinkedList<>(); + + public List> solveNQueens(int n) { + this.n = n; + cols = new int[n]; + hill = new int[2 * n + 1]; + dale = new int[2 * n + 1]; + queens = new int[n]; + + backtrace(0); + return result; + } + + private void backtrace(int row) { + for (int col = 0; col < n; col ++) { + if (ok(row, col)) { + placeQueen(row, col); + if (row + 1 == n) { + addResult(queens); + } + backtrace(row + 1); + removeQueen(row, col); + } + } + } + + private void removeQueen(int row, int col) { + cols[col] = 0; + hill[row + col] = 0; + dale[row - col + n] = 0; + queens[row]= 0; + } + + private void addResult(int[] queens) { + List temp = new LinkedList<>(); + for (int i = 0; i < queens.length; i ++) { + StringBuffer buffer = new StringBuffer(); + for (int j = 0; j < queens[i]; j ++) { + buffer.append("."); + } + buffer.append("Q"); + for (int j = queens[i] + 1; j < queens.length; j ++) { + buffer.append("."); + } + temp.add(buffer.toString()); + } + result.add(temp); + } + + private void placeQueen(int row, int col) { + cols [col] = 1; + hill[row + col ] = 1; + dale[row - col + n] = 1; + queens[row] = col; + } + + private boolean ok(int row, int col) { + int res = cols[col] + hill[row + col] + dale[row - col + n]; + return res == 0; + } + + +} diff --git a/Week 02/id_363/LeetCode_589_363.java b/Week 02/id_363/LeetCode_589_363.java new file mode 100644 index 000000000..1c1e6017b --- /dev/null +++ b/Week 02/id_363/LeetCode_589_363.java @@ -0,0 +1,87 @@ +package com.test.leetcode.week02; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Stack; + + +public class SolutionPreOrder589 { + + + @Test + public void test1() { + Node node1 = new Node(); node1.val = 1; + Node node2 = new Node(); node2.val = 2; + Node node3 = new Node(); node3.val = 3; + Node node4 = new Node(); node4.val = 4; + Node node5 = new Node(); node5.val = 5; + Node node6 = new Node(); node6.val = 6; + Node node7 = new Node(); node7.val = 7; + node1.children = Arrays.asList(node3, node2, node4); + node3.children = Arrays.asList(node5, node6); + System.out.println(preorder(node1)); + System.out.println(preorder2(node1)); + + } + + + /** + * stack : 出栈 + result + 存放子节点 + * @param root + * @return + */ + public List preorder2(Node root) { + List result = new LinkedList<>(); + if (root == null) { + return result; + } + Stack stack = new Stack<>(); + stack.push(root); + while (! stack.isEmpty()) { + Node node = stack.pop(); + result.add(node.val); + List children = node.children; + if (children != null && children.size() > 0) { + for (int i = children.size() - 1; i >= 0; i --) { + stack.push(children.get(i)); + } + } + } + return result; + } + + + /** + * N 叉树的前序遍历 中左右 + * 1.递归 + * 2.栈 + * @param root + * @return + */ + public List preorder(Node root) { + List result = new LinkedList<>(); + helper(root, result); + return result; + } + + private void helper(Node root, List result) { + // 1. 终止条件 + if (root == null) { + return; + } + // 2. 处理当前层 + result.add(root.val); + // 3. 下探到下一层 + List children = root.children; + if (children != null && children.size() > 0) { + for (Node child : children) { + helper(child, result); + } + } + // 4. 清理当前层 + } + +} diff --git a/Week 02/id_363/LeetCode_590_363.java b/Week 02/id_363/LeetCode_590_363.java new file mode 100644 index 000000000..ab66aad00 --- /dev/null +++ b/Week 02/id_363/LeetCode_590_363.java @@ -0,0 +1,116 @@ +package com.test.leetcode.week02; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Stack; + + +public class SolutionPostOrder590 { + + + @Test + public void test1() { + Node node1 = new Node(); node1.val = 1; + Node node2 = new Node(); node2.val = 2; + Node node3 = new Node(); node3.val = 3; + Node node4 = new Node(); node4.val = 4; + Node node5 = new Node(); node5.val = 5; + Node node6 = new Node(); node6.val = 6; + Node node7 = new Node(); node7.val = 7; + node1.children = Arrays.asList(node3, node2, node4); + node3.children = Arrays.asList(node5, node6); + System.out.println(postorder(node1)); + System.out.println(postorder2(node1)); + System.out.println(postorder3(node1)); + } + + + /** + * 两个栈 + * 一个栈:放当前层的数据 + * 另一个栈:存放总结果数据 + * + * @param root + * @return + */ + public List postorder3(Node root) { + List result = new LinkedList<>(); + if (root == null) { + return result; + } + Stack levelStack = new Stack<>(); + Stack resultStack = new Stack<>(); + levelStack.push(root); + while (!levelStack.isEmpty()) { + Node node = levelStack.pop(); + resultStack.push(node); + List children = node.children; + if (children != null && children.size() > 0) { + for (Node child : children) { + levelStack.push(child); + } + } + } + while (!resultStack.isEmpty()) { + result.add(resultStack.pop().val); + } + return result; + } + + + /** + * 使用一个栈:把当前node的val放到result的第0个节点 + * 把当前层的子节点数据存放到 stack中 + * @param root + * @return + */ + public List postorder2(Node root) { + List result = new LinkedList<>(); + Stack stack = new Stack(); + if (root == null) { + return result; + } + stack.push(root); + Node top; + int len; + while(!stack.isEmpty()) { + top = stack.pop(); + result.add(0, top.val); + List children = top.children; + if (children != null && children.size() > 0) { + len = children.size(); + for (int i = 0; i < len; i ++) { + stack.push(children.get(i)); + } + } + } + return result; + } + + public List postorder(Node root) { + List result = new LinkedList<>(); + helper(root, result); + return result; + } + + private void helper(Node root, List result) { + // 1.终止条件 + if (root == null) { + return; + } + // 2.处理当前层 + List children = root.children; + if (children != null && children.size() > 0) { + for (Node child : children) { + helper(child, result); + } + } + result.add(root.val); + // 3.下探到下一层 + // 4.清理当前层 + } + +} diff --git a/Week 02/id_363/LeetCode_77_363.java b/Week 02/id_363/LeetCode_77_363.java new file mode 100644 index 000000000..0fdfef78c --- /dev/null +++ b/Week 02/id_363/LeetCode_77_363.java @@ -0,0 +1,51 @@ +package com.test.leetcode.week02; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + + +public class SolutionCombine77 { + + + @Test + public void test1() { + System.out.println(combine(4, 2)); + } + + /** + * 返回1 - n 中所有可能的k个数的组合 + * @param n + * @param k + * @return + */ + List> output = new LinkedList<>(); + int n; + int k; + + public List> combine(int n, int k) { + this.n = n; + this.k = k; + backtrace(1, new LinkedList()); + return output; + } + + private void backtrace(int first, LinkedList cur) { + // 递归终止条件 + if (cur.size() == k) { + output.add(new LinkedList<>(cur)); + return; + } + // 处理当前层 + for (int i = first; i < n + 1; i ++) { + cur.add(i); + // 处理下一层 + backtrace(i + 1, cur); + // 清理当前层 + cur.removeLast(); + + } + } +} diff --git a/Week 02/id_363/LeetCode_94_363.java b/Week 02/id_363/LeetCode_94_363.java new file mode 100644 index 000000000..4e8670647 --- /dev/null +++ b/Week 02/id_363/LeetCode_94_363.java @@ -0,0 +1,80 @@ +package com.test.leetcode.week02; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Stack; + +import static com.sun.tools.internal.xjc.reader.Ring.add; + + +public class SolutionInorderTraversal94 { + + + @Test + public void test1() { + TreeNode node1 = new TreeNode(1); + TreeNode node2 = new TreeNode(2); + TreeNode node3 = new TreeNode(3); + node1.right = node2; + node2.left = node3; + inorderTraversal_digui(node1); + System.out.println(inorderTraversal0(node1)); + System.out.println(inorderTraversal2(node1)); + } + + + public List inorderTraversal2(TreeNode root) { + List result = new LinkedList<>(); + Stack stack = new Stack(); + TreeNode cur = root; + while (cur != null || !stack.isEmpty()) { + // 处理左节点 所有的左节点入栈 + while(cur != null){ + stack.push(cur); + cur = cur.left; + } + cur = stack.pop(); + result.add(cur.val); + cur = cur.right; + } + return result; + } + + /** + * 递归:自己掉用自己 + * @param root + */ + public void inorderTraversal_digui(TreeNode root) { + if (root == null) { + return; + } + inorderTraversal_digui(root.left); + System.out.println(root.val); + inorderTraversal_digui(root.right); + } + + + public List inorderTraversal0(TreeNode root) { + List result = new LinkedList<>(); + helper(root, result); + return result; + } + + private void helper(TreeNode root, List result) { + // 1.终止条件 + if (root == null) { + return; + } + // 2.处理当前层 + helper(root.left, result); + result.add(root.val); + // 3.下探到下一层 + helper(root.right, result); + // 4.清理当前层的状态 + } + + +} diff --git a/Week 02/id_363/NOTE.md b/Week 02/id_363/NOTE.md index a6321d6e2..ce0a24c25 100644 --- a/Week 02/id_363/NOTE.md +++ b/Week 02/id_363/NOTE.md @@ -1,4 +1,84 @@ # NOTE +### 学习总结 +- 1.重要思想: 升维,空间换时间 +- 2.树的面试题的解法一般使用递归 +- 3.递归和循环的效率其实差不多,使用递归的时候注意剪枝和重复计算 +- 4.递归代码模板(重要) 递归终止条件 处理当前层逻辑 下探到下一层 清理当前层 +- 5.递归三个思维要点: 1.不要人肉递归 2.找最近重复子问题 3.数学归纳法:假设n时成立,推到n+1时成立 +- 6.分治和回溯的本质都是递归, 递归,分治,回溯都可以套用递归的模板,分治的模板需要在第三步和第四部之间加上"合并子结果" - +### java 1.8 hashMap 分析 +介绍: hashMap 实现了Map接口,允许null value 和null key,基本等同于hashTable,但是HashMap不是线程安全的。 +hashMap中get和put操作的时间复杂度是O(1). +put方法: +``` +public V put(K key, V value) { + return putVal(hash(key), key, value, false, true); +} + +// hash值的计算是: key的hash值的高低16位做异或操作 +static final int hash(Object key) { + int h; + return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); +} + +final V putVal(int hash, K key, V value, boolean onlyIfAbsent, + boolean evict) { + // tab用来存储key-value,p是当前key应该在tab中存储的下标对应的node + // n是tab数组的长度 + Node[] tab; Node p; int n, i; + // tab 初始化 + if ((tab = table) == null || (n = tab.length) == 0) + n = (tab = resize()).length; + // 下标计算:(n - 1) & hash: hash值和数组的长度减去一做与操作 + if ((p = tab[i = (n - 1) & hash]) == null) + tab[i] = newNode(hash, key, value, null); + else { + // e是p中存在和key一致的node ,k是p的key值 + Node e; K k; + // 判断p节点的key值是否和要存入的key值一致 + if (p.hash == hash && + ((k = p.key) == key || (key != null && key.equals(k)))) + e = p; + else if (p instanceof TreeNode) + // 如果p是树形结构,那么把当前节点插入到树中 + e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); + else { + // 在链表中找到key值和要存入的key一致的node,如果不存在,那么新建一个node,加入到链表的末尾 + for (int binCount = 0; ; ++binCount) { + if ((e = p.next) == null) { + p.next = newNode(hash, key, value, null); + // 如果链表长度等于TREEIFY_THRESHOLD=8, 将链表转换成树 + if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st + treeifyBin(tab, hash); + break; + } + if (e.hash == hash && + ((k = e.key) == key || (key != null && key.equals(k)))) + break; + p = e; + } + } + // 如果存在和key相同的node,将新的value值替换旧的value + if (e != null) { // existing mapping for key + V oldValue = e.value; + if (!onlyIfAbsent || oldValue == null) + e.value = value; + afterNodeAccess(e); + return oldValue; + } + } + ++modCount; + // 如果当前tab中的数量大于threshold, 进行扩容处理 + // threshold = capacity * load factor + if (++size > threshold) + resize(); + afterNodeInsertion(evict); + return null; +} + +// 链表转变成树结构 +// 扩容 + +``` \ No newline at end of file diff --git a/Week 02/id_368/LeetCode_104_368.java b/Week 02/id_368/LeetCode_104_368.java new file mode 100644 index 000000000..6b8088b6c --- /dev/null +++ b/Week 02/id_368/LeetCode_104_368.java @@ -0,0 +1,72 @@ +import java.util.LinkedList; +import java.util.Queue; + +public class LeetCode_104_368 { + + /*给定一个二叉树,找出其最大深度。 + 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 + 说明: 叶子节点是指没有子节点的节点。 + https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/public*/ + + public static void main(String[] args) { + + } + + public int maxDepth(TreeNode root) { + return depth(root, 0); + } + + private int depth(TreeNode root, int depth) { + // terminator + if (root == null) { + return depth; + } + // process current logic + depth++; + // drill down + int maxLeft = depth(root.left, depth); + int maxRight = depth(root.right, depth); + // reverse states + return Math.max(maxLeft, maxRight); + } + + public int maxDepthDFS(TreeNode root) { + if (root == null) return 0; + Queue queue = new LinkedList<>(); + Queue depth = new LinkedList<>(); + queue.add(root); + depth.add(1); + int dep = 0; + while (!queue.isEmpty()) { + TreeNode curr = queue.poll(); + int temp = depth.poll(); + dep = Math.max(temp, dep); + if (curr.left != null) { + queue.add(curr.left); + depth.add(temp + 1); + } + if (curr.right != null) { + queue.add(curr.right); + depth.add(temp + 1); + } + } + return dep; + } + + public int maxDepthBFS(TreeNode root) { + if (root == null) return 0; + Queue queue = new LinkedList<>(); + int depth = 0; + queue.offer(root); + while (!queue.isEmpty()) { + depth++; + int size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode curr = queue.poll(); + if (curr.left != null) queue.offer(curr.left); + if (curr.right != null) queue.offer(curr.right); + } + } + return depth; + } +} diff --git a/Week 02/id_368/LeetCode_105_368.java b/Week 02/id_368/LeetCode_105_368.java new file mode 100644 index 000000000..a268a264b --- /dev/null +++ b/Week 02/id_368/LeetCode_105_368.java @@ -0,0 +1,31 @@ +public class LeetCode_105_368 { + + /*根据一棵树的前序遍历与中序遍历构造二叉树。 + 注意: 你可以假设树中没有重复的元素。 + https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/*/ + + public static void main(String[] args) { + + } + + public TreeNode buildTree(int[] preorder, int[] inorder) { + if (preorder.length != inorder.length) return null; + return build(preorder, 0, inorder, 0, inorder.length - 1); + } + + private TreeNode build(int[] preorder, int preLeft, int[] inorder, int inLeft, int inRight) { + // terminator + if (inLeft > inRight) { + return null; + } + // process current logic + TreeNode root = new TreeNode(preorder[preLeft]); + int k = inLeft; + while (inorder[k] != root.val) { + k++; + } + root.left = build(preorder, preLeft + 1, inorder, inLeft, k - 1); + root.right = build(preorder, preLeft + k - inLeft + 1, inorder, k + 1, inRight); + return root; + } +} diff --git a/Week 02/id_368/LeetCode_106_368.java b/Week 02/id_368/LeetCode_106_368.java new file mode 100644 index 000000000..d1c2dffdc --- /dev/null +++ b/Week 02/id_368/LeetCode_106_368.java @@ -0,0 +1,37 @@ +/** + * @author: liuyanhui@daojia-inc.com + * @date: 2019/11/1 + */ +public class LeetCode_106_368 { + + /* + 根据一棵树的中序遍历与后序遍历构造二叉树。 + 你可以假设树中没有重复的元素。 + https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/ + */ + + public static void main(String[] args) { + + } + + public TreeNode buildTree(int[] inorder, int[] postorder) { + if (inorder.length != postorder.length) { + return null; + } + return build(inorder, 0, inorder.length - 1, postorder, postorder.length - 1); + } + + private TreeNode build(int[] inorder, int inLeft, int inRight, int[] postorder, int postRight) { + if (inLeft > inRight) { + return null; + } + int k = inLeft; + while (inorder[k] != postorder[postRight]) { + k++; + } + TreeNode root = new TreeNode(postorder[postRight]); + root.left = build(inorder, 0, k - 1, postorder, postRight - inRight + k - 1); + root.right = build(inorder, k + 1, inRight, postorder, postRight - 1); + return root; + } +} diff --git a/Week 02/id_368/LeetCode_111_368.java b/Week 02/id_368/LeetCode_111_368.java new file mode 100644 index 000000000..af4c7235d --- /dev/null +++ b/Week 02/id_368/LeetCode_111_368.java @@ -0,0 +1,39 @@ +import java.util.LinkedList; +import java.util.Queue; + +public class LeetCode_111_368 { + + /*给定一个二叉树,找出其最小深度。 + 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 + 说明: 叶子节点是指没有子节点的节点。 + https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/*/ + + public static void main(String[] args) { + + } + + public int minDepth(TreeNode root) { + if (root == null) return 0; + if (root.left == null) return minDepth(root.right) + 1; + if (root.right == null) return minDepth(root.left) + 1; + return Math.min(minDepth(root.left), minDepth(root.right)) + 1; + } + + public int minDepthBFS(TreeNode root) { + if (root == null) return 0; + int depth = 0; + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + depth++; + int size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode cur = queue.poll(); + if (cur.left == null && cur.right == null) return depth; + if (cur.left != null) queue.offer(cur.left); + if (cur.right != null) queue.offer(cur.right); + } + } + return depth; + } +} diff --git a/Week 02/id_368/LeetCode_144_368.java b/Week 02/id_368/LeetCode_144_368.java new file mode 100644 index 000000000..68fc6c175 --- /dev/null +++ b/Week 02/id_368/LeetCode_144_368.java @@ -0,0 +1,51 @@ +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +public class LeetCode_144_368 { + + /*给定一个二叉树,返回它的 前序 遍历。 + https://leetcode-cn.com/problems/binary-tree-preorder-traversal/*/ + + public static void main(String[] args) { + + } + + public static List preorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + helper(root, res); + return res; + } + + public static void helper (TreeNode root, List res) { + if (root != null) { + res.add(root.val); + if (root.left != null) { + helper(root.left, res); + } + if (root.right != null) { + helper(root.right, res); + } + } + } + + public static List preorderTraversalIterator(TreeNode root) { + List res = new ArrayList<>(); + if (root == null) { + return res; + } + LinkedList stack = new LinkedList<>(); + stack.addFirst(root); + while (!stack.isEmpty()) { + TreeNode node = stack.removeFirst(); + res.add(node.val); + if (node.right != null) { + stack.addFirst(node.right); + } + if (node.left != null) { + stack.addFirst(node.left); + } + } + return res; + } +} \ No newline at end of file diff --git a/Week 02/id_368/LeetCode_169_368.java b/Week 02/id_368/LeetCode_169_368.java new file mode 100644 index 000000000..178c01522 --- /dev/null +++ b/Week 02/id_368/LeetCode_169_368.java @@ -0,0 +1,64 @@ +import java.util.HashMap; +import java.util.Map; + +public class LeetCode_169_368 { + + /*给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。 + 你可以假设数组是非空的,并且给定的数组总是存在众数。 + https://leetcode-cn.com/problems/majority-element/*/ + + public static void main(String[] args) { + + } + + public int majorityElementIterator(int[] nums) { + HashMap map = new HashMap<>(); + int countMajor = nums.length / 2; + for (int num : nums) { + if (map.get(num) == null) { + map.put(num, 0); + } else { + map.put(num, map.get(num) + 1); + } + } + for (Map.Entry entry : map.entrySet()) { + if (entry.getKey() > countMajor) { + return entry.getValue(); + } + } + return -1; + } + + private int countInRange(int[] nums, int num, int lo, int hi) { + int count = 0; + for (int i = lo; i <= hi; i++) { + if (nums[i] == num) { + count++; + } + } + return count; + } + + private int majority(int[] nums, int lo, int hi) { + if (lo == hi) { + return nums[lo]; + } + + int mid = (hi-lo)/2 + lo; + int left = majority(nums, lo, mid); + int right = majority(nums, mid+1, hi); + + if (left == right) { + return left; + } + + int leftCount = countInRange(nums, left, lo, hi); + int rightCount = countInRange(nums, right, lo, hi); + + return leftCount > rightCount ? left : right; + } + + public int majorityElement(int[] nums) { + return majority(nums, 0, nums.length-1); + } +} diff --git a/Week 02/id_368/LeetCode_17_368.java b/Week 02/id_368/LeetCode_17_368.java new file mode 100644 index 000000000..45f04ffa0 --- /dev/null +++ b/Week 02/id_368/LeetCode_17_368.java @@ -0,0 +1,59 @@ +import java.util.*; + +public class LeetCode_17_368 { + + /*给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。 + 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 + https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/*/ + + public static void main(String[] args) { + + } + + public List letterCombinations(String digits) { + if (digits == null || digits.length() == 0) return new ArrayList(); + Map map = new HashMap<>(); + map.put('2', "abc"); + map.put('3', "def"); + map.put('4', "ghi"); + map.put('5', "jkl"); + map.put('6', "mno"); + map.put('7', "pqrs"); + map.put('8', "tuv"); + map.put('9', "wxyz"); + StringBuilder s = new StringBuilder(); + List res = new ArrayList(); + combine(map, digits, 0, res, ""); + return res; + } + + public void combine (Map map, String digits, int index, List res, String str) { + // terminator + if (index == digits.length()) { + res.add(str); + return; + } + + // process current logic + String letters = map.get(digits.charAt(index)); + for (int i = 0; i < letters.length(); i++) { + // drill down + combine(map, digits, index + 1, res, str + letters.charAt(i)); + } + } + + public List letterCombinationsIterater(String digits) { + LinkedList res = new LinkedList<>(); + if (digits == null || digits.length() == 0) return new ArrayList(); + String[] mapping = new String[] {"0", "1", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; + res.add(""); + while (res.peek().length() != digits.length()) { + String str = res.remove(); + String mapper = mapping[digits.charAt(str.length()) - '0']; + for (char c : mapper.toCharArray()) { + res.add(str + c); + } + } + return res; + } +} diff --git a/Week 02/id_368/LeetCode_226_368.java b/Week 02/id_368/LeetCode_226_368.java new file mode 100644 index 000000000..276d45383 --- /dev/null +++ b/Week 02/id_368/LeetCode_226_368.java @@ -0,0 +1,38 @@ +import java.util.LinkedList; +import java.util.Queue; + +public class LeetCode_226_368 { + + /*翻转一棵二叉树。 + https://leetcode-cn.com/problems/invert-binary-tree/*/ + + public static void main(String[] args) { + + } + + public TreeNode invertTree(TreeNode root) { + if (root == null) { + return null; + } + TreeNode left = invertTree(root.left); + TreeNode right = invertTree(root.right); + root.left = right; + root.right = left; + return root; + } + + public TreeNode invertTreeIterator(TreeNode root) { + if (root == null) return null; + Queue queue = new LinkedList<>(); + queue.add(root); + while (!queue.isEmpty()) { + TreeNode current = queue.poll(); + TreeNode temp = current.right; + current.right = current.left; + current.left = temp; + if (current.left != null) queue.add(current.left); + if (current.right != null) queue.add(current.right); + } + return root; + } +} diff --git a/Week 02/id_368/LeetCode_22_368.java b/Week 02/id_368/LeetCode_22_368.java new file mode 100644 index 000000000..6139f8f44 --- /dev/null +++ b/Week 02/id_368/LeetCode_22_368.java @@ -0,0 +1,72 @@ +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +public class LeetCode_22_368 { + + /*给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。 + https://leetcode-cn.com/problems/generate-parentheses/*/ + + public static void main(String[] args) { + LeetCode_22_368 test = new LeetCode_22_368(); + List strings = test.generateParenthesis(3); + strings.stream().forEach(s -> System.out.println(s)); + } + + public List generateParenthesis(int n) { + List res = new ArrayList<>(); + generate(0, 2 * n, "", res); + generate02(0, 0, n, "", res); + return res; + } + + private void generate(int level, int max, String s, List res) { + // terminator + if (level >= max) { + if (filter(s)) { + res.add(s); + } + return; + } + + // process current logic & drill down + generate(level + 1, max, s + "(", res); + generate(level + 1, max, s + ")", res); + + // reverse states + } + + private void generate02(int left, int right, int max, String s, List res) { + // terminator + if (left == max && right == max) { + res.add(s); + return; + } + + // process current logic & drill down + if (left < max) { + generate02(left + 1, right, max, s + "(", res); + } + if (right < left) { + generate02(left, right + 1, max, s + ")", res); + } + // reverse states + } + + private boolean filter(String s) { + LinkedList stack = new LinkedList<>(); + for (char c : s.toCharArray()) { + if ('(' == c) { + stack.addFirst(c); + } + if (')' == c) { + if (stack.isEmpty()) { + return false; + } + stack.removeFirst(); + } + } + return stack.isEmpty(); + } + +} diff --git a/Week 02/id_368/LeetCode_236_368.java b/Week 02/id_368/LeetCode_236_368.java new file mode 100644 index 000000000..c8c0e4b0a --- /dev/null +++ b/Week 02/id_368/LeetCode_236_368.java @@ -0,0 +1,18 @@ +public class LeetCode_236_368 { + + /*给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 + 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” + 链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree*/ + + public static void main(String[] args) { + + } + + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + if (root == null || root == p || root == q) return root; + TreeNode left = lowestCommonAncestor(root.left, p, q); + TreeNode right = lowestCommonAncestor(root.right, p, q); + if (right != null && left != null) return root; + return left == null ? right : left; + } +} diff --git a/Week 02/id_368/LeetCode_242_368.java b/Week 02/id_368/LeetCode_242_368.java new file mode 100644 index 000000000..7c399c040 --- /dev/null +++ b/Week 02/id_368/LeetCode_242_368.java @@ -0,0 +1,41 @@ +import java.util.Arrays; + +public class LeetCode_242_368 { + + /*给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 + https://leetcode-cn.com/problems/valid-anagram/description/*/ + + public static void main(String[] args) { + String s = "anagram"; + String t = "nagaram"; + System.out.println(isAnagram(s, t)); + } + + public static boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + char[] charS = s.toCharArray(); + char[] charT = t.toCharArray(); + Arrays.sort(charS); + Arrays.sort(charT); + return Arrays.equals(charS, charT); + } + + public static boolean isAnagram01(String s, String t) { + if (s.length() != t.length()) { + return false; + } + int[] counter = new int[26]; + for (int i = 0; i < s.length(); i++) { + counter[s.charAt(i) - 'a']++; + counter[t.charAt(i) - 'a']--; + } + for (int count : counter) { + if (count != 0) { + return false; + } + } + return true; + } +} diff --git a/Week 02/id_368/LeetCode_297_368.java b/Week 02/id_368/LeetCode_297_368.java new file mode 100644 index 000000000..745dbcf92 --- /dev/null +++ b/Week 02/id_368/LeetCode_297_368.java @@ -0,0 +1,54 @@ +import java.util.Arrays; +import java.util.LinkedList; + +public class LeetCode_297_368 { + + /*序列化是将一个数据结构或者对象转换为连续的比特位的操作, + 进而可以将转换后的数据存储在一个文件或者内存中, + 同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。 + 请设计一个算法来实现二叉树的序列化与反序列化。 + 这里不限定你的序列 / 反序列化算法执行逻辑, + 你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。 + 链接:https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree*/ + + + public static void main(String[] args) { + + } + + // Encodes a tree to a single string. + public String serialize(TreeNode root) { + StringBuilder s = new StringBuilder(); + buildString(root, s); + return s.toString(); + } + + private void buildString(TreeNode root, StringBuilder s) { + if (root == null) { + s.append("#").append(","); + } else { + s.append(root.val).append(","); + buildString(root.left, s); + buildString(root.right, s); + } + } + + // Decodes your encoded data to tree. + public TreeNode deserialize(String data) { + LinkedList nodes = new LinkedList<>(); + nodes.addAll(Arrays.asList(data.split(","))); + return buildTree(nodes); + } + + private TreeNode buildTree(LinkedList nodes) { + String s = nodes.removeFirst(); + if ("#".equals(s)) { + return null; + } else { + TreeNode node = new TreeNode(Integer.valueOf(s)); + node.left = buildTree(nodes); + node.right = buildTree(nodes); + return node; + } + } +} diff --git a/Week 02/id_368/LeetCode_429_368.java b/Week 02/id_368/LeetCode_429_368.java new file mode 100644 index 000000000..e360d0e49 --- /dev/null +++ b/Week 02/id_368/LeetCode_429_368.java @@ -0,0 +1,36 @@ +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public class LeetCode_429_368 { + + /*给定一个 N 叉树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)。 + https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/*/ + + public static void main(String[] args) { + + } + + public List> levelOrder(Node root) { + List> res = new ArrayList<>(); + if (root == null) { + return res; + } + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + List currList = new ArrayList<>(); + int len = queue.size(); + for (int i = 0; i < len; i++) { + Node node = queue.poll(); + currList.add(node.val); + for (Node no : node.children) { + queue.offer(no); + } + } + res.add(currList); + } + return res; + } +} diff --git a/Week 02/id_368/LeetCode_46_368.java b/Week 02/id_368/LeetCode_46_368.java new file mode 100644 index 000000000..525258b4f --- /dev/null +++ b/Week 02/id_368/LeetCode_46_368.java @@ -0,0 +1,63 @@ +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class LeetCode_46_368 { + + /*给定一个没有重复数字的序列,返回其所有可能的全排列。 + https://leetcode-cn.com/problems/permutations/*/ + + public static void main(String[] args) { + + } + + public List> permuteIterator(int[] nums) { + List> res = new ArrayList<>(); + if (nums == null || nums.length == 0) { + return res; + } + for (int i = 0; i < nums.length; i++) { + res.add(Arrays.asList(nums[i])); + } + for (int j = 1; j < nums.length; j++) { + List> temp = new ArrayList<>(); + for (List list : res) { + for (int k = 0; k < nums.length; k++) { + if (!list.contains(nums[k])) { + List newList = new ArrayList<>(list); + newList.add(nums[k]); + temp.add(newList); + } + } + } + res = temp; + } + return res; + } + + public List> permute(int[] nums) { + List> res = new ArrayList<>(); + if (nums == null || nums.length == 0) { + return res; + } + int[] visited = new int[nums.length]; + List row = new ArrayList<>(); + allSort(res, nums, row, visited); + return res; + } + + private void allSort(List> res, int[] nums, List row, int[] visited) { + if (row.size() == nums.length) { + res.add(new ArrayList<>(row)); + return; + } + for (int i = 0; i < nums.length; i++) { + if (visited[i] == 1) continue; + visited[i] = 1; + row.add(nums[i]); + allSort(res, nums, row, visited); + visited[i] = 0; + row.remove(row.size() - 1); + } + } +} diff --git a/Week 02/id_368/LeetCode_47_368.java b/Week 02/id_368/LeetCode_47_368.java new file mode 100644 index 000000000..6e1570ea3 --- /dev/null +++ b/Week 02/id_368/LeetCode_47_368.java @@ -0,0 +1,36 @@ +import java.util.ArrayList; +import java.util.List; + +public class LeetCode_47_368 { + + /*给定一个可包含重复数字的序列,返回所有不重复的全排列。 + https://leetcode-cn.com/problems/permutations-ii/*/ + + public static void main(String[] args) { + + } + + public List> permuteUnique(int[] nums) { + List> res = new ArrayList<>(); + if (nums == null || nums.length == 0) return res; + int[] visited = new int[nums.length]; + uniqueSort(nums, visited, res, new ArrayList()); + return res; + } + + private void uniqueSort(int[] nums, int[] visited, List> res, ArrayList currentList) { + if (currentList.size() == nums.length) { + res.add(new ArrayList<>(currentList)); + return; + } + for (int i = 0; i < nums.length; i++) { + if (visited[i] == 1) continue; + if (i > 0 && nums[i] == nums[i - 1] && visited[i - 1] == 0) continue; + visited[i] = 1; + currentList.add(nums[i]); + uniqueSort(nums, visited, res, currentList); + visited[i] = 0; + currentList.remove(currentList.size() - 1); + } + } +} diff --git a/Week 02/id_368/LeetCode_49_368.java b/Week 02/id_368/LeetCode_49_368.java new file mode 100644 index 000000000..39e611b9e --- /dev/null +++ b/Week 02/id_368/LeetCode_49_368.java @@ -0,0 +1,33 @@ +import java.util.*; + +public class LeetCode_49_368 { + + /*给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 + https://leetcode-cn.com/problems/group-anagrams/*/ + + public static void main(String[] args) { + String[] strs = {"eat", "tea", "tan", "ate", "nat", "bat"}; + List> list = groupAnagrams(strs); + list.stream().forEach(strArray -> { + strArray.stream().forEach(str-> System.out.print(str + "")); + }); + System.out.println(); + } + + public static List> groupAnagrams(String[] strs) { + if (strs == null) { + return new ArrayList(); + } + Map map = new HashMap<>(); + for (String str : strs) { + char[] array = str.toCharArray(); + Arrays.sort(array); + String s = String.valueOf(array); + if (!map.containsKey(s)) { + map.put(s, new ArrayList()); + } + map.get(s).add(str); + } + return new ArrayList(map.values()); + } +} diff --git a/Week 02/id_368/LeetCode_50_368.java b/Week 02/id_368/LeetCode_50_368.java new file mode 100644 index 000000000..112fecf5d --- /dev/null +++ b/Week 02/id_368/LeetCode_50_368.java @@ -0,0 +1,25 @@ +public class LeetCode_50_368 { + + /*实现 pow(x, n) ,即计算 x 的 n 次幂函数。 + https://leetcode-cn.com/problems/powx-n/*/ + + public static void main(String[] args) { + + } + + public double myPow(double x, int n) { + if (n < 0) { + x = 1 / x; + n = -n; + } + return pow(x, n); + } + + public double pow (double x, int n) { + if (n == 0) { + return 1.0; + } + double half = pow(x, n/2); + return n % 2 == 0 ? half * half : half * half * x; + } +} diff --git a/Week 02/id_368/LeetCode_51_368.java b/Week 02/id_368/LeetCode_51_368.java new file mode 100644 index 000000000..9240afdef --- /dev/null +++ b/Week 02/id_368/LeetCode_51_368.java @@ -0,0 +1,65 @@ +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class LeetCode_51_368 { + + /*n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。 + https://leetcode-cn.com/problems/n-queens/*/ + + public static void main(String[] args) { + + } + + private Set col; + private Set left; + private Set right; + private List> res; + + public List> solveNQueens(int n) { + res = new ArrayList<>(); + if (n == 0) return res; + col = new HashSet<>(); + left = new HashSet<>(); + right = new HashSet<>(); + solve(n, 0, new ArrayList()); + return res; + } + + public void solve(int n, int row, List current){ + if (row == n) { + res.add(cover(current)); + return; + } + for (int i = 0; i < n; i++) { + if (!col.contains(i) && !left.contains(row + i) && !right.contains(row - i)) { + current.add(i); + col.add(i); + left.add(row + i); + right.add(row - i); + solve(n, row + 1, current); + col.remove(i); + left.remove(row + i); + right.remove(row - i); + current.remove(current.size() - 1); + } + } + } + + public List cover(List current){ + List result = new ArrayList<>(); + for (int num : current) { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < current.size(); i++) { + if (num == i) { + s.append("Q"); + } else { + s.append("."); + } + } + result.add(s.toString()); + } + return result; + } +} diff --git a/Week 02/id_368/LeetCode_589_368.java b/Week 02/id_368/LeetCode_589_368.java new file mode 100644 index 000000000..35895038b --- /dev/null +++ b/Week 02/id_368/LeetCode_589_368.java @@ -0,0 +1,45 @@ +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +public class LeetCode_589_368 { + + /*给定一个 N 叉树,返回其节点值的前序遍历。 + https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/description/*/ + + public static void main(String[] args) { + + } + + public static List preorderIterator(Node root) { + List res = new ArrayList<>(); + if (root == null) { + return res; + } + LinkedList stack = new LinkedList<>(); + stack.addFirst(root); + while (!stack.isEmpty()) { + Node node = stack.removeFirst(); + res.add(node.val); + for (int i=node.children.size() - 1; i >= 0; i--) { + stack.addFirst(node.children.get(i)); + } + } + return res; + } + + public static List preorderRecursion(Node root) { + List res = new ArrayList<>(); + hepler(root, res); + return res; + } + + public static void hepler (Node root, List res) { + if (root != null) { + res.add(root.val); + for (Node node : root.children) { + hepler(node, res); + } + } + } +} diff --git a/Week 02/id_368/LeetCode_590_368.java b/Week 02/id_368/LeetCode_590_368.java new file mode 100644 index 000000000..4433ac805 --- /dev/null +++ b/Week 02/id_368/LeetCode_590_368.java @@ -0,0 +1,51 @@ +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class LeetCode_590_368 { + + /*给定一个 N 叉树,返回其节点值的后序遍历。 + https://leetcode-cn.com/problems/group-anagrams/*/ + + public static void main(String[] args) { + + } + + public static List postorder(Node root) { + List res = new ArrayList<>(); + if (root == null) { + return res; + } + LinkedList stack = new LinkedList<>(); + stack.addFirst(root); + while(!stack.isEmpty()){ + Node node = stack.removeFirst(); + res.add(node.val); + for (Node no : node.children) { + stack.addFirst(no); + } + + } + Collections.reverse(res); + return res; + } + + public static List postorderRecursion(Node root) { + List res = new ArrayList<>(); + if (root == null) { + return res; + } + helper(root, res); + return res; + } + + public static void helper (Node root, List res) { + if (root != null) { + for (Node node : root.children) { + helper(node, res); + } + res.add(root.val); + } + } +} diff --git a/Week 02/id_368/LeetCode_77_368.java b/Week 02/id_368/LeetCode_77_368.java new file mode 100644 index 000000000..189209e73 --- /dev/null +++ b/Week 02/id_368/LeetCode_77_368.java @@ -0,0 +1,58 @@ +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +public class LeetCode_77_368 { + + /*给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 + https://leetcode-cn.com/problems/combinations/*/ + + public static void main(String[] args) { + + } + + public List> combine(int n, int k) { + List> res = new ArrayList<>(); + if (n == 0 || k == 0 || n < k) { + return res; + } + LinkedList combine = new LinkedList<>(); + findCombinations(res, combine, 1, n, k); + return res; + } + + private void findCombinations(List> res, LinkedList combine, int cur, int n, int k) { + if (combine.size() == k) { + res.add(new ArrayList<>(combine)); + return; + } + for (int i = cur; i <= n - (k - combine.size()) + 1; i++) { + combine.add(i); + findCombinations(res, combine, i + 1, n, k); + combine.removeLast(); + } + } + + public List> combineIterator(int n, int k) { + List> res = new ArrayList<>(); + if (n == 0 || k == 0 || k > n) { + return res; + } + // 第一个数字 + for (int i = 1; i <= n - k + 1; i++) res.add(Arrays.asList(i)); + // 之后的数字,知道k个 + for (int j = 2; j <= k; j++) { + List> temp = new ArrayList<>(); + for (List list : res) { + for (int p = (int)list.get(list.size() - 1) + 1; p <= n - (k - j); p++) { + List newList = new ArrayList<>(list); + newList.add(p); + temp.add(newList); + } + } + res = temp; + } + return res; + } +} \ No newline at end of file diff --git a/Week 02/id_368/LeetCode_78_368.java b/Week 02/id_368/LeetCode_78_368.java new file mode 100644 index 000000000..9cbcb5456 --- /dev/null +++ b/Week 02/id_368/LeetCode_78_368.java @@ -0,0 +1,49 @@ +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class LeetCode_78_368 { + + /*给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。 + 说明:解集不能包含重复的子集。 + https://leetcode-cn.com/problems/subsets/*/ + + public static void main(String[] args) { + + } + + public List> subsetsIterator(int[] nums) { + // iterator + List> res = new ArrayList<>(); + res.add(Collections.emptyList()); + for (int i = 0; i < nums.length; i++) { + List> temp = new ArrayList<>(); + for (List list : res) { + List newList = new ArrayList<>(list); + newList.add(nums[i]); + temp.add(newList); + } + res.addAll(temp); + } + return res; + } + + public List> subsets(int[] nums) { + List> res = new ArrayList<>(); + if (nums == null || nums.length == 0) return res; + subset(res, new ArrayList(), nums, 0); + return res; + } + + private void subset(List> res, ArrayList currentList, int[] nums, int index) { + if (index == nums.length) { + res.add(new ArrayList<>(currentList)); + return; + } + // 二分法:不添加当前元素 + subset(res, currentList, nums, index + 1); + currentList.add(nums[index]); + subset(res, currentList, nums, index + 1); + currentList.remove(currentList.size() - 1); + } +} diff --git a/Week 02/id_368/LeetCode_94_368.java b/Week 02/id_368/LeetCode_94_368.java new file mode 100644 index 000000000..5d0d891c4 --- /dev/null +++ b/Week 02/id_368/LeetCode_94_368.java @@ -0,0 +1,30 @@ +import java.util.ArrayList; +import java.util.List; + +public class LeetCode_94_368 { + + /*给定一个二叉树,返回它的中序 遍历。 + https://leetcode-cn.com/problems/binary-tree-inorder-traversal/*/ + + public static void main(String[] args) { + + } + + public static List inorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + helper(root, res); + return res; + } + + public static void helper (TreeNode root, List res) { + if (root != null) { + if (root.left != null) { + helper(root.left, res); + } + res.add(root.val); + if (root.right != null) { + helper(root.right, res); + } + } + } +} diff --git a/Week 02/id_368/LeetCode_98_368.java b/Week 02/id_368/LeetCode_98_368.java new file mode 100644 index 000000000..e1871f4b8 --- /dev/null +++ b/Week 02/id_368/LeetCode_98_368.java @@ -0,0 +1,51 @@ +import java.util.ArrayList; +import java.util.List; + +public class LeetCode_98_368 { + + /*给定一个二叉树,判断其是否是一个有效的二叉搜索树。 + 假设一个二叉搜索树具有如下特征: + 节点的左子树只包含小于当前节点的数。 + 节点的右子树只包含大于当前节点的数。 + 所有左子树和右子树自身必须也是二叉搜索树。 + 链接:https://leetcode-cn.com/problems/validate-binary-search-tree*/ + + public static void main(String[] args) { + + } + + List res = new ArrayList<>(); + + public boolean isValidBST(TreeNode root) { + inOrder(root); + return check(res); + } + + public boolean isValidBST02 (TreeNode root) { + return validCheck(root, null, null); + } + + private boolean validCheck(TreeNode root, Integer minVale, Integer maxValue) { + if (root == null) return true; + if ((minVale != null && root.val <= minVale) || (maxValue != null && root.val >= maxValue)) return false; + return validCheck(root.left, minVale, root.val) && validCheck(root.right, root.val, maxValue); + } + + public boolean check (List res) { + for (int i = 1; i < res.size(); i++) { + if (res.get(i) <= res.get(i-1)) { + return false; + } + } + return true; + } + + public void inOrder (TreeNode root) { + if (root == null) { + return; + } + inOrder(root.left); + res.add(root.val); + inOrder(root.right); + } +} diff --git a/Week 02/id_368/Node.java b/Week 02/id_368/Node.java new file mode 100644 index 000000000..398a55460 --- /dev/null +++ b/Week 02/id_368/Node.java @@ -0,0 +1,13 @@ +import java.util.List; + +public class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val, List _children) { + val = _val; + children = _children; + } +} diff --git a/Week 02/id_368/TreeNode.java b/Week 02/id_368/TreeNode.java new file mode 100644 index 000000000..7013e92fa --- /dev/null +++ b/Week 02/id_368/TreeNode.java @@ -0,0 +1,8 @@ +public class TreeNode { + + int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { val = x; } + +} diff --git a/Week 02/id_373/week02_373_homework/LeetCode_144_373.cpp b/Week 02/id_373/week02_373_homework/LeetCode_144_373.cpp new file mode 100644 index 000000000..ccff7a2cb --- /dev/null +++ b/Week 02/id_373/week02_373_homework/LeetCode_144_373.cpp @@ -0,0 +1,114 @@ +#include +#include +#include +using namespace std; + +//144. ǰ +struct TreeNode { + int val; + TreeNode *left; + TreeNode *right; + + TreeNode(int x) : val(x), left(NULL), right(NULL) { } +}; + + +//vector result; +/* +˼·һݹ +ʱ临ӶȣO(n) +ȸڵ̽һȻ...... +*/ +#if 0 +vector preorderTraversal(TreeNode *root) { + if (root != NULL) { + result.push_back(root->val); + preorderTraversal(root->left); + preorderTraversal(root->right); + } + + return result; +} +#endif + +/* +˼·ʹstack +ʱ临ӶȣO(n) +Ӹڵ㿪ʼֱӱڵӣҺӷŵջ...... +*/ +#if 0 +vector preorderTraversal(TreeNode *root) { + vector result; + stack treeNodeStack; + TreeNode *cur = root; + while (cur != NULL || !treeNodeStack.empty()) { + while (cur != NULL) { + if (cur->right != NULL) { + treeNodeStack.push(cur->right); + } + result.push_back(cur->val); + cur = cur->left; + } + + if (!treeNodeStack.empty()) { //cur->right is emptytreeNodeStackΪգȻpopͳֶδ + cur = treeNodeStack.top(); + treeNodeStack.pop(); + } + } + return result; +} +#endif + +/* +˼·ʹstack +ʱ临ӶȣO(n) +ȸڵ㿪ʼ浽ջУȻȰҺӱ浽ջУڱӣȻ󵯳topҲӣظ...... +*/ +vector preorderTraversal(TreeNode *root) { + vector result; + if (root == NULL) + return result; + + stack treeNodeStack; + + TreeNode * cur = root; + treeNodeStack.push(cur); + while (!treeNodeStack.empty()) { + cur = treeNodeStack.top(); + result.push_back(cur->val); + + treeNodeStack.pop(); + + if (cur->right != NULL) { + treeNodeStack.push(cur->right); + } + + if (cur->left != NULL) { + treeNodeStack.push(cur->left); + } + } + + return result; +} + +//ĵݹ鷽ʽ + +int main() +{ + TreeNode node3(3); + TreeNode node2(2); + node2.left = &node3; + TreeNode node1(1); + node1.right = &node2; + + vector result = preorderTraversal(&node1); + + for (int item : result) + { + cout << item << endl; + } + + + while (1); + return 0; +} \ No newline at end of file diff --git a/Week 02/id_373/week02_373_homework/LeetCode_1_373.cpp b/Week 02/id_373/week02_373_homework/LeetCode_1_373.cpp new file mode 100644 index 000000000..c8bdd51c3 --- /dev/null +++ b/Week 02/id_373/week02_373_homework/LeetCode_1_373.cpp @@ -0,0 +1,101 @@ +#include +#include +#include +using namespace std; + +//1. ֮ +/* +˼·һ +ʱ临ӶȣO(n^2) +飬ҵtarget-nums[i]ֵ +*/ +#if 0 +vector twoSum(vector& nums, int target) +{ + vector result; + for (int i = 0; i < nums.size(); i++) + { + for (int j = i + 1; j < nums.size(); j++) + { + if (nums[i] == target - nums[j]) + { + result.push_back(i); + result.push_back(j); + } + } + } + + return result; +} +#endif + +/* +˼·߱mapⷨ еԪشΪmapkeyc++еmaphash mapԴһǣеԪشظʱʹˡ +ʱ临ӶȣO(n) +飬mapǷдڵkey +*/ +#if 0 +vector twoSum(vector& nums, int target) +{ + vector result; + map tempMap; + for (int i = 0; i < nums.size(); i++) + { + tempMap[nums[i]] = i; + } + + for (int i = 0; i < nums.size(); i++) + { + if (tempMap.find(target - nums[i]) != tempMap.end() && tempMap[target - nums[i]] != i) + { + result.push_back(tempMap[target - nums[i]]); + } + } + + return result; +} +#endif + +/* +˼·һmap еԪشΪmapkey +ʱ临ӶȣO(n) +飬mapǷдڵkey +*/ +vector twoSum(vector& nums, int target) +{ + unordered_map m; + vector ret; + + for (int i = 0; i < nums.size(); i++) + { + if (m.find(target - nums[i]) != m.end()) + { + ret.push_back(m[target - nums[i]]); + ret.push_back(i); + break; + } + m[nums[i]] = i; + } + return ret; +} + +/* +int main() +{ + int value[4] = {2, 7, 2, 15}; + + vector nums; + for (int i = 0; i < 4; i++) + nums.push_back(value[i]); + + vector result = twoSum(nums, 9); + for (int item : result) + { + cout << item << endl; + } + + while (1); + + return 0; +} +*/ \ No newline at end of file diff --git a/Week 02/id_373/week02_373_homework/LeetCode_242_373.cpp b/Week 02/id_373/week02_373_homework/LeetCode_242_373.cpp new file mode 100644 index 000000000..d4e5d80bb --- /dev/null +++ b/Week 02/id_373/week02_373_homework/LeetCode_242_373.cpp @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include +#include +using namespace std; + +//242. Чĸλ +/* +ȷеĵ㣺 +1СдǷ +*/ + +/* +˼· +1ʼΪַasciiֵԱȣֲԣ磺ac, bb +bool isAnagram(string s, string t) +{ + if (s.length() != t.length()) + return false; + + int sCount = 0; + int tCount = 0; + for (int i = 0; i < s.length(); i++) + { + sCount += s.at(i); + tCount += t.at(i); + } + if (sCount == tCount) + return true; + else + return false; +} +*/ + +/* +˼·һö +ʱ临ӶȣO(n^2) +һñöٵʱû뵽ڶַظֵһַеijַȻ磺"aacc", "ccac" +*/ +#if 0 +bool isAnagram(string s, string t) +{ + if (s.length() != t.length()) + return false; + + for (int i = 0; i < s.length(); i++) + { + for (int j = 0; j < t.length(); j++) + { + if (s.at(i) == t.at(j)) + { + t.replace(j, 1, ""); + break; + } + else + { + if (j == t.length() - 1) + return false; + + continue; + } + } + } + + return true; +} +#endif + +/* +˼·ö +ʱ临ӶȣO(nlogn) +ַsortһ飬ȻȽsortĽ +*/ +#if 0 +bool isAnagram(string s, string t) +{ + sort(s.begin(), s.end()); + sort(t.begin(), t.end()); + + cout << s << endl;; + cout << s.size() << endl; + cout << t << endl; + cout << t.size() << endl; + + if (!s.compare(t)) return true; + + return false; +} +#endif + +/* +˼·hashӳ +ʱ临ӶȣO(n) +sгֵַһtг֣ȽÿַڸַгֵĴ + newһ26intԪص飬ÿԪؼ¼ӦֵַĴ + һԪصIJΪ㣬Ͳanagramַ +*/ +#if 0 +bool isAnagram(string s, string t) +{ + if (s.length() != t.length()) { + return false; + } + int counter[26] = {0}; + for (int i = 0; i < s.length(); i++) { + counter[s.at(i) - 'a']++; + counter[t.at(i) - 'a']--; + } + for (int count : counter) { + if (count != 0) { + return false; + } + } + return true; +} +#endif + +bool isAnagram(string s, string t) +{ + if (s.length() != t.length()) + return false; + + int counter[26] = {0}; + for (int i = 0; i < s.length(); i++) + { + counter[s.at(i) - 'a']++; + } + for (int i = 0; i < t.length(); i++) + { + int count = counter[t.at(i) - 'a']--; + if(count < 0) + return false; + } + + return true; +} + + +/* +int main() +{ + string s, t; + s = "anagram"; + t = "nagaram"; + //isAnagram(s, t); + + if (isAnagram(s, t)) + printf("yes"); + else + printf("no"); + + while (1); + + return 0; +} +*/ + diff --git a/Week 02/id_373/week02_373_homework/LeetCode_49_373.cpp b/Week 02/id_373/week02_373_homework/LeetCode_49_373.cpp new file mode 100644 index 000000000..0070760fa --- /dev/null +++ b/Week 02/id_373/week02_373_homework/LeetCode_49_373.cpp @@ -0,0 +1,168 @@ +#include +#include +#include +#include +#include +#include +using namespace std; + +//49. һַ飬ĸλһĸλָĸͬвַͬ + +//ĽؼǣijһַеijһĸֵƵַĶӦĸijƵͬ +//λòͬˣǵķǿԺλãÿֵַƵ + +/* +˼·һʹmapsort +ʱ临ӶȣO(Knlogn) Kַ +ʹmapÿһsortstringkey飬һַsortĽmapkeyѾڣ + 뵽listȥڵĻһµkey +*/ +#if 0 +vector> groupAnagrams(vector& strs) +{ + map> strMap; + vector> resultVec; + + if (0 == strs.size()) + return resultVec; + //vector strSrc = strs; //sortķֵvoidcopyһsrc vectorĽʹʱͲcopyһݡ + for (int i = 0; i < strs.size(); i++) + { + string temp = strs[i]; + sort(strs[i].begin(), strs[i].end()); + + if(strMap.find(strs[i]) == strMap.end()) + { + strMap[strs[i]] = vector(); + } + + strMap[strs[i]].push_back(temp); + } + + for (pair> item : strMap) + resultVec.push_back(item.second); + + /*map>::iterator strMap_iter = strMap.begin(); + for (; strMap_iter != strMap.end(); strMap_iter++) + { + resultVec.push_back(strMap_iter->second); + }*/ + + return resultVec; +} +#endif + +//滻汾 +#if 0 +vector> groupAnagrams(vector& strs) { + + map> strMap; + vector> resultVec; + + if (0 == strs.size()) + return resultVec; + + for (int i = 0; i < strs.size(); i++) + { + string temp = strs[i]; + sort(strs[i].begin(), strs[i].end()); + + strMap[strs[i]].push_back(temp); + } + + for (pair> item : strMap) + resultVec.push_back(item.second); + + return resultVec; + +} +#endif + +//leetcode ϲο +#if 0 +vector> groupAnagrams(vector& strs) +{ + map > ma; + vector> res; + for (auto str : strs) + { + string tmp = str; + sort(tmp.begin(), tmp.end()); + ma[tmp].push_back(str); + } + for (const auto& m : ma) + res.push_back(m.second); + return res; +} +#endif + +/* +˼·ʹmap +ʱ临ӶȣO(Kn) Kַ +ͳÿַÿַгֵƵ26ĸcountȻcountתstringΪmapĿԣȻvector + һַcount stringmapеkeyУͰӵµkey +*/ +#if 1 +vector> groupAnagrams(vector& strs) +{ + vector> resultVec; + map> strMap; + if (0 == strs.size()) + return resultVec; + + for (string item : strs) + { + stringstream s; + int count[26] = { 0 }; + for (int i = 0; i < item.length(); i++) + { + count[item.at(i) - 'a'] ++; + } + + for (int i = 0; i < 26; i++) + { + s << count[i]; + } + + /* + if (strMap.find(s.str()) == strMap.end()) + strMap[s.str()] = vector(); + */ + + strMap[s.str()].push_back(item); + } + + for (pair> item : strMap) + resultVec.push_back(item.second); + + /* + map>::iterator map_iter = strMap.begin(); + for (; map_iter != strMap.end(); map_iter++) + { + resultVec.push_back(map_iter->second); + } + */ + + return resultVec; +} +#endif + +/* +int main() +{ + + string strs[6] = {"are","bat", "ear", "code", "tab", "era"}; + vector strV; + cout << strs->size() << endl; + for (int i = 0; i < 6; i++) + { + strV.push_back(strs[i]); + } + cout << strV.size() << endl; + + vector> result = groupAnagrams(strV); + + while (1); + return 0; +} +*/ \ No newline at end of file diff --git a/Week 02/id_373/week02_373_homework/LeetCode_94_373.cpp b/Week 02/id_373/week02_373_homework/LeetCode_94_373.cpp new file mode 100644 index 000000000..8b9b6da1e --- /dev/null +++ b/Week 02/id_373/week02_373_homework/LeetCode_94_373.cpp @@ -0,0 +1,62 @@ +#include +#include +#include +using namespace std; + +//94. +struct TreeNode { + int val; + TreeNode * left; + TreeNode * right; + TreeNode(int x) : val(x), left(NULL), right(NULL) {} +}; +vector result; + +/* +˼·һݹ +ʱ临ӶȣO(n) +һһ̽Ҷӽڵʱ򷵻һaddrootڵ㣬Ȼһһ̽...... +*/ + +#if 0 +vector inorderTraversal(TreeNode* root) { + if (root != NULL) { + inorderTraversal(root->left); + result.push_back(root->val); + inorderTraversal(root->right); + } + + return result; +} +#endif + +/* +˼·ʹջ +ʱ临ӶȣO(n) +ÿһ̽ҶӽڵͰѸĽڵ㵯Ȼڲ....... +*/ +vector inorderTraversal(TreeNode* root) { + vector result; + stack treeStack; + TreeNode * cur = root; + while (cur != NULL || !treeStack.empty()) { + while (cur != NULL) { + treeStack.push(cur); + cur = cur->left; + } + + result.push_back(treeStack.top()->val); //44д50дһһһĻϵ + cur = treeStack.top()->right; //Բcur != NULL,treeStack is emptyĹϵDztreeStackΪջpopIJ + treeStack.pop(); + } + + return result; +} + +/* +int main() +{ + + return 0; +} +*/ \ No newline at end of file diff --git a/Week 02/id_383/LeetCode_1_383.java b/Week 02/id_383/LeetCode_1_383.java new file mode 100644 index 000000000..30bfbee4d --- /dev/null +++ b/Week 02/id_383/LeetCode_1_383.java @@ -0,0 +1,35 @@ +import java.util.HashMap; +import java.util.Map; + +public class LeetCode_1_383 { + + public int[] twoSum1(int[] nums, int target) { + int len = nums.length; + for (int i = 0, j = len - 1; i < j; i++) { + for (int k = i + 1; k < len; k++) { + if ((nums[i] + nums[k]) == target) { + return new int[] {i, k}; + } + } + } + return new int[] {}; + } + + public int[] twoSum2(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0, len = nums.length; i < len; i++) { + map.put(nums[i], i); + } + for (int i = 0,len = nums.length - 1; i < len; i++) { + int num = target - nums[i]; + if (map.containsKey(num)) { + int index = (int)map.get(num); + if (i != index) { + return new int[] {i, index}; + } + } + } + return new int[] {}; + } + +} \ No newline at end of file diff --git a/Week 02/id_383/LeetCode_242_383.java b/Week 02/id_383/LeetCode_242_383.java new file mode 100644 index 000000000..42509288f --- /dev/null +++ b/Week 02/id_383/LeetCode_242_383.java @@ -0,0 +1,86 @@ +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class LeetCode_242_383 { + + /** + * 解法一:将两个字符串排序,排序后如果相等即是异位词 + * @param s + * @param t + * @return + */ + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + char[] chars1 = s.toCharArray(); + char[] chars2 = t.toCharArray(); + Arrays.sort(chars1); + Arrays.sort(chars2); + return Arrays.equals(chars1, chars2); + } + + /** + * 解法二:将遍历s,用map统计字符出现的次数,然后遍历t,每遍历一个字符就将map中对应的统计数减1, + * 最后map为空就代表s和t是异位词 + * @param s + * @param t + * @return + */ + public boolean isAnagram2(String s, String t) { + if (s.length() != t.length()) { + return false; + } + Map map = new HashMap(); + for (int i = 0, len = s.length(); i < len; i++) { + char c = s.charAt(i); + if (!map.containsKey(c)) { + map.put(c, 0); + } + map.put(c, (int)map.get(c) + 1); + } + for (int i = 0, len = t.length(); i < len; i++) { + char c = t.charAt(i); + if (!map.containsKey(c)) { + return false; + } + int count = (int) map.get(c); + count -= 1; + if (count == 0) { + map.remove(c); + } else { + map.put(c, count); + } + } + return map.isEmpty(); + } + + + public boolean isAnagram3(String s, String t) { + if (s.length() != t.length()) { + return false; + } + int[] counter = new int[26]; + for (int i = 0, len = s.length(); i < len; i++) { + counter[s.charAt(i) - 'a']++; + } + for (int i = 0, len = t.length(); i < len; i++) { + counter[t.charAt(i) - 'a']--; + if (counter[t.charAt(i) - 'a'] < 0) { + return false; + } + } + return true; + } + + public boolean isAnagram4(String s, String t) { + if (s.length() != t.length()) return false; + int[] alphabet = new int[26]; + for (int i = 0; i < s.length(); i++) alphabet[s.charAt(i) - 'a']++; + for (int i = 0; i < t.length(); i++) alphabet[t.charAt(i) - 'a']--; + for (int i : alphabet) if (i != 0) return false; + return true; + } + +} \ No newline at end of file diff --git a/Week 02/id_383/LeetCode_49_383.java b/Week 02/id_383/LeetCode_49_383.java new file mode 100644 index 000000000..968771db0 --- /dev/null +++ b/Week 02/id_383/LeetCode_49_383.java @@ -0,0 +1,25 @@ +import java.util.*; + +public class LeetCode_49_383 { + + /** + * 将字符串排序,排序后的字符串作为key放入map中,相同key的字符串以链表的形式存储在map中 + * @param strs + * @return + */ + public List> groupAnagrams(String[] strs) { + if (strs == null || strs.length == 0) return new ArrayList<>(); + Map> map = new HashMap<>(); + for (String str : strs) { + char[] chars = str.toCharArray(); + Arrays.sort(chars); + String key = Arrays.toString(chars); + if (!map.containsKey(key)) { + map.put(key, new LinkedList<>()); + } + map.get(key).add(str); + } + return new ArrayList<>(map.values()); + } + +} \ No newline at end of file diff --git a/Week 02/id_383/LeetCode_94_383.java b/Week 02/id_383/LeetCode_94_383.java new file mode 100644 index 000000000..9b47ca8f1 --- /dev/null +++ b/Week 02/id_383/LeetCode_94_383.java @@ -0,0 +1,25 @@ +import java.util.LinkedList; +import java.util.List; + +public class LeetCode_94_383 { + + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + } + + public List inorderTraversal(TreeNode root) { + List result = new LinkedList<>(); + traversing(root, result); + return result; + } + + public void traversing(TreeNode node, List result) { + if (node == null) return; + if (node.left != null) traversing(node.left, result); + result.add(node.val); + if (node.right != null) traversing(node.right, result); + } + +} diff --git a/Week 02/id_388/LeeCode_104_388.java b/Week 02/id_388/LeeCode_104_388.java new file mode 100644 index 000000000..cd1a72914 --- /dev/null +++ b/Week 02/id_388/LeeCode_104_388.java @@ -0,0 +1,48 @@ +package com.company.leetcode.editor.cn;//给定一个二叉树,找出其最大深度。 +// +// 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 +// +// 说明: 叶子节点是指没有子节点的节点。 +// +// 示例: +//给定二叉树 [3,9,20,null,null,15,7], +// +// 3 +// / \ +// 9 20 +// / \ +// 15 7 +// +// 返回它的最大深度 3 。 +// Related Topics 树 深度优先搜索 + + + +//leetcode submit region begin(Prohibit modification and deletion) +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class TreeNode_104 { + int val; + TreeNode_104 left; + TreeNode_104 right; + TreeNode_104(int x) { val = x; } +} +class Solution_104 { + public int maxDepth(TreeNode_104 root) { + return helper(root,0); + } + + private int helper(TreeNode_104 root, int depth) { + if (root == null) { return depth; } + depth++; + return Math.max(helper(root.left,depth),helper(root.right,depth)); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_105_388.java b/Week 02/id_388/LeeCode_105_388.java new file mode 100644 index 000000000..edc1db27f --- /dev/null +++ b/Week 02/id_388/LeeCode_105_388.java @@ -0,0 +1,72 @@ +package com.company.leetcode.editor.cn; +//根据一棵树的前序遍历与中序遍历构造二叉树。 +// +// 注意: +//你可以假设树中没有重复的元素。 +// +// 例如,给出 +// +// 前序遍历 preorder = [3,9,20,15,7] +//中序遍历 inorder = [9,3,15,20,7] +// +// 返回如下的二叉树: +// +// 3 +// / \ +// 9 20 +// / \ +// 15 7 +// Related Topics 树 深度优先搜索 数组 + + + +//leetcode submit region begin(Prohibit modification and deletion) + +import java.util.HashMap; +import java.util.Map; + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + +class TreeNode_105 { + int val; + TreeNode_105 left; + TreeNode_105 right; + TreeNode_105(int x) { val = x; } +} +class Solution_105 { + private int preIndex = 0; + private Map valueMap; + + public TreeNode_105 buildTree(int[] preorder, int[] inorder) { + preIndex = 0; + valueMap = new HashMap<>(); + for (int i = 0; i < inorder.length; i++) { + valueMap.put(inorder[i],i); + } + + return helper(0,preorder.length,preorder); + } + + private TreeNode_105 helper(int startIndex, int endIndex, int[] preorder) { + if (startIndex >= endIndex) { + return null; + } + + TreeNode_105 root = new TreeNode_105(preorder[preIndex]); + preIndex++; + int mid = valueMap.get(root.val).intValue(); + root.left = helper(startIndex,mid,preorder); + root.right = helper(mid + 1,endIndex,preorder); + return root; + } + +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_111_388.java b/Week 02/id_388/LeeCode_111_388.java new file mode 100644 index 000000000..f7bad11d7 --- /dev/null +++ b/Week 02/id_388/LeeCode_111_388.java @@ -0,0 +1,61 @@ +package com.company.leetcode.editor.cn;//给定一个二叉树,找出其最小深度。 +// +// 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 +// +// 说明: 叶子节点是指没有子节点的节点。 +// +// 示例: +// +// 给定二叉树 [3,9,20,null,null,15,7], +// +// 3 +// / \ +// 9 20 +// / \ +// 15 7 +// +// 返回它的最小深度 2. +// Related Topics 树 深度优先搜索 广度优先搜索 + + + +//leetcode submit region begin(Prohibit modification and deletion) +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class TreeNode_111 { + int val; + TreeNode_111 left; + TreeNode_111 right; + TreeNode_111(int x) { val = x; } +} +class Solution_111 { + public int minDepth(TreeNode_111 root) { + return helper(root,0); + } + + //递归法 + private int helper(TreeNode_111 root, int depth) { + if (root == null) { + return depth; + } + depth++; + + if (root.left == null) { + return helper(root.right,depth); + } + + if (root.right == null) { + return helper(root.left,depth); + } + + return Math.min(helper(root.left,depth),helper(root.right,depth)); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_144_388.java b/Week 02/id_388/LeeCode_144_388.java new file mode 100644 index 000000000..38d012ee7 --- /dev/null +++ b/Week 02/id_388/LeeCode_144_388.java @@ -0,0 +1,58 @@ +package com.company.leetcode.editor.cn;//给定一个二叉树,返回它的 前序 遍历。 +// +// 示例: +// +// 输入: [1,null,2,3] +// 1 +// \ +// 2 +// / +// 3 +// +//输出: [1,2,3] +// +// +// 进阶: 递归算法很简单,你可以通过迭代算法完成吗? +// Related Topics 栈 树 + + + +//leetcode submit region begin(Prohibit modification and deletion) + +import java.util.ArrayList; +import java.util.List; + +/** + * Definition for a binary tree node. + * + */ +class TreeNode_144 { + int val; + TreeNode_144 left; + TreeNode_144 right; + TreeNode_144(int x) { val = x; } +} +class Solution_144 { + + List res; + public List preorderTraversal(TreeNode_144 root) { + res = new ArrayList<>(); + helper(root); + return res; + } + + //按模板编写 + private void helper(TreeNode_144 node){ + if (node == null) { + return; + } + res.add(node.val); + if (node.left != null) { + helper(node.left); + } + if (node.right != null) { + helper(node.right); + } + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_169_388.java b/Week 02/id_388/LeeCode_169_388.java new file mode 100644 index 000000000..88799cc44 --- /dev/null +++ b/Week 02/id_388/LeeCode_169_388.java @@ -0,0 +1,60 @@ +package com.company.leetcode.editor.cn; +//给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。 +// +// 你可以假设数组是非空的,并且给定的数组总是存在众数。 +// +// 示例 1: +// +// 输入: [3,2,3] +//输出: 3 +// +// 示例 2: +// +// 输入: [2,2,1,1,1,2,2] +//输出: 2 +// +// Related Topics 位运算 数组 分治算法 + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_169 { + + //暴力求解 + public int majorityElement(int[] nums) { + int n = nums.length / 2; + List arr = new ArrayList<>(); + for (int i = 0; i < nums.length; i++) { + arr.add(nums[i]); + } + Collections.sort(arr); + int count = 1; + int currentValue = (int) arr.get(0); + for (int i = 1; i < arr.size(); i++) { + + if (count > n) { + return currentValue; + } + + if (arr.get(i) == currentValue) { + count++; + continue; + } + + currentValue = arr.get(i); + count = 1; + } + return currentValue; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// int val = s.majorityElement(new int[]{-1,1,1,1,2,1}); +// System.out.println(val); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_17_388.java b/Week 02/id_388/LeeCode_17_388.java new file mode 100644 index 000000000..28cd062c0 --- /dev/null +++ b/Week 02/id_388/LeeCode_17_388.java @@ -0,0 +1,67 @@ +package com.company.leetcode.editor.cn; +//给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。 +// +// 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 +// +// +// +// 示例: +// +// 输入:"23" +//输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]. +// +// +// 说明: +//尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。 +// Related Topics 字符串 回溯算法 + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_51 { + public List letterCombinations(String digits) { + + List res = new ArrayList<>(); + + if (digits.length() == 0) { + return res; + } + + Map numsMap = new HashMap<>(); + numsMap.put('2',"abc"); + numsMap.put('3',"def"); + numsMap.put('4',"ghi"); + numsMap.put('5',"jkl"); + numsMap.put('6',"mno"); + numsMap.put('7',"pqrs"); + numsMap.put('8',"tuv"); + numsMap.put('9',"wxyz"); + + helper(digits,res,numsMap, 0,""); + return res; + } + + private void helper(String digits, List res, Map numsMap,int index,String s ) { + + if (index == digits.length()) { + res.add(s); + return; + } + String letters = numsMap.get(digits.charAt(index)); + for (int i = 0; i < letters.length(); i++) { + helper(digits,res,numsMap,index + 1,s + letters.charAt(i)); + } + + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// List res = s.letterCombinations("23"); +// System.out.println(res.toString()); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_1_388.java b/Week 02/id_388/LeeCode_1_388.java new file mode 100644 index 000000000..6e0c4fc52 --- /dev/null +++ b/Week 02/id_388/LeeCode_1_388.java @@ -0,0 +1,22 @@ +import java.util.HashMap; +import java.util.Map; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_1 { + public int[] twoSum(int[] nums, int target) { + Map cacheMap = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + + if (cacheMap.containsKey(nums[i])){ + int[] res = new int[]{cacheMap.get(nums[i]),i}; + return res; + } + Integer key = target - nums[i]; + cacheMap.put(key,i); + } + int[] res = new int[0]; + return res; + } + +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_20_388.java b/Week 02/id_388/LeeCode_20_388.java new file mode 100644 index 000000000..a1e61e5a9 --- /dev/null +++ b/Week 02/id_388/LeeCode_20_388.java @@ -0,0 +1,78 @@ +//给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。 +// +// 有效字符串需满足: +// +// +// 左括号必须用相同类型的右括号闭合。 +// 左括号必须以正确的顺序闭合。 +// +// +// 注意空字符串可被认为是有效字符串。 +// +// 示例 1: +// +// 输入: "()" +//输出: true +// +// +// 示例 2: +// +// 输入: "()[]{}" +//输出: true +// +// +// 示例 3: +// +// 输入: "(]" +//输出: false +// +// +// 示例 4: +// +// 输入: "([)]" +//输出: false +// +// +// 示例 5: +// +// 输入: "{[]}" +//输出: true +// Related Topics 栈 字符串 + + +import java.util.Map; +import java.util.HashMap; +import java.util.Stack; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_20 { + public boolean isValid(String s) { + Stack stack = new Stack<>(); + Map tmp = new HashMap<>(); + //'(',')','{','}','[',']' + tmp.put('(',')'); + tmp.put('{','}'); + tmp.put('[',']'); + + //放入stack + for (int i = s.length() - 1; i >= 0; i--) { + Character current = s.charAt(i); + + if (current == ' '){ + continue; + } + + //是否与栈顶的成一对,是则弹出 + Character right = tmp.get(current); + if (stack.size() > 0 && right == stack.peek()) { + stack.pop(); + continue; + } + + stack.add(current); + } + return stack.empty(); + + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_226_388.java b/Week 02/id_388/LeeCode_226_388.java new file mode 100644 index 000000000..9056ef665 --- /dev/null +++ b/Week 02/id_388/LeeCode_226_388.java @@ -0,0 +1,70 @@ +package com.company.leetcode.editor.cn;//翻转一棵二叉树。 +// +// 示例: +// +// 输入: +// +// 4 +// / \ +// 2 7 +// / \ / \ +//1 3 6 9 +// +// 输出: +// +// 4 +// / \ +// 7 2 +// / \ / \ +//9 6 3 1 +// +// 备注: +//这个问题是受到 Max Howell 的 原问题 启发的 : +// +// 谷歌:我们90%的工程师使用您编写的软件(Homebrew),但是您却无法在面试时在白板上写出翻转二叉树这道题,这太糟糕了。 +// Related Topics 树 + + + +//leetcode submit region begin(Prohibit modification and deletion) +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class TreeNode_226 { + int val; + TreeNode_226 left; + TreeNode_226 right; + TreeNode_226(int x) { val = x; } +} +class Solution_226 { + public TreeNode_226 invertTree(TreeNode_226 root) { + if (root == null) { + return null; + } + helper(root,root.left,root.right); + return root; + } + + private void helper(TreeNode_226 root, TreeNode_226 left, TreeNode_226 right) { + + if (root == null) { + return; + } + root.left = right; + root.right = left; + if (left != null) { + helper(left,left.left,left.right); + } + if (right != null) { + helper(right,right.left,right.right); + } + + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_22_388.java b/Week 02/id_388/LeeCode_22_388.java new file mode 100644 index 000000000..a4ef6b89b --- /dev/null +++ b/Week 02/id_388/LeeCode_22_388.java @@ -0,0 +1,46 @@ +package com.company.leetcode.editor.cn; +//给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。 +// +// 例如,给出 n = 3,生成结果为: +// +// [ +// "((()))", +// "(()())", +// "(())()", +// "()(())", +// "()()()" +//] +// +// Related Topics 字符串 回溯算法 + + +import java.util.ArrayList; +import java.util.List; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_22 { + private List res; + public List generateParenthesis(int n) { + res = new ArrayList(); + helper(0,0,n,""); + + return res; + } + + private void helper(int left,int right,int n,String s) { + if (left == n && right == n) { + res.add(s); + return; + } + + if (left < n) { + helper(left + 1,right,n,s + "("); + } + if (right < left) { + helper(left,right + 1,n,s + ")"); + } + + } + +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_236_388.java b/Week 02/id_388/LeeCode_236_388.java new file mode 100644 index 000000000..cae565ee1 --- /dev/null +++ b/Week 02/id_388/LeeCode_236_388.java @@ -0,0 +1,80 @@ +package com.company.leetcode.editor.cn; +//给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 +// +// 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” +// +// 例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4] +// +// +// +// +// +// 示例 1: +// +// 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 +//输出: 3 +//解释: 节点 5 和节点 1 的最近公共祖先是节点 3。 +// +// +// 示例 2: +// +// 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4 +//输出: 5 +//解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。 +// +// +// +// +// 说明: +// +// +// 所有节点的值都是唯一的。 +// p、q 为不同节点且均存在于给定的二叉树中。 +// +// Related Topics 树 + + + +//leetcode submit region begin(Prohibit modification and deletion) +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class TreeNode_236 { + int val; + TreeNode_236 left; + TreeNode_236 right; + TreeNode_236(int x) { val = x; } +} +class Solution_236 { + private TreeNode_236 ans; + public TreeNode_236 lowestCommonAncestor(TreeNode_236 root, TreeNode_236 p, TreeNode_236 q) { + helper(root,p,q); + return ans; + } + + private boolean helper(TreeNode_236 root, TreeNode_236 p, TreeNode_236 q) { + + if (root == null) { + return false; + } + + int mid = 0; + if (root == p || root == q) { + mid = 1; + } + int left = helper(root.left,p,q) ? 1:0; + int right = helper(root.right,p,q) ? 1:0; + + if (mid + left + right >= 2) { + ans = root; + } + return (mid + left + right) > 0; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_242_388.java b/Week 02/id_388/LeeCode_242_388.java new file mode 100644 index 000000000..0ca7ed175 --- /dev/null +++ b/Week 02/id_388/LeeCode_242_388.java @@ -0,0 +1,80 @@ +package com.company.leetcode.editor.cn;//给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 +// +// 示例 1: +// +// 输入: s = "anagram", t = "nagaram" +//输出: true +// +// +// 示例 2: +// +// 输入: s = "rat", t = "car" +//输出: false +// +// 说明: +//你可以假设字符串只包含小写字母。 +// +// 进阶: +//如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况? +// Related Topics 排序 哈希表 + + +import java.util.HashMap; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_242 { + + //数组实现 + public boolean isAnagram(String s,String t) { + if (s.length() != t.length()) { + return false; + } + + int[] counter = new int[26]; + for (int i = 0; i < s.length(); i++) { + counter[s.charAt(i) - 'a']++; + } + for (int i = 0; i < t.length(); i++) { + counter[t.charAt(i) - 'a']--; + } + for (int i = 0; i < counter.length; i++) { + if (counter[i] != 0) { + return false; + } + } + return true; + } + + //hash表 + public boolean isAnagram1(String s, String t) { + + HashMap countMap = new HashMap(); + + for (int i = 0; i < s.length(); i++) { + Character current = s.charAt(i); + if (countMap.containsKey(current)) { + Integer count = countMap.get(current) + 1; + countMap.put(current,count); + continue; + } + countMap.put(current,1); + } + + for (int i = 0; i < t.length(); i++) { + Character current = t.charAt(i); + if (countMap.containsKey(current)) { + Integer count = countMap.get(current) - 1; + if (count == 0){ + countMap.remove(current); + continue; + } + countMap.put(current,count); + continue; + } + return false; + } + + return countMap.size() == 0; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_297_388.java b/Week 02/id_388/LeeCode_297_388.java new file mode 100644 index 000000000..585503b7b --- /dev/null +++ b/Week 02/id_388/LeeCode_297_388.java @@ -0,0 +1,90 @@ +package com.company.leetcode.editor.cn;//序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。 +// +// 请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。 +// +// 示例: +// +// 你可以将以下二叉树: +// +// 1 +// / \ +// 2 3 +// / \ +// 4 5 +// +//序列化为 "[1,2,3,null,null,4,5]" +// +// 提示: 这与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。 +// +// 说明: 不要使用类的成员 / 全局 / 静态变量来存储状态,你的序列化和反序列化算法应该是无状态的。 +// Related Topics 树 设计 + + + +//leetcode submit region begin(Prohibit modification and deletion) + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.lang.*; + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class TreeNode_297 { + int val; + TreeNode_297 left; + TreeNode_297 right; + TreeNode_297(int x) { val = x; } +} +class Codec_297 { + + // Encodes a tree to a single string. + + public String serialize(TreeNode_297 root) { + return helperSerialize(root,""); + } + + private String helperSerialize(TreeNode_297 root, String s) { + if (root == null) { + s += "null,"; + return s; + } + s += helperSerialize(root.left,s); + s += helperSerialize(root.right,s); + return s; + } + + // Decodes your encoded data to tree. + public TreeNode_297 deserialize(String data) { + String[] res = data.split(","); + List list = new LinkedList<>(Arrays.asList(res)); + return helperDeserialize(list); + } + + private TreeNode_297 helperDeserialize(List list) { + + if (list.get(0).equals("null")) { + list.remove(0); + return null; + } + TreeNode_297 root = new TreeNode_297(Integer.valueOf(list.get(0))); + list.remove(0); + root.left = helperDeserialize(list); + root.right = helperDeserialize(list); + return root; + } + + +} + +// Your Codec object will be instantiated and called as such: +// Codec codec = new Codec(); +// codec.deserialize(codec.serialize(root)); +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_429_388.java b/Week 02/id_388/LeeCode_429_388.java new file mode 100644 index 000000000..ba93fa5ea --- /dev/null +++ b/Week 02/id_388/LeeCode_429_388.java @@ -0,0 +1,80 @@ +package com.company.leetcode.editor.cn;//给定一个 N 叉树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)。 +// +// 例如,给定一个 3叉树 : +// +// +// +// +// +// +// +// 返回其层序遍历: +// +// [ +// [1], +// [3,2,4], +// [5,6] +//] +// +// +// +// +// 说明: +// +// +// 树的深度不会超过 1000。 +// 树的节点总数不会超过 5000。 +// Related Topics 树 广度优先搜索 + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +//leetcode submit region begin(Prohibit modification and deletion) +/* +// Definition for a Node_429. + +*/ +class Node_429 { + public int val; + public List children; + + public Node_429() {} + + public Node_429(int _val,List _children) { + val = _val; + children = _children; + } +} +class Solution_429 { + List> res = new ArrayList<>(); + public List> levelOrder(Node_429 root) { + //map key记录层级 + helper(root,0); + return res; + } + private void helper(Node_429 Node_429,int index){ + if (Node_429 == null) { + return; + } + List tmp = null; + if (res.size() < (index + 1)) { + tmp = new ArrayList(); + res.add(tmp); + }else { + tmp = res.get(index); + } + tmp.add(Node_429.val); + //children + if (Node_429.children == null || Node_429.children.size() == 0){ + return; + } + for (int i = 0; i < Node_429.children.size(); i++) { + helper(Node_429.children.get(i),index+1); + } + + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_42_388.java b/Week 02/id_388/LeeCode_42_388.java new file mode 100644 index 000000000..f45c7fd23 --- /dev/null +++ b/Week 02/id_388/LeeCode_42_388.java @@ -0,0 +1,55 @@ +package com.company.leetcode.editor.cn; +//给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 +// +// +// +// 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。 +// +// 示例: +// +// 输入: [0,1,0,2,1,0,1,3,2,1,2,1] +//输出: 6 +// Related Topics 栈 数组 双指针 + + +import java.util.Stack; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_42 { + + private Stack stack = new Stack(); + + public int trap(int[] height) { + + if (height.length == 0) { + return 0; + } + int area = 0; + int current = 0; + while (current < height.length) { + + while (!stack.isEmpty() && height[current] > height[stack.peek()]) { + int top = stack.pop(); + if (stack.isEmpty()){ + break; + } + + int distance = current - stack.peek() - 1; + int h = Math.min(height[current],height[stack.peek()]) - height[top]; + area += distance * h; + } + + stack.push(current++); + } + + return area; + } + + public static void main(String[] args) { + Solution_42 s = new Solution_42(); + int area = s.trap(new int[]{0,1,0,2,1,0,1,3,2,1,2,1}); +// int area = s.trap(new int[]{4,2,3}); + System.out.println(area); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_46_388.java b/Week 02/id_388/LeeCode_46_388.java new file mode 100644 index 000000000..251a1ba1f --- /dev/null +++ b/Week 02/id_388/LeeCode_46_388.java @@ -0,0 +1,67 @@ +package com.company.leetcode.editor.cn;//给定一个没有重复数字的序列,返回其所有可能的全排列。 +// +// 示例: +// +// 输入: [1,2,3] +//输出: +//[ +// [1,2,3], +// [1,3,2], +// [2,1,3], +// [2,3,1], +// [3,1,2], +// [3,2,1] +//] +// Related Topics 回溯算法 + + +import java.util.*; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_46 { + List> outPut = new LinkedList(); + private int[] nums; + public List> permute(int[] nums) { + this.nums = nums; + LinkedList curr = new LinkedList(); + LinkedList left = new LinkedList(); + for (int i = 0; i < nums.length; i++) { + left.add(nums[i]); + } + + helper(curr,left); + return outPut; + } + + private void helper(LinkedList curr,LinkedList left) { + if (curr.size() == nums.length) { + LinkedList tmp1 = new LinkedList(); + for (int i = 0; i < curr.size(); i++) { + tmp1.add(curr.get(i)); + } + outPut.add(tmp1); + return; + } + + for (int j = 0; j < left.size(); j++) { + int value = (int) left.get(j); + curr.add(value); + LinkedList tmp = new LinkedList(); + for (int i = 0; i < left.size(); i++) { + tmp.add(left.get(i)); + } + tmp.remove(j); + helper(curr,tmp); + curr.removeLast(); + } + + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// List res = s.permute(new int[]{1,2,3,4}); +// System.out.println(res.toString()); +// } + +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_49_388.java b/Week 02/id_388/LeeCode_49_388.java new file mode 100644 index 000000000..6191882cf --- /dev/null +++ b/Week 02/id_388/LeeCode_49_388.java @@ -0,0 +1,90 @@ +package com.company.leetcode.editor.cn; +//给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 +// +// 示例: +// +// 输入: ["eat", "tea", "tan", "ate", "nat", "bat"], +//输出: +//[ +// ["ate","eat","tea"], +// ["nat","tan"], +// ["bat"] +//] +// +// 说明: +// +// +// 所有输入均为小写字母。 +// 不考虑答案输出的顺序。 +// +// Related Topics 哈希表 字符串 + + +import java.util.*; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_49 { + + //key自己计算 + public List> groupAnagrams(String[] strs) { + int[] count = new int[26]; + Map> resMap = new HashMap<>(); + for (int i = 0; i < strs.length; i++) { + Arrays.fill(count,0); + String s = strs[i]; + //计算key + for (int j = 0; j < s.length(); j++) { + count[s.charAt(j) - 'a']++; + } + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < count.length; j++) { + sb.append(count[j]).append("#"); + } + String key = sb.toString(); + if (resMap.containsKey(key)) { + List tmp = resMap.get(key); + tmp.add(s); + continue; + } + List tmp = new ArrayList(); + tmp.add(s); + resMap.put(key,tmp); + } + return new ArrayList<>(resMap.values()); + } + + //counter作为map的key + public List> groupAnagrams1(String[] strs) { + + //计算每一个的counter + Map,List> counterMap = new HashMap<>(); + for (int i = 0; i < strs.length; i++) { + //计算counter + Map counter = new HashMap<>(); + for (int j = 0; j < strs[i].length(); j++) { + Character c = strs[i].charAt(j); + if (counter.containsKey(c)){ + Integer count = counter.get(c) + 1; + counter.put(c,count); + continue; + } + counter.put(c,1); + } + if (counterMap.containsKey(counter)) { + List list = counterMap.get(counter); + list.add(strs[i]); + continue; + } + List list = new ArrayList<>(); + list.add(strs[i]); + counterMap.put(counter,list); + } + List> res = new ArrayList<>(); + for (List value : counterMap.values()) { + res.add(value); + } + return res; + } + +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_50_388.java b/Week 02/id_388/LeeCode_50_388.java new file mode 100644 index 000000000..9d5c4e563 --- /dev/null +++ b/Week 02/id_388/LeeCode_50_388.java @@ -0,0 +1,59 @@ +package com.company.leetcode.editor.cn; +//实现 pow(x, n) ,即计算 x 的 n 次幂函数。 +// +// 示例 1: +// +// 输入: 2.00000, 10 +//输出: 1024.00000 +// +// +// 示例 2: +// +// 输入: 2.10000, 3 +//输出: 9.26100 +// +// +// 示例 3: +// +// 输入: 2.00000, -2 +//输出: 0.25000 +//解释: 2-2 = 1/22 = 1/4 = 0.25 +// +// 说明: +// +// +// -100.0 < x < 100.0 +// n 是 32 位有符号整数,其数值范围是 [−231, 231 − 1] 。 +// +// Related Topics 数学 二分查找 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_50 { + public double myPow(double x, int n) { + long N = n;//注意N为long.int出现其他问题 + if (n < 0) { + N = -N; + x = 1 / x; + } + + return helper(N,x); + } + + private double helper(long n,double x) { + if (n == 0) { + return 1.0; + } + if (n == 1) { + return x; + } + double half = helper(n / 2,x); + if (n % 2 == 0) { + return half * half; + } + return half * half * x; + } + +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_51_388.java b/Week 02/id_388/LeeCode_51_388.java new file mode 100644 index 000000000..55045d0a6 --- /dev/null +++ b/Week 02/id_388/LeeCode_51_388.java @@ -0,0 +1,114 @@ +package com.company.leetcode.editor.cn; +//n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。 +// +// +// +// 上图为 8 皇后问题的一种解法。 +// +// 给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。 +// +// 每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。 +// +// 示例: +// +// 输入: 4 +//输出: [ +// [".Q..", // 解法 1 +// "...Q", +// "Q...", +// "..Q."], +// +// ["..Q.", // 解法 2 +// "Q...", +// "...Q", +// ".Q.."] +//] +//解释: 4 皇后问题存在两个不同的解法。 +// +// Related Topics 回溯算法 + + +import javafx.util.Pair; + +import java.util.*; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + + + private Set col; + private Set pie; + private Set na; + + private List> finalRes = new ArrayList<>(); + + public List> solveNQueens(int n) { + + col = new HashSet<>(); + pie = new HashSet<>(); + na = new HashSet<>(); + + List board = this.generateBoard(n); + helper(0,n,board); + + return finalRes; + } + + private List generateBoard(int n){ + List board = new ArrayList<>(); + for (int i = 0; i < n; i++) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < n; j++) { + sb.append("."); + } + board.add(sb); + } + return board; + } + + private void helper(int row, int n,List board) { + if (row >= n) { + List newBoard = new ArrayList<>(); + for (int i = 0; i < board.size(); i++) { + newBoard.add(board.get(i).toString()); + } + finalRes.add(newBoard); + return; + } + //row + for (int i = 0; i < n; i++) { + //c + if (col.contains(i)) { + continue; + } + + //bie + if (this.pie.contains(row + i)) { + continue; + } + + //na + if (this.na.contains(row - i)) { + continue; + } + + this.col.add(i); + this.pie.add(row + i); + this.na.add(row - i); + + board.get(row).replace(i,i + 1,"Q"); + + helper(row + 1,n,board); + + //恢复状态 + board.get(row).replace(i,i + 1,"."); + this.col.remove(i); + this.pie.remove(row + i); + this.na.remove(row - i); + + } + + } + +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_590_388.java b/Week 02/id_388/LeeCode_590_388.java new file mode 100644 index 000000000..42d99f428 --- /dev/null +++ b/Week 02/id_388/LeeCode_590_388.java @@ -0,0 +1,65 @@ +package com.company.leetcode.editor.cn;//给定一个 N 叉树,返回其节点值的后序遍历。 +// +// 例如,给定一个 3叉树 : +// +// +// +// +// +// +// +// 返回其后序遍历: [5,6,3,2,4,1]. +// +// +// +// 说明: 递归法很简单,你可以使用迭代法完成此题吗? Related Topics 树 + + +import java.util.ArrayList; +import java.util.List; + +//leetcode submit region begin(Prohibit modification and deletion) +/* +// Definition for a Node_590. + +*/ +class Node_590 { + public int val; + public List children; + + public Node_590() {} + + public Node_590(int _val,List _children) { + val = _val; + children = _children; + } +} +class Solution_590 { + List res = new ArrayList<>(); + + //递归写法 + public List postorder(Node_590 root) { + helper(root); + return res; + } + + //按模板后序遍历 + private void helper(Node_590 Node_590){ + if (Node_590 == null) { + return; + } + + if (Node_590.children == null) { + res.add(Node_590.val); + return; + } + + for (int i = 0; i < Node_590.children.size(); i++) { + Node_590 tmp = Node_590.children.get(i); + helper(tmp); + } + + res.add(Node_590.val); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_77_388.java b/Week 02/id_388/LeeCode_77_388.java new file mode 100644 index 000000000..37f317af4 --- /dev/null +++ b/Week 02/id_388/LeeCode_77_388.java @@ -0,0 +1,56 @@ +package com.company.leetcode.editor.cn; +//给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 +// +// 示例: +// +// 输入: n = 4, k = 2 +//输出: +//[ +// [2,4], +// [3,4], +// [2,3], +// [1,2], +// [1,3], +// [1,4], +//] +// Related Topics 回溯算法 + + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_77 { + private int n; + private int k; + List> res = new ArrayList(); + //暴力解法 + public List> combine(int n, int k) { + + this.n = n; + this.k = k; + LinkedList curr = new LinkedList(); + helper(1,curr); + return res; + } + + private void helper(int first,LinkedList curr) { + if (curr.size() == k) { + res.add(new LinkedList<>(curr)); + } + for (int i = first; i < n + 1; i++) { + curr.add(i); + helper(i + 1,curr); + curr.removeLast();//回溯 + } + + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// List list = s.combine(2,1); +// System.out.println(list.toString()); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_78_388.java b/Week 02/id_388/LeeCode_78_388.java new file mode 100644 index 000000000..e6798aab7 --- /dev/null +++ b/Week 02/id_388/LeeCode_78_388.java @@ -0,0 +1,48 @@ +package com.company.leetcode.editor.cn; +//给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。 +// +// 说明:解集不能包含重复的子集。 +// +// 示例: +// +// 输入: nums = [1,2,3] +//输出: +//[ +// [3], +//  [1], +//  [2], +//  [1,2,3], +//  [1,3], +//  [2,3], +//  [1,2], +//  [] +//] +// Related Topics 位运算 数组 回溯算法 + + +import java.util.ArrayList; +import java.util.List; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_78 { + public List> subsets(int[] nums) { + List> res = new ArrayList<>(); + helper(res,new ArrayList<>(),0,nums); + return res; + } + + private void helper(List> res, List curr, int index, int[] nums) { + + //t + if (index == nums.length) { + res.add(new ArrayList<>(curr)); + return; + } + + helper(res,curr,index + 1,nums); + curr.add(nums[index]); + helper(res,curr,index + 1,nums); + curr.remove(curr.size() - 1); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_94_388.java b/Week 02/id_388/LeeCode_94_388.java new file mode 100644 index 000000000..88b33ac42 --- /dev/null +++ b/Week 02/id_388/LeeCode_94_388.java @@ -0,0 +1,84 @@ +package com.company.leetcode.editor.cn; +//给定一个二叉树,返回它的中序 遍历。 +// +// 示例: +// +// 输入: [1,null,2,3] +// 1 +// \ +// 2 +// / +// 3 +// +//输出: [1,3,2] +// +// 进阶: 递归算法很简单,你可以通过迭代算法完成吗? +// Related Topics 栈 树 哈希表 + + + +//leetcode submit region begin(Prohibit modification and deletion) + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * Definition for a binary tree node. + + */ +class TreeNode_94 { + int val; + TreeNode_94 left; + TreeNode_94 right; + TreeNode_94(int x) { val = x; } +} +class Solution_94 { + + private List res; + + /** + * 栈的方式 + * @param root + * @return + */ + public List inorderTraversal(TreeNode_94 root) { + res = new ArrayList<>(); + Stack stack = new Stack(); + TreeNode_94 curr = root; + while (curr != null || !stack.isEmpty()) { + while (curr != null) { + stack.push(curr); + curr = curr.left; + } + curr = stack.pop(); + res.add(curr.val); + curr = curr.right; + } + + return res; + } + + //递归方式 + public List inorderTraversal2(TreeNode_94 root) { + res = new ArrayList<>(); + travel(root); + return res; + } + + private void travel(TreeNode_94 node){ + + if (node == null){ + return; + } + + if (node.left != null) { + travel(node.left); + } + res.add(node.val); + if (node.right != null) { + travel(node.right); + } + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/LeeCode_98_388.java b/Week 02/id_388/LeeCode_98_388.java new file mode 100644 index 000000000..d0a96bbb8 --- /dev/null +++ b/Week 02/id_388/LeeCode_98_388.java @@ -0,0 +1,80 @@ +package com.company.leetcode.editor.cn;//给定一个二叉树,判断其是否是一个有效的二叉搜索树。 +// +// 假设一个二叉搜索树具有如下特征: +// +// +// 节点的左子树只包含小于当前节点的数。 +// 节点的右子树只包含大于当前节点的数。 +// 所有左子树和右子树自身必须也是二叉搜索树。 +// +// +// 示例 1: +// +// 输入: +// 2 +// / \ +// 1 3 +//输出: true +// +// +// 示例 2: +// +// 输入: +// 5 +// / \ +// 1 4 +//  / \ +//  3 6 +//输出: false +//解释: 输入为: [5,1,4,null,null,3,6]。 +//  根节点的值为 5 ,但是其右子节点值为 4 。 +// +// Related Topics 树 深度优先搜索 + + + +//leetcode submit region begin(Prohibit modification and deletion) +/** + * Definition for a binary tree node. + + */ +class TreeNode_98 { + int val; + TreeNode_98 left; + TreeNode_98 right; + TreeNode_98(int x) { val = x; } +} +class Solution_98 { + public boolean isValidBST(TreeNode_98 root) { + return helper(root.val,root.val,root.left,root,root.right); + } + + private boolean helper(int min, int max, TreeNode_98 left, TreeNode_98 currentRoot, TreeNode_98 right) { + if (currentRoot == null) { + return false; + } + + if (left.val > currentRoot.val) { + return false; + } + + if (right.val < currentRoot.val) { + return false; + } + + if (left.val < min) { + return false; + } + + if (right.val > max) { + return false; + } + boolean res = helper(Integer.MIN_VALUE,max,left.left,left,left.right); + if (!res){ + return false; + } + res = helper(min, Integer.MAX_VALUE,right.left,right,right.right); + return res; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_388/NOTE.md b/Week 02/id_388/NOTE.md index a6321d6e2..97eb53d41 100644 --- a/Week 02/id_388/NOTE.md +++ b/Week 02/id_388/NOTE.md @@ -1,4 +1,82 @@ # NOTE - +### 学习总结 +四件套:1、过一遍 2、最优 3、code 4、测试样例 + +#### 1、数据结构 +1、hash +根据您keyvalue直接访问。映射函数叫三列函数,存放记录的数组叫哈希表(散列表)。 平均时间复杂度是o1,严重的会退化到on + +2、Set +不重复元素的集合 +hashset实现:add map put. hashmap-put和get treemap 和trset都是logn + +3、 tree +linklist是另一种形式的tree,tree是另一种形式的图 +树的遍历:前序遍历,中序遍历,后续遍历,这期做了几个关于遍历的题目。如序列化,反序列化,根据前序和中序遍历恢复tree +二叉搜索树。左子树的值都小于它的根节点,柚子树大于大的根节点。查询和操作都是 logn + +#### 2、算法 + +##### a、递归模板 + +1、递归终结条件 +2、处理这一层的逻辑 +3、下一层 +4、清理当前层 +``` +public void recur(int level,int param) { + if (level > MAX_LEVEL) { + //process result + return; + } + process(level,param); + + recur(level + 1,newParam) + //restore current status +} + +``` + +#### b、分治代码模板,两者很相似 +``` +def divide_conquer(problem, param1, param2, ...): + # recursion terminator + if problem is None: + print_result + return + + # prepare data + data = prepare_data(problem) + subproblems = split_problem(problem, data) + + # conquer subproblems + subresult1 = self.divide_conquer(subproblems[0], p1, ...) + subresult2 = self.divide_conquer(subproblems[1], p1, ...) + subresult3 = self.divide_conquer(subproblems[2], p1, ...) + … + + # process and generate the final result + result = process_result(subresult1, subresult2, subresult3, …) + + # revert the current level states + + +``` + +###### c、关于n换后的对角线问题 + +pie: row + col的值,对角线的值都一样,如2,2,2;3,3,3,3 +| 0 | 1 |2 | 3 | +| --- | --- | --- | --- | +| 1 | 2 | 3 | 4 | +| 2 | 3 | 4 | 5 | +| 3 | 4 | 5 | 6 | + +na:row - col的值,na方向的值都一样 +| 0 | -1 |-2 |-3 | +| --- | --- | --- | --- | +| 1 | 0 | -1 | -2 | +| 2 | 1 | 0 | -1 | +| 3 | 2 | 1 | 0 | \ No newline at end of file diff --git a/Week 02/id_393/LeetCode_144_393.java b/Week 02/id_393/LeetCode_144_393.java new file mode 100644 index 000000000..9220bd058 --- /dev/null +++ b/Week 02/id_393/LeetCode_144_393.java @@ -0,0 +1,27 @@ +package no144; + +import base.TreeNode; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author boyiren + * @date 2019-10-27 + */ +public class Solution { + public List preorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + helper(root, res); + return res; + } + + private void helper(TreeNode root, List res) { + if (root == null) { + return; + } + res.add(root.val); + helper(root.left, res); + helper(root.right, res); + } +} diff --git a/Week 02/id_393/LeetCode_242_393.java b/Week 02/id_393/LeetCode_242_393.java new file mode 100644 index 000000000..5f952fb27 --- /dev/null +++ b/Week 02/id_393/LeetCode_242_393.java @@ -0,0 +1,20 @@ +package no242; + +import java.util.Arrays; + +/** + * @author boyiren + * @date 2019-10-27 + */ +public class Solution { + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + char[] str1 = s.toCharArray(); + char[] str2 = t.toCharArray(); + Arrays.sort(str1); + Arrays.sort(str2); + return Arrays.equals(str1, str2); + } +} diff --git a/Week 02/id_393/LeetCode_94_393.java b/Week 02/id_393/LeetCode_94_393.java new file mode 100644 index 000000000..0203b1645 --- /dev/null +++ b/Week 02/id_393/LeetCode_94_393.java @@ -0,0 +1,30 @@ +package no94; + +import base.TreeNode; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author boyiren + * @date 2019-10-27 + */ +public class Solution { + public List inorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + helper(root, res); + return res; + } + + public void helper(TreeNode root, List res) { + if (root != null) { + if (root.left != null) { + helper(root.left, res); + } + res.add(root.val); + if (root.right != null) { + helper(root.right, res); + } + } + } +} diff --git a/Week 02/id_408/LeetCode_105_408.c++ b/Week 02/id_408/LeetCode_105_408.c++ new file mode 100644 index 000000000..1b48d6ac5 --- /dev/null +++ b/Week 02/id_408/LeetCode_105_408.c++ @@ -0,0 +1,30 @@ +class Solution { +public: + map idx; + TreeNode* helper(vector& preorder, vector& inorder, int& pre_idx, int inleft, int inright){ + if(inleft == inright) + return NULL; + + int root_val = preorder[pre_idx]; + TreeNode* root = new TreeNode(root_val); + + int index = idx[root_val]; + + pre_idx++; + + root->left = helper(preorder, inorder, pre_idx, inleft, index); + + root->right = helper(preorder, inorder, pre_idx, index + 1, inright); + + return root; + + } + + TreeNode* buildTree(vector& preorder, vector& inorder) { + for(int i = 0; i < inorder.size(); i++){ + idx[inorder[i]] = i; + } + int pre_idx = 0; + return helper(preorder, inorder, pre_idx, 0, inorder.size()); + } +}; diff --git a/Week 02/id_408/LeetCode_236_408.c++ b/Week 02/id_408/LeetCode_236_408.c++ new file mode 100644 index 000000000..023696420 --- /dev/null +++ b/Week 02/id_408/LeetCode_236_408.c++ @@ -0,0 +1,18 @@ +class Solution { +public: + TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { + TreeNode* res=NULL; + helper(root,p,q,res); + return res; + } + bool helper(TreeNode* root,TreeNode* p,TreeNode* q,TreeNode* &res) + { + if(!root) return false; + bool left=helper(root->left,p,q,res); + bool right=helper(root->right,p,q,res); + bool self=root==p||root==q; + if((left&&right)||(self&&(left||right)))res=root; + return left||right||self; + } +}; + diff --git a/Week 02/id_418/LeetCode_105_418.java b/Week 02/id_418/LeetCode_105_418.java new file mode 100644 index 000000000..4132fb4f1 --- /dev/null +++ b/Week 02/id_418/LeetCode_105_418.java @@ -0,0 +1,67 @@ +package com.ljg.leetcode.week02.a_01.construct_binary_tree_from_preorder_and_inorder_traversal; + +import java.util.HashMap; +import java.util.Map; + +/** + * ConstructBinaryTree + */ +public class ConstructBinaryTree { + + public static class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + + private int preIndex = 0; + private int[] preOrder; + private int[] inOrder; + private Map map = new HashMap(); + + private TreeNode build(int leftIndex, int rightIndex) { + if (leftIndex == rightIndex) { + return null; + } + + int value = preOrder[preIndex]; + TreeNode treeNode = new TreeNode(value); + preIndex++; + + int index = map.get(value); + + treeNode.left = build(leftIndex, index); + + treeNode.right = build(index + 1, rightIndex); + + return treeNode; + } + + public TreeNode buildTree(int[] preorder, int[] inorder) { + + this.preOrder = preorder; + this.inOrder = inorder; + + int index = 0; + for (int value : inorder) { + map.put(value, index++); + } + + return build(0, inorder.length); + } + + public static void main(String[] args) { + + int[] preorder = new int[] { 3, 9, 20, 15, 7 }; + int[] inorder = new int[] { 9, 3, 15, 20, 7 }; + + ConstructBinaryTree constructBinaryTree = new ConstructBinaryTree(); + TreeNode treeNode = constructBinaryTree.buildTree(preorder, inorder); + System.out.println(treeNode); + + } +} \ No newline at end of file diff --git a/Week 02/id_418/LeetCode_236_418.java b/Week 02/id_418/LeetCode_236_418.java new file mode 100644 index 000000000..8fb589fe5 --- /dev/null +++ b/Week 02/id_418/LeetCode_236_418.java @@ -0,0 +1,41 @@ +package com.ljg.leetcode.week02.a_02.lowest_common_ancestor_of_a_binary_tree; + +/** + * Solution + */ +public class Solution { + + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + + private TreeNode commonAncestorNode; + + private boolean recursive(TreeNode root, TreeNode p, TreeNode q) { + if (root == null) { + return false; + } + + int left = recursive(root.left, p, q) ? 1 : 0; + int right = recursive(root.right, p, q) ? 1 : 0; + + int mid = (root == p || root == q) ? 1 : 0; + + if ((left + right + mid) >= 2) { + this.commonAncestorNode = root; + } + + return (left + right + mid) > 0; + } + + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + this.recursive(root, p, q); + return this.commonAncestorNode; + } +} \ No newline at end of file diff --git a/Week 02/id_423/LeetCode_1_423.java b/Week 02/id_423/LeetCode_1_423.java new file mode 100644 index 000000000..a84df1d9b --- /dev/null +++ b/Week 02/id_423/LeetCode_1_423.java @@ -0,0 +1,12 @@ +class Solution { + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + int complement = target - nums[i]; + if (map.containsKey(complement)) + return new int[] { map.get(complement), i }; + map.put(nums[i], i); + } + throw new IllegalArgumentException("No two sum solution"); + } +} \ No newline at end of file diff --git a/Week 02/id_423/LeetCode_94_423.java b/Week 02/id_423/LeetCode_94_423.java new file mode 100644 index 000000000..b4616b1c9 --- /dev/null +++ b/Week 02/id_423/LeetCode_94_423.java @@ -0,0 +1,19 @@ +class Solution { + public List inorderTraversal(TreeNode root) { + List res = new ArrayList<> (); + helper(root, res); + return res; + } + + public void helper(TreeNode root, List res) { + if (root != null) { + if (root.left != null) { + helper(root.left, res); + } + res.add(root.val); + if (root.right != null) { + helper(root.right, res); + } + } + } +} \ No newline at end of file diff --git a/Week 02/id_428/LeeCode_242_428.java b/Week 02/id_428/LeeCode_242_428.java new file mode 100644 index 000000000..01749c428 --- /dev/null +++ b/Week 02/id_428/LeeCode_242_428.java @@ -0,0 +1,34 @@ + +class Solution { + public boolean isAnagram(String s, String t) { + if (s == null || t == null) return false; + if (s.length() != t.length()) return false; + int[] counter = new int[26]; + for (int i = 0; i < s.length(); i++) { + counter[s.charAt(i) - 'a']++; + counter[t.charAt(i) - 'a']--; + } + for (int count : counter) { + if (count != 0) { + return false; + } + } + + //hash ִСд +// int[] counter = new int[26*2+6]; +// for (int i = 0; i < s.length(); i++) { +// counter[s.charAt(i) - 'A']++; +// counter[t.charAt(i) - 'A']--; +// } + + //Arrays sort 39% +// char[] sc = s.toCharArray(); +// char[] tc = t.toCharArray(); +// Arrays.sort(sc); +// Arrays.sort(tc); +// return Arrays.equals(sc, tc); + + return true; + } + +} \ No newline at end of file diff --git a/Week 02/id_428/LeeCode_77_428.java b/Week 02/id_428/LeeCode_77_428.java new file mode 100644 index 000000000..b2805c599 --- /dev/null +++ b/Week 02/id_428/LeeCode_77_428.java @@ -0,0 +1,22 @@ +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.List; +class Solution { + public static List> combine(int n, int k) { + List> combs = new ArrayList>(); + comb(1, n, k, new ArrayList(), combs); + return combs; + } + + private static void comb(int start, int end, int k, ArrayList comb, List> combs) { + if (k == 0) { + combs.add(new ArrayList(comb)); + return; + } + for (int i = start; i <= (end - k + 1); i++) { + comb.add(i); + comb(i + 1, end, k-1, comb, combs); + comb.remove(comb.size() - 1); + } + } +} \ No newline at end of file diff --git a/Week 02/id_428/LeeCode_94_428.java b/Week 02/id_428/LeeCode_94_428.java new file mode 100644 index 000000000..87a5c40c7 --- /dev/null +++ b/Week 02/id_428/LeeCode_94_428.java @@ -0,0 +1,28 @@ + +class Solution { + public List inorderTraversal(TreeNode root) { + List list = new ArrayList(); + // ݹ + if (root != null) traversal(root, list); + + //ջķʽ +// Stack stack = new Stack(); +// TreeNode cur = root; +// while(cur!=null || !stack.empty()){ +// while(cur!=null){ +// stack.add(cur); +// cur = cur.left; +// } +// cur = stack.pop(); +// list.add(cur.val); +// cur = cur.right; +// } + return list; + } + private void traversal(TreeNode node, List list) { + if (node.left != null) traversal(node.left, list); + list.add(node.val); + if (node.right != null) traversal(node.right, list); + } + +} \ No newline at end of file diff --git a/Week 02/id_428/NOTE.md b/Week 02/id_428/NOTE.md index a6321d6e2..3f322aa78 100644 --- a/Week 02/id_428/NOTE.md +++ b/Week 02/id_428/NOTE.md @@ -1,4 +1,188 @@ -# NOTE +# Week_02_学习总结 - +## 1、学习过程 +​ 1)对平时使用较多的数据结构,重新在底层实现上学习一遍,对实际应用更加清晰性能和限制。 + +​ 2)对Hash函数有了比较全面的认识,及在java数据结构中的应用细节 + +​ 3)重新学习树、图的知识 + +​ 4)练习递归和回溯的代码,思考过程有困难,需要更多的练习,掌握terminator、process result、 + +​ process current logic、drill down、restore current status的过程 + +​ + +## 2、本周学习内容(待细化) + +​ 1)哈希表 + +​ 2)映射 + +​ 3)集合 + +​ 4)树 + +​ 二叉树遍历 + +​ 前序遍历:当前节点,左节点,右节点 + +​ 中序遍历:左节点,当前节点,右节点 + +​ 后序遍历:左节点,右节点,当前节点 + +​ + +​ 5)图 + +​ 6)二叉树 + +​ 7)二叉搜索树 + +​ 8)泛型递归 + +​ 9)树的递归 + +​ 10)分治、回溯 + +## 3、HashMap + +1)构造函数 + +```java +无参构造方法,构造一个空的HashMap,初始容量为16,负载因子为0.75 +``` + +设定`threshold`。 这个threshold = capacity * load factor 。当HashMap的size到了threshold时,就要进行resize,也就是扩容。 + +在构造方法中,并没有对table这个成员变量进行初始化,table的初始化被推迟到了put方法中,在put方法中会对threshold重新计算 。 + +2)存储结构 + +HashMap使用链表法避免哈希冲突(相同hash值),当链表长度大于TREEIFY_THRESHOLD(默认为8)时,将链表转换为红黑树,当然小于UNTREEIFY_THRESHOLD(默认为6)时,又会转回链表以达到性能均衡。 + +3)键Key的Hash方法 + +```java +/** + * key 的 hash值的计算是通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^ (h >>> 16) + * 主要是从速度、功效、质量来考虑的,这么做可以在数组table的length比较小的时候 + * 也能保证考虑到高低Bit都参与到Hash的计算中,同时不会有太大的开销 + */ + static final int hash(Object key) { + int h; + return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); + } +``` + +4)扩容 + +**什么时候扩容:**通过HashMap源码可以看到是在put操作时,即向容器中添加元素时,判断当前容器中元素的个数是否达到阈值(当前数组长度乘以加载因子的值)的时候,就要自动扩容了。 + +**扩容(resize):**其实就是重新计算容量;而这个扩容是计算出所需容器的大小之后重新定义一个新的容器,将原来容器中的元素放入其中。 + +5)put方法 + +```java +//实现put和相关方法。 + final V putVal(int hash, K key, V value, boolean onlyIfAbsent, + boolean evict) { + Node[] tab; Node p; int n, i; + //如果table为空或者长度为0,则resize() + if ((tab = table) == null || (n = tab.length) == 0) + n = (tab = resize()).length; + //确定插入table的位置,算法是(n - 1) & hash,在n为2的幂时,相当于取摸操作。 + ////找到key值对应的槽并且是第一个,直接加入 + if ((p = tab[i = (n - 1) & hash]) == null) + tab[i] = newNode(hash, key, value, null); + //在table的i位置发生碰撞,有两种情况,1、key值是一样的,替换value值, + //2、key值不一样的有两种处理方式:2.1、存储在i位置的链表;2.2、存储在红黑树中 + else { + Node e; K k; + //第一个node的hash值即为要加入元素的hash + if (p.hash == hash && + ((k = p.key) == key || (key != null && key.equals(k)))) + e = p; + //2.2 + else if (p instanceof TreeNode) + e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); + //2.1 + else { + //不是TreeNode,即为链表,遍历链表 + for (int binCount = 0; ; ++binCount) { + ///链表的尾端也没有找到key值相同的节点,则生成一个新的Node, + //并且判断链表的节点个数是不是到达转换成红黑树的上界达到,则转换成红黑树。 + if ((e = p.next) == null) { + // 创建链表节点并插入尾部 + p.next = newNode(hash, key, value, null); + ////超过了链表的设置长度8就转换成红黑树 + if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st + treeifyBin(tab, hash); + break; + } + if (e.hash == hash && + ((k = e.key) == key || (key != null && key.equals(k)))) + break; + p = e; + } + } + //如果e不为空就替换旧的oldValue值 + if (e != null) { // existing mapping for key + V oldValue = e.value; + if (!onlyIfAbsent || oldValue == null) + e.value = value; + afterNodeAccess(e); + return oldValue; + } + } + ++modCount; + if (++size > threshold) + resize(); + afterNodeInsertion(evict); + return null; + } +``` + +6)1.7和1.8的HashMap的不同点 + +(1)JDK1.7用的是头插法,而JDK1.8及之后使用的都是尾插法,那么为什么要这样做呢?因为JDK1.7是用单链表进行的纵向延伸,当采用头插法就是能够提高插入的效率,但是也会容易出现逆序且环形链表死循环问题。但是在JDK1.8之后是因为加入了红黑树使用尾插法,能够避免出现逆序且链表死循环的问题。 + +(2)扩容后数据存储位置的计算方式也不一样: + +1. 在JDK1.7的时候是直接用hash值和需要扩容的二进制数进行&(这里就是为什么扩容的时候为啥一定必须是2的多少次幂的原因所在,因为如果只有2的n次幂的情况时最后一位二进制数才一定是1,这样能最大程度减少hash碰撞)(hash值 & length-1) 。 +2. 而在JDK1.8的时候直接用了JDK1.7的时候计算的规律,也就是扩容前的原始位置+扩容的大小值=JDK1.8的计算方式,而不再是JDK1.7的那种异或的方法。但是这种方式就相当于只需要判断Hash值的新增参与运算的位是0还是1就直接迅速计算出了扩容后的储存方式。 + +(3)JDK1.7的时候使用的是数组+ 单链表的数据结构。但是在JDK1.8及之后时,使用的是数组+链表+红黑树的数据结构(当链表的深度达到8的时候,也就是默认阈值,就会自动扩容把链表转成红黑树的数据结构来把时间复杂度从O(N)变成O(logN)提高了效率)。 + +7)HashMap为什么是线程不安全的? + +HashMap 在并发时可能出现的问题主要是两方面: + +1. put的时候导致的多线程数据不一致 + 比如有两个线程A和B,首先A希望插入一个key-value对到HashMap中,首先计算记录所要落到的 hash桶的索引坐标,然后获取到该桶里面的链表头结点,此时线程A的时间片用完了,而此时线程B被调度得以执行,和线程A一样执行,只不过线程B成功将记录插到了桶里面,假设线程A插入的记录计算出来的 hash桶索引和线程B要插入的记录计算出来的 hash桶索引是一样的,那么当线程B成功插入之后,线程A再次被调度运行时,它依然持有过期的链表头但是它对此一无所知,以至于它认为它应该这样做,如此一来就覆盖了线程B插入的记录,这样线程B插入的记录就凭空消失了,造成了数据不一致的行为。 +2. resize而引起死循环 + 这种情况发生在HashMap自动扩容时,当2个线程同时检测到元素个数超过 数组大小 × 负载因子。此时2个线程会在put()方法中调用了resize(),两个线程同时修改一个链表结构会产生一个循环链表(JDK1.7中,会出现resize前后元素顺序倒置的情况)。接下来再想通过get()获取某一个元素,就会出现死循环。 + +8)HashMap和HashTable的区别 + +HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。 + +1. HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。 +2. HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。 +3. 另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。 +4. 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。 +5. HashMap不能保证随着时间的推移Map中的元素次序是不变的。 + +**需要注意的重要术语**: + +1. sychronized意味着在一次仅有一个线程能够更改Hashtable。就是说任何线程要更新Hashtable时要首先获得同步锁,其它线程要等到同步锁被释放之后才能再次获得同步锁更新Hashtable。 +2. Fail-safe和iterator迭代器相关。如果某个集合对象创建了Iterator或者ListIterator,然后其它的线程试图“结构上”更改集合对象,将会抛出ConcurrentModificationException异常。但其它线程可以通过set()方法更改集合对象是允许的,因为这并没有从“结构上”更改集合。但是假如已经从结构上进行了更改,再调用set()方法,将会抛出IllegalArgumentException异常。 +3. 结构上的更改指的是删除或者插入一个元素,这样会影响到map的结构。 + +**HashMap可以通过下面的语句进行同步:Map m = Collections.synchronizeMap(hashMap);** + +参考: + +作者:小北觅 +https://www.jianshu.com/p/ee0de4c99f87来源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 \ No newline at end of file diff --git a/Week 02/id_433/LeetCode_429_433.py b/Week 02/id_433/LeetCode_429_433.py new file mode 100644 index 000000000..12200b478 --- /dev/null +++ b/Week 02/id_433/LeetCode_429_433.py @@ -0,0 +1,15 @@ +""" +# Definition for a Node. +class Node: + def __init__(self, val, children): + self.val = val + self.children = children +""" +class Solution: + def levelOrder(self, root: 'Node') -> List[List[int]]: + res = [] + q = root and [root] + while q: + res.append([node.val for node in q]) + q = [child for node in q for child in node.children] + return res \ No newline at end of file diff --git a/Week 02/id_433/LeetCode_589_433.py b/Week 02/id_433/LeetCode_589_433.py new file mode 100644 index 000000000..0c30cb939 --- /dev/null +++ b/Week 02/id_433/LeetCode_589_433.py @@ -0,0 +1,16 @@ +""" +# Definition for a Node. +class Node: + def __init__(self, val, children): + self.val = val + self.children = children +""" +class Solution: + def preorder(self, root: 'Node') -> List[int]: + res = [] + stack = root and [root] + while stack: + top = stack.pop() + res.append(top.val) + stack += reversed(top.children) + return res \ No newline at end of file diff --git a/Week 02/id_443/LeetCode_144_443.java b/Week 02/id_443/LeetCode_144_443.java new file mode 100644 index 000000000..24a9a7f8f --- /dev/null +++ b/Week 02/id_443/LeetCode_144_443.java @@ -0,0 +1,93 @@ +//给定一个二叉树,返回它的 前序 遍历。 +// +// 示例: +// +// 输入: [1,null,2,3] +// 1 +// \ +// 2 +// / +// 3 +// +//输出: [1,2,3] +// +// +// 进阶: 递归算法很简单,你可以通过迭代算法完成吗? +// Related Topics 栈 树 + +package com.modds.alltest.leetcode.editor.cn; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +public class LeetCode_144_443_BinaryTreePreorderTraversal { + public static void main(String[] args) { + Solution solution = new LeetCode_144_443_BinaryTreePreorderTraversal().new Solution(); + } + + +//leetcode submit region begin(Prohibit modification and deletion) + + /** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + class Solution { + private List list = new ArrayList<>(); + + /** + * 循环 + * + * @param root + * @return + */ + public List preorderTraversal(TreeNode root) { + Stack stack = new Stack<>(); + if (root == null) return list; + stack.add(root); + while (!stack.empty()) { + TreeNode node = stack.pop(); + + list.add(node.val); + if (node.right != null) { + stack.push(node.right); + } + if (node.left != null) { + stack.push(node.left); + } + } + return list; + } + + /** + * 递归 + * + * @param root + * @return + */ + public List preorderTraversal1(TreeNode root) { + if (root == null) return list; + list.add(root.val); + preorderTraversal(root.left); + preorderTraversal(root.right); + return list; + } + } +//leetcode submit region end(Prohibit modification and deletion) + + private class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } +} \ No newline at end of file diff --git a/Week 02/id_443/LeetCode_17_443.java b/Week 02/id_443/LeetCode_17_443.java new file mode 100644 index 000000000..0679ed58b --- /dev/null +++ b/Week 02/id_443/LeetCode_17_443.java @@ -0,0 +1,66 @@ +//给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。 +// +// 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 +// +// +// +// 示例: +// +// 输入:"23" +//输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]. +// +// +// 说明: +//尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。 +// Related Topics 字符串 回溯算法 + +package com.modds.alltest.leetcode.editor.cn; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class LeetCode_17_443_LetterCombinationsOfAPhoneNumber { + public static void main(String[] args) { + Solution solution = new LeetCode_17_443_LetterCombinationsOfAPhoneNumber().new Solution(); + } + + + //leetcode submit region begin(Prohibit modification and deletion) + class Solution { + private List list = new ArrayList<>(); + private Map map = new HashMap() {{ + put('2', "abc"); + put('3', "def"); + put('4', "ghi"); + put('5', "jkl"); + put('6', "mno"); + put('7', "pqrs"); + put('8', "tuv"); + put('9', "wxyz"); + }}; + + public List letterCombinations(String digits) { + if (digits.length() == 0) return list; + sub("", digits.toCharArray(), 0); + return list; + } + + private void sub(String sb, char[] cs, int index) { + if (index >= cs.length) { + list.add(sb); + return; + } + + char c = cs[index]; + String re = map.get(c); + + for (int i = 0; i < re.length(); i++) { + char sc = re.charAt(i); + sub(sb + sc, cs, index + 1); + } + } + } +//leetcode submit region end(Prohibit modification and deletion) +} \ No newline at end of file diff --git a/Week 02/id_443/LeetCode_1_443.java b/Week 02/id_443/LeetCode_1_443.java new file mode 100644 index 000000000..33733f62f --- /dev/null +++ b/Week 02/id_443/LeetCode_1_443.java @@ -0,0 +1,53 @@ +//给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 +// +// 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 +// +// 示例: +// +// 给定 nums = [2, 7, 11, 15], target = 9 +// +//因为 nums[0] + nums[1] = 2 + 7 = 9 +//所以返回 [0, 1] +// +// Related Topics 数组 哈希表 + +package com.modds.alltest.leetcode.editor.cn; + +import java.util.HashMap; +import java.util.Map; + +public class LeetCode_1_443_TwoSum { + public static void main(String[] args) { + Solution solution = new LeetCode_1_443_TwoSum().new Solution(); + } + + + //leetcode submit region begin(Prohibit modification and deletion) + class Solution { + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + int m = target - nums[i]; + if (map.containsKey(m)) { + return new int[]{map.get(m), i}; + } else { + map.put(nums[i], i); + } + } + return new int[0]; + } + + public int[] twoSum1(int[] nums, int target) { + for (int i = 0; i < nums.length; i++) { + for (int j = i + 1; j < nums.length; j++) { + if (nums[i] + nums[j] == target) { + return new int[]{i, j}; + } + } + } + return new int[]{}; + } + } +//leetcode submit region end(Prohibit modification and deletion) + +} \ No newline at end of file diff --git a/Week 02/id_443/LeetCode_226_443.java b/Week 02/id_443/LeetCode_226_443.java new file mode 100644 index 000000000..6c916f9f8 --- /dev/null +++ b/Week 02/id_443/LeetCode_226_443.java @@ -0,0 +1,70 @@ +//翻转一棵二叉树。 +// +// 示例: +// +// 输入: +// +// 4 +// / \ +// 2 7 +// / \ / \ +//1 3 6 9 +// +// 输出: +// +// 4 +// / \ +// 7 2 +// / \ / \ +//9 6 3 1 +// +// 备注: +//这个问题是受到 Max Howell 的 原问题 启发的 : +// +// 谷歌:我们90%的工程师使用您编写的软件(Homebrew),但是您却无法在面试时在白板上写出翻转二叉树这道题,这太糟糕了。 +// Related Topics 树 + +package com.modds.alltest.leetcode.editor.cn; + +public class LeetCode_226_443_InvertBinaryTree { + public static void main(String[] args) { + Solution solution = new LeetCode_226_443_InvertBinaryTree().new Solution(); + } + + +//leetcode submit region begin(Prohibit modification and deletion) + + /** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + class Solution { + public TreeNode invertTree(TreeNode root) { + if (root == null) return root; + + TreeNode node = root.left; + root.left = root.right; + root.right = node; + + invertTree(root.right); + invertTree(root.left); + return root; + } + } +//leetcode submit region end(Prohibit modification and deletion) + + private class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } +} \ No newline at end of file diff --git a/Week 02/id_443/LeetCode_22_443.java b/Week 02/id_443/LeetCode_22_443.java new file mode 100644 index 000000000..781e765ed --- /dev/null +++ b/Week 02/id_443/LeetCode_22_443.java @@ -0,0 +1,50 @@ +//给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。 +// +// 例如,给出 n = 3,生成结果为: +// +// [ +// "((()))", +// "(()())", +// "(())()", +// "()(())", +// "()()()" +//] +// +// Related Topics 字符串 回溯算法 + +package com.modds.alltest.leetcode.editor.cn; + +import java.util.ArrayList; +import java.util.List; + +public class LeetCode_22_443_GenerateParentheses { + public static void main(String[] args) { + Solution solution = new LeetCode_22_443_GenerateParentheses().new Solution(); + } + + + //leetcode submit region begin(Prohibit modification and deletion) + class Solution { + public List generateParenthesis(int n) { + List list = new ArrayList<>(); + generate(list, new StringBuilder(), n, n); + return list; + + } + + private void generate(List list, StringBuilder sb, int nl, int nr) { + if (nl == 0 && nr == 0) { + list.add(sb.toString()); + return; + } + if (nl > 0) { + generate(list, new StringBuilder(sb).append("("), nl - 1, nr); + } + if (nr > nl) { + generate(list, new StringBuilder(sb).append(")"), nl, nr - 1); + } + } + } +//leetcode submit region end(Prohibit modification and deletion) + +} \ No newline at end of file diff --git a/Week 02/id_443/LeetCode_242_443.java b/Week 02/id_443/LeetCode_242_443.java new file mode 100644 index 000000000..75f5e13f3 --- /dev/null +++ b/Week 02/id_443/LeetCode_242_443.java @@ -0,0 +1,64 @@ +//给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 +// +// 示例 1: +// +// 输入: s = "anagram", t = "nagaram" +//输出: true +// +// +// 示例 2: +// +// 输入: s = "rat", t = "car" +//输出: false +// +// 说明: +//你可以假设字符串只包含小写字母。 +// +// 进阶: +//如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况? +// Related Topics 排序 哈希表 + +package com.modds.alltest.leetcode.editor.cn; + +import java.util.HashMap; +import java.util.Map; + +public class ValidAnagram { + public static void main(String[] args) { + Solution solution = new ValidAnagram().new Solution(); + } + + + //leetcode submit region begin(Prohibit modification and deletion) + class Solution { + public boolean isAnagram(String s, String t) { + Map map = new HashMap(); + for (int i = 0; i < s.length(); i++) { + Character c = s.charAt(i); + if (map.containsKey(c)) { + map.put(c, map.get(c) + 1); + } else { + map.put(c, 1); + } + } + + for (int i = 0; i < t.length(); i++) { + Character c = t.charAt(i); + Integer count = map.get(t.charAt(i)); + if (count != null) { + count--; + if (count == 0) { + map.remove(c); + } else { + map.put(c, count); + } + } else { + return false; + } + } + return map.isEmpty(); + } + } +//leetcode submit region end(Prohibit modification and deletion) + +} \ No newline at end of file diff --git a/Week 02/id_443/LeetCode_49_443.java b/Week 02/id_443/LeetCode_49_443.java new file mode 100644 index 000000000..3423d2991 --- /dev/null +++ b/Week 02/id_443/LeetCode_49_443.java @@ -0,0 +1,55 @@ +//给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 +// +// 示例: +// +// 输入: ["eat", "tea", "tan", "ate", "nat", "bat"], +//输出: +//[ +// ["ate","eat","tea"], +// ["nat","tan"], +// ["bat"] +//] +// +// 说明: +// +// +// 所有输入均为小写字母。 +// 不考虑答案输出的顺序。 +// +// Related Topics 哈希表 字符串 + +package com.modds.alltest.leetcode.editor.cn; + +import java.util.*; + +public class GroupAnagrams { + public static void main(String[] args) { + Solution solution = new GroupAnagrams().new Solution(); + } + + + //leetcode submit region begin(Prohibit modification and deletion) + class Solution { + + // hash char sort + public List> groupAnagrams(String[] strs) { + if (strs.length == 0) return Collections.emptyList(); + Map> sorted = new HashMap<>(); + for (String str : strs) { + char[] cs = str.toCharArray(); + Arrays.sort(cs); + String n = String.valueOf(cs); + if (sorted.containsKey(n)) { + sorted.get(n).add(str); + } else { + List l = new ArrayList<>(); + l.add(str); + sorted.put(n, l); + } + } + return new ArrayList<>(sorted.values()); + } + } +//leetcode submit region end(Prohibit modification and deletion) + +} \ No newline at end of file diff --git a/Week 02/id_443/LeetCode_589_443.java b/Week 02/id_443/LeetCode_589_443.java new file mode 100644 index 000000000..e0938fb51 --- /dev/null +++ b/Week 02/id_443/LeetCode_589_443.java @@ -0,0 +1,88 @@ +//给定一个 N 叉树,返回其节点值的前序遍历。 +// +// 例如,给定一个 3叉树 : +// +// +// +// +// +// +// +// 返回其前序遍历: [1,3,5,6,2,4]。 +// +// +// +// 说明: 递归法很简单,你可以使用迭代法完成此题吗? Related Topics 树 + +package com.modds.alltest.leetcode.editor.cn; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +public class LeetCode_589_443_NAryTreePreorderTraversal { + public static void main(String[] args) { + Solution solution = new LeetCode_589_443_NAryTreePreorderTraversal().new Solution(); + } + + + //leetcode submit region begin(Prohibit modification and deletion) +/* +// Definition for a Node. +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +}; +*/ + class Solution { + private List list = new ArrayList<>(); + + public List preorder(Node root) { + if(root == null) return list; + Stack stack = new Stack<>(); + stack.push(root); + while (!stack.empty()) { + Node node = stack.pop(); + list.add(node.val); + for (int i = node.children.size() - 1; i >= 0; i--) { + stack.push(node.children.get(i)); + } + } + return list; + } + + public List preorder1(Node root) { + if (root == null) return list; + + list.add(root.val); + for (Node child : root.children) { + preorder(child); + } + + return list; + } + } +//leetcode submit region end(Prohibit modification and deletion) + + class Node { + public int val; + public List children; + + public Node() { + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } + } + + ; +} \ No newline at end of file diff --git a/Week 02/id_443/LeetCode_590_443.java b/Week 02/id_443/LeetCode_590_443.java new file mode 100644 index 000000000..001850264 --- /dev/null +++ b/Week 02/id_443/LeetCode_590_443.java @@ -0,0 +1,86 @@ +//给定一个 N 叉树,返回其节点值的后序遍历。 +// +// 例如,给定一个 3叉树 : +// +// +// +// +// +// +// +// 返回其后序遍历: [5,6,3,2,4,1]. +// +// +// +// 说明: 递归法很简单,你可以使用迭代法完成此题吗? Related Topics 树 + +package com.modds.alltest.leetcode.editor.cn; + +import java.util.LinkedList; +import java.util.List; +import java.util.Stack; + +public class LeetCode_590_443_NAryTreePostorderTraversal { + public static void main(String[] args) { + Solution solution = new LeetCode_590_443_NAryTreePostorderTraversal().new Solution(); + } + + + //leetcode submit region begin(Prohibit modification and deletion) +/* +// Definition for a Node. +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +}; +*/ + class Solution { + private List list = new LinkedList<>(); + + public List postorder(Node root) { + if (root == null) return list; + Stack stack = new Stack<>(); + stack.push(root); + while (!stack.empty()) { + Node node = stack.pop(); + list.add(0, node.val); + for (Node child : node.children) { + stack.push(child); + } + } + + return list; + } + + public List postorder1(Node root) { + if (root == null) return list; + for (Node child : root.children) { + postorder(child); + } + list.add(root.val); + + return list; + } + } +//leetcode submit region end(Prohibit modification and deletion) + + private class Node { + public int val; + public List children; + + public Node() { + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } + } +} \ No newline at end of file diff --git a/Week 02/id_443/LeetCode_70_443.java b/Week 02/id_443/LeetCode_70_443.java new file mode 100644 index 000000000..c3b826975 --- /dev/null +++ b/Week 02/id_443/LeetCode_70_443.java @@ -0,0 +1,54 @@ +//假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 +// +// 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? +// +// 注意:给定 n 是一个正整数。 +// +// 示例 1: +// +// 输入: 2 +//输出: 2 +//解释: 有两种方法可以爬到楼顶。 +//1. 1 阶 + 1 阶 +//2. 2 阶 +// +// 示例 2: +// +// 输入: 3 +//输出: 3 +//解释: 有三种方法可以爬到楼顶。 +//1. 1 阶 + 1 阶 + 1 阶 +//2. 1 阶 + 2 阶 +//3. 2 阶 + 1 阶 +// +// Related Topics 动态规划 + +package com.modds.alltest.leetcode.editor.cn; + +public class LeetCode_70_443_ClimbingStairs { + public static void main(String[] args) { + Solution solution = new LeetCode_70_443_ClimbingStairs().new Solution(); + } + + + //leetcode submit region begin(Prohibit modification and deletion) + class Solution { + public int climbStairs(int n) { + if(n<=2) return n; + int n1 = 1; + int n2 = 2; + for (int i = 3; i <= n; i++) { + n2 = n1 + n2; + n1 = n2 - n1; + } + return n2; + } + + public int climbStairs1(int n) { + if (n == 1) return 1; + if (n == 2) return 2; + return climbStairs(n - 1) + climbStairs(n - 2); + } + } +//leetcode submit region end(Prohibit modification and deletion) +} \ No newline at end of file diff --git a/Week 02/id_443/LeetCode_94_443.java b/Week 02/id_443/LeetCode_94_443.java new file mode 100644 index 000000000..ce1dbb4ce --- /dev/null +++ b/Week 02/id_443/LeetCode_94_443.java @@ -0,0 +1,81 @@ +//给定一个二叉树,返回它的中序 遍历。 +// +// 示例: +// +// 输入: [1,null,2,3] +// 1 +// \ +// 2 +// / +// 3 +// +//输出: [1,3,2] +// +// 进阶: 递归算法很简单,你可以通过迭代算法完成吗? +// Related Topics 栈 树 哈希表 + +package com.modds.alltest.leetcode.editor.cn; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +public class LeetCode_94_443_BinaryTreeInorderTraversal { + public static void main(String[] args) { + Solution solution = new LeetCode_94_443_BinaryTreeInorderTraversal().new Solution(); + } + + +//leetcode submit region begin(Prohibit modification and deletion) + + /** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + class Solution { + List list = new ArrayList<>(); + + public List inorderTraversal(TreeNode root) { + Stack stack = new Stack<>(); + TreeNode node = root; + while (node != null || !stack.empty()) { + while (node != null) { + stack.push(node); + node = node.left; + } + + node = stack.pop(); + list.add(node.val); + node = node.right; + } + return list; + } + + public List inorderTraversal1(TreeNode root) { + if (root == null) return list; + + inorderTraversal(root.left); + list.add(root.val); + inorderTraversal(root.right); + + return list; + } + + } + + //leetcode submit region end(Prohibit modification and deletion) + private class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } +} diff --git a/Week 02/id_443/LeetCode_98_443.java b/Week 02/id_443/LeetCode_98_443.java new file mode 100644 index 000000000..abc6261f3 --- /dev/null +++ b/Week 02/id_443/LeetCode_98_443.java @@ -0,0 +1,84 @@ +//给定一个二叉树,判断其是否是一个有效的二叉搜索树。 +// +// 假设一个二叉搜索树具有如下特征: +// +// +// 节点的左子树只包含小于当前节点的数。 +// 节点的右子树只包含大于当前节点的数。 +// 所有左子树和右子树自身必须也是二叉搜索树。 +// +// +// 示例 1: +// +// 输入: +// 2 +// / \ +// 1 3 +//输出: true +// +// +// 示例 2: +// +// 输入: +// 5 +// / \ +// 1 4 +//  / \ +//  3 6 +//输出: false +//解释: 输入为: [5,1,4,null,null,3,6]。 +//  根节点的值为 5 ,但是其右子节点值为 4 。 +// +// Related Topics 树 深度优先搜索 + +package com.modds.alltest.leetcode.editor.cn; + +public class LeetCode_98_443_ValidateBinarySearchTree { + public static void main(String[] args) { + Solution solution = new LeetCode_98_443_ValidateBinarySearchTree().new Solution(); + } + + +//leetcode submit region begin(Prohibit modification and deletion) + + /** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + class Solution { + public boolean isValidBST(TreeNode root) { + return isValidBST(root, null, null); + } + + public boolean isValidBST(TreeNode node, Integer min, Integer max) { + if (node == null) return true; + if (node.left != null) { + if (node.left.val >= node.val || (min != null && node.left.val <= min)) { + return false; + } + } + if (node.right != null) { + if (node.right.val <= node.val || (max != null && node.right.val >= max)) { + return false; + } + } + return isValidBST(node.left, min, node.val) && isValidBST(node.right, node.val, max); + } + } +//leetcode submit region end(Prohibit modification and deletion) + + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } +} \ No newline at end of file diff --git a/Week 02/id_448/LeetCode_1_448.java b/Week 02/id_448/LeetCode_1_448.java new file mode 100644 index 000000000..cc0ea3eaa --- /dev/null +++ b/Week 02/id_448/LeetCode_1_448.java @@ -0,0 +1,17 @@ +class Solution { + public int[] twoSum(int[] nums, int target) { + if (nums == null || nums.length < 2) { + return new int[]{-1, -1}; + } + int[] res = new int[]{-1, -1}; + HashMap map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + if (map.containsKey(target - nums[i])) { + res[0] = map.get(target - nums[i]); + res[1] = i; + break; + } else map.put(nums[i], i); + } + return res; + } +} diff --git a/Week 02/id_468/leetCode70_2_468.java b/Week 02/id_468/leetCode70_2_468.java new file mode 100644 index 000000000..0bd7d15c0 --- /dev/null +++ b/Week 02/id_468/leetCode70_2_468.java @@ -0,0 +1,33 @@ +package week2; + +/** + * @program: leetcode + * @description: 爬楼梯 + * @author: 王瑞全 + * @create: 2019-10-2420:19 + **/ + + +public class leetCode70_2_468 { + public int climbStairs(int n) { + if(n==1||n==0){ + return 1; + } + int total=climbStairs(n-1)+climbStairs(n-2); + return total; + } + public int climbStairs1(int n) { + if(n==1||n==0){ + return n; + } + int count1=1; + int count2=1; + for(int i=2;i<=n;i++){ + int temp=count2; + count2=count2+count1; + count1=temp; + } + return count2; + + } +} diff --git a/Week 02/id_468/leetcode105_2_468.java b/Week 02/id_468/leetcode105_2_468.java new file mode 100644 index 000000000..65ae5dd67 --- /dev/null +++ b/Week 02/id_468/leetcode105_2_468.java @@ -0,0 +1,47 @@ +package week2; + +import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiNilLoader; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * @program: leetcode + * @description: Construct Binary Tree from Preorder and Inorder Traversal + * @author: 王瑞全 + * @create: 2019-10-2519:13 + **/ + + +public class leetcode105_2_468 { + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + + public TreeNode buildTree(int[] preorder, int[] inorder) { + return helper(0,0,inorder.length,preorder,inorder); + } + + public TreeNode helper(int preStart, int inStart, int inEnd, int[] preOrder, int[] inOrder) { + if(preStart>preOrder.length-1||inStart>inEnd){ + return null; + } + TreeNode treeNode=new TreeNode(preOrder[preStart]); + int index=0; + for(int i=inStart;i<=inEnd;i++){ + if(treeNode.val==inOrder[i]){ + index=i; + } + } + treeNode.left=helper(preStart+1,inStart,index-1,preOrder,inOrder); + treeNode.right=helper(preStart+index-inStart+1,index+1,inEnd,preOrder,inOrder); + return treeNode; + } +} diff --git a/Week 02/id_468/leetcode144_2_468.java b/Week 02/id_468/leetcode144_2_468.java new file mode 100644 index 000000000..f36736fd6 --- /dev/null +++ b/Week 02/id_468/leetcode144_2_468.java @@ -0,0 +1,40 @@ +package week2; + +import java.util.ArrayList; +import java.util.List; + +/** + * @program: leetcode + * @description: 二叉树的前序遍历 + * @author: 王瑞全 + * @create: 2019-10-2220:44 + **/ + + +public class leetcode144_2_468 { + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + + public List preorderTraversal(TreeNode root) { + List list = new ArrayList<>(); + helper(root, list); + return list; + } + + public List helper(TreeNode root, List list) { + if (root != null) { + list.add(root.val); + helper(root.left, list); + helper(root.right, list); + } + return list; + } +} + diff --git a/Week 02/id_468/leetcode169_2_468.java b/Week 02/id_468/leetcode169_2_468.java new file mode 100644 index 000000000..5ae8cc573 --- /dev/null +++ b/Week 02/id_468/leetcode169_2_468.java @@ -0,0 +1,18 @@ +package week2; + +import java.util.Arrays; + +/** + * @program: leetcode + * @description: 169. Majority Element + * @author: 王瑞全 + * @create: 2019-10-2811:52 + **/ + + +public class leetcode169_2_468 { + public int majorityElement(int[] nums) { + Arrays.sort(nums); + return nums[nums.length/2]; + } +} diff --git a/Week 02/id_468/leetcode17_2_468.java b/Week 02/id_468/leetcode17_2_468.java new file mode 100644 index 000000000..44af84e76 --- /dev/null +++ b/Week 02/id_468/leetcode17_2_468.java @@ -0,0 +1,36 @@ +package week2; + +import java.util.ArrayList; +import java.util.List; + +/** + * @program: leetcode + * @description: 17. Letter Combinations of a Phone Number + * @author: 王瑞全 + * @create: 2019-10-2819:56 + **/ + + +public class leetcode17_2_468 { + String[][] refer = {{}, {}, {"a", "c", "b"}, {"d", "e", "f"}, {"g", "h", "i"}, {"j", "k", "l"}, {"m", "n", "o"}, {"p", "q", "r", "s"}, {"t", "u", "v"}, {"w", "x", "y", "z"}}; + + public List letterCombinations(String digits) { + List answer = new ArrayList<>(); + if (!digits.equals("")) { + solve(digits,answer,""); + } + return answer; + } + + public void solve(String digits, List anwser, String item) { + if (digits.length() == 0) { + anwser.add(item); + return; + } + int idx = Integer.parseInt(digits.substring(0, 1)); + for (String k : refer[idx]) { + solve(digits.substring(1, digits.length()), anwser,item + k); + } + return; + } +} diff --git a/Week 02/id_468/leetcode226_2_468.java b/Week 02/id_468/leetcode226_2_468.java new file mode 100644 index 000000000..4448b33dd --- /dev/null +++ b/Week 02/id_468/leetcode226_2_468.java @@ -0,0 +1,34 @@ +package week2; + +/** + * @program: leetcode + * @description: Invert Binary Tree + * @author: 王瑞全 + * @create: 2019-10-2518:25 + **/ + + +public class leetcode226_2_468 { + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { val = x; } + } + public TreeNode invertTree(TreeNode root) { + if(root==null){ + return root; + } + TreeNode temp=root.right; + root.right=root.left; + root.left=temp; + if(root.left!=null) { + invertTree(root.left); + } + if(root.right!=null) { + invertTree(root.right); + } + return root; + + } +} diff --git a/Week 02/id_468/leetcode22_2_468.java b/Week 02/id_468/leetcode22_2_468.java new file mode 100644 index 000000000..f0161865b --- /dev/null +++ b/Week 02/id_468/leetcode22_2_468.java @@ -0,0 +1,33 @@ +package week2; + +import java.util.ArrayList; +import java.util.List; + +/** + * @program: leetcode + * @description: 括号生成 + * @author: 王瑞全 + * @create: 2019-10-2420:42 + **/ + + +public class leetcode22_2_468 { + public List generateParenthesis(int n) { + List list=new ArrayList<>(); + backtrack(list,"",0,0,n); + return list; + } + public void backtrack(List list,String str,int open,int close,int max){ + if(str.length()==max*2){ + list.add(str); + return ; + } + if(open children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } + }; + public List> postorder(Node root) { + List> lists=new ArrayList<>(); + dfs(root,0,lists); + return lists; + } + + public void dfs(Node root, int depth, List> result){ + if(root==null)return ; + if(depth()); + } + result.get(depth).add(root.val); + depth++; + for(Node node:root.children){ + dfs(node,depth,result); + } + } +} diff --git a/Week 02/id_468/leetcode46_2_468.java b/Week 02/id_468/leetcode46_2_468.java new file mode 100644 index 000000000..9eceaa29f --- /dev/null +++ b/Week 02/id_468/leetcode46_2_468.java @@ -0,0 +1,35 @@ +package week2; + +import java.util.ArrayList; +import java.util.List; + +/** + * @program: leetcode + * @description: 给定一个没有重复数字的序列,返回其所有可能的全排列。 + * @author: 王瑞全 + * @create: 2019-10-2719:05 + **/ + + +public class leetcode46_2_468 { + public List> permute(int[] nums) { + List> list=new ArrayList<>(); + if(nums.length==0){ + return null; + } + solve(nums,list,new ArrayList<>(),0); + return list; + } + public void solve(int[] nums,List> list,List item,int start){ + if(item.size()==nums.length){ + list.add(item); + return; + } + for(int i=0;i<=item.size();i++) { + List myitem = new ArrayList<>(item); + myitem.add(i,nums[start]); + solve(nums, list, myitem, start + 1); + } + } + +} diff --git a/Week 02/id_468/leetcode47_2_468.java b/Week 02/id_468/leetcode47_2_468.java new file mode 100644 index 000000000..ffb3f1076 --- /dev/null +++ b/Week 02/id_468/leetcode47_2_468.java @@ -0,0 +1,44 @@ +package week2; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @program: leetcode + * @description: 给定一个可包含重复数字的序列,返回所有不重复的全排列。 + * @author: 王瑞全 + * @create: 2019-10-2719:31 + **/ + + +public class leetcode47_2_468 { + public static List> permuteUnique(int[] nums) { + List> list=new ArrayList<>(); + if(nums==null||nums.length==0) return list; + Arrays.sort(nums); + boolean[] used = new boolean[nums.length]; + resolve(list,new ArrayList<>(),nums,used); + return list; + } + public static void resolve(List> list,List item,int[] nums,boolean[] userd){ + if(item.size()==nums.length){ + list.add(new ArrayList<>(item)); + return ; + } + for(int i=0;i0&&nums[i-1]==nums[i]&&!userd[i-1]) continue; + userd[i]=true; + item.add(nums[i]); + resolve(list,item,nums,userd); + userd[i]=false; + item.remove(item.size()-1); + } + } + + public static void main(String[] args) { + int[] test={1,1,3,4}; + permuteUnique(test); + } +} diff --git a/Week 02/id_468/leetcode49_2_468.java b/Week 02/id_468/leetcode49_2_468.java new file mode 100644 index 000000000..ad843d0d0 --- /dev/null +++ b/Week 02/id_468/leetcode49_2_468.java @@ -0,0 +1,36 @@ +package week2; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +/** + * @program: leetcode + * @description: Group Anagrams + * @author: 王瑞全 + * @create: 2019-10-2123:37 + **/ + + +public class leetcode49_2_468 { + public List> groupAnagrams(String[] strs) { + HashMap> map = new HashMap<>(); + for (String str : strs) { + char[] a = str.toCharArray(); + Arrays.sort(a); + String sorted = String.valueOf(a); + List list = map.get(sorted); + if (list == null) { + list = new ArrayList<>(); + } + list.add(str); + map.put(sorted, list); + } + List> ds = new ArrayList<>(); + for (List d : map.values()) { + ds.add(d); + } + return ds; + } +} diff --git a/Week 02/id_468/leetcode50_2_468.java b/Week 02/id_468/leetcode50_2_468.java new file mode 100644 index 000000000..3c89713ba --- /dev/null +++ b/Week 02/id_468/leetcode50_2_468.java @@ -0,0 +1,23 @@ +package week2; + +/** + * @program: leetcode + * @description: 实现 pow(x, n) ,即计算 x 的 n 次幂函数。 + * @author: 王瑞全 + * @create: 2019-10-2811:26 + **/ + + +public class leetcode50_2_468 { + public double myPow(double x, int n) { + if(n==0){ + return 1; + } + if(n<0){ + n=-n; + x=1/x; + } + + return n%2==0?myPow(x*x,n/2):myPow(x*x,n/2)*x; + } +} diff --git a/Week 02/id_468/leetcode589_2_468.java b/Week 02/id_468/leetcode589_2_468.java new file mode 100644 index 000000000..4229827c7 --- /dev/null +++ b/Week 02/id_468/leetcode589_2_468.java @@ -0,0 +1,41 @@ +package week2; + +import java.util.ArrayList; +import java.util.List; + +/** + * @program: leetcode + * @description: N叉树的前序遍历 + * @author: 王瑞全 + * @create: 2019-10-2319:17 + **/ + + +public class leetcode589_2_468 { + class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } + }; + public List postorder(Node root) { + List list=new ArrayList<>(); + return solve(root,list); + } + + public List solve(Node root, List list){ + if(root==null)return list; + list.add(root.val); + + List childs=root.children; + for(Node node:childs){ + solve(node,list); + } + return list; + } +} diff --git a/Week 02/id_468/leetcode590_2_468.java b/Week 02/id_468/leetcode590_2_468.java new file mode 100644 index 000000000..ebddb6c87 --- /dev/null +++ b/Week 02/id_468/leetcode590_2_468.java @@ -0,0 +1,40 @@ +package week2; + +import java.util.ArrayList; +import java.util.List; + +/** + * @program: leetcode + * @description: N叉树的后序遍历 + * @author: 王瑞全 + * @create: 2019-10-2318:56 + **/ + + +public class leetcode590_2_468 { + class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } + }; + public List postorder(Node root) { + List list=new ArrayList<>(); + return solve(root,list); + } + + public List solve(Node root,List list){ + if(root==null)return list; + List childs=root.children; + for(Node node:childs){ + solve(node,list); + } + list.add(root.val); + return list; + } +} diff --git a/Week 02/id_468/leetcode77_2_468.java b/Week 02/id_468/leetcode77_2_468.java new file mode 100644 index 000000000..e498e73ed --- /dev/null +++ b/Week 02/id_468/leetcode77_2_468.java @@ -0,0 +1,33 @@ +package week2; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @program: leetcode + * @description: combinations + * @author: 王瑞全 + * @create: 2019-10-2522:55 + **/ + + +public class leetcode77_2_468 { + public List> combine(int n, int k) { + List> list=new ArrayList<>(); + combine(list,new ArrayList<>(),1,n,k); + return list; + } + public void combine(List> conbines,List conb,int start,int n,int k){ + + if(k==0){ + conbines.add(new ArrayList<>(conb)); + return ; + } + for(int i=start;i<=n;i++){ + conb.add(i); + combine(conbines,conb,i+1,n,k-1); + conb.remove(conb.size()-1); + } + } +} diff --git a/Week 02/id_468/leetcode78_2_468.java b/Week 02/id_468/leetcode78_2_468.java new file mode 100644 index 000000000..831ea0108 --- /dev/null +++ b/Week 02/id_468/leetcode78_2_468.java @@ -0,0 +1,18 @@ +package week2; + +import java.util.List; + +/** + * @program: leetcode + * @description: Note: The solution set must not contain duplicate subsets. + * @author: 王瑞全 + * @create: 2019-10-2811:47 + **/ + + +public class leetcode78_2_468 { + public List> subsets(int[] nums) { + return null; + } + +} diff --git a/Week 02/id_468/leetcode94_2_468.java b/Week 02/id_468/leetcode94_2_468.java new file mode 100644 index 000000000..8e595ddc3 --- /dev/null +++ b/Week 02/id_468/leetcode94_2_468.java @@ -0,0 +1,40 @@ +package week2; + +import java.util.ArrayList; +import java.util.List; + +/** + * @program: leetcode + * @description: Binary Tree Inorder Traversal + * @author: 王瑞全 + * @create: 2019-10-2219:50 + **/ + + +public class leetcode94_2_468 { + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { + val = x; + } + } + + public List inorderTraversal(TreeNode root) { + if(root==null){ + return null; + } + List list=new ArrayList<>(); + helper(root,list); + return list; + } + public List helper(TreeNode root,List list){ + if(root!=null) { + helper(root.left, list); + list.add(root.val); + helper(root.right, list); + } + return list; + } +} diff --git a/Week 02/id_473/LeetCode_242.java b/Week 02/id_473/LeetCode_242.java new file mode 100644 index 000000000..6d2cdb9a0 --- /dev/null +++ b/Week 02/id_473/LeetCode_242.java @@ -0,0 +1,27 @@ +import java.util.Arrays; + +/** + * 242. 有效字母异位词 + * @Author CJ + * @create 2019/10/27 + */ + +public class LeetCode_242 { + public static void main(String[] args) { + String s = "anagram", t = "nagaram"; + System.out.println(isAnagram(s,t)); + } + //字母排序 + public static boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + char[] s1 = s.toCharArray(); + Arrays.sort(s1); + //System.out.println("s排序后:" + String.valueOf(s1)); + char[] t1 = t.toCharArray(); + Arrays.sort(t1); + //System.out.println("t排序后:" + String.valueOf(t1)); + return String.valueOf(s1).equals(String.valueOf(t1)); + } +} \ No newline at end of file diff --git a/Week 02/id_473/LeetCode_49.java b/Week 02/id_473/LeetCode_49.java new file mode 100644 index 000000000..3203e2770 --- /dev/null +++ b/Week 02/id_473/LeetCode_49.java @@ -0,0 +1,40 @@ +import java.util.*; + +/** + * 49. 字母异位词分组 + * @Author CJ + * @create 2019/10/27 + */ + +public class LeetCode_49 { + public static void main(String[] args) { + String[] str = new String[]{"eat", "tea", "tan", "ate", "nat", "bat"}; + List> result = LeetCode_49.groupAnagrams(str); + for (List list: + result ) { + for (String s: + list) { + System.out.println(s); + } + } + } + //哈希表 + public static List> groupAnagrams(String[] strs) { + if(strs.length == 0){ + return new ArrayList<>(); + } + + Map map = new HashMap<>(); + for (String str: + strs) { + char[] toCharArray = str.toCharArray(); + Arrays.sort(toCharArray); + String toString = String.valueOf(toCharArray); + if(!map.containsKey(toString)){ + map.put(toString, new ArrayList()); + } + map.get(toString).add(str); + } + return new ArrayList(map.values()); + } +} \ No newline at end of file diff --git a/Week 02/id_473/NOTE.md b/Week 02/id_473/NOTE.md index a6321d6e2..7a4eaaa9b 100644 --- a/Week 02/id_473/NOTE.md +++ b/Week 02/id_473/NOTE.md @@ -1,4 +1,28 @@ -# NOTE +# 关于HashMap的小总结 +######1.8以下的版本没有老师说的这两个方法。由于本人能力一般水平有限,以下纯属个人理解,有理解不对的地方还请多多指正。 +###putVal方法 + +- 首先判断链表是否为空,是则重新给链表初始化大小 +- 然后获取要插入元素在链表上的位置,如果没有找到则创建个新节点。 +- 如果新插入的节点与第一个相等则直接覆盖掉。如果定位到的元素是一个树节点那么就在该树节点中插入一个树节点。 +- 判断定位到的元素是否是普通的链表,且next为空则直接在后面插入。 +- 校验链表长度超过了8则进行树化。 +- 如果下一个节点不为空且这个节点是key对应的节点则终止循环。 + + +###getNode方法 +该方法是get方法的具体实现, +- 首先确定链表的首节点,判断首节点是否就是对应的节点,是的话则返回value值 +- 然后判断首节点的下一个节点是否为空 +- 如果首节点的下一个节点不是空,则校验首节点是否为树节点。 +- 如果首节点是树节点,则丛中取出对应的value值。不是,则继续往下遍历比对链表,直到最后一个节点指向空为止。 + + +--- + +### 第二周学习总结 +1. 本周学习了哈希表、集合、树、二叉树、二叉搜索树邓书记结构,同时再原有基础上又加深了对树结构的理解和应用。 +2. 学习了递归以及分治和回溯等算法,了解了分治和回溯的实现、特性。 diff --git "a/Week 02/id_493/144.\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.js" "b/Week 02/id_493/144.\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.js" new file mode 100644 index 000000000..48d97729d --- /dev/null +++ "b/Week 02/id_493/144.\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.js" @@ -0,0 +1,62 @@ +/* + * @lc app=leetcode.cn id=144 lang=javascript + * + * [144] 二叉树的前序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {number[]} + */ +var preorderTraversal = function(root) { + if (root == null) { + return []; + } + + let result = []; + let stArr = [root]; + while (stArr.length > 0) { + let ff = stArr.pop(); + result.push(ff.val); + if (ff.right !== null) { + stArr.push(ff.right); + } + if (ff.left !== null) { + stArr.push(ff.left); + } + } + return result; + + // let result = []; + // result.push(root.val); + + // let left = postorderTraversal(root.left); + // let right = postorderTraversal(root.right); + // result.push(...left); + // result.push(...right); + + // return result; + + // let result = []; + // let stArr = []; + // while (root != null || stArr.length > 0) { + // while (root !== null) { + // stArr.push(root); + // result.push(root.val); + // root = root.left; + // } + // root = stArr.pop(); + // root = root.right; + // } + // return result; +}; +// @lc code=end + diff --git "a/Week 02/id_493/236.\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.js" "b/Week 02/id_493/236.\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.js" new file mode 100644 index 000000000..95a088151 --- /dev/null +++ "b/Week 02/id_493/236.\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.js" @@ -0,0 +1,39 @@ +/* + * @lc app=leetcode.cn id=236 lang=javascript + * + * [236] 二叉树的最近公共祖先 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @param {TreeNode} p + * @param {TreeNode} q + * @return {TreeNode} + */ +var lowestCommonAncestor = function(root, p, q) { + let result = null; + function lowestCommon(root, p, q) { + if (root == null) { + return false; + } + let left = lowestCommon(root.left, p, q) ? 1 : 0; + let right = lowestCommon(root.right, p, q) ? 1 : 0; + let mid = (root == p || root == q) ? 1 : 0; + if (left + right + mid >= 2) { + result = root; + } + return (left + right + mid) > 0; + } + lowestCommon(root, p, q); + return result; +}; +// @lc code=end + diff --git "a/Week 02/id_493/242.\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.js" "b/Week 02/id_493/242.\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.js" new file mode 100644 index 000000000..ace015016 --- /dev/null +++ "b/Week 02/id_493/242.\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.js" @@ -0,0 +1,38 @@ +/* + * @lc app=leetcode.cn id=242 lang=javascript + * + * [242] 有效的字母异位词 + */ + +// @lc code=start +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function(s, t) { + if (s.length !== t.length) { + return false; + } + let numObj = {}; + for (let a of s) { + if (numObj.hasOwnProperty(a)) { + numObj[a] = numObj[a] + 1; + } else { + numObj[a] = 1; + } + } + for (let a of t) { + if (numObj.hasOwnProperty(a)) { + numObj[a] = numObj[a] - 1; + if (numObj[a] < 0) { + return false; + } + } else { + return false; + } + } + return true; +}; +// @lc code=end + diff --git "a/Week 02/id_493/429.n\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.js" "b/Week 02/id_493/429.n\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.js" new file mode 100644 index 000000000..63b526bd9 --- /dev/null +++ "b/Week 02/id_493/429.n\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.js" @@ -0,0 +1,39 @@ +/* + * @lc app=leetcode.cn id=429 lang=javascript + * + * [429] N叉树的层序遍历 + */ + +// @lc code=start +/** + * // Definition for a Node. + * function Node(val,children) { + * this.val = val; + * this.children = children; + * }; + */ +/** + * @param {Node} root + * @return {number[][]} + */ +var levelOrder = function(root) { + if (root == null) { + return []; + } + let stArr = [{level: 0, value: root}]; + let result1 = []; + while (stArr.length > 0) { + let d = stArr.shift(); + let level = d.level; + let value = d.value; + result1[level] = result1[level] || []; + result1[level].push(d.value.val); + + for (let i = 0; i < value.children.length; i++) { + stArr.push({level: level + 1, value: value.children[i]}); + } + } + return result1; +}; +// @lc code=end + diff --git "a/Week 02/id_493/49.\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204.js" "b/Week 02/id_493/49.\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204.js" new file mode 100644 index 000000000..a8334bfb8 --- /dev/null +++ "b/Week 02/id_493/49.\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204.js" @@ -0,0 +1,26 @@ +/* + * @lc app=leetcode.cn id=49 lang=javascript + * + * [49] 字母异位词分组 + */ + +// @lc code=start +/** + * @param {string[]} strs + * @return {string[][]} + */ +var groupAnagrams = function(strs) { + let sObj = {}; + for (let str of strs) { + let key = Array.from(str).sort().toString(); + sObj[key] = sObj[key] || []; + sObj[key].push(str); + } + let result = []; + for (let key in sObj) { + result.push(sObj[key]); + } + return result; +}; +// @lc code=end + diff --git "a/Week 02/id_493/589.n\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.js" "b/Week 02/id_493/589.n\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.js" new file mode 100644 index 000000000..3e401574b --- /dev/null +++ "b/Week 02/id_493/589.n\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.js" @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode.cn id=589 lang=javascript + * + * [589] N叉树的前序遍历 + */ + +// @lc code=start +/** + * // Definition for a Node. + * function Node(val,children) { + * this.val = val; + * this.children = children; + * }; + */ +/** + * @param {Node} root + * @return {number[]} + */ +var preorder = function(root) { + if (root == null) { + return []; + } + let stArr = [root]; + let result = []; + while (stArr.length > 0) { + let d = stArr.pop(); + result.push(d.val); + for (let i = d.children.length - 1; i >= 0; i--) { + stArr.push(d.children[i]); + } + } + return result; +}; +// @lc code=end + diff --git "a/Week 02/id_493/590.n\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.js" "b/Week 02/id_493/590.n\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.js" new file mode 100644 index 000000000..951b5e3a7 --- /dev/null +++ "b/Week 02/id_493/590.n\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.js" @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode.cn id=590 lang=javascript + * + * [590] N叉树的后序遍历 + */ + +// @lc code=start +/** + * // Definition for a Node. + * function Node(val,children) { + * this.val = val; + * this.children = children; + * }; + */ +/** + * @param {Node} root + * @return {number[]} + */ +var postorder = function(root) { + if (root == null) { + return []; + } + let stArr = [root]; + let result = []; + while (stArr.length > 0) { + let d = stArr.pop(); + result.unshift(d.val); + for (let i = 0; i < d.children.length; i++) { + stArr.push(d.children[i]); + } + } + return result; +}; +// @lc code=end + diff --git "a/Week 02/id_493/94.\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.js" "b/Week 02/id_493/94.\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.js" new file mode 100644 index 000000000..3517ed2f5 --- /dev/null +++ "b/Week 02/id_493/94.\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.js" @@ -0,0 +1,55 @@ +/* + * @lc app=leetcode.cn id=94 lang=javascript + * + * [94] 二叉树的中序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {number[]} + */ +var inorderTraversal = function(root) { + + if (root == null) { + return []; + } + + let result = []; + let stArr = [root]; + while (stArr.length > 0) { + let ff = stArr.pop(); + result.push(ff.val); + if (ff.right !== null) { + stArr.push(ff.right); + } + if (ff.left !== null) { + stArr.push(ff.left); + } + } + return result; + + + + // let result = []; + // let stArr = []; + // while (root != null || stArr.length > 0) { + // while (root !== null) { + // stArr.push(root); + // root = root.left; + // } + // root = stArr.pop(); + // result.push(root.val); + // root = root.right; + // } + // return result; +}; +// @lc code=end + diff --git a/Week 02/id_503/NOTE.md b/Week 02/id_503/NOTE.md index a6321d6e2..7a7fb6f85 100644 --- a/Week 02/id_503/NOTE.md +++ b/Week 02/id_503/NOTE.md @@ -1,4 +1,87 @@ -# NOTE +# 二叉树 Binary Tree +* 每个节点**最多**有两个子节点,分别是左子节点和右子节点 +* 二叉树的遍历 + * 前序遍历(pre-order):根-左-右 + * 时间复杂度:T(n) = O(n) + * 代码模板 + ``` javascript + function preorder(root) { + if (!root) { + return; + } + + console.log(root.val); + preorder(root.left); + preorder(root.right); + } + ``` + * 中序遍历(in-order):左-根-右 + * 时间复杂度:T(n) = O(n) + * 代码模板 + ``` javascript + function inorder(root) { + if (!root) { + return; + } + + console.log(root.val); + inorder(root.left); + inorder(root.right); + } + ``` + * 后序遍历(post-order):左-右-根 + * 时间复杂度:T(n) = O(n) + * 代码模板 + ``` javascript + function postorder(root) { + if (!root) { + return; + } + + postorder(root.left); + postorder(root.right); + console.log(root.val); + } + ``` - +# 二叉搜索树 Binary Search Tree +* 也称**二叉查找树**(Binary Search Tree)、**有序二叉树**(Ordered Binary Tree)、**排序二叉树**(Sorted Binary Tree) +* 性质 + 1. 左子树上**所有结点**的值均**小于**它的根结点的值 + 2. 右子树上**所有结点**的值均**大于**它的根结点的值 + 3. 以此类推:左、右子树也分别为二叉查找树(这就是**重复性**!) + 4. 可通过**中序遍历**得到一个**升序排列** +* 时间复杂度 + * 平均情况 + * access:O(logn) + * search:O(logn) + * insertion:O(logn) + * deletion:O(logn) + * 极端情况,以上时间复杂度为:O(n) +# 递归 +* **树**的面试题**解法**一般都是**递归** +* 求解递归问题思路 + 1. **不要人肉**进行递归(最大误区) + 2. 找到最近最简方法,将其**拆解成可重复**解决的问题(重复子问题) + * 拆解成子问题 + * **问题**与**子问题**除了数据规模不同,求解思路一致 + 3. 多运用**数学归纳法**思维 + 4. 存在**终止条件** +* 代码模板 + ```javascript + function recursion(level, ...params) { + // 1. recursion terminator 递归终止条件 + if (level > MAX_LEVEL) { + // 2. process result 处理结果 + return; + } + + // 3. process current logic 处理当前逻辑 + + // 4. drill down 递归处理下一层 + recursion(level + 1, ...params); + + // 5. restore current status if needed 如果需要,还原当前状态 + } + ``` \ No newline at end of file diff --git a/Week 02/id_503/leetcode_49_503.go b/Week 02/id_503/leetcode_49_503.go new file mode 100644 index 000000000..d8c56a8fd --- /dev/null +++ b/Week 02/id_503/leetcode_49_503.go @@ -0,0 +1,30 @@ +package week02 + +func groupAnagrams(strs []string) [][]string { + + group := make(map[string][]string) + for _, e := range strs { + + // 将字符串转换为 map 的 key 值 + key := generateKey(e) + + group[key] = append(group[key], e) + } + + // 将 map 统计的异位词转化切片 + res := make([][]string, 0, len(group)) + for _, v := range group { + res = append(res, v) + } + + return res +} + +func generateKey(s string) string { + r := make([]rune, 26) + for _, e := range s { + r[e-'a']++ + } + + return string(r) +} diff --git a/Week 02/id_503/leetcode_589_503.js b/Week 02/id_503/leetcode_589_503.js new file mode 100644 index 000000000..69f7d82f4 --- /dev/null +++ b/Week 02/id_503/leetcode_589_503.js @@ -0,0 +1,50 @@ +/** + * // Definition for a Node. + * function Node(val,children) { + * this.val = val; + * this.children = children; + * }; + */ +/** + * @param {Node} root + * @return {number[]} + */ +var preorder = function (root) { + if (!root) { + return []; + } + + let orderNums = []; + orderNums.push(root.val); + + if (root.children) { + for (let i = 0; i < root.children.length; i++) { + orderNums = orderNums.concat(preorder(root.children[i])); + } + } + + return orderNums; +}; + +// 迭代法 +var preorderWithIteration = function (root) { + if (!root) { + return []; + } + + let orderNums = []; + const stack = [root]; // 模拟栈 + + while (stack.length) { + const node = stack.pop(); + orderNums.push(node.val); + + if (node.children) { + for (let i = node.children.length - 1; i >= 0; i--) { + stack.push(node.children[i]); + } + } + } + + return orderNums; +}; \ No newline at end of file diff --git a/Week 02/id_508/LeetCode_046_508.cpp b/Week 02/id_508/LeetCode_046_508.cpp new file mode 100644 index 000000000..0fa032958 --- /dev/null +++ b/Week 02/id_508/LeetCode_046_508.cpp @@ -0,0 +1,29 @@ +class Solution { +public: + vector> permute(vector& nums) { + vector> result; + vector state; + recursePermute(nums.size(),result,state,0,nums); + return result; + } + void recursePermute(const int N,vector>&result,vector& state,int count,vector& nums) { + if(state.size()==N) { + result.push_back(state); + return; + } + for(auto i:nums) { + bool flag = true; + for(auto j:state) { + if(j==i) { + flag = false; + break; + } + } + if(flag) { + state.push_back(i); + recursePermute(N,result,state,count+1,nums); + state.pop_back(); + } + } + } +}; diff --git a/Week 02/id_508/LeetCode_047_508.cpp b/Week 02/id_508/LeetCode_047_508.cpp new file mode 100644 index 000000000..ea7cef802 --- /dev/null +++ b/Week 02/id_508/LeetCode_047_508.cpp @@ -0,0 +1,39 @@ +class Solution { +public: + vector> permuteUnique(vector& nums) { + vector> result; + vector current; + sort(nums.begin(),nums.end()); + recursePermuteUnique(nums,current,result); + return result; + } + void recursePermuteUnique(vector&nums, vector& current, vector>& result) { + if(nums.size() == current.size()) { + vector temp ; + for(auto i:current) temp.push_back(nums[i]); + result.push_back(temp); + return; + } + vector mute; + + for(int i=0;i& current,int n) { + for(auto iter:current) { + if(iter==n) return false; + } + return true; + } + +}; diff --git a/Week 02/id_508/LeetCode_049_508.cpp b/Week 02/id_508/LeetCode_049_508.cpp new file mode 100644 index 000000000..63eb26cf7 --- /dev/null +++ b/Week 02/id_508/LeetCode_049_508.cpp @@ -0,0 +1,17 @@ +class Solution { +public: + vector> groupAnagrams(vector& strs) { + vector> result; + unordered_map> hash_map; + for(auto iter:strs) { + string str = iter; + sort(str.begin(),str.end()); + hash_map[str].push_back(iter); + } + + for(auto iter:hash_map) { + result.push_back(iter.second); + } + return result; + } +}; diff --git a/Week 02/id_508/LeetCode_051_508.cpp b/Week 02/id_508/LeetCode_051_508.cpp new file mode 100644 index 000000000..a446ca303 --- /dev/null +++ b/Week 02/id_508/LeetCode_051_508.cpp @@ -0,0 +1,52 @@ +class Solution { +public: + vector> solveNQueens(int n) { + unsigned long long N = (1<> result; + vector> rn; + vector current;; + sQueen(N,result,0,current,0,0,0); + show(result,rn,n); + return rn; + } + void show(vector>& current,vector>& result,int n) { + + vector>::iterator iters = current.begin(); + for(iters = current.begin();iters!=current.end();iters++) { + vector::iterator iter2 = iters->begin(); + vector str; + + for(iter2=iters->begin();iter2!=iters->end();iter2++) { + string temp = ""; + for(int i=0;i>1; + } + str.push_back(temp); + } + result.push_back(str); + } + } + void display(vector> result,vector> rn) { + int nrn = rn.size(); + + } + void sQueen( unsigned long long N, vector>& result, int n, vector current,unsigned long long last ,unsigned long long left,unsigned long long right) { + if (n ==N ) { + result.push_back(current); + return; + } + + + long pos = N&~(last|left|right); + while(pos) { + int p = pos&(~pos+1); + pos-=p; + current.push_back(p); + sQueen(N,result,n+p,current,last+p,(left+p)<<1,(right+p)>>1); + current.pop_back(); + } + + } +}; diff --git a/Week 02/id_508/LeetCode_070_508.cpp b/Week 02/id_508/LeetCode_070_508.cpp new file mode 100644 index 000000000..bcaa655cc --- /dev/null +++ b/Week 02/id_508/LeetCode_070_508.cpp @@ -0,0 +1,15 @@ +class Solution { + typedef unsigned long long ull; +public: + int climbStairs(int n) { + ull pprev=1,prev=1; + int cur = 0; + ull sum = 0; + while(cur++> combine(int n, int k) { + vector> result; + vector status; + combine(n,k,status,0,result); + return result; + + } + void combine(const int n, int k, vector& status,int ii, vector>& result ) { + if(0==k) { + result.push_back(status); + return; + } + for(int i=ii+1;i<=n;++i) { + if(!valid(i,status)) continue; + status.push_back(i); + combine(n,k-1,status,i,result); + status.pop_back(); + } + } + bool valid(int n, vector& status) { + for(auto i:status) { + if(i==n) return false; + } + return true; + } +}; diff --git a/Week 02/id_508/LeetCode_144_508.cpp b/Week 02/id_508/LeetCode_144_508.cpp new file mode 100644 index 000000000..c4245e495 --- /dev/null +++ b/Week 02/id_508/LeetCode_144_508.cpp @@ -0,0 +1,24 @@ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ +class Solution { +public: + vector preorderTraversal(TreeNode* root) { + vector result; + if(root) + preOrder(root,result); + return result; + } + void preOrder(const TreeNode* root, vector& result) { + //if(!root) return; + result.push_back(root->val); + if(root->left) preOrder(root->left,result); + if(root->right) preOrder(root->right,result); + } +}; diff --git a/Week 02/id_508/LeetCode_169_508.cpp b/Week 02/id_508/LeetCode_169_508.cpp new file mode 100644 index 000000000..5eac29347 --- /dev/null +++ b/Week 02/id_508/LeetCode_169_508.cpp @@ -0,0 +1,28 @@ +class Solution { +public: + int majorityElement(vector& nums) { + + return divide(nums,0,nums.size()-1); + + } + int divide(vector&nums,int i,int j) { + if(i==j) return nums[i]; + else { + int m1 = divide(nums,i,(i+j)/2); + int m2 = divide(nums,(i+j)/2+1,j); + return merge(nums,i,j,m1,m2); + + } + } + int merge(vector&nums,int i,int j,int m1,int m2) { + if(m1==m2) return m1; + + int cm1=0; + int cm2 = 0; + for(int ii=i;ii<=j;ii++) { + if(nums[ii]==m1) cm1++; + else if(nums[ii]==m2) cm2++; + } + return cm1>=cm2?m1:m2; + } +}; diff --git a/Week 02/id_523/LeetCode_46_523.cs b/Week 02/id_523/LeetCode_46_523.cs new file mode 100644 index 000000000..bbea89b50 --- /dev/null +++ b/Week 02/id_523/LeetCode_46_523.cs @@ -0,0 +1,31 @@ +public class Solution +{ + public IList> Permute(int[] nums) + { + List> result = new List>(); + for (int i = 0; i < nums.Length; i++) + { + Find(result, nums, new List { nums[i] }); + } + + return result; + } + + static void Find(List> result, int[] nums, List currentList) + { + if (currentList.Count == nums.Length) + { + result.Add(new List(currentList)); + return; + } + foreach (var item in nums) + { + if (!currentList.Contains(item)) + { + currentList.Add(item); + Find(result, nums, currentList); + currentList.RemoveAt(currentList.Count - 1); + } + } + } +} \ No newline at end of file diff --git a/Week 02/id_523/LeetCode_47.523.cs b/Week 02/id_523/LeetCode_47.523.cs new file mode 100644 index 000000000..ece1ad933 --- /dev/null +++ b/Week 02/id_523/LeetCode_47.523.cs @@ -0,0 +1,34 @@ +public class Solution +{ + public IList> PermuteUnique(int[] nums) + { + nums = nums.OrderBy(q => q).ToArray(); + + List> result = new List>(); + + Find(result, nums, new List { }); + + return result; + } + + static void Find(List> result, int[] nums, List currentList) + { + if (currentList.Count == nums.Length) + { + result.Add(currentList.Select(q => nums[q]).ToList()); + return; + } + + for (int i = 0; i < nums.Length; i++) + { + if (currentList.Contains(i)) continue; + + if (i > 0 && nums[i - 1] == nums[i] && !currentList.Contains(i - 1)) continue; + + currentList.Add(i); + Find(result, nums, currentList); + + currentList.RemoveAt(currentList.Count - 1); + } + } +} \ No newline at end of file diff --git a/Week 02/id_533/LeetCode_104_533.js b/Week 02/id_533/LeetCode_104_533.js new file mode 100644 index 000000000..9f8957b1f --- /dev/null +++ b/Week 02/id_533/LeetCode_104_533.js @@ -0,0 +1,60 @@ +// https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/ + +/** + * @param {TreeNode} root + * @return {number} + */ +// 递归遍历 时间复杂度O(logn) +var maxDepth = function(root) { + if (root === null) return 0; + return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; +}; + +// 节点 +function TreeNode (val) { + this.val = val; + this.left = null; + this.right = null; +} +// 二叉搜索树 +function BinarySearchTree () { + this.root = null; +} +// 添加节点 +BinarySearchTree.prototype.insert = function (val) { + if(val === null || val === undefined) return; + var node = new TreeNode(val); + if (!this.root) { + this.root = node; + return; + } + var cur = this._getTreeNode(val); + if (val < cur.val) + cur.left = node; + else + cur.right = node; +} +// 在树中遍历查找可以添加val的节点 +BinarySearchTree.prototype._getTreeNode = function (val, find = false) { + var cur = this.root; + while (true) { + if (val < cur.val) { + if (!cur.left) break; + cur = cur.left; + } + if (val >= cur.val) { + if (!cur.right) break; + cur = cur.right; + } + } + return cur; +} + +var bst = new BinarySearchTree(); +bst.insert(6); +bst.insert(1); +bst.insert(3); +bst.insert(4); +bst.insert(5); +bst.insert(8); +console.log(maxDepth(bst.root)); \ No newline at end of file diff --git a/Week 02/id_533/LeetCode_105_533.js b/Week 02/id_533/LeetCode_105_533.js new file mode 100644 index 000000000..073e90494 --- /dev/null +++ b/Week 02/id_533/LeetCode_105_533.js @@ -0,0 +1,25 @@ +// https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ + +/** + * @param {number[]} preorder + * @param {number[]} inorder + * @return {TreeNode} + */ +var buildTree = function(preorder, inorder) { + if(preorder.length === 0) return; + var root = new TreeNode(preorder[0]) + var mid = inorder.indexOf(preorder[0]); + root.left = buildTree(preorder.slice(1, mid + 1), inorder.slice(0, mid)) + root.right = buildTree(preorder.slice(mid+1), inorder.slice(mid+1)) + return root; +} + +// 节点 +function TreeNode (val) { + this.val = val; + this.left = null; + this.right = null; +} + +var result = buildTree([3,9,20,15,7], [9,3,15,20,7]); +console.log(result) \ No newline at end of file diff --git a/Week 02/id_533/LeetCode_111_533.js b/Week 02/id_533/LeetCode_111_533.js new file mode 100644 index 000000000..b93c6fb07 --- /dev/null +++ b/Week 02/id_533/LeetCode_111_533.js @@ -0,0 +1,62 @@ +// https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/ + +/** + * @param {TreeNode} root + * @return {number} + */ +// 递归遍历 时间复杂度O(logn) +// 当 root 节点左右孩子有一个为空时,返回不为空的孩子节点的深度 +var minDepth = function(root) { + if(root === null) return 0; + if(root.left === null || root.right === null) return minDepth(root.left) + minDepth(root.right) + 1; + return Math.min(minDepth(root.left), minDepth(root.right)) + 1; +}; + +// 节点 +function TreeNode (val) { + this.val = val; + this.left = null; + this.right = null; +} +// 二叉搜索树 +function BinarySearchTree () { + this.root = null; +} +// 添加节点 +BinarySearchTree.prototype.insert = function (val) { + if(val === null || val === undefined) return; + var node = new TreeNode(val); + if (!this.root) { + this.root = node; + return; + } + var cur = this._getTreeNode(val); + if (val < cur.val) + cur.left = node; + else + cur.right = node; +} +// 在树中遍历查找可以添加val的节点 +BinarySearchTree.prototype._getTreeNode = function (val, find = false) { + var cur = this.root; + while (true) { + if (val < cur.val) { + if (!cur.left) break; + cur = cur.left; + } + if (val >= cur.val) { + if (!cur.right) break; + cur = cur.right; + } + } + return cur; +} + +var bst = new BinarySearchTree(); +bst.insert(6); +bst.insert(1); +bst.insert(3); +bst.insert(4); +bst.insert(5); +bst.insert(8); +console.log(minDepth(bst.root)); \ No newline at end of file diff --git a/Week 02/id_533/LeetCode_144_533.js b/Week 02/id_533/LeetCode_144_533.js new file mode 100644 index 000000000..35f78f7d6 --- /dev/null +++ b/Week 02/id_533/LeetCode_144_533.js @@ -0,0 +1,96 @@ +// https://leetcode-cn.com/problems/binary-tree-preorder-traversal/ + +/** + * @param {TreeNode} root + * @return {number[]} + */ +// 递归遍历 +// 时间复杂度O(n) 空间复杂度O(logn) +var preorderTraversal = function(root) { + var result = []; + traversal(root); + return result; + function traversal (root) { + if (root === null) return; + result.push(root.val); + traversal(root.left); + traversal(root.right); + } +}; + +/** + * @param {TreeNode} root + * @return {number[]} + */ +// 莫里斯遍历 +// 时间复杂度O(n) 空间复杂度O(1) +var preorderTraversal = function(root) { + var result = [], + cur = root; + while (cur) { + result.push(cur.val) + if (cur.left) { + var pre = cur.left; + while (pre.right) pre = pre.right; + if (cur.right) pre.right = cur.right; + var tmp = cur; + cur = cur.left; + tmp = null; + } else { + cur = cur.right; + } + } + return result; +}; + +// 节点 +function TreeNode (val) { + this.val = val; + this.left = null; + this.right = null; +} + +// 二叉搜索树 +function SearchTree () { + this.root = null; +} + +// 添加节点 +SearchTree.prototype.insert = function (val) { + if(val === null || val === undefined) return; + var node = new TreeNode(val); + if (!this.root) { + this.root = node; + return; + } + var curNode = this._getTreeNode(val); + if (val < curNode.val) + curNode.left = node; + else + curNode.right = node; +} + +// 在树中遍历查找可以添加val的节点 +SearchTree.prototype._getTreeNode = function (val) { + var curNode = this.root; + while (true) { + if (val < curNode.val) { + if (!curNode.left) break; + curNode = curNode.left; + } + if (val >= curNode.val) { + if (!curNode.right) break; + curNode = curNode.right; + } + } + return curNode; +} + +var searchTree = new SearchTree(); +searchTree.insert(1); +searchTree.insert(null); +searchTree.insert(2); +searchTree.insert(3); + +var result = preorderTraversal(searchTree.root) +console.log(result); \ No newline at end of file diff --git a/Week 02/id_533/LeetCode_226_533.js b/Week 02/id_533/LeetCode_226_533.js new file mode 100644 index 000000000..92dce93d2 --- /dev/null +++ b/Week 02/id_533/LeetCode_226_533.js @@ -0,0 +1,70 @@ +// https://leetcode-cn.com/problems/invert-binary-tree/ + +/** + * @param {TreeNode} root + * @return {TreeNode} + */ +// 递归交换 +// 时间复杂度O(logn) +var invertTree = function(root) { + invert(root); + return root; + function invert (root) { + if (root === null) return; + var tmp = root.left; + root.left = root.right; + root.right = tmp; + invert(root.left); + invert(root.right); + } +}; + +// 节点 +function TreeNode (val) { + this.val = val; + this.left = null; + this.right = null; +} +// 二叉搜索树 +function BinarySearchTree () { + this.root = null; +} +// 添加节点 +BinarySearchTree.prototype.insert = function (val) { + if(val === null || val === undefined) return; + var node = new TreeNode(val); + if (!this.root) { + this.root = node; + return; + } + var cur = this._getTreeNode(val); + if (val < cur.val) + cur.left = node; + else + cur.right = node; +} +// 在树中遍历查找可以添加val的节点 +BinarySearchTree.prototype._getTreeNode = function (val, find = false) { + var cur = this.root; + while (true) { + if (val < cur.val) { + if (!cur.left) break; + cur = cur.left; + } + if (val >= cur.val) { + if (!cur.right) break; + cur = cur.right; + } + } + return cur; +} + +var bst = new BinarySearchTree(); +bst.insert(6); +bst.insert(1); +bst.insert(3); +bst.insert(4); +bst.insert(5); +bst.insert(8); +console.log(bst.root); +console.log(invertTree(bst.root)); \ No newline at end of file diff --git a/Week 02/id_533/LeetCode_22_533.js b/Week 02/id_533/LeetCode_22_533.js new file mode 100644 index 000000000..29c66003d --- /dev/null +++ b/Week 02/id_533/LeetCode_22_533.js @@ -0,0 +1,22 @@ +// https://leetcode-cn.com/problems/generate-parentheses/ + +/** + * @param {number} n + * @return {string[]} + */ +var generateParenthesis = function(n) { + var result = [] + generate(0, 0, n, '') + return result; + function generate(left, right, n, s) { + if (left === n && right ==n) { + result.push(s); + return; + } + if(left < n) generate(left + 1, right, n, s + '('); + if(left > right) generate(left, right + 1, n, s + ')'); + } +}; + +var result = generateParenthesis(3); +console.log(result); \ No newline at end of file diff --git a/Week 02/id_533/LeetCode_236_533.js b/Week 02/id_533/LeetCode_236_533.js new file mode 100644 index 000000000..633f92bf5 --- /dev/null +++ b/Week 02/id_533/LeetCode_236_533.js @@ -0,0 +1,67 @@ +// https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/ + +/** + * @param {TreeNode} root + * @param {TreeNode} p + * @param {TreeNode} q + * @return {TreeNode} + */ +var lowestCommonAncestor = function(root, p, q) { + return recurseTree(root, p, q) + function recurseTree (root, p, q) { + if (root === null || root === p || root === q) return root; // 递归终止条件 + var left = recurseTree(root.left, p, q); + var right = recurseTree(root.right, p, q); + return left ? (right ? root : left) : right + } +}; + +// 节点 +function TreeNode (val) { + this.val = val; + this.left = null; + this.right = null; +} +// 二叉搜索树 +function BinarySearchTree () { + this.root = null; +} +// 添加节点 +BinarySearchTree.prototype.insert = function (val) { + if(val === null || val === undefined) return; + var node = new TreeNode(val); + if (!this.root) { + this.root = node; + return; + } + var cur = this._getTreeNode(val); + if (val < cur.val) + cur.left = node; + else + cur.right = node; +} +// 在树中遍历查找可以添加val的节点 +BinarySearchTree.prototype._getTreeNode = function (val, find = false) { + var cur = this.root; + while (true) { + if (val < cur.val) { + if (!cur.left) break; + cur = cur.left; + } + if (val >= cur.val) { + if (!cur.right) break; + cur = cur.right; + } + } + return cur; +} + +var bst = new BinarySearchTree(); +bst.insert(45); +bst.insert(40); +bst.insert(50); +bst.insert(35); +bst.insert(42); +bst.insert(48); + +console.log(lowestCommonAncestor(bst.root, bst.root.left.left, bst.root.left.right)) \ No newline at end of file diff --git a/Week 02/id_533/LeetCode_242_533.js b/Week 02/id_533/LeetCode_242_533.js new file mode 100644 index 000000000..36245ddf4 --- /dev/null +++ b/Week 02/id_533/LeetCode_242_533.js @@ -0,0 +1,53 @@ +// https://leetcode-cn.com/problems/valid-anagram/description/ + +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +// 暴力求解 +var isAnagram = function(s, t) { + if (s.length !== t.length) return false; + return s.split('').sort().join('') === t.split('').sort().join(''); +}; + +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +// 哈希表(针对只有字母的字符串) +// 时间复杂度O(n) 空间复杂度O(1) +var isAnagram = function(s, t) { + if (s.length !== t.length) return false; + var length = s.length, + charCount = new Array(26).fill(0) + for (var i = 0; i < length; i++) { + charCount[s[i].charCodeAt() - 97]++; + charCount[t[i].charCodeAt() - 97]--; + } + for(var count in charCount) { + if (charCount[count] !== 0) return false; + } + return true; +}; + +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +// 哈希表(通用) +// 时间复杂度O(n) 空间复杂度O(1) +var isAnagram = function(s, t) { + if (s.length !== t.length) return false; + var length = s.length, + charCount = {}; + for (var i = 0; i < length; i++) charCount[s[i]] = charCount[s[i]] ? ++charCount[s[i]] : 1; + for (var i = 0; i < length; i++) charCount[t[i]] = charCount[t[i]] ? --charCount[t[i]] : 1; + for (count in charCount) if(charCount[count] !== 0) return false; + return true; +}; + +console.log(isAnagram('anagram', 'nagaram')); +console.log(isAnagram('rat', 'car')); \ No newline at end of file diff --git a/Week 02/id_533/LeetCode_429_533.js b/Week 02/id_533/LeetCode_429_533.js new file mode 100644 index 000000000..c0f4d4b9b --- /dev/null +++ b/Week 02/id_533/LeetCode_429_533.js @@ -0,0 +1,44 @@ +// https://leetcode-cn.com/problems/n-ary-tree-postorder-traversal/ + +/** + * @param {Node} root + * @return {number[]} + */ +// 递归遍历 +// 时间复杂度O(n) 空间复杂度O(logn) +var postorder = function(root) { + var result = []; + traversal(root); + return result; + function traversal (root) { + if (root === null) return; + var length = root.length; + for (var i = 0; i < length; i++) { + traversal(root.children[i]) + } + result.push(root.val); + } +}; + +// 迭代遍历 +var postorder = function(root) { + if(root === null) return []; + var result = [], + stack = [], + prev = null; + stack.push(root); + while(stack.length > 0) { + var cur = stack.top(); + while (cur.children.length === 0 || cur.children.top() === prev) { + prev = stack.pop(); + result.push(prev.val) + } + for (var i = cur.children.length - 1; i >= 0 ; i--) { + stack.push(cur.children[i]) + } + } +}; + +Array.prototype.top = function () { + return this[this.length - 1] +} diff --git a/Week 02/id_533/LeetCode_46_533.js b/Week 02/id_533/LeetCode_46_533.js new file mode 100644 index 000000000..2678ff120 --- /dev/null +++ b/Week 02/id_533/LeetCode_46_533.js @@ -0,0 +1,29 @@ +// https://leetcode-cn.com/problems/permutations/ + +/** + * @param {number[]} nums + * @return {number[][]} + */ +var permute = function(nums) { + var result = []; + rpermute(nums, 0); + return result; + function rpermute(nums, begin) { + if (begin === nums.length - 1) { + result.push(nums.join(',').split(',')) + return; + } + var origin = nums.slice(0) + for (var i = begin; i < nums.length; i++) { + var tmp = nums[i]; + nums[i] = nums[begin]; + nums[begin] = tmp; + rpermute(nums, begin + 1) + nums = origin; + } + } +}; + +console.log(permute([1, 2, 3])) + + diff --git a/Week 02/id_533/LeetCode_49_533.js b/Week 02/id_533/LeetCode_49_533.js new file mode 100644 index 000000000..f7fb7157c --- /dev/null +++ b/Week 02/id_533/LeetCode_49_533.js @@ -0,0 +1,33 @@ +// https://leetcode-cn.com/problems/group-anagrams/ + +/** + * @param {string[]} strs + * @return {string[][]} + */ +// 按排序数组分类 +// 考虑到排序的时间复杂度 所以时间复杂度是高于O(n)的 +var groupAnagrams = function(strs) { + var length = strs.length, + charsObj = {}, + charsArr = [] + for (var i = 0; i < length; i++) { + var key = strs[i].split('').sort().join(''); + if(charsObj[key] === undefined) charsObj[key] = []; + charsObj[key].push(strs[i]) + } + for (chars in charsObj) charsArr.push(charsObj[chars]); + return charsArr; +}; +console.log(groupAnagrams(["eat", "tea", "tan", "ate", "nat", "bat"])) + +// 较慢的写法 +var groupAnagrams = function(strs) { + var length = strs.length, + charsObj = {}; + for (var i = 0; i < length; i++) { + var key = strs[i].split('').sort().join(''); + if(charsObj[key] === undefined) charsObj[key] = []; + charsObj[key].push(strs[i]) + } + return Object.values(charsObj); +}; diff --git a/Week 02/id_533/LeetCode_589_533.js b/Week 02/id_533/LeetCode_589_533.js new file mode 100644 index 000000000..b92c5bcfe --- /dev/null +++ b/Week 02/id_533/LeetCode_589_533.js @@ -0,0 +1,21 @@ +// https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/ + +/** + * @param {Node} root + * @return {number[]} + */ +// 递归遍历 +// 时间复杂度O(n) 空间复杂度O(logn) +var preorder = function(root) { + var result = []; + traversal(root); + return result; + function traversal (root) { + result.push(root.val); + if (root === null) return; + var length = root.length; + for (var i = 0; i < length; i++) { + traversal(root.children[i]) + } + } +}; \ No newline at end of file diff --git a/Week 02/id_533/LeetCode_590_533.js b/Week 02/id_533/LeetCode_590_533.js new file mode 100644 index 000000000..579c7534a --- /dev/null +++ b/Week 02/id_533/LeetCode_590_533.js @@ -0,0 +1,24 @@ +// https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/ + +/** + * @param {Node} root + * @return {number[][]} + */ +// 迭代法 +// 时间复杂度O(n) 空间复杂度O(n) +var levelOrder = function(root) { + if (root === null) return []; + var result = [], + queue = []; + queue.push({ level: 0, node: root }); + while (queue.length > 0) { + var cur = queue.shift(); + var level = cur.level; + if (!result[level]) result[level] = []; + result[level].push(cur.node.val); + for(var child of cur.node.children) { + queue.push({ level: level + 1, node: child} ) + } + } + return result; +}; \ No newline at end of file diff --git a/Week 02/id_533/LeetCode_70_533.js b/Week 02/id_533/LeetCode_70_533.js new file mode 100644 index 000000000..0cac06a46 --- /dev/null +++ b/Week 02/id_533/LeetCode_70_533.js @@ -0,0 +1,53 @@ +// 题目 https://leetcode-cn.com/problems/climbing-stairs/ + +/** + * @param {number} n + * @return {number} + */ +// 暴力破解法 时间复杂度O(2^n) 空间复杂度O(n) +var climbStairs = function (n) { + if (n < 3) return n; + return climbStairs(n - 1) + climbStairs(n - 2) +}; + +/** + * @param {number} n + * @return {number} + */ +// 暴力破解法的时间复杂度太高了 可以进行优化 缓存已经计算过的结果 +// 此时,时间复杂度O(n) 空间复杂度O(n) +// 优化过后在Leetc上运行 n比较大时会出现误差-->内存溢出 +// 老师的解答:解法方法就是高精度类型,类似java里的 BigInt 和 BigDecimal,底层实现就是维护一个数组来模拟非常大的数字的运算 +// https://zhuanlan.zhihu.com/p/56193640 +// https://leetcode-cn.com/problems/plus-one/(做完回来写感想) +var climbStairs = function (n) { + var caches = []; + return climbing(n) + function climbing (n) { + if (n < 3) return n; + if (caches[n]) return caches[n]; + caches[n] = climbing(n - 1) + climbing(n - 2); + return caches[n]; + } +}; + +/** + * @param {number} n + * @return {number} + */ +// 斐波那契数列的非递归解法 +// 时间复杂度O(n) 空间复杂度O(1) +var climbStairs = function (n) { + if (n < 3) return n; + var first = 1; + var second = 2; + for (var i = 3; i <= n; i++) { + var third = first + second; + first = second; + second = third; + } + return second; +}; + +var result = climbStairs(10) +console.log(result) diff --git a/Week 02/id_533/LeetCode_77_533.js b/Week 02/id_533/LeetCode_77_533.js new file mode 100644 index 000000000..093daa5ce --- /dev/null +++ b/Week 02/id_533/LeetCode_77_533.js @@ -0,0 +1,24 @@ +// https://leetcode-cn.com/problems/combinations/ + +/** + * @param {number} n + * @param {number} k + * @return {number[][]} + */ +// 递归 +var combine = function(n, k) { + var res = [] + rcombine(n, k, 1, []) + return res + function rcombine (n, k, l, pre) { + if (pre.length === k) { + res.push(pre) + return + } + for (var i = l; i <= n - (k - pre.length) + 1; i++) { + rcombine(n, k, i + 1, pre.concat([i])) + } + } +}; + +console.log(combine(4, 4)); \ No newline at end of file diff --git a/Week 02/id_533/LeetCode_94_533.js b/Week 02/id_533/LeetCode_94_533.js new file mode 100644 index 000000000..cb0c4cf10 --- /dev/null +++ b/Week 02/id_533/LeetCode_94_533.js @@ -0,0 +1,118 @@ +// https://leetcode-cn.com/problems/binary-tree-inorder-traversal/ + +/** + * @param {TreeNode} root + * @return {number[]} + */ +// 递归遍历 +// 时间复杂度O(n) 空间复杂度O(logn) +var inorderTraversal = function(root) { + var result = []; + travelsal(root); + return result; + function travelsal (root) { + if(root === null) return; + travelsal(root.left); + result.push(root.val); + travelsal(root.right); + } +}; + +/** + * @param {TreeNode} root + * @return {number[]} + */ +// 莫里斯遍历 +// 时间复杂度O(n) 空间复杂度O(1) +var inorderTraversal = function(root) { + var result = [], + cur = root; + while (cur) { + if (cur.left) { + var pre = cur.left; + while (pre.right) pre = pre.right; + pre.right = curr; + var tmp = curr; + curr = curr.left; + tmp.left = null; + } else { + result.push(cur.val); + cur = cur.right; + } + } + return result; +}; + +/** + * @param {TreeNode} root + * @return {number[]} + */ +// 基于栈遍历 +// 时间复杂度O(n) 空间复杂度O(n) +var inorderTraversal = function(root) { + var result = [], + stack = [], + cur = root; + while (cur || stack.length > 0) { + while (cur) { + stack.push(cur); + cur = cur.left; + } + cur = stack.pop(); + result.push(cur.val); + cur = cur.right; + } + return result; +}; + +// 节点 +function TreeNode (val) { + this.val = val; + this.left = null; + this.right = null; +} + +// 二叉搜索树 +function SearchTree () { + this.root = null; +} + +// 添加节点 +SearchTree.prototype.insert = function (val) { + if(val === null || val === undefined) return; + var node = new TreeNode(val); + if (!this.root) { + this.root = node; + return; + } + var cur = this._getTreeNode(val); + if (val < cur.val) + cur.left = node; + else + cur.right = node; +} + +// 在树中遍历查找可以添加val的节点 +SearchTree.prototype._getTreeNode = function (val) { + var cur = this.root; + while (true) { + if (val < cur.val) { + if (!cur.left) break; + cur = cur.left; + } + if (val >= cur.val) { + if (!cur.right) break; + cur = cur.right; + } + } + return cur; +} + +var searchTree = new SearchTree(); +searchTree.insert(1); +searchTree.insert(null); +searchTree.insert(2); +searchTree.insert(3); + +var result = inorderTraversal(searchTree.root) +console.log(result); diff --git a/Week 02/id_533/LeetCode_98_533.js b/Week 02/id_533/LeetCode_98_533.js new file mode 100644 index 000000000..361c60c39 --- /dev/null +++ b/Week 02/id_533/LeetCode_98_533.js @@ -0,0 +1,86 @@ +// https://leetcode-cn.com/problems/validate-binary-search-tree/ + +/** + * @param {TreeNode} root + * @param {TreeNode} prev + * @param {TreeNode} next + * @return {boolean} + */ +// 递归 +var isValidBST = function(root, prev, next) { + if (root === null) return true; + if(prev && prev.val >= root.val) return false; + if(next && next.val <= root.val) return false; + return isValidBST(root.left, prev, root) && isValidBST(root.right, root, next); +}; + +/** + * @param {TreeNode} root + * @return {boolean} + */ +// 中序遍历 +var isValidBST = function(root) { + var stack = [], + prev = Number.MIN_VALUE, + cur = root; + while (cur || stack.length > 0) { + while (cur) { + stack.push(cur); + cur = cur.left; + } + cur = stack.pop(); + if(prev >= cur.val) return false; + prev = cur.val; + cur = cur.right + } + return true; +}; + +// 节点 +function TreeNode (val) { + this.val = val; + this.left = null; + this.right = null; +} +// 二叉搜索树 +function BinarySearchTree () { + this.root = null; +} +// 添加节点 +BinarySearchTree.prototype.insert = function (val) { + if(val === null || val === undefined) return; + var node = new TreeNode(val); + if (!this.root) { + this.root = node; + return; + } + var cur = this._getTreeNode(val); + if (val < cur.val) + cur.left = node; + else + cur.right = node; +} +// 在树中遍历查找可以添加val的节点 +BinarySearchTree.prototype._getTreeNode = function (val, find = false) { + var cur = this.root; + while (true) { + if (val < cur.val) { + if (!cur.left) break; + cur = cur.left; + } + if (val >= cur.val) { + if (!cur.right) break; + cur = cur.right; + } + } + return cur; +} + +var bst = new BinarySearchTree(); +bst.insert(6); +bst.insert(1); +bst.insert(3); +bst.insert(4); +bst.insert(5); +bst.insert(8); +console.log(isValidBST(bst.root)); diff --git a/Week 02/id_543/LeetCode_242_543.java b/Week 02/id_543/LeetCode_242_543.java new file mode 100644 index 000000000..6ae227f54 --- /dev/null +++ b/Week 02/id_543/LeetCode_242_543.java @@ -0,0 +1,71 @@ +class Solution { + + //哈希表 43ms + public boolean isAnagram(String s, String t) { + if(s==null&&t==null){ + return true; + } + if(s.isEmpty()&&t.isEmpty()){ + return true; + } + if(s.length()!=t.length()){ + return false; + } + boolean result = true; + char[] schar = s.toCharArray(); + char[] tchar = t.toCharArray(); + Map resultMap = new HashMap(); + for(int i = 0;i> groupAnagrams(String[] strs) { + if(strs == null){ + return null; + } + List> reslutList = new ArrayList>(); + Map> map = new HashMap>(); + for (String s:strs){ + String tmp = s; + char[] tmpChars = s.toCharArray(); + Arrays.sort(tmpChars); + String tmpp = new String(tmpChars); + List tmpList = new ArrayList(); + if(map.containsKey(tmpp)){ + tmpList = map.get(tmpp); + } + tmpList.add(s); + map.put(tmpp,tmpList); + } + for(List anagramList:map.values()){ + reslutList.add(anagramList); + } + return reslutList; + } + + + //官方题解 + public List> groupAnagrams(String[] strs) { + if (strs.length == 0) return new ArrayList(); + Map ans = new HashMap(); + for (String s : strs) { + char[] ca = s.toCharArray(); + Arrays.sort(ca); + String key = String.valueOf(ca); + if (!ans.containsKey(key)) ans.put(key, new ArrayList()); + ans.get(key).add(s); + } + return new ArrayList(ans.values()); + } + + + /** + * 1.map中对象修改,无需再重新再put一遍 + * 2.new ArrayList(ans.valuse()) + * 3.new String(char[]) 与string.values(char[])都可将char[]转为String。char[].toString=[C@4367e003。 + * @后是数组的哈希码,默认情况下,该哈希码是数组的内存地址。 + */ +} \ No newline at end of file diff --git a/Week 02/id_548/LeetCode_144_548.java b/Week 02/id_548/LeetCode_144_548.java new file mode 100644 index 000000000..677183930 --- /dev/null +++ b/Week 02/id_548/LeetCode_144_548.java @@ -0,0 +1,30 @@ +import java.util.ArrayList; +import java.util.List; + + +class TreeNode { + int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { val = x; } +} + +public class LeetCode_144_548 { + + public List preorderTraversal(TreeNode root) { + List list = new ArrayList<> (); + helper(root, list); + return list; + } + + public void helper(TreeNode node, List res) { + if (node != null) { + res.add(node.val); + helper(node.left, res); + helper(node.right, res); + } + } + +} + + diff --git a/Week 02/id_548/LeetCode_94_548.java b/Week 02/id_548/LeetCode_94_548.java new file mode 100644 index 000000000..d2c16a345 --- /dev/null +++ b/Week 02/id_548/LeetCode_94_548.java @@ -0,0 +1,25 @@ +import java.util.ArrayList; +import java.util.List; + +public class LeetCode_94_548 { + class Solution { + public List inorderTraversal(TreeNode root) { + if (root == null) { + return new ArrayList<> (); + } + List result = new ArrayList<>(); + result.addAll(inorderTraversal(root.left)); + result.add(root.val); + result.addAll(inorderTraversal(root.right)); + return result; + } + + } +} + +class TreeNode { + int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { val = x; } + } diff --git a/Week 02/id_558/HashMapAnalysis.java b/Week 02/id_558/HashMapAnalysis.java new file mode 100644 index 000000000..4c1996d21 --- /dev/null +++ b/Week 02/id_558/HashMapAnalysis.java @@ -0,0 +1,13 @@ +package Week02; + +/** + * HashMap源码分析 + * 参见:https://zhuanlan.zhihu.com/p/21673805 + * 2019/10/27. + */ +public class HashMapAnalysis { + //1、继承关系 + //2、存储结构:数组(哈希表) + 链表 + 红黑树(jdk1.8) + //3、扩容、负载因子 + //4、线程安全 +} diff --git a/Week 02/id_558/LeetCode_17_558.java b/Week 02/id_558/LeetCode_17_558.java new file mode 100644 index 000000000..05543bd89 --- /dev/null +++ b/Week 02/id_558/LeetCode_17_558.java @@ -0,0 +1,52 @@ +package Week02; + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * @Date 2019/10/27. + * @see https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/ + * 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。 + */ + +public class LeetCode_17_558 { + /** + * 思想:只关注当前层级不要人肉递归 + * 步骤拆解: + * 1、选择合适的容器,存储数据 + * 2、模型转换(左右括号问题) + */ + public List letterCombinations(String digits) { + if (digits == null || digits.length() == 0) { + return new ArrayList(); + } + HashMap letterMap = new HashMap(); + letterMap.put('2', "abc"); + letterMap.put('3', "def"); + letterMap.put('4', "ghi"); + letterMap.put('5', "jkl"); + letterMap.put('6', "mno"); + letterMap.put('7', "pqrs"); + letterMap.put('8', "tuv"); + letterMap.put('9', "wxyz"); + + char[] chars = digits.toCharArray(); + List list = new ArrayList(); + genLetterCombinations(list, letterMap, chars, "", 0); + return list; + } + + public void genLetterCombinations(List list, HashMap letterMap, char[] chars, String s, int level) { + if (level == chars.length) { + list.add(s); + return; + } + String currentLetter = letterMap.get(chars[level]); + for (int j = 0; j < currentLetter.length(); j++) { + genLetterCombinations(list, letterMap, chars, s + currentLetter.charAt(j), level + 1); + + } + } +} diff --git a/Week 02/id_558/LeetCode_429_558.java b/Week 02/id_558/LeetCode_429_558.java new file mode 100644 index 000000000..45d068720 --- /dev/null +++ b/Week 02/id_558/LeetCode_429_558.java @@ -0,0 +1,57 @@ +package Week02; + + +import java.util.ArrayList; +import java.util.List; + +/** + * @Date 2019/10/27. + * @see https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/ + * N叉树的层序遍历。 + */ + +public class LeetCode_429_558 { + /** + * 解题思路: + * 1、递归(树的前中后序遍历) + * 2、遍历的时候带上层级 + */ + public List> levelOrder(Node root) { + List> result = new ArrayList>(); + traversal(result, root, 0); + return result; + } + + public void traversal(List> result, Node root, int level) { + if (root == null) return; + while (level >= result.size()) { + result.add(new ArrayList()); + } + List list = result.get(level); + if (list == null) { + list = new ArrayList(); + result.add(level, list); + } + list.add(root.val); + List children = root.children; + if (children != null) { + for (int i = 0; i < children.size(); i++) { + traversal(result, children.get(i), level + 1); + } + } + } + + + class Node { + public int val; + public List children; + + public Node() { + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } + } +} diff --git a/Week 02/id_558/LeetCode_49_558.java b/Week 02/id_558/LeetCode_49_558.java new file mode 100644 index 000000000..cb4f4a927 --- /dev/null +++ b/Week 02/id_558/LeetCode_49_558.java @@ -0,0 +1,41 @@ +package Week02; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +/** + * @Date 2019/10/27. + * @see https://leetcode-cn.com/problems/group-anagrams/ + * 给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 + */ +public class LeetCode_49_558 { + + /** + * 哈希表法 + * 解题思路:对key排序保证唯一性 + */ + public static List> groupAnagrams(String[] strs) { + if (strs == null || strs.length == 0) { + return new ArrayList>(); + } + HashMap> map = new HashMap>(); + for (String str : strs) { + char[] chars = str.toCharArray(); + Arrays.sort(chars); + String key = String.valueOf(chars); + if (!map.containsKey(key)) { + map.put(key, new ArrayList()); + } + map.get(key).add(str); + } + return new ArrayList>(map.values()); + } + + public static void main(String[] args) { +// String[] arr = {"eat", "tea", "tan", "ate", "nat", "bat"}; + String[] arr = {"", "", ""}; + System.out.println(groupAnagrams(arr)); + } +} diff --git a/Week 02/id_558/LeetCode_77_558.java b/Week 02/id_558/LeetCode_77_558.java new file mode 100644 index 000000000..2a28f2996 --- /dev/null +++ b/Week 02/id_558/LeetCode_77_558.java @@ -0,0 +1,46 @@ +package Week02; + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +/** + * @Date 2019/10/27. + * @see https://leetcode-cn.com/problems/combinations/ + * 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 + */ + +public class LeetCode_77_558 { + /** + * 核心:确定终止条件(terminator) + */ + public static List> combine(int n, int k) { + List> lists = new ArrayList>(); + List list = new ArrayList(); + traversal(lists, list, n, k, 1); + return lists; + } + + private static void traversal(List> lists, List list, int n, int k, int position) { + //terminator + if (list.size() == k) { + lists.add(new ArrayList(list)); + return; + } + //process logic + for (int i = position; i <= n; i++) { + list.add(i); + //drill down + traversal(lists, list, n, k, i + 1); + //reset state + list.remove(new Integer(i)); + } + } + + + public static void main(String[] args) { + System.out.println(combine(4, 2)); + } +} \ No newline at end of file diff --git a/Week 02/id_558/NOTE.md b/Week 02/id_558/NOTE.md index a6321d6e2..b8a2e2c4c 100644 --- a/Week 02/id_558/NOTE.md +++ b/Week 02/id_558/NOTE.md @@ -1,4 +1,47 @@ # NOTE +#### 知识回顾 + +* 数据结构:哈希表、树 +* 算法思想:递归回溯、分治 +* 递归步骤: + * terminator + * process logic + * drill down + * reset status + > 1. 不要人肉进行递归(最大误区) + >2. 找到最近最简方法,将其拆解成可重复解决的问题(重复子问题) + >3. 数学归纳法思维 +* 分治步骤: + * recursion terminator + * prepare data + * conquer subproblems + * process and generate the final result + * revert the current level state + + +#### 处理输出 +* 练习题目 + * [有效字符异位词](https://leetcode-cn.com/problems/valid-anagram/description/) + * [字母异位词分组](https://leetcode-cn.com/problems/group-anagrams/) + * [两数之和](https://leetcode-cn.com/problems/two-sum/) + * [树的中序遍历](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/) + * [树的前序遍历](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/) + * [N叉树的后续遍历](https://leetcode-cn.com/problems/n-ary-tree-postorder-traversal/) + * [N叉树的前序遍历](https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/description/) + * [N叉树的层序遍历](https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/) + * [组合](https://leetcode-cn.com/problems/combinations/) + * [括号生成](https://leetcode-cn.com/problems/generate-parentheses/) + * [pow(x,n)](https://leetcode-cn.com/problems/powx-n/) + * [子集](https://leetcode-cn.com/problems/subsets/) + * [求众数](https://leetcode-cn.com/problems/majority-element/description/) + * [电话号码字母组合](https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/) +#### 疑难点 +* N皇后问题 +* 树的生成(补充练习) +#### 总结改进 +* 难于理解的题目,强行理解记忆,重复练习直至融会贯通 +* 练习四部练习法第四部:test case +* 难题强化练习s diff --git a/Week 02/id_563/Leecode_169_563.go b/Week 02/id_563/Leecode_169_563.go new file mode 100644 index 000000000..abba8a261 --- /dev/null +++ b/Week 02/id_563/Leecode_169_563.go @@ -0,0 +1,8 @@ +/** +* 用sort的话非常巧妙好记,而且看了下复杂度还可以 +*/ + +func majorityElement(nums []int) int { + sort.Ints(nums) + return nums[len(nums)/2] +} diff --git a/Week 02/id_563/Leecode_94_563.go b/Week 02/id_563/Leecode_94_563.go new file mode 100644 index 000000000..86fddd694 --- /dev/null +++ b/Week 02/id_563/Leecode_94_563.go @@ -0,0 +1,22 @@ +/** + * 用了helper的方法 + * 学了一下6.2的染色思路 + */ +func inorderTraversal(root *TreeNode) []int { + ret := make([]int, 0) + if root == nil { + return ret + } + stack := list.New() + for root != nil || stack.Len() != 0 { + for root != nil { + stack.PushBack(root) + root = root.Left + } + root = stack.Back().Value.(*TreeNode) + ret = append(ret, root.Val) + stack.Remove(stack.Back()) + root = root.Right + } + return ret +} diff --git a/Week 02/id_568/LeetCode_242_568.java b/Week 02/id_568/LeetCode_242_568.java new file mode 100644 index 000000000..6ea10e4cb --- /dev/null +++ b/Week 02/id_568/LeetCode_242_568.java @@ -0,0 +1,24 @@ +package com.leetcode.kelvin; + +/** + * @author kelvin + * @date 2019/10/27 10:17 PM + */ +public class LeetCode_242_568 { + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + int[] counter = new int[26]; + for (int i = 0; i < s.length(); i++) { + counter[s.charAt(i) - 'a']++; + counter[t.charAt(i) - 'a']--; + } + for (int count : counter) { + if (count != 0) { + return false; + } + } + return true; + } +} diff --git a/Week 02/id_568/LeetCode_94_568.java b/Week 02/id_568/LeetCode_94_568.java new file mode 100644 index 000000000..6a0b50f70 --- /dev/null +++ b/Week 02/id_568/LeetCode_94_568.java @@ -0,0 +1,38 @@ +package com.leetcode.kelvin; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author kelvin + * @date 2019/10/27 11:00 PM + */ +public class LeetCode_94_568 { + public List inoorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + helper(root, res); + return res; + } + + public void helper(TreeNode root, List res) { + if (root != null) { + if (root.left != null) { + helper(root.left, res); + } + res.add(root.val); + if (root.right != null) { + helper(root.right, res); + } + } + } +} + +class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } +} diff --git a/Week 02/id_573/NOTE.md b/Week 02/id_573/NOTE.md index a6321d6e2..600e98efb 100644 --- a/Week 02/id_573/NOTE.md +++ b/Week 02/id_573/NOTE.md @@ -1,4 +1,80 @@ # NOTE - +###树相关 +一,树 + 树(Tree)是n(n≥0)个结点的有限集。在任意一棵非空树中: + (1)有且仅有一个特定的被称为根(Root)的结点; + (2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1,T2,…,Tm,其中每一个集合本身又是一棵树,并且称为根的子树(SubTree)。 + 结点的度(Degree):结点拥有的子树数称为结点的度(Degree)。度为0的结点称为叶子(Leaf)或终端结点。度不为0的结点称为非终端结点或分支结点。 + 树的度:是树内各结点的度的最大值。 + 孩子和双亲:结点的子树的根称为该结点的孩子(Child),相应地,该结点称为孩子的双亲(Parent)。 + 结点的层次(Level):是从根结点开始计算起,根为第一层,根的孩子为第二层,依次类推。树中结点的最大层次称为树的深度(Depth)或高度。 + 如果将树中结点的各子树看成从左至右是有次序的(即不能互换),则称该树为有序树,否则称为无序树。 + + 树有多个节点(node),用以储存元素。某些节点之间存在一定的关系,用连线表示,连线称为边(edge)。边的上端节点称为父节点,下端称为子节点。树像是一个不断分叉的树根。 + 每个节点可以有多个子节点(children),而该节点是相应子节点的父节点(parent)。比如说,3,5是6的子节点,6是3,5的父节点;1,8,7是3的子节点, 3是1,8,7的父节点。树有一个没有父节点的节点,称为根节点(root),如图中的6。没有子节点的节点称为叶节点(leaf),比如图中的1,8,9,5节点。从图中还可以看到,上面的树总共有4个层次,6位于第一层,9位于第四层。树中节点的最大层次被称为深度。也就是说,该树的深度(depth)为4。 + +二,二叉树 + + 二叉树(Binary Tree)的特点是每个结点至多具有两棵子树(即在二叉树中不存在度大于2的结点),并且子树之间有左右之分。 + 二叉树的性质: + (1)、在二叉树的第i层上至多有2i-1个结点(i≥1)。 + (2)、深度为k的二叉树至多有2k-1个结点(k≥1)。 + (3)、对任何一棵二叉树,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1。 + +三,二叉查找树(左<中<右) + + 我们从一种特殊的、使用很广泛的二叉树入手:二叉查找树。 + 二叉查找树的性质: + (1)、若它的左子树不为空,则左子树上所有结点的值均小于它的根结点的值; + (2)、若它的右子树不为空,则右子树上所有结点的值均大于它的根结点的值; + (3)、它的左、右子树也分别为二叉查找树。 + 用一句话概括,二叉查找树的特点是,一个节点的左子节点的关键字值小于这个节点,右子节点的关键字值大于或等于这个父节点。 + + 二叉查找树的基本操作是查找,插入,删除,遍历,下面一一介绍: + 1,查找(search) + 我们已经知道,二叉搜索树的特点是左子节点小于父节点,右子节点大于或等于父节点。查找某个节点时,先从根节点入手,如果该元素值小于根节点,则转向左子节点,否则转向右子节点,以此类推,直到找到该节点,或者到最后一个叶子节点依然没有找到,则证明树中没有该节点 + 2,插入(insert) + 插入一个新节点首先要确定插入的位置,关键思路是确定新节点父节点所在的位置。 + 3,删除(delete) + 删除BST中的一个节点是最麻烦的操作,总结一下大概下面两种方法: + Case 1:删除点没有左孩子,这是只需要将该节点的父节点和当前节点的有孩子相连即可 + Case2:删除点有左孩子.这种情况下先找到当前节点的左子树的最右节点,因为一个节点的左子树的最右节点也比右子树的最左节点小,把最右节点复制给删除点,然后删除最右节点 + + +###递归 + + 递归,就是在运行的过程中调用自己。 + 递归关系就是实体自己和自己建立关系。 +构成递归需具备的条件: + + 1. 子问题须与原始问题为同样的事,且更为简单; + 2. 不能无限制地调用本身,须有个出口,化简为非递归状况处理。 + + 在数学和计算机科学中,递归指由一种(或多种)简单的基本情况定义的一类对象或方法,并规定其他所有情况都能被还原为其基本情况。 + + 例如,下列为某人祖先的递归定义: + 某人的双亲是他的祖先(基本情况)。某人祖先的双亲同样是某人的祖先(递归步骤)。斐波纳契数列(Fibonacci Sequence),又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21..... I [1] + + 斐波纳契数列是典型的递归案例: + Fib(0) = 1 [基本情况] Fib(1) = 1 [基本情况] 对所有n > 1的整数:Fib(n) = (Fib(n-1) + Fib(n-2)) [递归定义] 尽管有许多数学函数均可以递归表示,但在实际应用中,递归定义的高开销往往会让人望而却步。例如: + 阶乘(1) = 1 [基本情况] 对所有n > 1的整数:阶乘(n) = (n * 阶乘(n-1)) [递归定义] 一种便于理解的心理模型,是认为递归定义对对象的定义是按照“先前定义的”同类对象来定义的。例如:你怎样才能移动100个箱子?答案:你首先移动一个箱子,并记下它移动到的位置,然后再去解决较小的问题:你怎样才能移动99个箱子?最终,你的问题将变为怎样移动一个箱子,而这时你已经知道该怎么做的。 + + 如此的定义在数学中十分常见。例如,集合论对自然数的正式定义是:1是一个自然数,每个自然数都有一个后继,这一个后继也是自然数。 + + 德罗斯特效应 + 德罗斯特效应是递归的一种视觉形式。图中女性手持的物体中有一幅她本人手持同一物体的小图片,进而小图片中还有更小的一幅她手持同一物体的图片,依此类推。 + 又例如,我们在两面相对的镜子之间放一根正在燃烧的蜡烛,我们会从其中一面镜子里看到一根蜡烛,蜡烛后面又有一面镜子,镜子里面又有一根蜡烛……这也是递归的表现。 + +####递归应用 + 递归算法一般用于解决三类问题: + (1)数据的定义是按递归定义的。(Fibonacci函数) + (2)问题解法按递归算法实现。 + 这类问题虽则本身没有明显的递归结构,但用递归求解比迭代求解更简单,如Hanoi问题。 + (3)数据的结构形式是按递归定义的。 + 如二叉树、广义表等,由于结构本身固有的递归特性,则它们的操作可递归地描述。 + + 递归的缺点: + 递归算法解题相对常用的算法如普通循环等,运行效率较低。因此,应该尽量避免使用递归,除非没有更好的算法或者某种特定情况,递归更为适合的时候。在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。 + 递归典型问题: 梵塔问题(汉诺塔问题) \ No newline at end of file diff --git a/Week 02/id_573/leetcode_1.573.go b/Week 02/id_573/leetcode_1.573.go new file mode 100644 index 000000000..a1c7c7c59 --- /dev/null +++ b/Week 02/id_573/leetcode_1.573.go @@ -0,0 +1,26 @@ +package main + +import "fmt" + +func twoSum(nums []int, target int) []int { + var numMap map[int]int + numMap = make(map[int]int) + for i := 0; i < len(nums); i++ { + var difference = target - nums[i] + if v, ok := numMap[difference]; ok { + return []int{v, i} + } + _, oks := numMap[nums[i]] + if oks { + continue + } + numMap[nums[i]] = i + } + + return []int{} +} + +func main() { + var nums = []int{2, 3, 4, 4, 0, 1, 8, 5, 6, 5, 6, 7, 8} + fmt.Println(twoSum(nums, 9)) +} diff --git a/Week 02/id_573/leetcode_1_573.java b/Week 02/id_573/leetcode_1_573.java new file mode 100644 index 000000000..e1f850d87 --- /dev/null +++ b/Week 02/id_573/leetcode_1_573.java @@ -0,0 +1 @@ +class Solution { public int[] twoSum(int[] nums, int target) { Map map = new HashMap<>(); for (int i = 0; i < nums.length; i++) { int complement = (target - nums[i]); System.out.println("complement:" + complement + " i:" + i + " nums:" + nums[i]); if (map.containsKey(complement)) { return new int[]{map.get(complement), i}; } //如果条件是最先的俩个数累加需加上该判断,否则不需要 if (map.containsKey(nums[i])) { continue; } map.put(nums[i], i); } throw new IllegalArgumentException("No two sum solution"); } public static void main(String[] args) { Solution solution = new Solution(); //给定 nums = [2, 7, 11, 15], target = 9 //因为 nums[0] + nums[1] = 2 + 7 = 9 //所以返回 [0, 1] int[] nums = new int[]{10, 3, 3, 7, 6, 11, 15}; int target = 9; int[] ns = new int[2]; ns = solution.twoSum(nums, target); for (int i : ns) { System.out.println(i); } } } \ No newline at end of file diff --git a/Week 02/id_573/leetcode_242_573.java b/Week 02/id_573/leetcode_242_573.java new file mode 100644 index 000000000..58ddfa23a --- /dev/null +++ b/Week 02/id_573/leetcode_242_573.java @@ -0,0 +1 @@ +class Solution { public boolean isAnagram(String s, String t) { if (s.length() != t.length()) { return false; } int[] table = new int[26]; for (int i = 0; i < s.length(); i++) { table[s.charAt(i) - 'a']++; } for (int i = 0; i < t.length(); i++) { table[t.charAt(i) - 'a']--; if (table[t.charAt(i) - 'a'] < 0) { return false; } } return true; } public static void main(String[] args) { Solution solution = new Solution(); String s = "anagram"; String t = "nagaram"; System.out.println(solution.isAnagram(s, t)); s = "rat"; t = "car"; System.out.println(solution.isAnagram(s, t)); } } \ No newline at end of file diff --git a/Week 02/id_573/leetcode_49_573.java b/Week 02/id_573/leetcode_49_573.java new file mode 100644 index 000000000..478c2ed3d --- /dev/null +++ b/Week 02/id_573/leetcode_49_573.java @@ -0,0 +1 @@ +class Solution { public List> groupAnagrams(String[] strs) { if (strs.length == 0) { return new ArrayList(); } Map ans = new HashMap(); int[] count = new int[26]; for (String s : strs) { Arrays.fill(count, 0); for (char c : s.toCharArray()) { count[c - 'a']++; } StringBuilder sb = new StringBuilder(""); for (int i = 0; i < 26; i++) { sb.append('#'); sb.append(count[i]); } String key = sb.toString(); if (!ans.containsKey(key)) { ans.put(key, new ArrayList()); } ans.get(key).add(s); } return new ArrayList(ans.values()); } public static void main(String[] args) { Solution solution = new Solution(); String[] strings = new String[]{"eat", "tea", "tan", "ate", "nat", "bat"}; System.out.println(solution.groupAnagrams(strings)); } } \ No newline at end of file diff --git a/Week 02/id_573/leetcode_70_573.java b/Week 02/id_573/leetcode_70_573.java new file mode 100644 index 000000000..ee21db6d9 --- /dev/null +++ b/Week 02/id_573/leetcode_70_573.java @@ -0,0 +1 @@ +class Solution { public int climbStairs(int n) { if (n == 1) { return 1; } int[] dp = new int[n + 1]; dp[1] = 1; dp[2] = 2; for (int i = 3; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } return dp[n]; } public static void main(String[] args) { Solution solution = new Solution(); int n = 7; System.out.println(solution.climbStairs(n)); for (int i = 1; i <= n; i++) { System.out.println(solution.climbStairs(i)); } } } \ No newline at end of file diff --git a/Week 02/id_578/LeetCode_105_578.java b/Week 02/id_578/LeetCode_105_578.java new file mode 100644 index 000000000..8250d0aef --- /dev/null +++ b/Week 02/id_578/LeetCode_105_578.java @@ -0,0 +1,33 @@ +package com.hand.week2; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/27 + */ +public class LeetCode_105_578 { + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + if (root == null || root == p || root == q) return root; + TreeNode left = lowestCommonAncestor(root.left, p, q); + TreeNode right = lowestCommonAncestor(root.right, p, q); + //return left == null ? right : right == null ? left : root; + if (left == null) { + return right; + } else if (right == null) { + return left; + } else { + return root; + } + } + + class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } +} diff --git a/Week 02/id_578/LeetCode_144_578.java b/Week 02/id_578/LeetCode_144_578.java new file mode 100644 index 000000000..f4e8f19ec --- /dev/null +++ b/Week 02/id_578/LeetCode_144_578.java @@ -0,0 +1,37 @@ +package com.hand.week2; + +import java.util.ArrayList; +import java.util.List; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/27 + */ +public class LeetCode_144_578 { + List result = new ArrayList<>(); + + public List preorderTraversal(TreeNode root) { + helper(root); + return result; + } + + private void helper(TreeNode root) { + if(root==null) return ; + result.add(root.val); + helper(root.left); + helper(root.right); + } + + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + +} diff --git a/Week 02/id_578/LeetCode_1_578.java b/Week 02/id_578/LeetCode_1_578.java new file mode 100644 index 000000000..1fc95a464 --- /dev/null +++ b/Week 02/id_578/LeetCode_1_578.java @@ -0,0 +1,27 @@ +package com.hand.week2; + +import java.util.HashMap; +import java.util.Map; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/27 + */ +public class LeetCode_1_578 { + public int[] twoSum(int[] nums, int target) { + //key nums[i] value i + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; ++i) { + //target=nums[i]+tmp + int tmp = target - nums[i]; + if (map.containsKey(tmp)) { + return new int[]{map.get(tmp), i}; + } else { + map.put(nums[i], i); + } + } + return new int[]{}; + } +} diff --git a/Week 02/id_578/LeetCode_242_578.java b/Week 02/id_578/LeetCode_242_578.java new file mode 100644 index 000000000..8825c8bdc --- /dev/null +++ b/Week 02/id_578/LeetCode_242_578.java @@ -0,0 +1,22 @@ +package com.hand.week2; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/27 + */ +public class LeetCode_242_578 { + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) return false; + int[] counter = new int[26]; + for (int i = 0; i < s.length(); ++i) { + counter[s.charAt(i) - 'a']++; + counter[t.charAt(i) - 'a']--; + } + for (int i = 0; i < counter.length; ++i) { + if (counter[i] != 0) return false; + } + return true; + } +} diff --git a/Week 02/id_578/LeetCode_429_578.java b/Week 02/id_578/LeetCode_429_578.java new file mode 100644 index 000000000..eb9289645 --- /dev/null +++ b/Week 02/id_578/LeetCode_429_578.java @@ -0,0 +1,41 @@ +package com.hand.week2; + +import java.util.ArrayList; +import java.util.List; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/27 + */ +public class LeetCode_429_578 { + List> result = new ArrayList<>(); + + public List> levelOrder(Node root) { + helper(root, 0); + return result; + } + + private void helper(Node root, int depth) { + if (root == null) return; + if (depth >= result.size()) result.add(new ArrayList<>()); + result.get(depth).add(root.val); + for (int i = 0; i < root.children.size(); ++i) { + helper(root.children.get(i), depth + 1); + } + } + + class Node { + public int val; + public List children; + + public Node() { + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } + } +} diff --git a/Week 02/id_578/LeetCode_46_578.java b/Week 02/id_578/LeetCode_46_578.java new file mode 100644 index 000000000..174b33d32 --- /dev/null +++ b/Week 02/id_578/LeetCode_46_578.java @@ -0,0 +1,36 @@ +package com.hand.week2; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/27 + */ +public class LeetCode_46_578 { + List> result = new ArrayList<>(); + + public List> permute(int[] nums) { + int[] visited = new int[nums.length]; + helper(0, visited, nums, new Stack()); + return result; + } + + private void helper(int depth, int[] visited, int[] nums, Stack stack) { + if (depth == nums.length) { + result.add(new ArrayList(stack)); + return; + } + for (int i = 0; i < nums.length; ++i) { + if (visited[i] == 1) continue; + stack.push(nums[i]); + visited[i] = 1; + helper(depth + 1, visited, nums, stack); + stack.pop(); + visited[i] = 0; + } + } +} diff --git a/Week 02/id_578/LeetCode_47_578.java b/Week 02/id_578/LeetCode_47_578.java new file mode 100644 index 000000000..140c5e21b --- /dev/null +++ b/Week 02/id_578/LeetCode_47_578.java @@ -0,0 +1,40 @@ +package com.hand.week2; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Stack; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/27 + */ +public class LeetCode_47_578 { + List> result = new ArrayList<>(); + + public List> permuteUnique(int[] nums) { + int[] visited = new int[nums.length]; + Arrays.sort(nums); + helper(0, visited, nums, new Stack()); + return result; + } + + private void helper(int depth, int[] visited, int[] nums, Stack stack) { + if (depth == nums.length) { + result.add(new ArrayList(stack)); + return; + } + for (int i = 0; i < nums.length; ++i) { + if (visited[i] == 1) continue; + if (i > 0 && nums[i] == nums[i - 1] && visited[i - 1] == 0) continue; + stack.push(nums[i]); + visited[i] = 1; + helper(depth + 1, visited, nums, stack); + stack.pop(); + visited[i] = 0; + } + } + +} diff --git a/Week 02/id_578/LeetCode_49_578.java b/Week 02/id_578/LeetCode_49_578.java new file mode 100644 index 000000000..927e49a77 --- /dev/null +++ b/Week 02/id_578/LeetCode_49_578.java @@ -0,0 +1,28 @@ +package com.hand.week2; + +import java.util.*; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/27 + */ +public class LeetCode_49_578 { + public List> groupAnagrams(String[] strs) { + Map> map = new HashMap<>(); + for (String str : strs) { + char[] chars = str.toCharArray(); + Arrays.sort(chars); + String tmp = String.valueOf(chars); + if (map.containsKey(tmp)) { + map.get(tmp).add(str); + } else { + List list = new ArrayList<>(); + list.add(str); + map.put(tmp, list); + } + } + return new ArrayList>(map.values()); + } +} diff --git a/Week 02/id_578/LeetCode_589_578.java b/Week 02/id_578/LeetCode_589_578.java new file mode 100644 index 000000000..6467452dd --- /dev/null +++ b/Week 02/id_578/LeetCode_589_578.java @@ -0,0 +1,40 @@ +package com.hand.week2; + +import java.util.ArrayList; +import java.util.List; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/27 + */ +public class LeetCode_589_578 { + List result = new ArrayList<>(); + + public List preorder(Node root) { + helper(root); + return result; + } + + private void helper(Node root) { + if(root==null) return ; + result.add(root.val); + for(int i=0;i children; + + public Node() { + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } + } +} diff --git a/Week 02/id_578/LeetCode_590_578.java b/Week 02/id_578/LeetCode_590_578.java new file mode 100644 index 000000000..4a0703a3b --- /dev/null +++ b/Week 02/id_578/LeetCode_590_578.java @@ -0,0 +1,40 @@ +package com.hand.week2; + +import java.util.ArrayList; +import java.util.List; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/27 + */ +public class LeetCode_590_578 { + List result = new ArrayList<>(); + + public List postorder(Node root) { + helper(root); + return result; + } + + private void helper(Node root) { + if (root == null) return; + for (int i = 0; i < root.children.size(); ++i) { + helper(root.children.get(i)); + } + result.add(root.val); + } + + class Node { + public int val; + public List children; + + public Node() { + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } + } +} diff --git a/Week 02/id_578/LeetCode_77_578.java b/Week 02/id_578/LeetCode_77_578.java new file mode 100644 index 000000000..a1d092b08 --- /dev/null +++ b/Week 02/id_578/LeetCode_77_578.java @@ -0,0 +1,33 @@ +package com.hand.week2; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/27 + */ +public class LeetCode_77_578 { + List> result = new ArrayList<>(); + + //n个数 k个格子 + public List> combine(int n, int k) { + helper(1, n, k, new Stack<>()); + return result; + } + + private void helper(int depth, int n, int k, Stack stack) { + if (stack.size() == k) { + result.add(new ArrayList<>(stack)); + return; + } + for (int i = depth; i <= n; ++i) { + stack.push(i); + helper(i + 1, n, k, stack); + stack.pop(); + } + } +} diff --git a/Week 02/id_578/LeetCode_94_578.java b/Week 02/id_578/LeetCode_94_578.java new file mode 100644 index 000000000..3bb474177 --- /dev/null +++ b/Week 02/id_578/LeetCode_94_578.java @@ -0,0 +1,36 @@ +package com.hand.week2; + +import java.util.ArrayList; +import java.util.List; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/10/27 + */ +public class LeetCode_94_578 { + List result = new ArrayList<>(); + + public List inorderTraversal(TreeNode root) { + helper(root); + return result; + } + + private void helper(TreeNode root) { + if(root==null) return ; + helper(root.left); + result.add(root.val); + helper(root.right); + } + + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } +} diff --git a/Week 02/id_583/LeetCode_105_583.php b/Week 02/id_583/LeetCode_105_583.php new file mode 100644 index 000000000..cd7eb4aea --- /dev/null +++ b/Week 02/id_583/LeetCode_105_583.php @@ -0,0 +1,37 @@ +preorder = $preorder; + $this->inorder = $inorder; + $this->inmap = array_flip($inorder); + return $this->helper(0, count($inorder) - 1); + } + + function helper($start, $end) + { + if ($start > $end) { + return; + } + $nodeval = $this->preorder[$this->preindex]; + $inindex = $this->inmap[$nodeval]; + $node = new TreeNode($nodeval); + $this->preindex++; + + $node->left = $this->helper($start, $inindex - 1); + $node->right = $this->helper($inindex + 1, $end); + return $node; + } +} \ No newline at end of file diff --git a/Week 02/id_583/LeetCode_242_583.php b/Week 02/id_583/LeetCode_242_583.php new file mode 100644 index 000000000..91239f970 --- /dev/null +++ b/Week 02/id_583/LeetCode_242_583.php @@ -0,0 +1,30 @@ + $val) { + if ($val != $a2[$key]) { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/Week 02/id_588/LeetCode_105_588.java b/Week 02/id_588/LeetCode_105_588.java new file mode 100644 index 000000000..4d686a7c0 --- /dev/null +++ b/Week 02/id_588/LeetCode_105_588.java @@ -0,0 +1,40 @@ + +/** + * 从前序与中序遍历序列构造二叉树 + * https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ + */ +public class LeetCode_105_588 { + + public TreeNode buildTree(int[] preorder, int[] inorder) { + return _buildHelper(preorder, 0, preorder.length, inorder, 0, inorder.length); + } + + private TreeNode _buildHelper(int[] preorder, int pStart, int pEnd, int[] inorder, int inStart, int inEnd) { + if (pStart == pEnd) { + return null; + } + + int rootValue = preorder[pStart]; + TreeNode root = new TreeNode(rootValue); + int rootIndexInInOrder = 0; + for (int i = inStart; i < inEnd; i++) { + if (rootValue == inorder[i]) { + rootIndexInInOrder = i; + break; + } + } + int leftNum = rootIndexInInOrder - inStart; + root.left = _buildHelper(preorder, pStart + 1, pStart + leftNum + 1, inorder, inStart, rootIndexInInOrder); + root.right = _buildHelper(preorder, pStart + leftNum + 1, pEnd, inorder, rootIndexInInOrder + 1, inEnd); + return root; + } + + + public static void main(String[] args) { + + int[] preOrder = {3,9,20,15,7}; + int[] inOrder = {9,3,15,20,7}; + LeetCode_105_588 solution = new LeetCode_105_588(); + solution.buildTree(preOrder, inOrder); + } +} diff --git a/Week 02/id_588/LeetCode_17_588.java b/Week 02/id_588/LeetCode_17_588.java new file mode 100644 index 000000000..69cfa30d5 --- /dev/null +++ b/Week 02/id_588/LeetCode_17_588.java @@ -0,0 +1,46 @@ +import java.util.*; + +/** + * 电话号码的字母组合 + * https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/ + */ +public class LeetCode_17_588 { + + public List letterCombinations(String digits) { + if (null == digits || 0 == digits.length()) { + return new ArrayList<>(); + } + + Map map = new HashMap<>(); + map.put('2', "abc"); + map.put('3', "def"); + map.put('4', "ghi"); + map.put('5', "jkl"); + map.put('6', "mno"); + map.put('7', "pqrs"); + map.put('8', "tuv"); + map.put('9', "wxyz"); + List res = new LinkedList<>(); + search("", digits, 0, res, map); + return res; + } + + private void search(String s, String digits, int i, List res, Map map) { + if (i == digits.length()) { + res.add(s); + return; + } + + String letters = map.get(digits.charAt(i)); + for (int j = 0; j < letters.length(); j ++) { + search(s + letters.charAt(j), digits, i + 1, res, map); + } + } + + public static void main(String[] args) { + + LeetCode_17_588 solution = new LeetCode_17_588(); + System.out.println(solution.letterCombinations("23")); + } +} + diff --git a/Week 02/id_588/LeetCode_242_588.java b/Week 02/id_588/LeetCode_242_588.java new file mode 100644 index 000000000..bd8600ae5 --- /dev/null +++ b/Week 02/id_588/LeetCode_242_588.java @@ -0,0 +1,37 @@ +/** + * 有效的字母异位词 + * https://leetcode-cn.com/problems/valid-anagram/description/ + */ +public class LeetCode_242_588 { + + /** + * 思路: + * 可以计算两个字符串中每个字母的出现次数并进行比较,用一个计数器表计算s字母的频率,用t减少计数器表中的每个字母的计数器,然后检查计数器是否回到零 + */ + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + int[] counter = new int[26]; + for (int i = 0; i < s.length(); i ++) { + counter[s.charAt(i) - 'a'] ++; + counter[t.charAt(i) - 'a'] --; + } + for (int j = 0; j < counter.length; j ++) { + if (0 != counter[j]) { + return false; + } + } + return true; + } + + public static void main(String[] args) { + + String s = "abdacb"; + String t = "caabbd"; + String q = "abcdef"; + LeetCode_242_588 solution = new LeetCode_242_588(); + System.out.println(solution.isAnagram(s, t)); + System.out.println(solution.isAnagram(s, q)); + } +} diff --git a/Week 02/id_588/LeetCode_51_588.java b/Week 02/id_588/LeetCode_51_588.java new file mode 100644 index 000000000..d896bf2c1 --- /dev/null +++ b/Week 02/id_588/LeetCode_51_588.java @@ -0,0 +1,86 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * n皇后 + * https://leetcode-cn.com/problems/n-queens/ + */ +public class LeetCode_51_588 { + + public List> solveNQueens(int n) { + + List> list = new ArrayList<>(); + char[][] chessboard = new char[n][n]; + for (int i = 0; i < chessboard.length; i++) { + for (int j = 0; j < chessboard[i].length; j++) { + chessboard[i][j] = '.'; + } + } + _solveHelper(chessboard, 0, list); + return list; + } + + private void _solveHelper(char[][] chessboard, int row, List> list) { + if (chessboard.length == row) { + list.add(_buildListStringFromChessboard(chessboard)); + return; + } + + for (int column = 0; column < chessboard.length; column ++) { + if (_isValid(chessboard, row, column)) { + chessboard[row][column] = 'Q'; + _solveHelper(chessboard, row + 1, list); + // 解决了下一列的问题之后,将棋盘复原 + chessboard[row][column] = '.'; + } + } + } + + private boolean _isValid(char[][] chessboard, int row, int column) { + if (row < 1) { + return true; + } + + int leftUp = column - 1; + int rightUp = column + 1; + for (int i = row - 1; i >= 0 ; i --) { + // 竖直方向 + if ('Q' == chessboard[i][column]) { + return false; + } + // 左上角斜线 + if (leftUp >= 0) { + if ('Q' == chessboard[i][leftUp]) { + return false; + } + } + // 右上角斜线 + if (rightUp < chessboard.length) { + if ('Q' == chessboard[i][rightUp]) { + return false; + } + } + leftUp --; + rightUp ++; + } + + return true; + } + + private List _buildListStringFromChessboard(char[][] chessboard) { + List list = new ArrayList<>(); + for (int i = 0; i < chessboard.length; i++) { + String s = new String(chessboard[i]); + list.add(s); + } + return list; + } + + + public static void main(String[] args) { + + LeetCode_51_588 solution = new LeetCode_51_588(); + System.out.println(solution.solveNQueens(8)); + } + +} diff --git a/Week 02/id_588/LeetCode_94_588.java b/Week 02/id_588/LeetCode_94_588.java new file mode 100644 index 000000000..ccf87774e --- /dev/null +++ b/Week 02/id_588/LeetCode_94_588.java @@ -0,0 +1,54 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * 二叉树的中序遍历 + * https://leetcode-cn.com/problems/binary-tree-inorder-traversal/ + */ +public class LeetCode_94_588 { + + public List inorderTraversal(TreeNode root) { + + List list = new ArrayList<>(); + _traversal(root, list); + return list; + } + + private void _traversal(TreeNode root, List list) { + if (null == root) { + return; + } + if (null != root.left) { + _traversal(root.left, list); + } + + list.add(root.val); + + if (null != root.right) { + _traversal(root.right, list); + } + } + + public static void main(String[] args) { + + TreeNode node1 = new TreeNode(1); + TreeNode node2 = new TreeNode(2); + TreeNode node3 = new TreeNode(3); + TreeNode node4 = new TreeNode(4); + TreeNode node5 = new TreeNode(5); + TreeNode node6 = new TreeNode(6); + TreeNode node7 = new TreeNode(7); + + node1.left = node2; + node1.right = node3; + node2.right = node4; + node3.left = node5; + node4.left = node6; + node4.right = node7; + + LeetCode_94_588 solution = new LeetCode_94_588(); + System.out.println(solution.inorderTraversal(node1)); + + + } +} diff --git a/Week 02/id_588/NOTE.md b/Week 02/id_588/NOTE.md index a6321d6e2..4df06c686 100644 --- a/Week 02/id_588/NOTE.md +++ b/Week 02/id_588/NOTE.md @@ -1,4 +1,56 @@ -# NOTE +# 第二周学习总结 + +递归问题处理时候的思维要点: + - 1.不要人肉进行递归 + - 2.找最近重复子问题 + - 3.数学归纳法思维 + +要养成收藏精选代码的习惯 + + +## 第五课 + +哈希表,即散列表,通过将key映射到表中的一个位置来访问记录 + +好的哈希函数: + - 让映射后的数值尽量分散 + - 让碰撞概率较小 + +解决哈希碰撞的方法 + - 判断数组的下一个位置是否可以填入 + - 使用链表将哈希值一样的数据存储起来 + +## 第六课 + +二叉树的遍历 + - 前序遍历(preorder): 根-左-右 + - 中序遍历(inorder): 左-根-右 + - 后序遍历(postorder): 左-右-根 + +二叉搜索树 + - 左子树上的所有节点均小于根节点 + - 右子树上的所有节点均大于根节点 + - 左、右子树页分别为二叉搜索树 + - 对二叉搜索树进行中序遍历,得到的是一个升序排列的结果 + - 平均情况下,查询、插入、删除的复杂度是O(logn);最坏情况下(退化成链表的时候)查询、插入、删除的复杂度是O(n) + +## 第七课 + +递归代码模板: + - 递归终止条件 + - 处理当前层的逻辑 + - 下探到下一层 + - 清理当前层 + +## 第八课 + +分治和回溯,可以认为是一种特殊的递归 + +分治:将大问题分解为小问题,并将小问题的结果进行合并 +回溯:不断在每一层去试,看是否可以解决 + + + diff --git a/Week 02/id_593/LeetCode_105_593.java b/Week 02/id_593/LeetCode_105_593.java new file mode 100644 index 000000000..49922997c --- /dev/null +++ b/Week 02/id_593/LeetCode_105_593.java @@ -0,0 +1,55 @@ + +/** + * 根据一棵树的前序遍历与中序遍历构造二叉树。 + *

+ * 注意: + * 你可以假设树中没有重复的元素。 + *

+ * 例如,给出 + *

+ * 前序遍历 preorder = [3,9,20,15,7] + * 中序遍历 inorder = [9,3,15,20,7] + * 返回如下的二叉树: + *

+ *

3 + *

/ \ + *

9 20 + *

/ \ + *

15 7 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + *

+ * 优秀解答: + * https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solution/qian-xu-bian-li-python-dai-ma-java-dai-ma-by-liwei/ + * https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solution/qian-xu-zhong-xu-bian-li-gou-zao-er-cha-shu-mo-ni-/ + * + * @author jaryoung + */ +public class LeetCode_105_593 { + public TreeNode buildTree(int[] preorder, int[] inorder) { + return buildTree(preorder, 0, inorder, 0, inorder.length); + } + + private TreeNode buildTree(int[] preorder, int preOrderIndex, int[] inorder, int inorderLeft, int inorderRight) { + // terminator + if (inorderLeft >= inorderRight) { + return null; + } + // process + TreeNode root = new TreeNode(preorder[preOrderIndex]); + int inorderIndex = 0; + while (inorder[inorderIndex] != root.val) { + inorderIndex++; + } + // drill down + // 根据中序遍历,能得到左子树的结点数,inorderIndex - inorderLeft。从而可以计算右子树的根的位置等于, + // 前序遍历的左结点的位置 + (中序遍历的左子树的长度)+ 1 + root.left = buildTree(preorder, preOrderIndex + 1, + inorder, inorderLeft, inorderIndex); + root.right = buildTree(preorder, preOrderIndex + inorderIndex - inorderLeft + 1, + inorder, inorderIndex + 1, inorderRight); + return root; + } +} \ No newline at end of file diff --git a/Week 02/id_593/LeetCode_236_593.java b/Week 02/id_593/LeetCode_236_593.java new file mode 100644 index 000000000..b3d227bf1 --- /dev/null +++ b/Week 02/id_593/LeetCode_236_593.java @@ -0,0 +1,67 @@ +/** + * 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 + *

+ * 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x, + * 满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” + *

+ * 例如,给定如下二叉树:  root = [3,5,1,6,2,0,8,null,null,7,4] + *

3 + *

/ \ + *

5 1 + *

/ \ / \ + *

6 2 0 8 + *

/ \ + *

7 4 + * 示例 1: + *

+ * 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 + * 输出: 3 + * 解释: 节点 5 和节点 1 的最近公共祖先是节点 3。 + * 示例 2: + *

+ * 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4 + * 输出: 5 + * 解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + */ +public class LeetCode_236_593 { + + TreeNode result; + + /** + * 通过 递归 查询 找到二叉树中两个指定节点的最近公共祖先 + * + * @param root 根节点 + * @param p p结点 + * @param q q结点 + * @return 最近的公共祖先 + */ + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + result = null; + recursion(root, p, q); + return result; + } + + public int recursion(TreeNode root, TreeNode p, TreeNode q) { + // terminator + if (root == null) { + return 0; + } + // process ,drill down + int left = recursion(root.left, p, q); + int right = recursion(root.right, p, q); + + // 看是否已经找到 p 或者 q的结点 + int mid = root == p || root == q ? 1 : 0; + // 大于1的时候,说明已经找到了p 和 q的结点 ,并且当前结点就是我们要找的最近的祖先结点 + if (mid + left + right > 1) { + result = root; + } + return mid + left + right > 0 ? 1 : 0; + } +} \ No newline at end of file diff --git a/Week 02/id_593/LeetCode_46_593.java b/Week 02/id_593/LeetCode_46_593.java new file mode 100644 index 000000000..3916c82d0 --- /dev/null +++ b/Week 02/id_593/LeetCode_46_593.java @@ -0,0 +1,63 @@ +/** + * 给定一个没有重复数字的序列,返回其所有可能的全排列。 + *

+ * 示例: + *

+ * 输入: [1,2,3] + * 输出: + * [ + * [1,2,3], + * [1,3,2], + * [2,1,3], + * [2,3,1], + * [3,1,2], + * [3,2,1] + * ] + *

+ *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/permutations + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + */ +public class LeetCode_46_593 { + List> result; + + /** + * 思路: 递归,回溯 + * + * @param nums 目标序列 + * @return 返回其所有可能的全排列 + */ + public List> permute(int[] nums) { + if (nums == null || nums.length < 1) { + return Collections.emptyList(); + } + + result = new ArrayList<>(); + backtrack(nums, new LinkedList<>()); + return result; + } + + private void backtrack(int[] nums, LinkedList numberList) { + // terminator + int length = nums.length; + if (length == numberList.size()) { + result.add(new ArrayList<>(numberList)); + return; + } + // process + for (int i = 0; i < length; i++) { + // 只要列表没有出现,就往里面添加 + if (numberList.contains(nums[i])) { + continue; + } + numberList.addLast(nums[i]); + // drill down + backtrack(nums, numberList); + // reverse + numberList.removeLast(); + } + } +} \ No newline at end of file diff --git a/Week 02/id_593/LeetCode_77_593.java b/Week 02/id_593/LeetCode_77_593.java new file mode 100644 index 000000000..89d02d266 --- /dev/null +++ b/Week 02/id_593/LeetCode_77_593.java @@ -0,0 +1,72 @@ + +/** + * 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 + *

+ * 示例: + *

+ * 输入: n = 4, k = 2 + * 输出: + * [ + * [2,4], + * [3,4], + * [2,3], + * [1,2], + * [1,3], + * [1,4], + * ] + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/combinations + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + */ +public class LeetCode_77_593 { + private List> result = new LinkedList<>(); + + /** + * 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 + * + * @param n 最多的数 + * @param k 最多几个数为一组 + * @return 所有组合 + */ + public List> combineByRecursion(int n, int k) { + if (n == 0 || k == 0) { + return Collections.emptyList(); + } + combineByRecursion(1, n, k, new LinkedList<>()); + return result; + } + + /** + * 若组合完成- 添加到输出中。 + *

+ * 遍历从 index 到 n的所有整数。 + *

+ * 将整数 i 添加到现有组合 curr中。 + *

+ * 继续向组合中添加更多整数 : + * combineByRecursion(i + 1, curr). + *

+ * 将 i 从 curr中移除,实现回溯。 + *

+ * + * @param index 当前位置 + * @param n 最多的数 + * @param k 最多几个数为一组 + * @param current 当前组合 + */ + private void combineByRecursion(int index, int n, int k, LinkedList current) { + // 终止条件又写错了 index == k,脑回路了。 + if (current.size() == k) { + result.add(new ArrayList<>(current)); + } + // n 是从1开始的 + for (int i = index; i < n + 1; ++i) { + current.add(i); + combineByRecursion(i + 1, n, k, current); + // 上面 i + 1,current 传递的是引用,梦境会互相影响了,为了不影响当层梦境,所以这里回退清空状态 + current.removeLast(); + } + } \ No newline at end of file diff --git a/Week 02/id_593/NOTE.md b/Week 02/id_593/NOTE.md index a6321d6e2..2bf5d444b 100644 --- a/Week 02/id_593/NOTE.md +++ b/Week 02/id_593/NOTE.md @@ -1,4 +1,617 @@ -# NOTE +# 【593-Week 02】第二周总结 + +## 复习 + +### 一维数据结构 + +- 单链表 +- 数组 +- 队列 +- 栈 + +## 学习 + +### Hash Table + +- 也叫散列表,是根据关键码值(Key value)而直接进行访问的数据结构 +- Hash Function + + - 一个好的哈希函数就是逆天操作 + + - 尽可能分散,使得元素均匀分布 + + - 哈希碰撞 + + - 解决办法 + + - 拉链法 + +- 时间复杂度 + + - access + + - a/n + + - insert + + - O(1) + + - delete + + - O(1) + + - search + + - O(1) + + - 最差情况 + + - 都变成0(n),退化成链表 + +- 实现过程 + + - 输入 + + - 哈希函数 + + - 放置特定的位置 + +### Map + +- key-value + + - key + + - A map cannot contain duplicate keys + - each key can map to at most one value + +- Java 源码分析 + + - 基本操作 + + - add + - addAll + - remove + - clear + - contains + + - TreeMap + - HashMap + + - put + - getNode + + - ConcurrentHashMap + +- 工程实践 + + - 通讯录 + - 用户信息表 + - 缓存 + - 键值对存储 + +### Set + +- 规则 + + - 不包含重复元素 + - 可以包含一些特殊的元素 + + - null + +- Java Set 源码分析 + + - 基本操作 + + - put + - putAll + - remove + - keySet + - entrySet + - computeIfAbsent + + - 如果不存在,新建一个对象返回。 + + - computeIfPresent + + - 如果存在,替换为新建的对象,如果没有新建的对象remove当前元素 + + - TreeSet + - HashSet + +### Tree + +- Basic Elements + + - Root + - Level + + - from one + + - Node + + - Parent + - Child + + - Left + - Right + + - Sub-Tree + +- Type + + - Binary Tree + + - Full + - AVL + - Black Red Tree + - B+ Tree + - Directory Tree + - Binary Search Tree + + - query + - insert + - delete + +- 遍历树的策略 + + - 深度优先搜索(DFS) + + - Subtopic 4 + - Pre-Order + - In-Order + - Post-Order + + - 宽度优先搜索(BFS) + + - 逐层遍历 + +- traverse +- Linked List 是特殊化的 Tree +- 形成自己机械化的记忆 + + - TreeNode的Java代码 + - 前中后序的遍历 + + - 递归 + - 迭代 + +- 树的面试题解法一般都是递归,为什么? + + - 节点的定义 + - 重复性(自相似性) + +### Graph + +- type + + - Directed + - Undirected + +### Recursion + +- 通过函数体进行循环 +- Factorial +- 代码模板 + + - terminator + - process + - drill down + + - 分治都话,这里可能是多个下层梦境,然后后面醒来上一层都时候不断合并结果 + + - reverse if need + +- 细分 + + - 回溯 + - 分治 + + - 大问题找小问题和子问题 + + - 重复性 + + - 如何拆分子问题 + + - 难点 + +- 思维要点 + + - 不要人肉进行递归 + - 找到最近最简方法,将其拆解成可重复解决的问题(重复子问题) + + - 如果找出重复子问题? + + - 通过多个例子枚举找规律 + + - 5行10行代码解决 + + - 数学归纳法 + +### 实战题目 + +- 1. 两数之和 + + - 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 + 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 + - 限制条件 + + - 不能重复利用同样的数据 + - 返回的是数组的下标 + + - 本周次数 + + - 1 + + - Tue Oct 22 22:55:08 CST 2019 + - 什么时候会数组越界? + + - 数组index是从0开始的 + - 数组index超过数组的边界的时候 + - 例如,数组length为3,当你想访问[3]的时候会越界 + + - Java 代码的包装类和基本类型的区别需要了解 + - 下次最好统计一下耗时 + + + + - Tue Oct 24 22:55:08 + +- 49. 字母异位词分组 + + - 给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 + - 暴力法 + + - 将数组内的单词,都进行排序,并且通过额外数组存储起来, + + - 本周次数 + + - 1 + + - Wed Oct 23 00:14:08 CST 2019 + - 默写两种解法 + + + + - Wed Oct 23 10:08:12 CST 2019 + - 耗时约30min + +42. 有效的字母异位词 + + - 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 + - 思路 + + - 也可以排序后,进行字符串对比 + + - 时间复杂度 + - 空间复杂度 + + - 统计字母出现的频次 + + - 时间复杂度 + - 空间复杂度 + + - 本周次数 + + - 1 + + +- 94. 二叉树的中序遍历 + + - 给定一个二叉树,返回它的中序 遍历。 + - 思路 + + - 递归 + + - 思路很清晰 + + - 迭代 + + - 手动维护一个栈 + + - 颜色标记法 + + - 本周次数 + + - 1 + + - 通过手动维护栈的形式,并没有找到好理解的点 + + + +- 144. 二叉树的前序遍历 + + - 给定一个二叉树,返回它的 前序 遍历。 + - 本周次数 + + - 1 + + - 递归相对比较容易编写 + + + +- 145. 二叉树的后序遍历 + + - 给定一个二叉树,返回它的 后序 遍历。 + - 本周次数 + + - 1 + + - 递归相对比较容易编写 + - 前序的逆序就是后序的结果 + + + +- 590. N叉树的后序遍历 + + - 给定一个 N 叉树,返回其节点值的后序遍历。 + - 本周次数 + + - 1 + + - 递归思路清晰 + - 先前序,后翻转结果;或者利用 链表不断再插入链表头 + + + +- 589. N叉树的前序遍历 + + - 给定一个 N 叉树,返回其节点值的前序遍历。 + - 本周次数 + + - 1 + + - 前序遍历(根->左->右),利用栈,我们需要先根,因为栈是后进先出,所以我们需要将子结点从右到左入栈。 + + + +- 429. N叉树的层序遍历 + + - 给定一个 N 叉树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)。 + - 本周次数 + + - 1 + + +- 70. 爬楼梯 + + - 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 + 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? + 注意:给定 n 是一个正整数。 + - mutual exclusive + + - 互斥 + + - complete exclusive + + - 完全互斥 + + - 本周次数 + + - 1 + + +2. 括号生成 + + - 给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。 + - 本周次数 + + - 1 + + - 递归 + + - 终止条件写错了,下次记得思路要想清楚才写 + + + +26. 翻转二叉树 + + - 翻转一棵二叉树。 + - 本周次数 + + - 1 + + - 递归 + + - 思路比较清晰,就是一路到底,然后翻转 + + + +- 98. 验证二叉搜索树 + + - 给定一个二叉树,判断其是否是一个有效的二叉搜索树。 + 假设一个二叉搜索树具有如下特征: + 节点的左子树只包含小于当前节点的数。 + 节点的右子树只包含大于当前节点的数。 + 所有左子树和右子树自身必须也是二叉搜索树。 + - 本周次数 + + - 1 + + - 递归 + - 迭代方法需要多练习 + + + +- 104. 二叉树的最大深度 + + - 给定一个二叉树,找出其最大深度。 + +二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 + +说明: 叶子节点是指没有子节点的节点。 + - 本周次数 + + - 1 + + +- 111. 二叉树的最小深度 + + - 给定一个二叉树,找出其最小深度。 + +最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 + +说明: 叶子节点是指没有子节点的节点。 + - 本周次数 + + - 1 + + +97. 二叉树的序列化与反序列化 + + - 序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。 + 请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。 + - 本周次数 + + - 1 + + +- 169. 求众数 + + - 给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。 + +你可以假设数组是非空的,并且给定的数组总是存在众数。 + + - 关键点 + + - 次数大于 ⌊ n/2 ⌋ 的元素 == 众数 + + - 本周次数 + + - 1 + + +- 17. 电话号码的字母组合 + + - 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。 + 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 + + - 只有2-9的情况 + + - 本周次数 + + - 1 + + - 基本功还是不扎实,还需多练习 + + + +- 51. N皇后 + + - n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。 + + - 皇后的攻击范围:行,竖,撇,捺 + - 皇后的攻击范围的撇,捺如何计算,其实就是坐标轴的转换 + - 坐标 + + - 本周次数 + + - 1 + + + +## 总结 + +### 习惯 + +- clarification + + - 澄清说明 + +- possible solution -> optimal(space / time) + + - 通过各种分析,找出最优的 + +- code +- test case + + - 边界 + - 尽可能分散 + +### 学习总结 + +- 根据实际的时长来学习,不可能把所有的习题都做的滚瓜烂熟。 + + - 课堂讲的题目,先做个烂熟 + - 课后题目,找几道觉得有意思的题目,来进行研究多种解法 + +### 下周计划 + +- 先复习 +- 然后,根据上周的学习情况,进行本周的学习任务的优化 + +## 课后作业 + +### 236. 二叉树的最近公共祖先 + +- 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 + +百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” + +- 本周次数 + + - 1 + + - 递归 + + - 主要需要了解我们如何记录我能找到了p和q的结点,然后通过回溯,找到对应最近的祖先 + + + +### 105. 从前序与中序遍历序列构造二叉树 + +- 根据一棵树的前序遍历与中序遍历构造二叉树。 + + - 你可以假设树中没有重复的元素。 + +- 本周次数 + + - 1 + + - 递归 + - 前序和后序的特点,寻找左右子树的位置 + + - 前序遍历 + + - 根->左->右 + + - 中序遍历 + + - 左->根->右 +### 77. 组合 + +- 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 +- 本周次数 + + - 1 + + - 递归 + + - 回溯 + - 状态清空 + + - 根据递归模版进行编码练习 + + + +### 46. 全排列 + +- 给定一个没有重复数字的序列,返回其所有可能的全排列。 + + - 序列没有重复数字 + +- 本周次数 + + - 1 + + - 递归 + + - 回溯 + - process + + - 增加限制条件使得每个梦境都不一样 + + - 根据递归模版进行编码练习 diff --git a/Week 02/id_598/LeetCode_144_598.java b/Week 02/id_598/LeetCode_144_598.java new file mode 100644 index 000000000..96186e8b4 --- /dev/null +++ b/Week 02/id_598/LeetCode_144_598.java @@ -0,0 +1,74 @@ +import org.omg.PortableInterceptor.INACTIVE; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * LeetCode 144题 + * + * @author northleaf + * @create 2019年10月22日 + */ +public class LeetCode_144_598 { + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + + /** + * 前序遍历:根左右 + * @param root + * @return + */ + public List preorderTraversal(TreeNode root) { + List list = new ArrayList(); + preorderTraversal(root,list); + return list; + } + + private void preorderTraversal(TreeNode node, List list) { + if(node!=null){ + list.add(node.val); + preorderTraversal(node.left,list); + preorderTraversal(node.right,list); + } + } + + + /** + * 基于栈的前序遍历:根左右 + * 1. 定义List + * 2. 定义栈 + * 3. 将root入栈 + * 4. 出栈,访问,将右节点入栈,将左节点入栈 循环访问 + * @param root + * @return + */ + public List preorderTraversal2(TreeNode root) { + if (root == null) { + return new ArrayList() ; + } + List list = new ArrayList(); + Stack stack = new Stack(); + stack.push(root); + //栈不空则循环 + TreeNode curr = null; + while (!stack.isEmpty()){ + curr = stack.pop(); + if (curr != null) { + list.add(curr.val); + //先入右节点,再入左节点,将来就会是左节点先出 + stack.push(curr.right); + stack.push(curr.left); + } + } + + return list; + } +} diff --git a/Week 02/id_598/LeetCode_1_598.java b/Week 02/id_598/LeetCode_1_598.java new file mode 100644 index 000000000..8f33b168f --- /dev/null +++ b/Week 02/id_598/LeetCode_1_598.java @@ -0,0 +1,79 @@ +import java.util.HashMap; +import java.util.Map; + +/** + * LeetCode第一题 + * + * @author northleaf + * @create 2019年10月22日 + */ +public class LeetCode_1_598 { + public static void main(String[] args) { + System.out.println("he"); + } + + /** + * 双重循环法 + * 时间复杂度:O(n²) + * @param nums + * @param target + */ + public int[] twoSum1(int[] nums,int target){ + for(int i = 0;i< nums.length-1;i++){ + for(int j = i+1;j map = new HashMap(); + //将数据存放在map中,值为key,索引为value + for(int i = 0;i map = new HashMap(); + //在遍历的同时插入哈希表并进行比较 + for(int i = 0;i< nums.length;i++){ + if(map.get(target-nums[i]) !=null && map.get(target - nums[i])!=i ){ + //返回的时间注意数组中元素的顺序 + return new int[]{map.get(target-nums[i]),i}; + } + map.put(nums[i],i); + } + return new int[0]; + + + } + +} diff --git a/Week 02/id_598/LeetCode_242_598.java b/Week 02/id_598/LeetCode_242_598.java new file mode 100644 index 000000000..71bb84cde --- /dev/null +++ b/Week 02/id_598/LeetCode_242_598.java @@ -0,0 +1,74 @@ +import java.util.Arrays; + +/** + * leetcode 242题 + * 异位词是指两个字符串中含有的字母一样,同时每个字母的数量也一致 + * + * @author northleaf + * @create 2019年10月22日 + */ +public class LeetCode_242_598 { + + + /** + * 将两个字符串分别放在两个字符数组中,排序后比较数组中的元素是否一致,一致返回true,否则返回false + * 时间复杂度O(nlogn) 取决于排序的时间 + * 空间复杂度O(n) + * + * @param s + * @param t + * @return + */ + public boolean isAnagram1(String s, String t) { + + //如果字符串长度不一致返回false + if (s.length() != t.length()) { + return false; + } + //转换为数组 + char[] charS = s.toCharArray(); + char[] charT = t.toCharArray(); + //排序 + Arrays.sort(charS); + Arrays.sort(charT); + //比较 + for (int i = 0; i < charS.length; i++) { + if (charS[i] != charT[i]) { + return false; + } + } + return true; + } + + /** + * 数组 + * 借助一个额外的数组,这个数组只存储26个字母的个数,初始为0 + * 针对S中的每个字母求和,针对T中的每个字母做减法,最后判断数字中是否为非零元素 + * 如果有则返回false + *

+ * 时间复杂度:O(n) + * 空间复杂度:O(1) + * + * @param s + * @param t + * @return + */ + public boolean isAnagram2(String s, String t) { + //如果字符串长度不一致返回false + if (s.length() != t.length()) { + return false; + } + int[] count = new int[26]; + for (int i = 0; i < s.length(); i++) { + count[s.charAt(i) - 'a']++; + count[t.charAt(i) - 'a']--; + } + + for (int i = 0; i < 26; i++) { + if (count[i] != 0) { + return false; + } + } + return true; + } +} diff --git a/Week 02/id_598/LeetCode_46_598.java b/Week 02/id_598/LeetCode_46_598.java new file mode 100644 index 000000000..f090cecf5 --- /dev/null +++ b/Week 02/id_598/LeetCode_46_598.java @@ -0,0 +1,66 @@ +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * LeetCode 46题 + * + * @author northleaf + * @create 2019年10月27日 + */ +public class LeetCode_46_598 { + public List> permute(int[] nums) { + List> ans = new ArrayList>(); + List numList = new ArrayList(); + for(int i = 0;i numList, List> ans) { + //终止条件 + if(from ==n ){ + ans.add(new ArrayList(numList)); + return; + } + //处理当前层逻辑 + for(int i = from;i integers, List> ans) { +// //终止条件 +// if(integers.size() == nums.length){ +// ans.add(new ArrayList(integers)); +// return; +// } +// //处理当前层 +// for(int i = from;i> groupAnagrams1(String[] strs) { + List> result = new ArrayList>(); + //用于标识是否该字符串已经分类 + boolean[] isUsed = new boolean[strs.length]; + for (int i = 0; i < strs.length; i++) { + List list = new ArrayList(); + if (!isUsed[i]) { + list.add(strs[i]); + for (int j = i + 1; j < strs.length; j++) { + if (isAnagram2(strs[i], strs[j])) { + isUsed[j] = true; + list.add(strs[j]); + } + } + } + if (list.size() > 0) { + result.add(list); + } + } + return result; + } + + public boolean isAnagram2(String s, String t) { + //如果字符串长度不一致返回false + if (s.length() != t.length()) { + return false; + } + int[] count = new int[26]; + for (int i = 0; i < s.length(); i++) { + count[s.charAt(i) - 'a']++; + count[t.charAt(i) - 'a']--; + } + + for (int i = 0; i < 26; i++) { + if (count[i] != 0) { + return false; + } + } + return true; + } + + + /** + * 解法2: 此解法要比解法1优 + * 借助哈希表,将具有相同字符及顺序的元素放在一起 + * @param strs + * @return + */ + public List> groupAnagrams2(String[] strs) { + Map> map = new HashMap>(); + + for(int i=0;i list = new ArrayList(); + list.add(strs[i]); + map.put(str_key,list); + } + } + + return new ArrayList>(map.values()); + } +} diff --git a/Week 02/id_598/LeetCode_77_598.java b/Week 02/id_598/LeetCode_77_598.java new file mode 100644 index 000000000..73a961799 --- /dev/null +++ b/Week 02/id_598/LeetCode_77_598.java @@ -0,0 +1,40 @@ +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * leetcode 77题 + * + * @author northleaf + * @create 2019年10月23日 + */ +public class LeetCode_77_598 { + + + public List> combine(int n, int k) { + List> lists = new ArrayList>(); + + combine(1,n,k,new ArrayList(),lists); + + return lists; + } + + private void combine(int first, int n, int k, ArrayList integers, List> lists) { + //如果集合中的元素与要选择元素的个数相同,则可以终止 + if(integers.size() == k ){ + lists.add(new ArrayList(integers)); + return; + } + //处理当前层数据 + for(int i = first ;i inorderTraversal(TreeNode root) { + List list = new ArrayList(); + inorderTraversal(list,root) ; + return list; + } + + /** + * 中序遍历以Node为根的二叉树 + * @param list + * @param node + */ + private void inorderTraversal(List list, TreeNode node) { + if(node!=null){ + inorderTraversal(list,node.left); + list.add(node.val); + inorderTraversal(list,node.right); + + } + } + + + /** + * 基于栈的中序遍历:左根右 + * 1. 定义List + * 2. 定义栈 + * 3. 定义root节点为curr节点 + * 4. 将curr节点及其所有的左子树依次入栈 + * 5. 栈顶元素出栈并访问,同时获取右子树 + * 6. 对右子树重复执行4,5两步骤 + * @param root + * @return + */ + public List inorderTraversal2(TreeNode root) { + //定义结果集 + List list = new ArrayList(); + //定义栈 + Stack stack = new Stack(); + //将root节点设置为curr + TreeNode curr = root; + //循环遍历 --栈不为空说明栈中还有元素没有遍历 + while(curr!=null || !stack.isEmpty()){ + //将所有的左子树入栈 + while (curr!=null){ + stack.push(curr); + curr = curr.left; + } + //当前栈顶一定是最左下角的元素 + curr = stack.pop(); + list.add(curr.val); + //将节节的右子树分配给curr + curr = curr.right; + } + + return list; + } + +} diff --git a/Week 02/id_598/NOTE.md b/Week 02/id_598/NOTE.md index a6321d6e2..232f197ce 100644 --- a/Week 02/id_598/NOTE.md +++ b/Week 02/id_598/NOTE.md @@ -2,3 +2,27 @@ +## 递归的三大思维 + +1. 不要人肉进行递归(最大的误区) +2. 找到最近最科方法,将其拆解成可重复解决的问题(重复子问题) +3. 数学归纳法思维 + +## 递归函数的组成(代码模板) + +只要是递归函数,就按照以下的模板去写: + +1. 终止条件 +2. 处理当前层逻辑 +3. 处理下一层 +4. 清理当前层 + +## 算法面试四件套 + +1. clarification 和面试官过题目 +2. possible solutions - > optimal (time & space) 从可选的解决方案中找最优解:时间和空间复杂度最好 +3. code,写代码 +4. test cases, 测试样例 + + + diff --git a/Week 02/id_603/LeetCode_242_603.java b/Week 02/id_603/LeetCode_242_603.java new file mode 100644 index 000000000..3d37c775e --- /dev/null +++ b/Week 02/id_603/LeetCode_242_603.java @@ -0,0 +1,25 @@ +public class LeetCode_242_603 { + public static boolean isAnagram(String s, String t) { + if (s.length()!=t.length()){ + return false; + } +// System.out.println(s.charAt(1)); + int [] counter = new int[26]; + for(int i=0;i result){ + if (root !=null){ + if (root.left !=null){ + sorter(root.left,result); + } + result.add(root.val); + if (root.right !=null){ + sorter(root.right,result); + } + + } + } + + public List inorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + sorter(root, res); + return res; + } +} diff --git a/Week 02/id_608/NOTE.md b/Week 02/id_608/NOTE.md index a6321d6e2..8a76bbc5a 100644 --- a/Week 02/id_608/NOTE.md +++ b/Week 02/id_608/NOTE.md @@ -1,4 +1,4 @@ -# NOTE +本周总结 diff --git a/Week 02/id_608/binary-tree-postorder-traversal.go b/Week 02/id_608/binary-tree-postorder-traversal.go new file mode 100644 index 000000000..e60af9b57 --- /dev/null +++ b/Week 02/id_608/binary-tree-postorder-traversal.go @@ -0,0 +1,36 @@ +package main + +import "fmt" + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func main() { + root := TreeNode{Val: 1} + root.Right = &TreeNode{2, nil, nil} + root.Right.Left = &TreeNode{3, nil, nil} + r := postorderTraversal(&root) + fmt.Println(r) +} + +func postorderTraversal(root *TreeNode) []int { + var result []int + helpers(root, &result) + return result +} + +func helpers(root *TreeNode, result *[]int) { + if root == nil { + return + } + if root.Left != nil { + helpers(root.Left, result) + } + if root.Right != nil { + helpers(root.Right, result) + } + *result = append(*result, root.Val) +} diff --git a/Week 02/id_608/letter-combinations-of-a-phone-number.go b/Week 02/id_608/letter-combinations-of-a-phone-number.go new file mode 100644 index 000000000..05fc548bb --- /dev/null +++ b/Week 02/id_608/letter-combinations-of-a-phone-number.go @@ -0,0 +1,42 @@ +package main + +import ( + "fmt" + "strconv" +) + +func main1() { + r := letterCombinations("23") + fmt.Println(r) +} +func letterCombinations(digits string) []string { + var outPut []string + var maps = make(map[int]string) + maps[2] = "abc" + maps[3] = "def" + maps[4] = "ghi" + maps[5] = "jkl" + maps[6] = "mno" + maps[7] = "pqrs" + maps[8] = "tuv" + maps[9] = "wxyz" + if len(digits) != 0 { + backtrack("", digits, &outPut, maps) + } + return outPut +} + +func backtrack(combination string, nextDigits string, outPut *[]string, maps map[int]string) { + if len(nextDigits) == 0 { + *outPut = append(*outPut, combination) + } else { + runeStr := []rune(nextDigits) + digit, _ := strconv.Atoi(string(runeStr[0:1])) + letters := maps[digit] + letterRune := []rune(letters) + for i := 0; i < len(letters); i++ { + letter := letterRune[i : i+1] + backtrack(combination+string(letter), string(runeStr[1:]), outPut, maps) + } + } +} diff --git a/Week 02/id_608/qiu-zhong-shu-by-leetcode-2.go b/Week 02/id_608/qiu-zhong-shu-by-leetcode-2.go new file mode 100644 index 000000000..87012495f --- /dev/null +++ b/Week 02/id_608/qiu-zhong-shu-by-leetcode-2.go @@ -0,0 +1,26 @@ +package main + +import "fmt" + +func main3() { + nums := []int{2, 2, 1, 1, 1, 2, 2} + r := majorityElement(nums) + fmt.Println(r) +} + +func majorityElement(nums []int) int { + lenth := len(nums) + var count int + var newElement int + for i := 0; i < lenth; i++ { + if count == 0 { + newElement = nums[i] + } + if newElement == nums[i] { + count++ + } else { + count-- + } + } + return newElement +} diff --git a/Week 02/id_608/two_sum.go b/Week 02/id_608/two_sum.go new file mode 100644 index 000000000..13320237d --- /dev/null +++ b/Week 02/id_608/two_sum.go @@ -0,0 +1,25 @@ +package main + +import "fmt" + +func main2() { + nums := []int{2, 7, 11, 15} + r := twoSum(nums, 9) + fmt.Println(r) +} + +func twoSum(nums []int, target int) []int { + lenth := len(nums) + hash := make(map[int]int) + var result []int + for i := 0; i < lenth; i++ { + sub := target - nums[i] + if _, ok := hash[nums[i]]; ok { + result = append(result, hash[nums[i]], i) + break + } else { + hash[sub] = i + } + } + return result +} diff --git a/Week 02/id_613/NOTE.md b/Week 02/id_613/NOTE.md index a6321d6e2..485c7e1c2 100644 --- a/Week 02/id_613/NOTE.md +++ b/Week 02/id_613/NOTE.md @@ -1,4 +1,70 @@ # NOTE - +### 可以用递归来解决的问题 +1、可以分解成重复子问题 +2、具有明确的终止条件 +3、该问题和其子问题具有相同的解法 + +### 分治本质上也是一种递归 + +### 为什么很多题目可以用递归来解决 +计算机语言只有if else, for, loop, recursion这几种 +简单的指令,因此最后转化成这几种指令的时候,其实就是重复及 +迭代 + +### 用递归来解决问题的思路 +主要思路就是寻找其中的重复子问题 + +### 回溯,本质上也是一种递归的思想 +回溯就是在向前解决问题的过程中,每一步往前都有岔路口,分出N种不同的路径, +然后在这一步不断的去试,发现当前路径不行的时候,就回到岔路口再走另外一条 +路径,并最终求得正确答案 + +### 代码模板 +泛型递归模板 +``` +def recursion(level, param1, param2...) { + // 终止 + if (level > MAX_LEVEL) { + process_result + return + } + + // 处理当前逻辑 + process(level, data...) + + // 递归下一层 + recursion(level + 1, ...) + + // 清理当前层 + clean_up(level) +} +``` + +分治模板,比泛型递归多了一个合并结果的操作 +其中子问题拆分 及 合并 结果需要经验,需要考究怎么拆分及合并更合理高效 +``` +def divide_conquer(problem, param1, param2...) { + // 终止 + if problem is None: + print_result + return + + // prepare data + data = prepare_data(problem) + subproblems = split(problem,data) + + // 处理子问题 + subresult1 = divide_conquer(subproblems[0]...) + subresult2 = divide_conquer(subproblems[1]...) + subresult3 = divide_conquer(subproblems[2]...) + + // 合并 + result = process_result(subresult1, subresult2, subresult3...) + + // 清理当前层 + clear_up +} + +``` diff --git a/Week 02/id_613/java/src/main/Leetcode17SolutionOne.java b/Week 02/id_613/java/src/main/Leetcode17SolutionOne.java new file mode 100644 index 000000000..a40f581c1 --- /dev/null +++ b/Week 02/id_613/java/src/main/Leetcode17SolutionOne.java @@ -0,0 +1,72 @@ +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 电话号码的字母组合 + * + * 分治法 + * + * 时间复杂度O(2^n),空间复杂度O(n) + * + * 执行用时 : * 7 ms * , 在所有 java 提交中击败了 * 6.10% * 的用户 + * 内存消耗 : * 36.4 MB * , 在所有 java 提交中击败了 * 72.94% * 的用户 + */ +class Leetcode17SolutionOne { + private List combinationList; + private Map numDict; + + public Leetcode17SolutionOne() { + this.combinationList = new ArrayList<>(); + this.numDict = new HashMap<>(); + } + + public List letterCombinations(String digits) { + // init num dict + numDict.put('2', "abc".toCharArray()); + numDict.put('3', "def".toCharArray()); + numDict.put('4', "ghi".toCharArray()); + numDict.put('5', "jkl".toCharArray()); + numDict.put('6', "mno".toCharArray()); + numDict.put('7', "pqrs".toCharArray()); + numDict.put('8', "tuv".toCharArray()); + numDict.put('9', "wxyz".toCharArray()); + + if (digits.isEmpty()) { + return this.combinationList; + } + + // recursion + _generate(digits, ""); + return this.combinationList; + } + + private void _generate(String digits, String s) { + // terminator + if (digits.isEmpty()) { + // conquer + this.combinationList.add(s); + System.out.println(s); + return; + } + + // divided and conquer + char curChar = digits.charAt(0); + for (char j : this.numDict.get(curChar)) { +// System.out.printf("j=%c\n", j); + if (digits.length() <= 1) { + _generate("", s + j); + } else { + _generate(digits.substring(1), s + j); + } + } + + // clear up + } + + public static void main(String[] args) { + Leetcode17SolutionOne solution = new Leetcode17SolutionOne(); + solution.letterCombinations("234"); + } +} \ No newline at end of file diff --git a/Week 02/id_613/java/src/main/Leetcode1SolutionOne.java b/Week 02/id_613/java/src/main/Leetcode1SolutionOne.java new file mode 100644 index 000000000..fcb3ff924 --- /dev/null +++ b/Week 02/id_613/java/src/main/Leetcode1SolutionOne.java @@ -0,0 +1,29 @@ +import java.util.HashMap; +import java.util.Map; + +/** + * 两数之和 + * 将数组的元素都存到hashmap中,然后扫描数组,看看(target - 当前元素)是否在map中 + * 平均时间复杂度O(n),空间复杂度O(n) + * + * 执行用时 : * 3 ms * , 在所有 java 提交中击败了 * 98.87% * 的用户 + * 内存消耗 : * 37 MB * , 在所有 java 提交中击败了 * 92.85% * 的用户 + */ +class Leetcode1SolutionOne { + public int[] twoSum(int[] nums, int target) { + Map numsMap = new HashMap(); + int[] result = new int[2]; + for (int i = 0; i < nums.length; i++) { + if (numsMap.containsKey(target - nums[i])) { + int otherIdx = numsMap.get(target - nums[i]); + result[0] = Math.min(i, otherIdx); + result[1] = Math.max(i, otherIdx); + return result; + } else { + numsMap.put(nums[i], i); + } + } + + return result; + } +} \ No newline at end of file diff --git a/Week 02/id_613/java/src/main/Leetcode22SolutionOne.java b/Week 02/id_613/java/src/main/Leetcode22SolutionOne.java new file mode 100644 index 000000000..691685270 --- /dev/null +++ b/Week 02/id_613/java/src/main/Leetcode22SolutionOne.java @@ -0,0 +1,70 @@ +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * 括号生成:基本思想,n个位置看成n个格子,每个格子都可以放左括号 和 右括号, + * 把每种可能的情况都枚举出来,并排除掉其中不合法的摆放即可 + * + * 时间复杂度O(2^n),空间复杂度O(n) + * + * 执行用时 : * 114 ms * , 在所有 java 提交中击败了 * 5.09% * 的用户 + * 内存消耗 : * 39.7 MB * , 在所有 java 提交中击败了 * 39.05% * 的用户 + */ +public class Leetcode22SolutionOne { + private List parenthesisList; + + public Leetcode22SolutionOne() { + parenthesisList = new ArrayList<>(); + } + + public List getParenthesisList() { + return parenthesisList; + } + + public List generateParenthesis(int n) { + // 自顶向下编程 + _generate(0, 2 * n, ""); + return this.parenthesisList; + } + + // 递归生成括号 + private void _generate(int level, int max_level, String s) { + if (level >= max_level) { + if (_validParenthesis(s)) { + this.parenthesisList.add(s); + System.out.println(s); + } + return; + } + + _generate(level + 1, max_level, s + "("); + _generate(level + 1, max_level, s + ")"); + } + + // check parenthesis using a stack + private boolean _validParenthesis(String s) { + Stack st = new Stack<>(); + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '(') { + st.push(s.charAt(i)); + } else { // ')' + try { + char other = st.pop(); + if (other != '(') { + return false; + } + } catch(Exception e) { + return false; + } + } + } + + return st.isEmpty(); + } + + public static void main(String[] args) { + Leetcode22SolutionOne solution = new Leetcode22SolutionOne(); + solution.generateParenthesis(3); + } +} \ No newline at end of file diff --git a/Week 02/id_613/java/src/main/Leetcode22SolutionTwo.java b/Week 02/id_613/java/src/main/Leetcode22SolutionTwo.java new file mode 100644 index 000000000..3821fa6be --- /dev/null +++ b/Week 02/id_613/java/src/main/Leetcode22SolutionTwo.java @@ -0,0 +1,53 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * SolutionOne加速版 + * + * 括号生成:基本思想,n个位置看成n个格子,每个格子都可以放左括号 和 右括号, + * 在生成的过程中就进行排除,而不是等全部生成后再判断序列是否合法,从而加速整 + * 个过程 + * + * 在生成的过程中,合法性判断:左括号必须<=n,右括号<=左括号 + * + * 时间复杂度O(2^n),空间复杂度O(n) + * + * 执行用时 : * 12 ms * , 在所有 java 提交中击败了 * 9.12% * 的用户 + * 内存消耗 : * 36.5 MB * , 在所有 java 提交中击败了 * 97.67% * 的用户 + */ +public class Leetcode22SolutionTwo { + private List parenthesisList; + + public Leetcode22SolutionTwo() { + parenthesisList = new ArrayList<>(); + } + + public List getParenthesisList() { + return parenthesisList; + } + + public List generateParenthesis(int n) { + // 自顶向下编程 + _generate(0, 0, n, ""); + return this.parenthesisList; + } + // 递归生成括号 + private void _generate(int left, int right, int max_level, String s) { + if (left >= max_level && right >= max_level) { + parenthesisList.add(s); + System.out.println(s); + return; + } + if (left < max_level) { + _generate(left + 1, right, max_level, s + "("); + } + if (right < left) { + _generate(left, right + 1, max_level, s + ")"); + } + } + + public static void main(String[] args) { + Leetcode22SolutionTwo solution = new Leetcode22SolutionTwo(); + solution.generateParenthesis(3); + } +} \ No newline at end of file diff --git a/Week 02/id_613/java/src/main/Leetcode50SolutionOne.java b/Week 02/id_613/java/src/main/Leetcode50SolutionOne.java new file mode 100644 index 000000000..b5431a861 --- /dev/null +++ b/Week 02/id_613/java/src/main/Leetcode50SolutionOne.java @@ -0,0 +1,43 @@ +/** + * Pow(x,n) + * + * 分治法 + * + * 时间复杂度O(logn),空间复杂度O(1) + * + * 执行用时 : * 1 ms * , 在所有 java 提交中击败了 * 99.92% * 的用户 + * 内存消耗 : * 33.5 MB * , 在所有 java 提交中击败了 * 73.59% * 的用户 + */ +class Leetcode50SolutionOne { + public double myPow(double x, int n) { + // 处理边界条件 + if (n == 0) { + return 1.0; + } + + long ln = n; + + // 处理n为负数的情况 + if (ln < 0) { + x = 1 / x; + ln = ln * (-1); + } + + return fastPow(x, ln); + } + + private double fastPow(double x, long ln) { + if (ln == 1) { + return x; + } + + double half = fastPow(x, ln / 2); + return (ln % 2 == 0) ? half * half : half * half * x; + } + + public static void main(String[] args) { + Leetcode50SolutionOne solution = new Leetcode50SolutionOne(); + System.out.println(solution.myPow(2, 10)); + + } +} \ No newline at end of file diff --git a/Week 02/id_613/java/src/main/Leetcode70SolutionOne.java b/Week 02/id_613/java/src/main/Leetcode70SolutionOne.java new file mode 100644 index 000000000..4224b5bc6 --- /dev/null +++ b/Week 02/id_613/java/src/main/Leetcode70SolutionOne.java @@ -0,0 +1,41 @@ +/** + * 爬楼梯,非递归解法 + * 爬到第n级台阶的方法,等于先爬到n-1级台阶 和 先爬到n-2级台阶 的方法之和 + * f(n) = f(n-1) + f(n-2),本质上就是一个Fibonacci序列 + * + * 时间复杂度O(n),空间复杂度O(1) + * + * 执行用时 : * 0 ms * , 在所有 java 提交中击败了 * 100.00% * 的用户 + * 内存消耗 : * 33 MB * , 在所有 java 提交中击败了 * 72.58% * 的用户 + */ +class Leetcode70SolutionOne { + public int climbStairs(int n) { + int result = 0; + + // 假设给定n是正整数 + if (n < 2) { + return 1; + } + + // Fibonacci: 1,1,2,3,5,8,13,21,34,55... + // Fibonacci序列的第0项 f(0) + int first = 1; + // Fibonacci序列的第1项 f(1) + int second = 1; + + for (int i = 2; i <= n; i++) { + // f(n) = f(n-1) + f(n-2) + result = first + second; + first = second; + second = result; + } + + return result; + } + + public static void main(String[] args) { + Leetcode70SolutionOne solution1 = new Leetcode70SolutionOne(); + int result = solution1.climbStairs(20); + System.out.println(result); + } +} \ No newline at end of file diff --git a/Week 02/id_613/java/src/main/Leetcode70SolutionTwo.java b/Week 02/id_613/java/src/main/Leetcode70SolutionTwo.java new file mode 100644 index 000000000..307c23b76 --- /dev/null +++ b/Week 02/id_613/java/src/main/Leetcode70SolutionTwo.java @@ -0,0 +1,24 @@ +/** + * 爬楼梯,递归解法 + * 爬到第n级台阶的方法,等于先爬到n-1级台阶 和 先爬到n-2级台阶 的方法之和 + * f(n) = f(n-1) + f(n-2),本质上就是一个Fibonacci序列 + * + * 时间复杂度O(2^n),空间复杂度O(1) + * + * 超出时间限制 + */ +class Leetcode70SolutionTwo { + public int climbStairs(int n) { + // 假设给定n是正整数 + if (n < 2) { + return 1; + } + return climbStairs(n - 1) + climbStairs(n - 2); + } + + public static void main(String[] args) { + Leetcode70SolutionTwo solution2 = new Leetcode70SolutionTwo(); + int result = solution2.climbStairs(20); + System.out.println(result); + } +} \ No newline at end of file diff --git a/Week 02/id_613/java/src/main/Leetcode94SolutionOne.java b/Week 02/id_613/java/src/main/Leetcode94SolutionOne.java new file mode 100644 index 000000000..dcd2a2d2d --- /dev/null +++ b/Week 02/id_613/java/src/main/Leetcode94SolutionOne.java @@ -0,0 +1,33 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * 中序遍历,使用递归 + * + * 时间复杂度O(n),空间复杂度O(n) + * + * 执行用时 : * 1 ms * , 在所有 java 提交中击败了 * 96.47% * 的用户 + * 内存消耗 : * 34.6 MB * , 在所有 java 提交中击败了 * 40.99% * 的用户 + */ +public class Leetcode94SolutionOne { + private class TreeNode { + int val; + TreeNode right; + TreeNode(int x) { val = x; } + TreeNode left; + } + + public List inorderTraversal(TreeNode root) { + List result = new ArrayList(); + helper(root, result); + return result; + } + + public void helper(TreeNode root, List res) { + if (root != null) { + helper(root.left, res); + res.add(root.val); + helper(root.right, res); + } + } +} \ No newline at end of file diff --git a/Week 02/id_613/java/src/main/Leetcode98SolutionOne.java b/Week 02/id_613/java/src/main/Leetcode98SolutionOne.java new file mode 100644 index 000000000..9670e4588 --- /dev/null +++ b/Week 02/id_613/java/src/main/Leetcode98SolutionOne.java @@ -0,0 +1,53 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * 判断是否二叉搜索树 + * + * 中序遍历,然后判断中序遍历的数组是否有序 + * + * 时间复杂度O(n)(2n,要遍历2遍),空间复杂度O(n) + * + * 执行用时 : * 3 ms * , 在所有 java 提交中击败了 * 42.51% * 的用户 + * 内存消耗 : * 37.1 MB * , 在所有 java 提交中击败了 * 86.85% * 的用户 + */ +public class Leetcode98SolutionOne { + private class TreeNode { + int val; + TreeNode right; + TreeNode(int x) { val = x; } + TreeNode left; + } + + public boolean isValidBST(TreeNode root) { + List inorderList = inorderTraversal(root); + if (inorderList.isEmpty()) { + return true; + } + + int lastVal = inorderList.get(0); + for (int i = 1; i < inorderList.size(); i++) { + if (inorderList.get(i) <= lastVal) { + return false; + } + + lastVal = inorderList.get(i); + } + + return true; + } + + public List inorderTraversal(TreeNode root) { + List result = new ArrayList<>(); + helper(root, result); + return result; + } + + public void helper(TreeNode root, List res) { + if (root != null) { + helper(root.left, res); + res.add(root.val); + helper(root.right, res); + } + } +} \ No newline at end of file diff --git a/Week 02/id_613/java/src/test/Leetcode1Test.java b/Week 02/id_613/java/src/test/Leetcode1Test.java new file mode 100644 index 000000000..8b4040ee6 --- /dev/null +++ b/Week 02/id_613/java/src/test/Leetcode1Test.java @@ -0,0 +1,18 @@ +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +/** + * + */ +public class Leetcode1Test { + @Test + public void testSolutionOne1() { + int nums[] = new int[]{2, 7, 11, 15}; + int target = 9; + Leetcode1SolutionOne solution1 = new Leetcode1SolutionOne(); + int[] result = solution1.twoSum(nums, target); + + assertArrayEquals(new int[]{0, 1}, result); + } +} diff --git a/Week 02/id_613/java/src/test/Leetcode50Test.java b/Week 02/id_613/java/src/test/Leetcode50Test.java new file mode 100644 index 000000000..b28fad849 --- /dev/null +++ b/Week 02/id_613/java/src/test/Leetcode50Test.java @@ -0,0 +1,37 @@ +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * + */ +public class Leetcode50Test { + @Test + public void testSolutionOne1() { + Leetcode50SolutionOne solution = new Leetcode50SolutionOne(); + Assertions.assertEquals(1024.0, solution.myPow(2, 10)); + } + + @Test + public void testSolutionOne2() { + Leetcode50SolutionOne solution = new Leetcode50SolutionOne(); + Assertions.assertEquals(1.0, solution.myPow(2, 0)); + } + + @Test + public void testSolutionOne3() { + Leetcode50SolutionOne solution = new Leetcode50SolutionOne(); + Assertions.assertEquals(2.0, solution.myPow(2, 1)); + } + + @Test + public void testSolutionOne4() { + Leetcode50SolutionOne solution = new Leetcode50SolutionOne(); + Assertions.assertEquals(0.5, solution.myPow(2, -1)); + } + + @Test + public void testSolutionOne5() { + Leetcode50SolutionOne solution = new Leetcode50SolutionOne(); + Assertions.assertEquals(1.0, solution.myPow(1.0, -2147483648)); + } +} diff --git a/Week 02/id_613/java/src/test/Leetcode70Test.java b/Week 02/id_613/java/src/test/Leetcode70Test.java new file mode 100644 index 000000000..65d44019a --- /dev/null +++ b/Week 02/id_613/java/src/test/Leetcode70Test.java @@ -0,0 +1,49 @@ +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * + */ +public class Leetcode70Test { + @Test + public void testSolutionOne1() { + int n = 4; + Leetcode70SolutionOne solution1 = new Leetcode70SolutionOne(); + Assertions.assertEquals(5, solution1.climbStairs(n)); + } + + @Test + public void testSolutionOne2() { + int n = 22; + Leetcode70SolutionOne solution1 = new Leetcode70SolutionOne(); + Assertions.assertEquals(28657, solution1.climbStairs(n)); + } + + @Test + public void testSolutionOne3() { + int n = 1; + Leetcode70SolutionOne solution1 = new Leetcode70SolutionOne(); + Assertions.assertEquals(1, solution1.climbStairs(n)); + } + + @Test + public void testSolutionTwo1() { + int n = 4; + Leetcode70SolutionTwo solution2 = new Leetcode70SolutionTwo(); + Assertions.assertEquals(5, solution2.climbStairs(n)); + } + + @Test + public void testSolutionTwo2() { + int n = 22; + Leetcode70SolutionTwo solution2 = new Leetcode70SolutionTwo(); + Assertions.assertEquals(28657, solution2.climbStairs(n)); + } + + @Test + public void testSolutionTwo3() { + int n = 1; + Leetcode70SolutionTwo solution2 = new Leetcode70SolutionTwo(); + Assertions.assertEquals(1, solution2.climbStairs(n)); + } +} \ No newline at end of file diff --git a/Week 02/id_618/LeetCode_105_618.java b/Week 02/id_618/LeetCode_105_618.java new file mode 100644 index 000000000..b9b73aa03 --- /dev/null +++ b/Week 02/id_618/LeetCode_105_618.java @@ -0,0 +1,116 @@ +class Solution1 { + /** + * “preorder 中的第一个元素一定是树的根,这个根又将 inorder + * 序列分成了左右两棵子树”,此方案性能不佳,因为在迭代里存在构建新数组和哈希集合,后又了写了Solution2 + * + * @param preorder + * @param inorder + * @return + */ + public TreeNode buildTree(int[] preorder, int[] inorder) { + if (preorder == null || preorder.length == 0 || inorder == null || inorder.length == 0) { + return null; + } + + // 构建根节点 + int rootVal = preorder[0]; + TreeNode rootNode = new TreeNode(rootVal); + + if (preorder.length == 1) { + return rootNode; + } + + // 构建左中序和右中序 + int[] leftInorder = null; + int[] rightInorder = null; + + for (int i = 0; i < inorder.length; i++) { + if (inorder[i] == rootVal) { + leftInorder = new int[i]; + rightInorder = new int[inorder.length - i - 1]; + + System.arraycopy(inorder, 0, leftInorder, 0, leftInorder.length); + System.arraycopy(inorder, i + 1, rightInorder, 0, rightInorder.length); + + break; + } + } + + // 构建左前序和右前序 + Set leftVals = new HashSet<>(); + Set rightVals = new HashSet<>(); + + for (int i = 0; i < leftInorder.length; i++) { + leftVals.add(leftInorder[i]); + } + + for (int i = 0; i < rightInorder.length; i++) { + rightVals.add(rightInorder[i]); + } + + int[] leftPreorder = new int[leftInorder.length]; + int[] rightPreorder = new int[rightInorder.length]; + + for (int i = 1, left = 0, right = 0; i < preorder.length; i++) { + int val = preorder[i]; + + if (leftVals.contains(val)) { + leftPreorder[left++] = val; + } else if (rightVals.contains(val)) { + rightPreorder[right++] = val; + } + } + + rootNode.left = this.buildTree(leftPreorder, leftInorder); + rootNode.right = this.buildTree(rightPreorder, rightInorder); + + return rootNode; + } + +} + +class Solution2 { + private int[] preorder; + private int preorderIndex = 0; + private Map inorderIndexMap; + + /** + * 将数组拷贝的思路改为中序数组左右边界指针变动,不在迭代中产生新的数组 + * + * @param preorder + * @param inorder + * @return + */ + public TreeNode buildTree(int[] preorder, int[] inorder) { + this.preorder = preorder; + this.inorderIndexMap = new HashMap<>(inorder.length, 1); + + // 构建值在中序数组里的索引,可以快速将中序数组按根节点分为左右两边 + for (int i = 0; i < inorder.length; i++) { + inorderIndexMap.put(inorder[i], i); + } + + // 开始迭代,起始边界是中序数组的左右边界 + return this.buildTree0(0, inorder.length); + } + + private TreeNode buildTree0(int inorderLeft, int inorderRight) { + // 终止条件 + if (inorderLeft == inorderRight) { + return null; + } + + // 前序数组始终为当前迭代子树的根节点,取完后自增,定位下一个子树的根节点 + int rootVal = this.preorder[preorderIndex++]; + TreeNode root = new TreeNode(rootVal); + + // 获取根节点在中序数组的索引 + int inorderSplit = this.inorderIndexMap.get(rootVal); + + // 根据索引,将中序数组分为左右两个边界,递归执行,分别获取左右节点 + root.left = this.buildTree0(inorderLeft, inorderSplit); + root.right = this.buildTree0(inorderSplit + 1, inorderRight); + + return root; + } +} \ No newline at end of file diff --git a/Week 02/id_618/LeetCode_144_618.java b/Week 02/id_618/LeetCode_144_618.java new file mode 100644 index 000000000..d2b69af20 --- /dev/null +++ b/Week 02/id_618/LeetCode_144_618.java @@ -0,0 +1,20 @@ +class Solution { + + private List ret = new ArrayList<>(); + + public List preorderTraversal(TreeNode root) { + preorderTraversal0(root); + + return ret; + } + + private void preorderTraversal0(TreeNode root) { + if (root == null) { + return; + } + + ret.add(root.val); + preorderTraversal0(root.left); + preorderTraversal0(root.right); + } +} \ No newline at end of file diff --git a/Week 02/id_618/LeetCode_17_618.java b/Week 02/id_618/LeetCode_17_618.java new file mode 100644 index 000000000..f2928c35b --- /dev/null +++ b/Week 02/id_618/LeetCode_17_618.java @@ -0,0 +1,57 @@ +class Solution { + // 数字和字母的映射表 + private Map phoneLetters = new HashMap<>(8, 1); + // 结果集 + private List ret = new ArrayList<>(); + + public List letterCombinations(String digits) { + // 处理空值 + if (digits.length() == 0) { + return ret; + } + + // 构建映射表 + this.buildPhoneLetters(); + + // 开始执行,初始字母为空,起始为0 + this.letterCombinations("", digits, 0); + + return this.ret; + } + + /** + * 获取digitIndex位置的数字,获取对应字母,将每个字母追加到当前字母上,继续递归,直到最后一个数字 + * + * @param letters + * @param digits + * @param digitIndex + */ + private void letterCombinations(String letters, String digits, int digitIndex) { + if (digitIndex == digits.length()) { + ret.add(letters.toString()); + + return; + } + + String digit = String.valueOf(digits.charAt(digitIndex)); + String phoneLetter = phoneLetters.get(digit); + + for (int j = 0; j < phoneLetter.length(); j++) { + String letter = String.valueOf(phoneLetter.charAt(j)); + + this.letterCombinations(letters + letter, digits, digitIndex + 1); + } + + } + + private void buildPhoneLetters() { + phoneLetters.put("2", "abc"); + phoneLetters.put("3", "def"); + phoneLetters.put("4", "ghi"); + phoneLetters.put("5", "jkl"); + phoneLetters.put("6", "mno"); + phoneLetters.put("7", "pqrs"); + phoneLetters.put("8", "tuv"); + phoneLetters.put("9", "wxyz"); + } +} \ No newline at end of file diff --git a/Week 02/id_618/LeetCode_236_618 .java b/Week 02/id_618/LeetCode_236_618 .java new file mode 100644 index 000000000..271736103 --- /dev/null +++ b/Week 02/id_618/LeetCode_236_618 .java @@ -0,0 +1,68 @@ +class Solution { + + // 记录答案,如果已经找到,则终止递归 + private TreeNode ancestor; + + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + this.hasMatchNode(root, p, q, 2); + return this.ancestor; + } + + /** + * 判断当前子树是否有匹配节点,特别的,当targetMatch为0时表示"无须匹配",直接返回false + * + *

+ * 依次判断,当前根节点、左侧子树和右侧子树是否匹配,3者中只要存在一个匹配,即表示匹配,如果存在2个匹配,那么当前根节点就是公共祖先 + * + * @param current + * @param p + * @param q + * @param targetMatch 需要匹配的数量,其作用是避免不必要的遍历 + * @return + */ + private boolean hasMatchNode(TreeNode current, TreeNode p, TreeNode q, int targetMatch) { + // 父方法栈已经完成需要的匹配,当前栈无需继续 + if (targetMatch == 0) { + return false; + } + + // 已经找到的话,直接返回false终止递归 + if (this.ancestor != null) { + return false; + } + + if (current == null) { + return false; + } + + // 当前节点对应的匹配数 + int match = 0; + + // 当前是否匹配 + if (current.val == p.val || current.val == q.val) { + targetMatch--; + match++; + } + + // 左侧是否匹配 + boolean leftMatch = hasMatchNode(current.left, p, q, targetMatch); + if (leftMatch) { + targetMatch--; + match++; + } + + // 右侧是否匹配 + boolean rightMatch = hasMatchNode(current.right, p, q, targetMatch); + if (rightMatch) { + targetMatch--; + match++; + } + + // 存在2个匹配,那么当前节点就是答案 + if (match == 2) { + this.ancestor = current; + } + + return match > 0; + } +} \ No newline at end of file diff --git a/Week 02/id_618/LeetCode_242_618.java b/Week 02/id_618/LeetCode_242_618.java new file mode 100644 index 000000000..6fec7c94b --- /dev/null +++ b/Week 02/id_618/LeetCode_242_618.java @@ -0,0 +1,41 @@ +class Solution { + public boolean isAnagram(String s, String t) { + // 长度不一致,肯定不是 + if (s.length() != t.length()) { + return false; + } + + // 利用字符的asc2编码作为索引 + int[] map = new int[256]; + + // 遍历s,记录每个字符出现的次数 + for (char c : s.toCharArray()) { + int num = map[c]; + num++; + map[c] = num; + } + + // 遍历t,从map中找到对应字符,找到则减去出现的次数 + for (char c : t.toCharArray()) { + int num = map[c]; + + if (num == 0) { + // 不存在直接说明肯定不匹配 + return false; + } else { + num--; + } + + map[c] = num; + } + + // map中都是0,说明匹配,反之不匹配 + for (int i = 0; i < map.length; i++) { + if (map[i] != 0) { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/Week 02/id_618/LeetCode_429_618.java b/Week 02/id_618/LeetCode_429_618.java new file mode 100644 index 000000000..3775cf7a6 --- /dev/null +++ b/Week 02/id_618/LeetCode_429_618.java @@ -0,0 +1,46 @@ +class Solution { + private List> ret = new ArrayList<>(); + + public List> levelOrder(Node root) { + levelOrder0(root, 0); + + return ret; + } + + private List getCurrentLevel(int level) { + if (level < ret.size()) { + // 说明level层的节点已经被遍历过 + return ret.get(level); + } + + // 遍历时,按level递增的顺序,所以直接加到最后即可 + List currentLevel = new ArrayList<>(); + ret.add(currentLevel); + + return currentLevel; + } + + /** + * 前序遍历树,将遍历到的节点放入当前层的集合中 + * + * @param root + * @param level + */ + private void levelOrder0(Node root, int level) { + if (root == null) { + return; + } + + // 获取指定层的集合 + List currentLevel = getCurrentLevel(level); + + currentLevel.add(root.val); + + List children = root.children; + if (children != null && !children.isEmpty()) { + for (int i = 0; i < children.size(); i++) { + levelOrder0(children.get(i), level + 1); + } + } + } +} \ No newline at end of file diff --git a/Week 02/id_618/LeetCode_51_618.java b/Week 02/id_618/LeetCode_51_618.java new file mode 100644 index 000000000..12e97a647 --- /dev/null +++ b/Week 02/id_618/LeetCode_51_618.java @@ -0,0 +1,130 @@ +class Solution { + // 存放最终的结果 + private List> results = new ArrayList<>(); + + // 处于被攻击位置的列索引 + private int[] cols; + // 处于被攻击的左对角线 + private int[] leftDiagonals; + // 处于被攻击的右对角线 + private int[] rightDiagonals; + + public List> solveNQueens(int n) { + // 初始化以下被攻击位置的容器,用数组比hashset性能更好 + this.cols = new int[n]; + // 两个对角线总计为2n-1个;左对角线的索引为行列索引之和;右对角线所以为行列之差+n-1; + this.leftDiagonals = new int[2 * n - 1]; + this.rightDiagonals = new int[2 * n - 1]; + + // 从第1行开始执行 + this.solveNQueens0(0, new int[n]); + return results; + } + + /** + * 将result转换成要求的字符串格式 + * + * @param result + * @return + */ + private List buildResult(int[] result) { + List strRet = new ArrayList<>(); + + for (int i = 0; i < result.length; i++) { + StringBuilder line = new StringBuilder(); + + for (int j = 0; j < result.length; j++) { + if (j == result[i]) { + line.append("Q"); + } else { + line.append("."); + } + } + + strRet.add(line.toString()); + } + + return strRet; + } + + /** + * 指定位置是否处于被攻击位置 + * + * @param row + * @param col + * @return + */ + private boolean isUnderAttack(int row, int col) { + if (cols[col] == 1 || leftDiagonals[row + col] == 1 || rightDiagonals[row - col + cols.length - 1] == 1) { + return true; + } + + return false; + } + + /** + * 在指定位置放上皇后,result索引为行号,值是列索引,并标记其可攻击的位置,可攻击的位置标记为1 + * + * @param row + * @param col + * @param result + */ + private void putQueen(int row, int col, int[] result) { + result[row] = col; + + cols[col] = 1; + leftDiagonals[row + col] = 1; + rightDiagonals[row - col + cols.length - 1] = 1; + } + + /** + * 在指定位置移出皇后,并移出其可攻击的位置 + * + * @param row + * @param col + * @param result + */ + private void removeQueen(int row, int col, int[] result) { + result[row] = 0; + + cols[col] = 0; + leftDiagonals[row + col] = 0; + rightDiagonals[row - col + cols.length - 1] = 0; + } + + /** + * 在指定行,尝试摆放皇后, + * + * @param row + * @param result + */ + private void solveNQueens0(int row, int[] result) { + for (int col = 0; col < result.length; col++) { + // 当前位置会被攻击,忽略 + if (this.isUnderAttack(row, col)) { + continue; + } + + // 当前位置摆放皇后 + this.putQueen(row, col, result); + + boolean lastRow = row == result.length - 1; + if (lastRow) { + // 如果是最后一行,说明当前result是一个合法结果,存放到最终结果集里 + List strResult = this.buildResult(result); + this.results.add(strResult); + } else { + // 还有后续行,尝试去下一行继续摆放皇后 + this.solveNQueens0(row + 1, result); + } + + // 当前位置对应的结果都已经检索完成,将皇后移出 + this.removeQueen(row, col, result); + + // 如果是最后一行,后续列无须判断 + if (lastRow) { + break; + } + } + } +} \ No newline at end of file diff --git a/Week 02/id_618/LeetCode_589_618 .java b/Week 02/id_618/LeetCode_589_618 .java new file mode 100644 index 000000000..5f4e1425b --- /dev/null +++ b/Week 02/id_618/LeetCode_589_618 .java @@ -0,0 +1,24 @@ +class Solution { + private List ret = new ArrayList<>(); + + public List preorder(Node root) { + preorder0(root); + + return ret; + } + + private void preorder0(Node root) { + if (root == null) { + return; + } + + ret.add(root.val); + + List children = root.children; + if (children != null && !children.isEmpty()) { + for (int i = 0; i < children.size(); i++) { + preorder0(children.get(i)); + } + } + } +} \ No newline at end of file diff --git a/Week 02/id_618/LeetCode_590_618 .java b/Week 02/id_618/LeetCode_590_618 .java new file mode 100644 index 000000000..18121d121 --- /dev/null +++ b/Week 02/id_618/LeetCode_590_618 .java @@ -0,0 +1,24 @@ +class Solution { + private List ret = new ArrayList<>(); + + public List postorder(Node root) { + postorder0(root); + + return ret; + } + + private void postorder0(Node root) { + if (root == null) { + return; + } + + List children = root.children; + if (children != null && !children.isEmpty()) { + for (int i = 0; i < children.size(); i++) { + postorder0(children.get(i)); + } + } + + ret.add(root.val); + } +} \ No newline at end of file diff --git a/Week 02/id_618/LeetCode_94_618.java b/Week 02/id_618/LeetCode_94_618.java new file mode 100644 index 000000000..72a5724c4 --- /dev/null +++ b/Week 02/id_618/LeetCode_94_618.java @@ -0,0 +1,20 @@ +class Solution { + + private List ret = new ArrayList<>(); + + public List inorderTraversal(TreeNode root) { + inorderTraversal0(root); + + return ret; + } + + private void inorderTraversal0(TreeNode root) { + if (root == null) { + return; + } + + inorderTraversal0(root.left); + ret.add(root.val); + inorderTraversal0(root.right); + } +} \ No newline at end of file diff --git a/Week 02/id_618/NOTE.md b/Week 02/id_618/NOTE.md index a6321d6e2..466258ba8 100644 --- a/Week 02/id_618/NOTE.md +++ b/Week 02/id_618/NOTE.md @@ -1,4 +1,18 @@ -# NOTE +## 第7课 + +### 从前序与中序遍历序列构造二叉树 + +开始并没有思路,开始看题解,很快看到关键点:"preorder 中的第一个元素一定是树的根,这个根又将 inorder 序列分成了左右两棵子树",就有了思路,也就没有继续看下去,写了一个利用数组拷贝的方法,递归执行。 + +提交后,性能不令人满意,这也在我意料之中,因为每次迭代里都用了数组拷贝和构建哈希集合(用于构建左右前序数组),改进思路为:用中序数组左右边界指针代替中序数组拷贝,用前序数组步进指针获取当前迭代的根节点。 + +经过几次尝试,发现最终成果与官方题解思路基本一致。 + + ## 第8课 + + ### N皇后 + + 看了视频后,已经理解了思路,捣鼓了10来分钟就写出了正确的代码,但是性能表现一般,看了题解后,感觉问题在于:存储攻击位置用HashSet,读写性能不如用数组,于是改进了代码,再次提交性能与题解差不多。 + - diff --git a/Week 02/id_628/LeetCode_144_628.java b/Week 02/id_628/LeetCode_144_628.java new file mode 100644 index 000000000..87923710d --- /dev/null +++ b/Week 02/id_628/LeetCode_144_628.java @@ -0,0 +1,48 @@ +//һ ǰ +// +// ʾ: +// +// : [1,null,2,3] +// 1 +// \ +// 2 +// / +// 3 +// +//: [1,2,3] +// +// +// : ݹ㷨ܼ򵥣ͨ㷨 +// Related Topics ջ + + + +//leetcode submit region begin(Prohibit modification and deletion) + +import java.util.ArrayList; + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class LeetCode_144_628 { + public List preorderTraversal(TreeNode root) { + List res = new ArrayList(); + dfs(res,root); + return res; + } + + public void dfs(List reslist , TreeNode root){ + if(root == null) return ; + reslist.add(root.val); + if(root.left != null) dfs(reslist,root.left); + if (root.right != null) dfs(reslist , root.right); + return ; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_628/LeetCode_236_628.java b/Week 02/id_628/LeetCode_236_628.java new file mode 100644 index 000000000..4f1ed75f3 --- /dev/null +++ b/Week 02/id_628/LeetCode_236_628.java @@ -0,0 +1,53 @@ +//һ N ڵֵĺ +// +// 磬һ 3 : +// +// +// +// +// +// +// +// : [5,6,3,2,4,1]. +// +// +// +// ˵: ݹ鷨ܼ򵥣ʹõɴ? Related Topics + + +import java.util.ArrayList; + +//leetcode submit region begin(Prohibit modification and deletion) +/* +// Definition for a Node. +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +}; +*/ +class LeetCode_590_628 { + public List postorder(Node root) { + List res = new ArrayList(); + dfs(res,root); + return res; + } + + public void dfs(List reslist , Node root){ + if(root == null) return ; + if(root.children != null && root.children.size() != 0){ + for (Node node:root.children) { + dfs(reslist , node); + } + } + reslist.add(root.val); + return ; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_628/LeetCode_242_628.java b/Week 02/id_628/LeetCode_242_628.java new file mode 100644 index 000000000..b0054342f --- /dev/null +++ b/Week 02/id_628/LeetCode_242_628.java @@ -0,0 +1,39 @@ +//ַ s t дһж t Ƿ s ĸλʡ +// +// ʾ 1: +// +// : s = "anagram", t = "nagaram" +//: true +// +// +// ʾ 2: +// +// : s = "rat", t = "car" +//: false +// +// ˵: +//ԼַֻСдĸ +// +// : +//ַ unicode ַô죿ܷĽⷨӦ +// Related Topics ϣ + + + +//leetcode submit region begin(Prohibit modification and deletion) +class LeetCode_242_628 { + public boolean isAnagram(String s, String t) { + int[] arr = new int[26] ; + if(s.length() != t.length())return false; + for (int i = 0 ; i < s.length() ; i++ ){ + arr[s.charAt(i) - 'a']++; + arr[t.charAt(i) - 'a']--; + } + + for (int j = 0 ; j < arr.length ; j++){ + if(arr[j] != 0)return false; + } + return true; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_628/LeetCode_429_628.java b/Week 02/id_628/LeetCode_429_628.java new file mode 100644 index 000000000..198e7fa25 --- /dev/null +++ b/Week 02/id_628/LeetCode_429_628.java @@ -0,0 +1,74 @@ +//һ N ڵֵIJ (ң) +// +// 磬һ 3 : +// +// +// +// +// +// +// +// : +// +// [ +// [1], +// [3,2,4], +// [5,6] +//] +// +// +// +// +// ˵: +// +// +// Ȳᳬ 1000 +// Ľڵᳬ 5000 +// Related Topics + + +import java.util.LinkedList; + +//leetcode submit region begin(Prohibit modification and deletion) +/* +// Definition for a Node. +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +}; +*/ +class LeetCode_429_628 { + public List> levelOrder(Node root) { + if (root == null )return new ArrayList>(); + List> list = new ArrayList>(); + List curLevellist = new ArrayList(); + curLevellist.add(root); + dfs(curLevellist,list); + return list; + } + + + public void dfs(List curLevellist , List> reslist){ + if(curLevellist == null || curLevellist.size() == 0)return ; + List nextList = new LinkedList(); + List vallist = new LinkedList(); + for (Node node: curLevellist) { + vallist.add(node.val); + if(node.children != null){ + for(Node nnode : node.children){ + nextList.add(nnode); + } + } + } + reslist.add(vallist); + dfs(nextList,reslist); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_628/LeetCode_49_628.java b/Week 02/id_628/LeetCode_49_628.java new file mode 100644 index 000000000..c13636dd0 --- /dev/null +++ b/Week 02/id_628/LeetCode_49_628.java @@ -0,0 +1,41 @@ +//һַ飬ĸλһĸλָĸͬвַͬ +// +// ʾ: +// +// : ["eat", "tea", "tan", "ate", "nat", "bat"], +//: +//[ +// ["ate","eat","tea"], +// ["nat","tan"], +// ["bat"] +//] +// +// ˵ +// +// +// ΪСдĸ +// Ǵ˳ +// +// Related Topics ϣ ַ + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +//leetcode submit region begin(Prohibit modification and deletion) +class LeetCode_49_628 { + public List> groupAnagrams(String[] strs) { + if(strs == null || strs.length == 0)return new ArrayList>(); + HashMap> map = new HashMap>(); + for (String str : strs) { + char[] arr = str.toCharArray(); + Arrays.sort(arr); + String key = new String(arr); + if(!map.containsKey(key))map.put(key , new ArrayList()); + map.get(key).add(str); + } + return new ArrayList>(map.values()); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_628/LeetCode_589_628.java b/Week 02/id_628/LeetCode_589_628.java new file mode 100644 index 000000000..149aa75d3 --- /dev/null +++ b/Week 02/id_628/LeetCode_589_628.java @@ -0,0 +1,54 @@ +//һ N ڵֵǰ +// +// 磬һ 3 : +// +// +// +// +// +// +// +// ǰ: [1,3,5,6,2,4] +// +// +// +// ˵: ݹ鷨ܼ򵥣ʹõɴ? Related Topics + + +import java.util.ArrayList; + +//leetcode submit region begin(Prohibit modification and deletion) +/* +// Definition for a Node. +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +}; +*/ +class LeetCode_589_628 { + public List preorder(Node root) { + List res = new ArrayList(); + dfs(res,root); + return res; + } + + public void dfs(List reslist , Node root){ + if(root == null) return ; + reslist.add(root.val); + if(root.children != null && root.children.size() != 0){ + for (Node node:root.children) { + dfs(reslist , node); + } + } + + return ; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_628/LeetCode_590_628.java b/Week 02/id_628/LeetCode_590_628.java new file mode 100644 index 000000000..4f1ed75f3 --- /dev/null +++ b/Week 02/id_628/LeetCode_590_628.java @@ -0,0 +1,53 @@ +//һ N ڵֵĺ +// +// 磬һ 3 : +// +// +// +// +// +// +// +// : [5,6,3,2,4,1]. +// +// +// +// ˵: ݹ鷨ܼ򵥣ʹõɴ? Related Topics + + +import java.util.ArrayList; + +//leetcode submit region begin(Prohibit modification and deletion) +/* +// Definition for a Node. +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +}; +*/ +class LeetCode_590_628 { + public List postorder(Node root) { + List res = new ArrayList(); + dfs(res,root); + return res; + } + + public void dfs(List reslist , Node root){ + if(root == null) return ; + if(root.children != null && root.children.size() != 0){ + for (Node node:root.children) { + dfs(reslist , node); + } + } + reslist.add(root.val); + return ; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_628/LeetCode_94_628.java b/Week 02/id_628/LeetCode_94_628.java new file mode 100644 index 000000000..a649ae056 --- /dev/null +++ b/Week 02/id_628/LeetCode_94_628.java @@ -0,0 +1,48 @@ +//һ +// +// ʾ: +// +// : [1,null,2,3] +// 1 +// \ +// 2 +// / +// 3 +// +//: [1,3,2] +// +// : ݹ㷨ܼ򵥣ͨ㷨 +// Related Topics ջ ϣ + + + +//leetcode submit region begin(Prohibit modification and deletion) + +import java.util.ArrayList; + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class LeetCode_94_628 { + public List inorderTraversal(TreeNode root) { + List res = new ArrayList(); + dfs(res,root); + return res; + } + + public void dfs( List reslist , TreeNode root){ + if(root == null) return ; + if(root.left != null) dfs(reslist,root.left); + reslist.add(root.val); + if (root.right != null) dfs(reslist , root.right); + return ; + + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 02/id_628/NOTE.md b/Week 02/id_628/NOTE.md index a6321d6e2..1565f31fb 100644 --- a/Week 02/id_628/NOTE.md +++ b/Week 02/id_628/NOTE.md @@ -1,4 +1,672 @@ # NOTE +# ڶѧϰܽ᡿ + +## ֪ʶ㡿 + +ܽ5-8ڵѧϰݽṹת뵽µ㷨ѧϰҪѧϰµ֪ʶ㡣 + +- ϣϵʵԭĿн + +- ĸ˶Ƚṹʵս +- ˵ݹģ壬ݹĻ˼·Լݹϼ֦Ż +- ӵݹΡݵ㷨˼ά˶Ŀ + +## ѧϰܽ᡿ + +### ݹģ塿 + +дݹҪȷֹԼÿһĺ壬ͬʱԻѧûģ壬ģ͵˼ά޽ʦĵݹģ + +```java +public void recur(int level, int param) { + + // terminator -- ݹֹ + if (level > MAX_LEVEL) { + // process result + return; + } + + // process current logic -- ǰҵ߼ + process(level, param); + + // drill down -- һ + recur( level: level + 1, newParam); + + // restore current status --ʹȫֱ򹫹ҪʹúۼӰ㡣 + +} +``` + +ģ塿 + +ε˼ԴСѰظԪַֺ֮Ϻȥм㡣 + +```python +def divide_conquer(problem, param1, param2, ...): + # recursion terminator -- ֹ + if problem is None: + print_result + return + + # prepare data -- Ԥݣֽ + data = prepare_data(problem) + subproblems = split_problem(problem, data) + + # conquer subproblems -- ֿÿһ + subresult1 = self.divide_conquer(subproblems[0], p1, ...) + subresult2 = self.divide_conquer(subproblems[1], p1, ...) + subresult3 = self.divide_conquer(subproblems[2], p1, ...) + + + # process and generate the final result -- ϲ + result = process_result(subresult1, subresult2, subresult3, ) + + # revert the current level states +``` + + + + +# Դܽ᡿ڶ-mapjdk1.8汾 + +˽hashmapҪ˽javaеhashԶµ˽mapݽṹ + +## 1Object жʦү + +javaжǴObjectģ涨һhashCodeǼַhashֵһequalsDZȽ϶Ƿȣ==ȽϵʵJVMзڴĵַǷͬ + +```java +// +public native int hashCode(); +//ȽڴַǷһ +public boolean equals(Object obj) { + return (this == obj); +} + +``` + +## 2String Ӱŵ + +ObjectequalsȽϵڴַStringĻдequalshashcode¡equalsҪȽڲַǷÿһͬhashcodeǸÿַhashܺͣͻhashcodeͬequalsͬij + +pshashcodeеij31ѡҲһݵģȤɲο[](https://www.cnblogs.com/nullllun/archive/2018/01/25/8350178.html) + +```java +//StringдhashCode----ϣʵʹ +public int hashCode() { + int h = hash; + if (h == 0 && value.length > 0) { + char val[] = value; + //ܼʽs[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] + for (int i = 0; i < value.length; i++) { + //ѡ31ԭ + // 1ͻֲԾ + // 2.JVMŻ 31 * i = (i << 5) - i + h = 31 * h + val[i]; + } + hash = h; + } + return h; +} +//Stringдequals +public boolean equals(Object anObject) { + if (this == anObject) { + return true; + } + if (anObject instanceof String) { + String anotherString = (String)anObject; + int n = value.length; + //жϳǷһ + if (n == anotherString.value.length) { + char v1[] = value; + char v2[] = anotherString.value; + int i = 0; + //ַжǷ + while (n-- != 0) { + if (v1[i] != v2[i]) + return false; + i++; + } + return true; + } + } + return false; +} +``` + +## 3Map йϣ游 + +javaжMapӿڣҪӳĹкܶʵڣӳϵе游ӿڣķ£ + +```java + //ǰӳʵʴԪض + int size(); + //ǰǷǿյ + boolean isEmpty(); + //ǰӳǷkey + boolean containsKey(Object key); + //ǰӳǷvalue + boolean containsValue(Object value); + //ȡֵΪkeyӦvalue + V get(Object key); + //ΪkeyֵΪvalueļֵ + V put(K key, V value); + //ɾΪkeyļֵ + V remove(Object key); + //һmapȫ뵱ǰmap + void putAll(Map m); + //ǰmapԪ + void clear(); + //ȡǰmapеkey + Set keySet(); + //ȡǰmapеvalues + Collection values(); +``` + +˺ķ⣬ڲentryĽṹͷentryΪֵԵķʽmapУ + +```java + +interface Entry { + + //ȡǰentryڵkey + K getKey(); + //ȡǰentryڵvalue + V getValue(); + //õǰڵvalue + V setValue(V value); + //жϵǰڵoǷͬ + boolean equals(Object o); + //㵱ǰڵhashֵ + int hashCode(); + + //һͨkeyȽϵıȽ + public static , V> Comparator> comparingByKey() { + return (Comparator> & Serializable) + (c1, c2) -> c1.getKey().compareTo(c2.getKey()); + } + //ȡһͨvalueȽϵıȽ + public static > Comparator> comparingByValue() { + return (Comparator> & Serializable) + (c1, c2) -> c1.getValue().compareTo(c2.getValue()); + } + + //ȡһͨkeyȽϵıȽ + public static Comparator> comparingByKey(Comparator cmp) { + Objects.requireNonNull(cmp); + return (Comparator> & Serializable) + (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey()); + } + + //ȡһͨvalueȽϵıȽ + public static Comparator> comparingByValue(Comparator cmp) { + Objects.requireNonNull(cmp); + return (Comparator> & Serializable) + (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue()); + } + } +``` + + + +## 4AbstractMap Map + +mapеĹ࣬Ǽ峤ʵͨԼij󷽷entrySetʵ˴mapжĽӿڷ󷽷mapжڲʵʵȥʵ֡ + +```java +//Զij󷽷 +public abstract Set> entrySet(); +//entrySetͨʵͨget +public V get(Object key) { + Iterator> i = entrySet().iterator(); + if (key==null) { + while (i.hasNext()) { + Entry e = i.next(); + if (e.getKey()==null) + return e.getValue(); + } + } else { + while (i.hasNext()) { + Entry e = i.next(); + //ȽϱʱڵkeyǷkeyһж + if (key.equals(e.getKey())) + return e.getValue(); + } + } + return null; + } +``` + +## 5hashMap Mapѡ + +ĵhashmap˵ľͶˣʽǹ⻷ǾʹԴһ⻷ɡ + +### 5.1 + + + +```java + //Ĭijʼ + static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 + // + static final int MAXIMUM_CAPACITY = 1 << 30; + //Ĭϵļ + static final float DEFAULT_LOAD_FACTOR = 0.75f; + //ڵתΪڵֵ + static final int TREEIFY_THRESHOLD = 8; + //ڵ˻Ϊڵֵ + static final int UNTREEIFY_THRESHOLD = 6; + //תΪڵʱtableС + static final int MIN_TREEIFY_CAPACITY = 64; +``` + + + +### 5.2Խ + + + +```java + + transient Node[] table; + + transient Set> entrySet; + //ʵʴ洢ļֵԵĸ + transient int size; + //ڱԵʱƱ仯ĸ֪ + transient int modCount; + //ݴֵtable == {}ʱֵΪʼʼĬΪ16 + int threshold; + //ӣtableж٣Ĭ0.7575% + final float loadFactor; + +``` + + + +### 5.3ڲ + +ͨڵ + +```java +//̳mapӿڵentryڵ +static class Node implements Map.Entry { + final int hash; //hashֵ + final K key; // + V value; //ֵ + Node next; //һڵ + + Node(int hash, K key, V value, Node next) { + this.hash = hash; + this.key = key; + this.value = value; + this.next = next; + } + + public final K getKey() { return key; } + public final V getValue() { return value; } + public final String toString() { return key + "=" + value; } + + //ڲŶhashcode + public final int hashCode() { + //^1^0 = 1 , 1^1 = 0 , 0^1 = 1 , 0^0 = 0 + return Objects.hashCode(key) ^ Objects.hashCode(value); + } + + public final V setValue(V newValue) { + V oldValue = value; + value = newValue; + return oldValue; + } + + public final boolean equals(Object o) { + if (o == this) + return true; + if (o instanceof Map.Entry) { + Map.Entry e = (Map.Entry)o; + //ֱȽkeyvalueȫͬͬ + if (Objects.equals(key, e.getKey()) && + Objects.equals(value, e.getValue())) + return true; + } + return false; + } +} +``` + +ڵ + +```java +static final class TreeNode extends LinkedHashMap.Entry { + TreeNode parent; // red-black tree links + TreeNode left; + TreeNode right; + TreeNode prev; // needed to unlink next upon deletion + boolean red; + TreeNode(int hash, K key, V val, Node next) { + super(hash, key, val, next); + } + + + final TreeNode root() { + for (TreeNode r = this, p;;) { + if ((p = r.parent) == null) + return r; + r = p; + } + } + + + +} + +``` + + + +### 5.4췽 + + + +```java + //췽һ ʼinitialCapacityloadFactor + public HashMap(int initialCapacity, float loadFactor) { + if (initialCapacity < 0) + //ʼΪ + throw new IllegalArgumentException("Illegal initial capacity: " + + initialCapacity); + if (initialCapacity > MAXIMUM_CAPACITY) + //ʼֱֵʼΪֵ + initialCapacity = MAXIMUM_CAPACITY; + if (loadFactor <= 0 || Float.isNaN(loadFactor)) + //УӸʽͷΧ + throw new IllegalArgumentException("Illegal load factor: " + + loadFactor); + this.loadFactor = loadFactor; + //ݳʼֵ + this.threshold = tableSizeFor(initialCapacity); + } + + //ش2ݵ10򷵻16 + static final int tableSizeFor(int cap) { + //nĶΪ01xxx...xxx + int n = cap - 1; + //n1λ001xx...xxxλ011xx...xxx + n |= n >>> 1; + //n2Ϊ00011...xxxλ01111...xxx + n |= n >>> 2; + //n4Ϊ000001111...xxxλ011111111...xxx + n |= n >>> 4; + //ͬ818λ϶úλҲΪ1 + n |= n >>> 8; + //ͬ16116λ϶ú16λҲΪ1 + n |= n >>> 16; + //ˣjavaintΪֽڣҲ32λȷԭλұȫΪ1ҲǴʱn+1Ϊȡӽ2xݡ + return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; + } + + //췽 Ĭϼ0.75 + public HashMap(int initialCapacity) { + this(initialCapacity, DEFAULT_LOAD_FACTOR); + } + //췽 Ĭϼ0.75 + public HashMap() { + this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted + } + //췽ģͨmapmap + public HashMap(Map m) { + //üΪĬϼ + this.loadFactor = DEFAULT_LOAD_FACTOR; + //ԭmapԪص빹map + putMapEntries(m, false); + } + + + final void putMapEntries(Map m, boolean evict) { + //ȡԭhashԪ + int s = m.size(); + if (s > 0) { + //Ϊ + if (table == null) { // pre-size + //±δռ䣬зռ + //ҪĿռС + float ft = ((float)s / loadFactor) + 1.0F; + //ݱ߽жʵʷС + int t = ((ft < (float)MAXIMUM_CAPACITY) ? + (int)ft : MAXIMUM_CAPACITY); + if (t > threshold) + //ֵ + threshold = tableSizeFor(t); + } + else if (s > threshold) + resize();//ڵڵǰֵݣμ5.5 + for (Map.Entry e : m.entrySet()) { + K key = e.getKey(); + V value = e.getValue(); + //copyڵ㣬μ5.6 + putVal(hash(key), key, value, false, evict); + } + } + } + +``` + + + +### 5.5ݻ + +```java +// +final Node[] resize() { + //½ɽڵ -- oldTab + Node[] oldTab = table; + //ȡɽڵ -- oldCap + int oldCap = (oldTab == null) ? 0 : oldTab.length; + //ȡǰɽڵʱֵ -- oldThr + int oldThr = threshold; + //ݺ½ڵnewCapݴֵnewThr + int newCap, newThr = 0; + if (oldCap > 0) { + if (oldCap >= MAXIMUM_CAPACITY) { + //򴥷ֵΪֱֵӷԭ-- + threshold = Integer.MAX_VALUE; + return oldTab; + } + else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && + oldCap >= DEFAULT_INITIAL_CAPACITY) + //newCap= 2 * oldCap + //½ڵΪɵĽڵûҾɽڵڳʼֵҲӱ + newThr = oldThr << 1; //newThr= 2 * oldThr + } + else if (oldThr > 0) // initial capacity was placed in threshold + newCap = oldThr; + else { // zero initial threshold signifies using defaults + newCap = DEFAULT_INITIAL_CAPACITY; + newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); + } + if (newThr == 0) { + float ft = (float)newCap * loadFactor; + newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? + (int)ft : Integer.MAX_VALUE); + } + threshold = newThr; + @SuppressWarnings({"rawtypes","unchecked"}) + Node[] newTab = (Node[])new Node[newCap]; + table = newTab; + if (oldTab != null) { + for (int j = 0; j < oldCap; ++j) { + //tableб + Node e; + if ((e = oldTab[j]) != null) { + //ĸeݴ棬ͬʱtableӦֵΪnull + oldTab[j] = null; + if (e.next == null)//ΪյԪظƵtable + //ǴһµĿtableȻ½Ԫصputtableԭtable + newTab[e.hash & (newCap - 1)] = e; + + else if (e instanceof TreeNode) + ((TreeNode)e).split(this, newTab, j, oldCap); + else { // preserve order + Node loHead = null, loTail = null;//ڱputλ + Node hiHead = null, hiTail = null;//ڱputλ + Node next; + do { + next = e.next; + if ((e.hash & oldCap) == 0) { + //ĽΪ0ʾλͰеͷӵloheadlotailУͰлвλĽ㣬tail + if (loTail == null) + //ںloheadlotail浽tableʱloheadڱͷλãlotailжǷĩβ + loHead = e; + else + loTail.next = e; + loTail = e; + } + else { + if (hiTail == null) + hiHead = e; + else + hiTail.next = e; + hiTail = e; + } + } while ((e = next) != null); + if (loTail != null) { + //ѲλĽӵӦȥ + loTail.next = null; + newTab[j] = loHead; + } + if (hiTail != null) { + //λĽӵӦȥ + hiTail.next = null; + newTab[j + oldCap] = hiHead; + } + } + } + } + } + return newTab; +} + + + +``` + + + +### 5.6ڵ + +```java + public V put(K key, V value) { + return putVal(hash(key), key, value, false, true); + } + +final V putVal(int hash, K key, V value, boolean onlyIfAbsent, + boolean evict) { + + Node[] tab; Node p; int n, i; + // ʼͰ table + if ((tab = table) == null || (n = tab.length) == 0) + //ݷ + n = (tab = resize()).length; + // ǰkeyڣ½ֵԼ + if ((p = tab[i = (n - 1) & hash]) == null) + + tab[i] = newNode(hash, key, value, null); + + else { + Node e; K k; + // ֵԼڵ hash еĵһֵԽڵʱ e ָüֵ + if (p.hash == hash && + ((k = p.key) == key || (key != null && key.equals(k)))) + e = p; + //ڵݽṹΪú뷨 + else if (p instanceof TreeNode) + e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); + else { + // ṹ + for (int binCount = 0; ; ++binCount) { + //ڵǰҪĽڵ + if ((e = p.next) == null) { + //½һڵ + p.next = newNode(hash, key, value, null); + //ȳֵ8 + if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st + treeifyBin(tab, hash); + break; + } + //ҪĽڵѾ + if (e.hash == hash && + ((k = e.key) == key || (key != null && key.equals(k)))) + break; + p = e; + } + } + if (e != null) { // existing mapping for key + V oldValue = e.value; + if (!onlyIfAbsent || oldValue == null)//1.onlyIfAbsent ж + e.value = value; + afterNodeAccess(e); + return oldValue; + } + } + ++modCount; + if (++size > threshold) + resize(); + afterNodeInsertion(evict); + return null; +} + +``` + + + +### 5.7ڵ + + + +```java + //ȡԪأͨkey + public V get(Object key) { + Node e; + return (e = getNode(hash(key), key)) == null ? null : e.value; + } + //ͨkeyhashֵ + static final int hash(Object key) { + int h; + return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); + } + + + final Node getNode(int hash, Object key) { + Node[] tab; //Entry + Node first, e; + int n; //鳤 + K k; + // 1. λֵͰλã(n - 1) & hash ȼڶ length ȡࣩ + if ((tab = table) != null && (n = tab.length) > 0 && + (first = tab[(n - 1) & hash]) != null) { + //2.жϼֵhashcodeȣ + if (first.hash == hash && // always check first node + ((k = first.key) == key || (key != null && key.equals(k)))) + return first; + if ((e = first.next) != null) { + // 3.. first TreeNode ͣúںҷ + if (first instanceof TreeNode) + return ((TreeNode)first).getTreeNode(hash, key); + do { + if (e.hash == hash && + ((k = e.key) == key || (key != null && key.equals(k)))) + return e; + } while ((e = e.next) != null); + } + } + return null; + } + + +``` + + + diff --git a/Week 02/id_633/LeetCode_105_633.java b/Week 02/id_633/LeetCode_105_633.java new file mode 100644 index 000000000..d972e260d --- /dev/null +++ b/Week 02/id_633/LeetCode_105_633.java @@ -0,0 +1,38 @@ +package lesson8; + +import java.util.HashMap; + +public class LeetCode_105_633 { + + int preIndex = 0; + int[] preOrder; + int[] inOrder; + HashMap build_map = new HashMap(); + + public TreeNode build(int inLeft, int inRight) { + if (inLeft == inRight) + return null; + + int rootValue = preOrder[preIndex]; + TreeNode root = new TreeNode(rootValue); + + int index = build_map.get(rootValue); + + preIndex++; + root.left = build(inLeft, index); + root.right = build(index + 1, inRight); + return root; + } + + public TreeNode buildTree(int[] preOrder, int[] inOrder) { + this.preOrder = preOrder; + this.inOrder = inOrder; + + int build = 0; + for (Integer val : inOrder) + build_map.put(val, build++); + return build(0, inOrder.length); + } + +} + diff --git a/Week 02/id_633/LeetCode_236_633.java b/Week 02/id_633/LeetCode_236_633.java new file mode 100644 index 000000000..43d86ced9 --- /dev/null +++ b/Week 02/id_633/LeetCode_236_633.java @@ -0,0 +1,33 @@ +package lesson8; + +/** + * 最近相同祖先问题 + */ +public class LeetCode_236_633 { + + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + TreeNode lca = null; + lca(root, p, q, lca); + return lca; + } + private boolean lca(TreeNode currentNode, TreeNode p, TreeNode q, TreeNode lca) { + + // terminator 遍历到叶子结点上也没有在当前这根链表上找到p 或 q + if (currentNode == null) + return false; + + // process & split subProblems & drill down + int left = lca(currentNode.left, p, q, lca) ? 1 : 0; + + int right = lca(currentNode.right, p, q, lca) ? 1 : 0; + + int mid = (p == currentNode || q == currentNode) ? 1 : 0; + + if (left + right + mid >= 2) + lca = currentNode; // 当前被归回来的结点就是最近祖先结点 + + return left + mid + right > 0; // 左分支 or 右分支 找到了 p 或 q + } + + +} diff --git a/Week 02/id_638/Leetcode_144_638.java b/Week 02/id_638/Leetcode_144_638.java new file mode 100644 index 000000000..5a56aa0b4 --- /dev/null +++ b/Week 02/id_638/Leetcode_144_638.java @@ -0,0 +1,38 @@ +package test1.week_2; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Administrator on 2019/10/27. + */ +public class Leetcode_144_638 { + public static class TreeNode { + int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { val = x; } + } + + /** + * 二叉树前序遍历 + * @param root + * @return + */ + public List preorderTraversal(TreeNode root) { + List< Integer > list = new ArrayList(); + helperPre(root,list); + return list; + } + + public void helperPre(TreeNode root,List list){ + if(root != null){ + list.add(root.val); + // if(root.left != null) + helperPre(root.left,list); + // if(root.right != null){ + helperPre(root.right,list); + // } + } + } +} diff --git a/Week 02/id_638/Leetcode_226_638.java b/Week 02/id_638/Leetcode_226_638.java new file mode 100644 index 000000000..ecc2cf77e --- /dev/null +++ b/Week 02/id_638/Leetcode_226_638.java @@ -0,0 +1,35 @@ +package test1.week_2; + +/** + * Created by Administrator on 2019/10/27. + */ +public class Leetcode_226_638 { + + public static class TreeNode { + int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { val = x; } + } + + /** + * 反转二叉树 + * @param root + * @return + */ + public TreeNode invertTree(TreeNode root) { + helperPreInvert(root); + return root; + } + + public void helperPreInvert(TreeNode root){ + if(root != null){ + TreeNode temp = root.left; + root.left = root.right; + root.right = temp; + helperPreInvert(root.right); + helperPreInvert(root.left); + + } + } +} diff --git a/Week 02/id_638/Leetcode_242_638.java b/Week 02/id_638/Leetcode_242_638.java new file mode 100644 index 000000000..b96d2ccff --- /dev/null +++ b/Week 02/id_638/Leetcode_242_638.java @@ -0,0 +1,42 @@ +package test1.week_2; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by Administrator on 2019/10/27. + */ +public class Leetcode_242_638 { + + /** + * 异位词 + * @param s + * @param t + * @return + */ + public static boolean isAnagram(String s, String t) { + if(s.length() != t.length()){ + return false; + } + Map map = new HashMap(); + char[] sArr = s.toCharArray(); + char[] tArr = t.toCharArray(); + for (char elm:sArr){ + Integer count = map.get(String.valueOf(elm)) == null ? 0 : map.get(String.valueOf(elm)); + map.put(String.valueOf(elm),++count); + } + int zero = 0; + for (char elm:tArr){ + Integer count = map.get(String.valueOf(elm)); + if(count == null){ + return false; + }else { + map.put(String.valueOf(elm),--count); + if (count == 0){ + zero ++; + } + } + } + return zero == map.size(); + } +} diff --git a/Week 02/id_638/Leetcode_77_638.java b/Week 02/id_638/Leetcode_77_638.java new file mode 100644 index 000000000..7169f9f7f --- /dev/null +++ b/Week 02/id_638/Leetcode_77_638.java @@ -0,0 +1,51 @@ +package test1.week_2; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * Created by Administrator on 2019/10/27. + */ +public class Leetcode_77_638 { + + + /** + * 1------n,k个数组合 + * @param n + * @param k + * @return + */ + public static List> combine(int n, int k) { + if (n <= 0 || k <= 0 || n < k) { + return null; + } + List> res = new ArrayList>(); + Stack eleIntegers = new Stack(); + helperCombine(1,k,n,res,eleIntegers); + return res; + } + + private static void helperCombine(int first,int k,int n,List> res,Stack eleIntegers) { + if(eleIntegers.size() == k){ + res.add(new ArrayList(eleIntegers)); + return; + } + + //process + for(int i=1;i < n+1;i++){ + if (eleIntegers.contains(i)){ + continue; + } + if(!eleIntegers.isEmpty() && eleIntegers.peek() > i){ + continue; + } + eleIntegers.add(i); + helperCombine(first + 1,k,n,res,eleIntegers); + //回溯 + eleIntegers.pop(); + } + //reverse status + + } +} diff --git a/Week 02/id_638/Leetcode_94_638.java b/Week 02/id_638/Leetcode_94_638.java new file mode 100644 index 000000000..d0fff96e1 --- /dev/null +++ b/Week 02/id_638/Leetcode_94_638.java @@ -0,0 +1,43 @@ +package test1.week_2; + +import test1.Week2; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Administrator on 2019/10/27. + */ +public class Leetcode_94_638 { + + public static class TreeNode { + int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { val = x; } + } + + /** + * 二叉树中序遍历 + * @param root + * @return + */ + public static List< Integer > inorderTraversal(TreeNode root) { + List< Integer > list = new ArrayList(); + helper(root,list); + return list; + } + + public static void helper(TreeNode root, List list){ + if(root != null){ + if(root.left != null){ + helper(root.left,list); + } + list.add(root.val); + if(root.right != null){ + helper(root.right,list); + } + } + + } +} diff --git a/Week 02/id_638/NOTE.md b/Week 02/id_638/NOTE.md index a6321d6e2..0ef25ab58 100644 --- a/Week 02/id_638/NOTE.md +++ b/Week 02/id_638/NOTE.md @@ -1,4 +1,51 @@ # NOTE +HashMap源码分析:所有操作非线程安全的。 - +继承AbstractMap,实现Map,数据结构:哈希表。key和value都允许为空。 +HashMap容量为2的平方。初始容量为16。 +最大所能容纳的key-value 个数:2^30 +出于性能考虑。容量计算均通过位运算得出。所以也就不难理解为何都是2的平方 +hash函数计算出index. +hash碰撞: 拉链式解决碰撞 + +put方法: + + 根据key经过hash函数计算出hashcode,来确定node在数组中的位置。 + + 判断table有没有初始化,如果没有初始化,则调用resize()方法为table初始化容量,以及threshold的值。 + 根据hash值定位该key 对应的数组索引,如果对应的数组索引位置无值,则调用newNode()方法,为该索引创建Node节点 + 如果根据hash值定位的数组索引有Node,并且Node中的key和需要新增的key相等,则将对应的value值更新。 + 如果在已有的table中根据hash找到Node,其中Node中的hash值和新增的hash相等,但是key值不相等的,那么创建新的Node,放到当前已存在的Node的链表尾部。 +       如果当前Node的长度大于8,则调用treeifyBin()方法扩大table数组的容量,或者将当前索引的所有Node节点变成TreeNode节点,变成TreeNode节点的原因是由于TreeNode节点组成的链表索引元素会快很多。 + 将当前的key-value 数量标识size自增,然后和threshold对比,如果大于threshold的值,则调用resize()方法,扩大当前HashMap对象的存储容量。 + 返回oldValue或者null。 + put 方法比较经常使用的方法,主要功能是为HashMap对象添加一个Node 节点,如果Node存在则更新Node里面的内容。 + + +resize方法: + 如果当前数组为空,则初始化当前数组 + + 如果当前table数组不为空,则将当前的table数组扩大两倍,同时将阈值(threshold)扩大两倍 + + 数组长度和阈值扩大成两倍之后,将之前table数组中的值全部放到新的table中去 + +get略~ + + +BST:中序查询是有序的(有些算法题可以运用此特性求解) + +二叉树之所以用递归,个人觉得就是重复特性非常鲜明。 + +递归解题模板: + + 1:终结条件 + + 2:处理当前层逻辑 + + 3:下探下一层 + + 4:清理当前层 + + +分治,回溯 都是递归课题里面的子集, 回溯目前还有点迷糊,还需要加强联系。 diff --git a/Week 02/id_643/LeetCode_144_643.java b/Week 02/id_643/LeetCode_144_643.java new file mode 100644 index 000000000..275ac6194 --- /dev/null +++ b/Week 02/id_643/LeetCode_144_643.java @@ -0,0 +1,23 @@ +class Solution { + public List preorderTraversal(TreeNode root) { + LinkedList stack = new LinkedList<>(); + LinkedList output = new LinkedList<>(); + if (root == null) { + return output; + } + + stack.add(root); + while (!stack.isEmpty()) { + TreeNode node = stack.pollLast(); + output.add(node.val); + if (node.right != null) { + stack.add(node.right); + } + if (node.left != null) { + stack.add(node.left); + } + } + return output; + } +} + diff --git a/Week 02/id_643/LeetCode_94_643.java b/Week 02/id_643/LeetCode_94_643.java new file mode 100644 index 000000000..588ba8aec --- /dev/null +++ b/Week 02/id_643/LeetCode_94_643.java @@ -0,0 +1,20 @@ +class Solution { + public List < Integer > inorderTraversal(TreeNode root) { + List < Integer > res = new ArrayList < > (); + helper(root, res); + return res; + } + + public void helper(TreeNode root, List < Integer > res) { + if (root != null) { + if (root.left != null) { + helper(root.left, res); + } + res.add(root.val); + if (root.right != null) { + helper(root.right, res); + } + } + } +} + diff --git a/Week 02/id_648/LeetCode_144_648.java b/Week 02/id_648/LeetCode_144_648.java new file mode 100644 index 000000000..9a4110c8d --- /dev/null +++ b/Week 02/id_648/LeetCode_144_648.java @@ -0,0 +1,39 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * 给定一个二叉树,返回它的 前序 遍历。 + */ +public class LeetCode_144_648 { + public List preorderTraversal(TreeNode root) { + List < Integer > list = new ArrayList< >(); + helper(list, root); + return list; + } + private void helper(List list,TreeNode root){ + if(root!=null){ + list.add(root.val); + helper(list,root.left); + helper(list,root.right); + } + } + + + public static class TreeNode { + int val; + LeetCode_144_648.TreeNode left; + LeetCode_144_648.TreeNode right; + TreeNode(int x){ + this.val = x; + } + + } + public static void main(String[] args) { + LeetCode_144_648 leetCode_94_648 = new LeetCode_144_648(); + LeetCode_144_648.TreeNode treeNode = new LeetCode_144_648.TreeNode(1); + treeNode.right=new LeetCode_144_648.TreeNode(2); + treeNode.left=new LeetCode_144_648.TreeNode(3); + List < Integer > list = leetCode_94_648.preorderTraversal(treeNode); + System.out.println(list); + } +} diff --git a/Week 02/id_648/LeetCode_242_648.java b/Week 02/id_648/LeetCode_242_648.java new file mode 100644 index 000000000..7d6603b83 --- /dev/null +++ b/Week 02/id_648/LeetCode_242_648.java @@ -0,0 +1,36 @@ +import java.util.Arrays; + +/** + * 给定两个字符串 s 和 t , + * 编写一个函数来判断 t 是否是 s 的字母异位词 + */ +public class LeetCode_242_648 { + public boolean isAnagram(String s, String t) { + if(s==null||t==null)return false; + if(s.trim().equals(t.trim()))return true; + char[] sarry = s.toCharArray();char[] tarray = t.toCharArray(); + Arrays.sort(sarry);Arrays.sort(tarray); + return String.valueOf(sarry).equals(String.valueOf(tarray)); + } + public boolean isAnagram2(String s, String t) { + if(s==null||t==null)return false; + if(s.length()!=t.length())return false; + int[] counter = new int[26]; + for(int i=0;i> list= new ArrayList<>(); + + public List> permute(int[] nums) { + ArrayList nums_lst = new ArrayList<>(); + for(int num:nums){ + nums_lst.add(num); + } + List> output = new LinkedList<>(); + backtrack(nums.length,nums_lst,output,0); + return output; + } + private void backtrack(int n,ArrayList nums, List> output,int first){ + if(first==n){ + //如果层数等于nums长度结束递归 + output.add(new ArrayList(nums)); + } + for(int i=first;i> groupAnagrams(String[] strs) { + if(strs.length==0){ + return new ArrayList(); + } + HashMap> mapList = new HashMap<>(); + for(String str:strs){ + char[] strArray = str.toCharArray(); + Arrays.sort(strArray); + String strNew = String.valueOf(strArray); + if(!mapList.containsKey(strNew))mapList.put(strNew,new ArrayList()); + mapList.get(strNew).add(str); + } + return new ArrayList(mapList.values()); + } + + public static void main(String[] args) { + LeetCode_49_648 leetCode_49_648 = new LeetCode_49_648(); + String[] strs = new String[]{"eat", "tea", "tan", "ate", "nat", "bat"}; + leetCode_49_648.groupAnagrams(strs); + } +} \ No newline at end of file diff --git a/Week 02/id_648/LeetCode_590_648.java b/Week 02/id_648/LeetCode_590_648.java new file mode 100644 index 000000000..a66f740c0 --- /dev/null +++ b/Week 02/id_648/LeetCode_590_648.java @@ -0,0 +1,33 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * 给定一个 N 叉树,返回其节点值的后序遍历。 + */ +public class LeetCode_590_648 { + ArrayList list = new ArrayList<>(); + public List postorder(Node root) { + helper(root); + return list; + } + private void helper(Node node){ + if(node==null){return;} + List children = node.children; + for(int i=0;i children; + public Node() {} + public Node(int _val,List _children) { + val = _val; + children = _children; + } + }; + + +} diff --git a/Week 02/id_648/LeetCode_94_648.java b/Week 02/id_648/LeetCode_94_648.java new file mode 100644 index 000000000..b48193384 --- /dev/null +++ b/Week 02/id_648/LeetCode_94_648.java @@ -0,0 +1,43 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * 给定一个二叉树,返回它的中序 遍历 + * + */ +public class LeetCode_94_648 { + public List inorderTraversal(TreeNode root) { + List < Integer > list = new ArrayList< >(); + inorderTraver(list, root); + return list; + } + private void inorderTraver(List list,TreeNode root){ + if(root!=null){ + if(root.left!=null){ + inorderTraver(list,root.left); + } + list.add(root.val); + inorderTraver(list,root.right); + } + } + public static class TreeNode { + int val; + TreeNode left; + TreeNode right; + TreeNode(int x){ + this.val = x; + } + + } + + public static void main(String[] args) { + LeetCode_94_648 leetCode_94_648 = new LeetCode_94_648(); + TreeNode treeNode = new TreeNode(1); + treeNode.right=new TreeNode(2); + treeNode.left=new TreeNode(3); + List < Integer > list = leetCode_94_648.inorderTraversal(treeNode); + System.out.println(list); + } +} + + diff --git a/Week 02/id_653/LeetCode_236_653.java b/Week 02/id_653/LeetCode_236_653.java new file mode 100644 index 000000000..69508c661 --- /dev/null +++ b/Week 02/id_653/LeetCode_236_653.java @@ -0,0 +1,40 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + TreeNode ancestor; + public Solution() { + this.ancestor = null; + } + + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + preOrder(root, p, q); + return ancestor; + } + + public boolean preOrder(TreeNode root, TreeNode p, TreeNode q) { + if (root != null) { + // 分两个组进行遍历 + int left = preOrder(root.left, p, q) ? 1 : 0; + int right = preOrder(root.right, p, q) ? 1 : 0; + + int mid = (root == p || root == q) ? 1 : 0; + + //节点数量大于2 说明是公共祖先,然后向下递归 + if (mid + right + left >= 2) { + ancestor = root; + } + // 判断是否含有节点 + return (left + right + mid > 0); + } + + return false; + + } +} \ No newline at end of file diff --git a/Week 02/id_653/LeetCode_94_653.java b/Week 02/id_653/LeetCode_94_653.java new file mode 100644 index 000000000..7571cddcc --- /dev/null +++ b/Week 02/id_653/LeetCode_94_653.java @@ -0,0 +1,37 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + +/** + * 二叉树前中后序遍历, + * 就是指根节点元素的遍历次序, + * 先遍历根节点就叫做前序 + * 左中右 叫做中序 + * 左右中 叫做后序 + */ +class Solution { + public List largestValues(TreeNode root) { + + } + + void helper(TreeNode root, ArrayList result) { + if (root != null) { + if (root.left != null) { + helper(root.left, result); + } + + result.add(root.val); + + if (root.right != null) { + helper(root.right, result); + } + } + + } +} \ No newline at end of file diff --git a/Week 02/id_658/LeetCode_144_658.java b/Week 02/id_658/LeetCode_144_658.java new file mode 100644 index 000000000..b2d4a6377 --- /dev/null +++ b/Week 02/id_658/LeetCode_144_658.java @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode.cn id=144 lang=java + * + * [144] 二叉树的前序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List preorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + preOrder(root, res); + return res; + } + + private void preOrder(TreeNode root, List res) { + if (root == null) { + return; + } + res.add(root.val); + preOrder(root.left, res); + preOrder(root.right, res); + } +} diff --git a/Week 02/id_658/LeetCode_169_658.java b/Week 02/id_658/LeetCode_169_658.java new file mode 100644 index 000000000..40af497c3 --- /dev/null +++ b/Week 02/id_658/LeetCode_169_658.java @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode.cn id=169 lang=java + * + * [169] 求众数 + */ + +// @lc code=start +class Solution { + + public int majorityElement(int[] nums) { + Map counts = countNums(nums); + + Map.Entry majorityEntry = null; + for (Map.Entry entry : counts.entrySet()) { + if (majorityEntry == null || entry.getValue() > majorityEntry.getValue()) { + majorityEntry = entry; + } + } + + return majorityEntry.getKey(); + } + + private Map countNums(int[] nums) { + Map counts = new HashMap(); + for (int num : nums) { + if (!counts.containsKey(num)) { + counts.put(num, 1); + } + else { + counts.put(num, counts.get(num)+1); + } + } + return counts; + } +} diff --git a/Week 02/id_658/LeetCode_1_658.java b/Week 02/id_658/LeetCode_1_658.java new file mode 100644 index 000000000..b81cbbaa0 --- /dev/null +++ b/Week 02/id_658/LeetCode_1_658.java @@ -0,0 +1,55 @@ +import java.util.Map; + +/* + * @lc app=leetcode.cn id=1 lang=java + * + * [1] 两数之和 + * + * https://leetcode-cn.com/problems/two-sum/description/ + * + * algorithms + * Easy (45.73%) + * Total Accepted: 332.4K + * Total Submissions: 727K + * Testcase Example: '[2,7,11,15]\n9' + * + * 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 + * + * 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 + * + * 示例: + * + * 给定 nums = [2, 7, 11, 15], target = 9 + * + * 因为 nums[0] + nums[1] = 2 + 7 = 9 + * 所以返回 [0, 1] + * + * + */ +class Solution { + // 暴力解法双重for循环 + // public int[] twoSum(int[] nums, int target) { + // for(int i = 0; i < nums.length; i++) { + // for(int j = i + 1; j < nums.length; j++) { + // if(nums[i] == target - nums[j]) { + // return new int[] {i, j}; + // } + // } + // } + // return new int[2]; + // } + + // 空间换时间 用hash表存 + // 可以一边遍历一边存 + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap(); + for (int i = 0; i < nums.length; i++) { + int temp = target - nums[i]; + if (map.containsKey(temp)) { + return new int[] { map.get(temp), i }; + } + map.put(nums[i], i); + } + return new int[2]; + } +} diff --git a/Week 02/id_658/LeetCode_242_658.java b/Week 02/id_658/LeetCode_242_658.java new file mode 100644 index 000000000..2004713ef --- /dev/null +++ b/Week 02/id_658/LeetCode_242_658.java @@ -0,0 +1,25 @@ +/* + * @lc app=leetcode.cn id=242 lang=java + * + * [242] 有效的字母异位词 + */ + +// @lc code=start +class Solution { + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + int[] counts = new int[26]; + for (int i = 0; i < s.length(); i++) { + counts[s.charAt(i) - 'a']++; + counts[t.charAt(i) - 'a']--; + } + for (int count : counts) { + if (count != 0) { + return false; + } + } + return true; + } +} diff --git a/Week 02/id_658/LeetCode_94_658.java b/Week 02/id_658/LeetCode_94_658.java new file mode 100644 index 000000000..162b9c6ea --- /dev/null +++ b/Week 02/id_658/LeetCode_94_658.java @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode.cn id=94 lang=java + * + * [94] 二叉树的中序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List inorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + inOrder(root, res); + return res; + } + + private void inOrder(TreeNode root, List res) { + if (root == null) { + return; + } + inOrder(root.left, res); + res.add(root.val); + inOrder(root.right, res); + } +} diff --git a/Week 02/id_658/NOTE.md b/Week 02/id_658/NOTE.md index a6321d6e2..c4c2df002 100644 --- a/Week 02/id_658/NOTE.md +++ b/Week 02/id_658/NOTE.md @@ -1,4 +1,103 @@ -# NOTE +# 第二周学习总结 - +## 哈希表、映射、集合 +- 哈希表,也叫散列表,是根据关键码值即 `key-value` 而直接进行访问的数据结构,把关键码值通过`映射函数(散列函数 Hash Function)`映射到表中一个`位置(下标 Index)`来访问`记录(哈希表 散列表)`,以加快查找速度 +- 哈希函数要避免产生哈希碰撞(不同的数据经过哈希函数计算后,得到相同的值) +- `拉链式解决冲突法` 即工程中常用的增加维度的办法,在相同下标位置使用一个链表来存储数据 + +## 树、二叉树、二叉搜索树 + +- 有根节点和子节点的二维数据结构 +- 普通树查找内部节点需要全部遍历,时间复杂度为**O(n)** + +### 二叉树 + +- 每个父节点的子节点只有两个 +- Java 示例 + + ```java + public class TreeNode { + public int val; + public TreeNode left, right; + public TreeNode(int val) { + this.val = val; + this.left = null; + this.right = null; + } + } + ``` + +- 二叉树遍历有三种 + - 前序遍历 `Pre-Order` 根节点 - 左 - 右 + - 中序遍历 `In-Order` 左 - 根节点 - 右 + - 前序遍历 `Post-Order` 左 - 右 - 根节点 + +### 二叉搜索树 + +- 也称二叉查找树、有序二叉树、排序二叉树 +- 可能是一颗空树或者具有下列性质的二叉树 + - 左子树上`所有结点`的值均**小于**它的根结点的值 + - 右子树上`所有结点`的值均**大于**它的根结点的值 + - 左右子树也分别为二叉查找树(重复性) +- 中序遍历为升序排列 +- 查询、插入、创建、删除都为**O(logn)** + +### 与图的区别 + +- 树和图的最关键差别是看是否有`环` +- `LinkedList` 因为有两个 `next` 指针,可以看做特殊化的树 +- `Tree` 是特殊化的 `Graph`,即没有环的图就是树 + +## 递归 + +- 通过函数体来进行的循环 +- 写递归函数分为四个部分 + 1. 写递归终止条件 + 2. 处理当前层逻辑 + 3. 下探到下一层 + 4. 如果需要清理当前层 +- Java 示例 + + ```java + public void recur(int level, int param) { + // recursion terminator + if (level > MAX_LEVEL) { + // process result + return; + } + + // process logic in current level + process(level, param); + + // drill down + recur(level + 1, newParam); + + // restore current status + } + ``` + +- 递归要点 + - 尽量不要人肉进行递归 + - 找到最近最简方法,将其拆解成可重复解决的问题(重复子问题) + - 数学归纳法 + +## 分治 + +- 本质上是一种递归,将一个问题化解成多个子问题进行解决 +- 分治的递归终止本质上就是递归层级到达最下层即子问题都解决(叶子节点不含任何子节点) +- 处理当前层逻辑时,把大问题分解成子问题 + +## 回溯 + +- 试错的思想,尝试分步的去解决一个问题 +- 不断的在每一层去试,每一层有不同的方法 +- 最坏的情况下,回溯法会导致一次复杂度为指数时间的计算 + +## 总结 + +- 依旧是找重复子的问题 +- 递归解决问题是尽量不要去人肉递归 +- 需要锻炼自己递归的思路,总是容易看见递归的方法就往里去想然后绕进去 +- 回溯和分治还需要再去多回顾和练习 +- 多看别人的解题思路 diff --git a/Week 02/id_668/NOTE.md b/Week 02/id_668/NOTE.md index a6321d6e2..8c3a8b6b8 100644 --- a/Week 02/id_668/NOTE.md +++ b/Week 02/id_668/NOTE.md @@ -1,4 +1,3 @@ # NOTE - - +hello test diff --git a/Week 02/id_668/leetcode_22_668.py b/Week 02/id_668/leetcode_22_668.py new file mode 100644 index 000000000..e355c975c --- /dev/null +++ b/Week 02/id_668/leetcode_22_668.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: generate_parenthesis.py + @time: 2019/10/25 07:13 +""" +from collections import Counter + + +class Solution(object): + """ + 给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。 + 例如,给出 n = 3,生成结果为: + + [ + "((()))", + "(()())", + "(())()", + "()(())", + "()()()" + ] + + 思想: + 1. 想象格子中添加元素 + 2. 格子数对应递归层数 + """ + + def generate_parenthesis(self, n): + """ + :type n: int + :rtype: List[str] + + 暴力求解:先获取到所有可能的括号组合,然后再判断括号组合是否合法 + """ + strs = ['(', ')'] # 第一格可放置'(' or ')' + + for i in range(1, n << 1): # 这里从第二格开始判断 + pths = [] + + for s in strs: + pths.append(s + '(') + pths.append(s + ')') + + strs = pths + + def valid(parenthesis): + """ + parenthesis:判断括号是否合法 + """ + if Counter(parenthesis).values() == [n, n]: + stack = [] + + for c in parenthesis: + if c == '(': + stack.append(c) + else: + if stack: + stack.pop() + else: + return False + + return stack == [] + + return False + + return [s for s in strs if valid(s)] + + def generate_parenthesis2(self, n): + """ + :type n: int + :rtype: List[str] + + 分析: + 1. 终止条件 + 2. 处理当前层 + 3. 下探到下一层 + 4. 若需要,清理状态值 + """ + + def generate(grid, pths): + # 终止条件,格子数大于n * 2 + if grid > n << 1: + return pths + + # 处理当前层逻辑 + new_pths = [] + + for s in pths: + new_pths.append(s + '(') + new_pths.append(s + ')') + + # 下探到下一层 + return generate(grid + 1, new_pths) + + def valid(parenthesis): + """ + parenthesis:判断括号是否合法 + """ + if Counter(parenthesis).values() == [n, n]: + stack = [] + + for c in parenthesis: + if c == '(': + stack.append(c) + else: + if stack: + stack.pop() + else: + return False + + return stack == [] + + return False + + return [pth for pth in generate(2, ['(', ')']) if valid(pth)] + + def generate_parenthesis3(self, n): + """ + :type n: int + :rtype: List[str] + + 递推:n个格子形成的字符串str(n)等于n-1个格子的字符串str(n-1)+'('与str(n-1)+')' + 层的概念:从第1个格子 ~ 第n个格子 + + n对括号,相当于2n个格子,递归里讲到的层就是对应这里的格子数,比如第一层指第一个格子,第二层指第二个格子 + 在格子数不断增加(递归层数增加)的过程中,依据'('、')'数去判断当前生成的括号是否合法 + """ + ans = [] + + def generate(s='', left=0, right=0): + """ + s:原始字符串 + left:左括号数量 + right:由括号数量 + """ + + if len(s) == 2 * n: + ans.append(s) + + return + + if left < n: + # 左括号数量p,q的父节点q->q + 3. 判断p->p与q->q两个节点间是否有父子关系,若存在则结束求解, + 4. 若不存在,则将p->p当做p,q->q当做q,重复步骤2 + 4. 这种思考思路是由底向上的思想,从具体的节点开始,然后向上寻找各节点的父节点作判断 + 5. 问题的求解,要从root节点开始,由上往下判断 + """ + + def lowest_common_ancestor(self, root, p, q): + """ + :type root: TreeNode + :type p: TreeNode + :type q: TreeNode + :rtype: TreeNode + """ + + # 递归终止条件,当前结点如果为p或q,返回真值 + if not root or root == p or root == q: + return root + + # 判断当前结点的左节点是否为p或q,判断当前结点的右节点是否为p或q + left = self.lowest_common_ancestor(root.left, p, q) + right = self.lowest_common_ancestor(root.right, p, q) + + if left and right: + # 若p是左子节点,且q是右子节点,则当前结点满足条件 + return root + + return left or right + diff --git a/Week 02/id_668/leetcode_49_668.py b/Week 02/id_668/leetcode_49_668.py new file mode 100644 index 000000000..1a6a0abf3 --- /dev/null +++ b/Week 02/id_668/leetcode_49_668.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: group_anagram.py + @time: 2019/10/22 08:00 +""" +import collections + + +class Solution(object): + """ + 给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 + + 示例: + 输入: ["eat", "tea", "tan", "ate", "nat", "bat"], + 输出: + [ + ["ate","eat","tea"], + ["nat","tan"], + ["bat"] + ] + + 说明: + 所有输入均为小写字母。 + 不考虑答案输出的顺序。 + + 总结: + 1. dict中的key必须是可哈希的 + 2. 数字、字符串、tuple这些都是可哈希的,dict、list、set均不是可哈希的 + 3. collections中的Counter计数效率不高 + 4. g = collections.defaultdict(list),常用这种字典结构直接赋值,比如g['a'].append(xxx) + 5. dict对象的values以集合的方式返回结果 + """ + + def group_anagrams(self, strs): + """ + :type strs: List[str] + :rtype: List[List[str]] + + 1. 先对每个字符串排序,排完序之后通过join生成新的字符串 + 2. 作判断统计 + + 时间复杂度:O(nklogk) + 空间复杂度:O(nk) + """ + d = {} + + for s in strs: + ''' + sorted函数底层用的是快排,因此对于长度为k的字符串,sorted函数的时间复杂度为klogk + 总共有n个字符串,因此最终时间复杂度为O(nklogk);空间使用上因为每个字符串都会排序然后 + 生成数组再生成新的对象,此外还有额外的字典的开销,因此空间复杂度为O(nk) + ''' + sort_s = ''.join(sorted(s)) + + if sort_s not in d: + d[sort_s] = [] + + d[sort_s].append(s) + + return d.values() + + def group_anagrams2(self, strs): + """ + :type strs: List[str] + :rtype: List[List[str]] + + 1. 先对每个字符串排序,排完序之后通过tuple生成元组 + 2. 作判断统计 + + 时间复杂度:O(nklogk) + 空间复杂度:O(nk) + """ + d = {} + + for s in strs: + tuple_s = tuple(sorted(s)) + + if tuple_s not in d: + d[tuple_s] = [] + + d[tuple_s].append(s) + + return d.values() + + def group_anagrams3(self, strs): + """ + :type strs: List[str] + :rtype: List[List[str]] + + 当且仅当它们的字符计数(每个字符的出现次数)相同时,两个字符串是字母异位词。 + 使用python collections包中的defaultdict类 + + 时间复杂度:O(nklogk) + 空间复杂度:O(nk) + """ + groups = collections.defaultdict(list) + + for s in strs: + groups[tuple(sorted(s))].append(s) + + return groups.values() + + def group_anagrams4(self, strs): + """ + :type strs: List[str] + :rtype: List[List[str]] + + 当且仅当它们的字符计数(每个字符的出现次数)相同时,两个字符串是字母异位词。 + 将每个字符串用数组计数,然后将数组转成tuple结构,存入字典中 + + 时间复杂度:O(nk) + 空间复杂度:O(nk) + """ + ans = collections.defaultdict(list) + + for s in strs: + count = [0] * 26 + + for c in s: + ''' + 对字符串中的字符进行统计计数,然后再比较 + ''' + count[ord(c) - ord('a')] += 1 + + ans[tuple(count)].append(s) + + return ans.values() + diff --git a/Week 02/id_668/leetcode_94_668.py b/Week 02/id_668/leetcode_94_668.py new file mode 100644 index 000000000..6360bbace --- /dev/null +++ b/Week 02/id_668/leetcode_94_668.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: inorder.py + @time: 2019/10/23 20:45 +""" + + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution(object): + """ + 94. 给定一个二叉树,返回它的中序 遍历。 + + 示例: + + 输入: [1,null,2,3] + 1 + \ + 2 + / + 3 + + 输出: [1,3,2] + + 分析: + 树的中序遍历的顺序为:左根右 + """ + + def inorder_traversal(self, root): + """ + :type root: TreeNode + :rtype: List[int] + + 直接使用递归,从根节点开始遍历的顺序为左根右,假定当前层左子树、右子树均完成遍历, + 则此时对于根节点来说遍历的顺序就为左节点、根节点、右节点。左子树、右子树中任意节点均与根节点具有相同的遍历规律 + 因为树种每个节点都要被访问两遍,因此时间复杂度为O(n),空间上如果除了子节点外所有结点都具有左、右子节点,则空间复杂度为 + O(n),平均下来为O(logn) + + 时间复杂度:O(n) + 空间复杂度:O(logn) + """ + orders = [] + + def inorder(node): + if node: + inorder(node.left) + orders.append(node.val) + inorder(node.right) + + inorder(root) + + return orders + + def inorder_traversal2(self, root): + """ + :type root: TreeNode + :rtype: List[int] + + 这个解法其实是剖析递归实现的一种方法,用stack来存储访问到的但还未被处理的节点。首先中序遍历左根右的顺序,任意一节点 + 首先都需要先访问左节点,直到这个节点没有左节点为止,然后处理这个节点,最后再处理这个节点的右节点,当然处理右节点的规则 + 还是参照左根右的顺序,一直往下找下去,直到处理完所有的节点。 + + 时间复杂度:O(n) + 空间复杂度:最坏O(n),平均O(logn),这点的判断与leetcode官方题解不一样 + """ + res, stack = [], [] + cur = root + + while cur or len(stack) != 0: + while cur: + ''' + 对一节点执行左根右的遍历,其实就是不断地去寻找节点的左子结点,左子子节点... + ''' + stack.append(cur) + cur = cur.left + + cur = stack.pop() + res.append(cur.val) + cur = cur.right + + return res + diff --git a/Week 02/id_673/construct-binary-tree-from-preorder-and-inorder-traversal.py b/Week 02/id_673/construct-binary-tree-from-preorder-and-inorder-traversal.py new file mode 100644 index 000000000..d64e82f40 --- /dev/null +++ b/Week 02/id_673/construct-binary-tree-from-preorder-and-inorder-traversal.py @@ -0,0 +1,24 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None +class Solution: + p = 0 + preorder: List[int] + inorder: List[int] + def dfs(self,lb,rb): + if lb>rb: + return None + node = TreeNode(self.preorder[self.p]) + self.p+=1 + mid = self.inorder.index(node.val) + node.left = self.dfs(lb,mid-1) + node.right = self.dfs(mid+1,rb) + return node + def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode: + self.preorder = preorder + self.inorder = inorder + p = 0 + return self.dfs(0,len(inorder)-1) diff --git a/Week 02/id_673/valid-anagram.py b/Week 02/id_673/valid-anagram.py new file mode 100644 index 000000000..20fdb2678 --- /dev/null +++ b/Week 02/id_673/valid-anagram.py @@ -0,0 +1,19 @@ +class Solution: + def isAnagram(self, s: str, t: str) -> bool: + dic = {} + + for i in s: + if i in dic: + dic[i] += 1 + else: + dic[i] = 1 + + for i in t: + if i not in dic or dic[i] <= 0: + return False + dic[i] -= 1 + + for i in dic: + if dic[i] != 0: + return False + return True diff --git a/Week 02/id_683/LeetCode_144_683.java b/Week 02/id_683/LeetCode_144_683.java new file mode 100644 index 000000000..f61f0f075 --- /dev/null +++ b/Week 02/id_683/LeetCode_144_683.java @@ -0,0 +1,22 @@ +import java.util.ArrayList; +import java.util.Stack; + +class Solution { + public List preorderTraversal(TreeNode root) { + if (root == null) return new ArrayList<>(); + List result = new ArrayList<>(); + Stack stack = new Stack<>(); + stack.push(root); + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + result.add(node.val); + if (node.right != null) { + stack.push(node.right); + } + if (node.left != null) { + stack.push(node.left); + } + } + return result; + } +} \ No newline at end of file diff --git a/Week 02/id_683/LeetCode_17_683.java b/Week 02/id_683/LeetCode_17_683.java new file mode 100644 index 000000000..494d2930e --- /dev/null +++ b/Week 02/id_683/LeetCode_17_683.java @@ -0,0 +1,36 @@ +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; + +class Solution { + + public List letterCombinations(String digits) { + if (digits == null || digits.length() == 0) return new ArrayList<>(); + HashMap map = new HashMap<>(); + map.put('2', "abc"); + map.put('3', "def"); + map.put('4', "ghi"); + map.put('5', "jkl"); + map.put('6', "mno"); + map.put('7', "pqrs"); + map.put('8', "tuv"); + map.put('9', "wxyz"); + + List result = new LinkedList<>(); + combinations(digits, 0, map, result, ""); + return result; + } + + private void combinations(String digits, int i, HashMap map, List result, String s) { + if (s.length() == digits.length()) { + result.add(s); + return; + } + + String letters = map.get(digits.charAt(i)); + for (int j = 0; j < letters.length(); ++j) { + combinations(digits, i + 1, map, result, s + letters.charAt(j)); + } + + } +} \ No newline at end of file diff --git a/Week 02/id_683/LeetCode_226_683.java b/Week 02/id_683/LeetCode_226_683.java new file mode 100644 index 000000000..4226a2966 --- /dev/null +++ b/Week 02/id_683/LeetCode_226_683.java @@ -0,0 +1,31 @@ +class Solution { + + public TreeNode invertTree(TreeNode root) { + if (root == null) { + return root; + } + + TreeNode tmp = invertTree(root.left); + root.left = invertTree(root.right); + root.right = tmp; + return root; + } + + public TreeNode invertTreeOld(TreeNode root) { + invert(root); + return root; + } + + private void invert(TreeNode root) { + if (root == null) { + return; + } + + TreeNode tmp = root.left; + root.left = root.right; + root.right = tmp; + + invert(root.left); + invert(root.right); + } +} \ No newline at end of file diff --git a/Week 02/id_683/LeetCode_22_683.java b/Week 02/id_683/LeetCode_22_683.java new file mode 100644 index 000000000..78410074f --- /dev/null +++ b/Week 02/id_683/LeetCode_22_683.java @@ -0,0 +1,24 @@ +import java.util.ArrayList; + +class Solution { + public List generateParenthesis(int n) { + ArrayList result = new ArrayList<>(); + generate(0, 0, n, result, ""); + return result; + } + + private void generate(int left, int right, int n, List result, String s) { + if (left == n && right == n) { + result.add(s); + return; + } + + if (left < n) { + generate(left + 1, right, n, result, s + "("); + } + + if (right < left) { + generate(left, right + 1, n, result, s + ")"); + } + } +} \ No newline at end of file diff --git a/Week 02/id_683/LeetCode_236_683.java b/Week 02/id_683/LeetCode_236_683.java new file mode 100644 index 000000000..676d1c015 --- /dev/null +++ b/Week 02/id_683/LeetCode_236_683.java @@ -0,0 +1,12 @@ +class Solution { + + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + if (root == null || root == q || root == p) return root; + + TreeNode left = lowestCommonAncestor(root.left, p, q); + TreeNode right = lowestCommonAncestor(root.right, p, q); + + if (left != null && right != null) return root; + return left == null ? right : left; + } +} \ No newline at end of file diff --git a/Week 02/id_683/LeetCode_242_683.java b/Week 02/id_683/LeetCode_242_683.java new file mode 100644 index 000000000..60bbffd5d --- /dev/null +++ b/Week 02/id_683/LeetCode_242_683.java @@ -0,0 +1,23 @@ +class Solution { + public boolean isAnagram(String s, String t) { + if (s == null || t == null || s.length() != t.length()) { + return false; + } + + char[] sChar = s.toCharArray(); + char[] tChar = t.toCharArray(); + int[] counter = new int[26]; + + for (int i = 0; i < s.length(); ++i) { + counter[sChar[i] - 'a']++; + counter[tChar[i] - 'a']--; + } + + for (int num : counter) { + if (num != 0) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/Week 02/id_683/LeetCode_49_683.java b/Week 02/id_683/LeetCode_49_683.java new file mode 100644 index 000000000..17e402285 --- /dev/null +++ b/Week 02/id_683/LeetCode_49_683.java @@ -0,0 +1,26 @@ +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.List; + +class Solution { + public List> groupAnagrams(String[] strs) { + if (strs == null || strs.length == 0) { + return new ArrayList<>(); + } + HashMap map = new HashMap<>(); + for (String str : strs) { + int[] letters = new int[26]; + for (int i = 0; i < str.length(); ++i) { + letters[str.charAt(i) - 'a']++; + } + String key = Arrays.toString(letters); + if (!map.containsKey(key)) { + map.put(key, new ArrayList<>()); + } + map.get(key).add(str); + } + return new ArrayList(map.values()); + } +} \ No newline at end of file diff --git a/Week 02/id_683/LeetCode_50_683.java b/Week 02/id_683/LeetCode_50_683.java new file mode 100644 index 000000000..0f2400c45 --- /dev/null +++ b/Week 02/id_683/LeetCode_50_683.java @@ -0,0 +1,38 @@ +class Solution { + public double myPow(double x, int n) { + if (n < 0) { + x = 1 / x; + n = -n; + } + double result = 1.0; + double cur = x; + for (int i = n; i > 0; i /= 2) { + if (i % 2 == 1) { + result = result * cur; + } + cur = cur * cur; + } + return result; + } + + + public double myPow2(double x, int n) { + if (n < 0) { + x = 1 / x; + n = -n; + } + return fastPow(x, n); + } + + private double fastPow(double x, long n) { + if (n == 0) return 1.0; + + double result = fastPow(x, n / 2); + + if (n % 2 == 0) { + return result * result; + }else { + return result * result * x; + } + } +} \ No newline at end of file diff --git a/Week 02/id_683/LeetCode_51_683.java b/Week 02/id_683/LeetCode_51_683.java new file mode 100644 index 000000000..253773fcb --- /dev/null +++ b/Week 02/id_683/LeetCode_51_683.java @@ -0,0 +1,66 @@ +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +class Solution { + + List> result = new ArrayList<>(); + Set cols = new HashSet<>(); + Set pie = new HashSet<>(); + Set na = new HashSet<>(); + + + public List> solveNQueens(int n) { + + backtrack(n, 0, new ArrayList()); + + List> res = new ArrayList<>(); + + for (List cur : result) { + List solution = new ArrayList<>(); + for (int index : cur) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < n; ++i) { + if (index == i) { + sb.append("Q"); + continue; + } + sb.append("."); + } + solution.add(sb.toString()); + } + res.add(solution); + } + return res; + } + + private void backtrack(int n, int row, List cur_state) { + if (row == n) { + result.add(cur_state); + return; + } + + for (int i = 0; i < n; ++i) { + // check if can place queen in here + if (cols.contains(i) || pie.contains(row - i) || na.contains(row + i)) { + continue; + } + + // place queen and add place where it can attack + cols.add(i); + pie.add(row - i); + na.add(row + i); + + List newCur = new ArrayList(cur_state); + newCur.add(i); + backtrack(n, row + 1, newCur); + + // revert state + cols.remove(i); + pie.remove(row - i); + na.remove(row + i); + } + } +} \ No newline at end of file diff --git a/Week 02/id_683/LeetCode_590_683.java b/Week 02/id_683/LeetCode_590_683.java new file mode 100644 index 000000000..0f5b0b2a0 --- /dev/null +++ b/Week 02/id_683/LeetCode_590_683.java @@ -0,0 +1,23 @@ +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Stack; + +class Solution { + public List postorderTraversal(TreeNode root) { + if (root == null) return new ArrayList<>(); + LinkedList result = new LinkedList<>(); + Stack stack = new Stack<>(); + stack.push(root); + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + result.addFirst(node.val); + if (node.left != null) { + stack.push(node.left); + } + if (node.right != null) { + stack.push(node.right); + } + } + return result; + } +} \ No newline at end of file diff --git a/Week 02/id_683/LeetCode_94_683.java b/Week 02/id_683/LeetCode_94_683.java new file mode 100644 index 000000000..60092cefa --- /dev/null +++ b/Week 02/id_683/LeetCode_94_683.java @@ -0,0 +1,38 @@ +import java.util.ArrayList; +import java.util.Deque; +import java.util.Stack; + +import javax.swing.tree.TreeNode; + +class Solution { + + public List inorderTraversal(TreeNode root) { + List result = new ArrayList<>(); + Stack stack = new Stack<>(); + TreeNode cur = root; + while (cur != null || !stack.isEmpty()) { + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + cur = stack.pop(); + result.add(cur.val); + cur = cur.right; + } + return result; + } + + public List inorderTraversal2(TreeNode root) { + List result = new ArrayList<>(); + inorder(root, result); + return result; + } + + private void inorder(TreeNode node, List result) { + if (node == null) return; + + inorder(node.left, result); + result.add(node.val); + inorder(node.right, result); + } +} \ No newline at end of file diff --git a/Week 02/id_683/NOTE.md b/Week 02/id_683/NOTE.md index a6321d6e2..df83bf633 100644 --- a/Week 02/id_683/NOTE.md +++ b/Week 02/id_683/NOTE.md @@ -1,4 +1,141 @@ -# NOTE +# 第二周学习总结 - +## 递归 +解决递归问题的思维 + +- 不要进行人肉递归 +- 找到其重复性 +- 数学归纳法 + +递归题目代码模板 + +1. 处理递归退出条件 +2. 处理当前层逻辑 +3. 进入下一层递归 +4. 重置递归状态 + + + public void recur(int level, int param) { + + // terminator + if (level > MAX_LEVEL) { + // process result + return; + } + + // process current logic + process(level, param); + + // drill down + recur( level: level + 1, newParam); + + // restore current status + + } + +## 分治 + +分治问题可以说使一种特殊的递归,其核心一样在于找到问题的重复性,递归进行解决,所以分治的代码模板和递归的代码模板很相似 + + def divide_conquer(problem, param1, param2, ...): + # recursion terminator + if problem is None: + print_result + return + + # prepare data + data = prepare_data(problem) + subproblems = split_problem(problem, data) + + # conquer subproblems + subresult1 = self.divide_conquer(subproblems[0], p1, ...) + subresult2 = self.divide_conquer(subproblems[1], p1, ...) + subresult3 = self.divide_conquer(subproblems[2], p1, ...) + … + + # process and generate the final result + result = process_result(subresult1, subresult2, subresult3, …) + + # revert the current level states + +1. 处理递归结束条件 +2. 准备数据,将大问题拆分为几个子问题 +3. 递归处理几个子问题 +4. 合并子问题返回数据 +5. 重置状态 + + +## HashMap分析 + +HashMap存储结构是基于数组+链表+红黑树,从源码看,它有一个一个非常重要的字段`Node[] table;`,哈希桶数组。 + + static class Node implements Map.Entry { + final int hash; // 表示Node在Hash桶中的索引位置 + final K key; + V value; + Node next; // 链表的下一个Node + + Node(int hash, K key, V value, Node next) { + this.hash = hash; + this.key = key; + this.value = value; + this.next = next; + } + + public final K getKey() { return key; } + public final V getValue() { return value; } + public final String toString() { return key + "=" + value; } + + public final int hashCode() { + return Objects.hashCode(key) ^ Objects.hashCode(value); + } + + public final V setValue(V newValue) { + V oldValue = value; + value = newValue; + return oldValue; + } + + public final boolean equals(Object o) { + if (o == this) + return true; + if (o instanceof Map.Entry) { + Map.Entry e = (Map.Entry)o; + if (Objects.equals(key, e.getKey()) && + Objects.equals(value, e.getValue())) + return true; + } + return false; + } + } + +Node是HashMap的内部类,实现了Map.Entry接口,本质是一个映射(键值对)。 + +HashMap中的一些重要字段: + + // 容器内存储的键值对数量 + transient int size; + transient int modCount; + // 容纳键值对数量的阈值,当超过这个数量时会进行扩容,阈值等于哈希桶的容量×负载因子 + int threshold; + // 负载因子 + final float loadFactor; + +而且HashMap中哈希桶数组的默认大小使16,并且保证每次扩容后都是合数,而不是常规的使用素数,这是为了方便通过位运算做取模操作提高性能。 + +### get操作 + +1. 计算哈希值,找到哈希桶数组中对应位置 +2. 先检查该位置上的链表或树的第一个元素的key是否相等 +3. 如果key不相等则循环在该位置上的链表或红黑树中查找相等的key +4. 如果找到相等的key返回,如果未找到则返回null + +### put操作 + +1. 第一步判断哈希桶数组是否已经初始化,如果没有则通过resize函数分配 +2. 找到哈希桶数组中与Node元素key的hash值对应的位置,如果该位置为null则将元素放置在该位置 +3. 如果找到哈希桶数组中对应位置不为null,则判断该位置的首个元素是否与key相等,如果相等则覆盖该元素 +4. 如果发现首个元素类型为TreeNode(判断是否是红黑树),如果是,则通过树的插入方式插入该元素 +5. 遍历该位置的链表,插入至链表中如果key存在则直接覆盖,如果链表长度超过8则将链表转化为红黑树 +6. 插入成功后,如果size大于threshold则进行扩容 \ No newline at end of file diff --git a/Week 02/id_693/LeetCode_105_693.java b/Week 02/id_693/LeetCode_105_693.java new file mode 100644 index 000000000..f371601b5 --- /dev/null +++ b/Week 02/id_693/LeetCode_105_693.java @@ -0,0 +1,53 @@ +package id_693; + +import java.util.Arrays; + +/** + * @Author 李雷(KyLin) + * @Desc + * @Date 2019/10/25 + */ +public class LeetCode_105_693 { + //题解 + /* + 3 + / \ + 9 20 + / \ + 15 7 + 1、根据前序遍历,找到root节点 + 2、根据前序遍历的root节点 到 中序遍历中区分左右节点,并构建相应的数。 + */ + public TreeNode buildTree(int[] preorder,int[] inorder) { + return build(preorder,0,inorder,0,inorder.length); + } + + /** + * @param preorder 前序数组 + * @param preStart 起始坐标,() + * @param inorder + * @param inStart + * @param inEnd + * @return + * @desc1 preStart + 1 在前序数组原头节点的下一个索引位置,同时也是原头节点的左子树节点 + * @desc2 leftTreeSize左树长度(用于计算右子树的头索引,右子树的索引为: preStart + 1 + leftTreeSize) + */ + private TreeNode build(int[] preorder,int preStart,int[] inorder,int inStart,int inEnd) { + if (inStart >= inEnd) { + return null; + } + TreeNode root = new TreeNode(preorder[preStart]); + int inHead = inStart; + while (inorder[inHead] != root.val) { + inHead++; + } + int leftTreeSize = inHead - inStart; + root.left = build(preorder,preStart + 1,inorder,inStart,inHead); + root.right = build(preorder,preStart + 1 + leftTreeSize,inorder,inHead + 1,inEnd); + return root; + } + + public static void main(String[] args) { + new LeetCode_105_693().buildTree(new int[]{3,9,20,15,7},new int[]{9,3,15,20,7}); + } +} diff --git a/Week 02/id_693/LeetCode_144_693.java b/Week 02/id_693/LeetCode_144_693.java new file mode 100644 index 000000000..884f70051 --- /dev/null +++ b/Week 02/id_693/LeetCode_144_693.java @@ -0,0 +1,51 @@ +package id_693; + +import java.util.ArrayList; +import java.util.List; + +/** + * @Desc 144. 二叉树的前序遍历 https://leetcode-cn.com/problems/binary-tree-preorder-traversal/ + * @Auther 李雷(KyLin) + * @Date 2019/10/22 + */ +public class LeetCode_144_693 { + + //递归 + public List preorderTraversal(TreeNode root) { + List list = new ArrayList<>(); + recursionConvenience(root,list); + return list; + } + + public void recursionConvenience(TreeNode root,List list) { + if (root != null) { + list.add(root.val); + if (root.left != null) { + recursionConvenience(root.left,list); + } + if (root.right != null) { + recursionConvenience(root.right,list); + } + } + } + + //迭代,后补 + + public List preorderTraversal2(TreeNode root) { + List list = new ArrayList<>(); + iterativeConvenience(root,list); + return list; + } + + public void iterativeConvenience(TreeNode root,List list) { + while (root != null) { + list.add(root.val); + while (root.left != null) { + list.add(root.left.val); + } + while (root.right != null) { + list.add(root.right.val); + } + } + } +} diff --git a/Week 02/id_693/LeetCode_169_693.java b/Week 02/id_693/LeetCode_169_693.java new file mode 100644 index 000000000..f1ff3fa6b --- /dev/null +++ b/Week 02/id_693/LeetCode_169_693.java @@ -0,0 +1,31 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc + * @Date 2019/10/28 + */ +public class LeetCode_169_693 { + //Boyer-Moore 投票算法 + public int majorityElement(int[] nums) { + int count = 0; + int candidate = 0; + for (int num : nums) { + if (count == 0) { + candidate = num; + } + count += (num == candidate) ? 1 : -1; + } + return candidate; + } + + //分治 + public int majorityElement2(int[] nums) { + return 1; + } + + public static void main(String[] args) { + System.out.println(new LeetCode_169_693().majorityElement2(new int[]{3,2,3}) == 3); + System.out.println(new LeetCode_169_693().majorityElement2(new int[]{2,2,1,1,1,2,2}) == 2); + } +} diff --git a/Week 02/id_693/LeetCode_17_693.java b/Week 02/id_693/LeetCode_17_693.java new file mode 100644 index 000000000..34492d9d7 --- /dev/null +++ b/Week 02/id_693/LeetCode_17_693.java @@ -0,0 +1,97 @@ +package id_693; + +import java.util.ArrayList; +import java.util.List; + +/** + * @Desc 17. 电话号码的字母组合 https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/ + * @Auther 李雷(KyLin) + * @Date 2019/10/28 + */ +public class LeetCode_17_693 { + //回溯法,沉淀两天,终于自己求解了一次 + private char[][] elements = new char[][]{ + {'a', 'b', 'c'}, + {'d', 'e', 'f'}, + {'g', 'h', 'i'}, + {'j', 'k', 'l'}, + {'m', 'n', 'o'}, + {'p', 'q', 'r', 's'}, + {'t', 'u', 'v'}, + {'w', 'x', 'y', 'z'}}; + + public List letterCombinations(String digits) { + List list = new ArrayList<>(); + if (digits.length() == 0) { + return list; + } + backtrack(list, digits, "", 0); + return list; + } + + private void backtrack(List list, String digits, String temp, int first) { + if (digits.length() == temp.length()) { + list.add(temp); + return; + } + int index = digits.charAt(first) - 50; + for (int i = 0; i < elements[index].length; i++) { + //看了别人的代码发现这样写法是错误的,因为Java中字符串传递下去的时候都会复制一个新的字符串,所以直接temp + elements[index][i] 即可 + temp += elements[index][i]; + backtrack(list, digits, temp, first + 1); + temp = temp.substring(0, temp.length() - 1); + } + + } + + + //看别人的代码 进行的优化 + public List letterCombinations2(String digits) { + List list = new ArrayList<>(); + if (digits.length() == 0) { + return list; + } + backtrack2(list, digits, "", 0); + return list; + } + + private void backtrack2(List list, String digits, String temp, int first) { + if (digits.length() == temp.length()) { + list.add(temp); + return; + } + int index = digits.charAt(first) - 50; + for (int i = 0; i < elements[index].length; i++) { + //因为传递下去的是新的字符串,在Java中 字符串+字符串 会new String()新的字符串的。所以不用回溯 + backtrack2(list, digits, temp + elements[index][i], first + 1); + } + + } + + + //再次优化,后面复习加看别人的写法 + public List letterCombinations3(String digits) { + List list = new ArrayList<>(); + if(digits.length() == 0) { + return list; + } + String [] temp = new String[]{"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"}; + backtrack3(list,temp,digits,"",0); + return list; + } + private void backtrack3(List list,String[] temp,String digits,String str,int index) { + if(index == digits.length()) { + list.add(str); + return; + } + int val = (int) digits.charAt(index) - '0' - 2; + for (int i = 0; i < temp[val].length(); i++) { + backtrack3(list,temp,digits,str + temp[val].charAt(i),index + 1); + } + } + + public static void main(String[] args) { + System.out.println(new LeetCode_17_693().letterCombinations("89")); + + } +} \ No newline at end of file diff --git a/Week 02/id_693/LeetCode_1_693.java b/Week 02/id_693/LeetCode_1_693.java new file mode 100644 index 000000000..e80a22d55 --- /dev/null +++ b/Week 02/id_693/LeetCode_1_693.java @@ -0,0 +1,24 @@ +package id_693; + +import java.util.HashMap; +import java.util.Map; + +/** + * @Desc 第一周已经做过,不做回顾,只作为作业使用 + * @Auther 李雷(KyLin) + * @Date 2019/10/21 + */ +public class LeetCode_1_693 { + //再次优化 + public int[] twoSum3(int[] nums,int target) { + Map map = new HashMap<>(nums.length,1); + for (int i = 0; i < nums.length; i++) { + int temp = target - nums[i]; + if (map.containsKey(temp)) { + return new int[]{map.get(temp),i}; + } + map.put(nums[i],i); + } + return new int[0]; + } +} diff --git a/Week 02/id_693/LeetCode_236_693.java b/Week 02/id_693/LeetCode_236_693.java new file mode 100644 index 000000000..450139132 --- /dev/null +++ b/Week 02/id_693/LeetCode_236_693.java @@ -0,0 +1,32 @@ +package id_693; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @Author 李雷(KyLin) + * @Desc 236. 二叉树的最近公共祖先 https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/ + * @Date 2019/10/25 + */ +public class LeetCode_236_693 { + + //题解:pq不会重复 + /* + 从根节点遍历,递归向左右子树查询节点信息 + 递归终止条件:如果当前节点为空或等于p或q,则返回当前节点 + 1、递归遍历左右子树,如果左右子树查到节点都不为空,则表明p和q分别在左右子树中,因此,当前节点即为最近公共祖先; + 2、如果左右子树其中一个不为空,则返回非空节点。 + */ + public TreeNode lowestCommonAncestor(TreeNode root,TreeNode p,TreeNode q) { + if (root == null || root == p || root == q) { + return root; + } + TreeNode left = lowestCommonAncestor(root.left,p,q); + TreeNode right = lowestCommonAncestor(root.right,p,q); + if (left != null && right != null) { + return root; + } + return left == null ? right : left; + } +} diff --git a/Week 02/id_693/LeetCode_242_693.java b/Week 02/id_693/LeetCode_242_693.java new file mode 100644 index 000000000..fbffdd657 --- /dev/null +++ b/Week 02/id_693/LeetCode_242_693.java @@ -0,0 +1,51 @@ +package id_693; + + +import java.util.Arrays; + +/** + * @Desc 242. 有效的字母异位词 https://leetcode-cn.com/problems/valid-anagram/ + * @Auther 李雷(KyLin) + * @Date 2019/10/21 + */ +public class LeetCode_242_693 { + //暴力破解,直接对字符串排序,然后比较是否相等即可 + public boolean isAnagram(String s,String t) { + char[] a = s.toCharArray(); + Arrays.sort(a); + char[] b = t.toCharArray(); + Arrays.sort(b); + return Arrays.equals(a,b); + } + + //优化方案:采用哈希映射(标记法),直接把字符映射到数组,然后再循环一次看是否为0; 空间上,优于上面 + //解释:就是一个负责+ 一个负责-,最后在循环看是否都是0,否则就不对 + public boolean isAnagram2(String s,String t) { + if (s.length() != t.length()) { + return false; + } + int[] result = new int[26]; + for (int i = 0; i < s.length(); i++) { + result[s.charAt(i) - 'a']++; + result[t.charAt(i) - 'a']--; + } + for (int i = 0; i < result.length; i++) { + if (result[i] != 0) { + return false; + } + } + return true; + } + + public static void main(String[] args) { + System.out.println(new LeetCode_242_693().isAnagram("asd","asd") == true); + System.out.println(new LeetCode_242_693().isAnagram("asde","saed") == true); + System.out.println(new LeetCode_242_693().isAnagram("asdbe","saedc") == false); + System.out.println(new LeetCode_242_693().isAnagram("anagram","nagaram") == true); + + System.out.println(new LeetCode_242_693().isAnagram2("asd","asd") == true); + System.out.println(new LeetCode_242_693().isAnagram2("asde","saed") == true); + System.out.println(new LeetCode_242_693().isAnagram2("asdbe","saedc") == false); + System.out.println(new LeetCode_242_693().isAnagram2("anagram","nagaram") == true); + } +} diff --git a/Week 02/id_693/LeetCode_429_693.java b/Week 02/id_693/LeetCode_429_693.java new file mode 100644 index 000000000..a471eae51 --- /dev/null +++ b/Week 02/id_693/LeetCode_429_693.java @@ -0,0 +1,71 @@ +package id_693; + +import java.util.*; + +/** + * @Author 李雷(KyLin) + * @Desc 429. N叉树的层序遍历 https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/ + * @Date 2019/10/22 + */ +public class LeetCode_429_693 { + //迭代法 + 队列的方式进行实现, + // 采用队列来实现数据隔离,然后每次获取上一次保存的队列数量,相当于一次添加 内部在循环 + //第一个for:负责把当前节点的数据都添加到list集合 + //第二个for:负责把所有当前节点的子节点都添加到队列(嵌套在第一个for循环内) + public List> levelOrder(Node root) { + List> list = new ArrayList<>(); + if (root == null) { + return list; + } + Queue queue = new ArrayDeque<>(); + queue.offer(root); + while (!queue.isEmpty()) { + List res = new ArrayList<>(); + int size = queue.size(); + for (int i = 0; i < size; i++) { + Node temp = queue.poll(); + res.add(Objects.requireNonNull(temp).val); + for (Node node : temp.children) { + queue.offer(node); + } + } + list.add(res); + } + return list; + } + + //代码优化 + public List> levelOrder2(Node root) { + List> list = new ArrayList<>(); + if (root == null) return list; + Queue queue = new ArrayDeque<>(); + queue.offer(root); + while (!queue.isEmpty()) { + List res = new ArrayList<>(); + int size = queue.size(); + for (int i = 0; i < size; i++) { + res.add(Objects.requireNonNull(queue.peek()).val); + queue.addAll(Objects.requireNonNull(queue.poll()).children); + } + list.add(res); + } + return list; + } + + + public static void main(String[] args) { + //test que + Node l = new Node(); + l.val = 10; + l.children = Collections.EMPTY_LIST; + Node m = new Node(); + m.val = 1203; + m.children = Collections.EMPTY_LIST; + List d = new ArrayList<>(); + d.add(l); + d.add(m); + Node root = new Node(3,d); + System.out.println(new LeetCode_429_693().levelOrder(root)); + System.out.println(new LeetCode_429_693().levelOrder2(root)); + } +} diff --git a/Week 02/id_693/LeetCode_46_693.java b/Week 02/id_693/LeetCode_46_693.java new file mode 100644 index 000000000..e4c3c1c4a --- /dev/null +++ b/Week 02/id_693/LeetCode_46_693.java @@ -0,0 +1,58 @@ +package id_693; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @Desc 46. 全排列 https://leetcode-cn.com/problems/permutations/ + * @Auther 李雷(KyLin) + * @Date 2019/10/26 + */ +public class LeetCode_46_693 { + public List> permute(int[] nums) { + List> list = new ArrayList<>(); + List numArr = new ArrayList<>(); + for (int num : nums) { + numArr.add(num); + } + backtrack(list, numArr, 0); + return list; + } + + private void backtrack(List> list, List numArr, int first) { + if (first == numArr.size()) { + list.add(new ArrayList<>(numArr)); + } + for (int i = first; i < numArr.size(); i++) { + Collections.swap(numArr, first, i); + backtrack(list, numArr, first + 1); + Collections.swap(numArr, first, i); + } + } + + public static void main(String[] args) { + System.out.println(new LeetCode_46_693().permute(new int[]{1, 1, 2})); + } +/* +给定一个没有重复数字的序列,返回其所有可能的全排列。 + +示例: + + 输入: [1,2,3] + 输出: + [ + [1,2,3], + [1,3,2], + [2,1,3], + [2,3,1], + [3,1,2], + [3,2,1] + ] +在真实 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/permutations +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 +*/ +} diff --git a/Week 02/id_693/LeetCode_47_693.java b/Week 02/id_693/LeetCode_47_693.java new file mode 100644 index 000000000..b78483f87 --- /dev/null +++ b/Week 02/id_693/LeetCode_47_693.java @@ -0,0 +1,71 @@ +package id_693; + +import java.util.*; + +/** + * @Desc 47. 全排列 II https://leetcode-cn.com/problems/permutations-ii/ + * @Auther 李雷(KyLin) + * @Date 2019/10/26 + */ +public class LeetCode_47_693 { + List> list = new ArrayList<>(); + boolean[] used ; + + + public List> permuteUnique(int[] nums) { + Arrays.sort(nums); + used = new boolean[nums.length]; + backtrack(nums, new Stack<>(), 0); + return list; + } + + // 思路:先排序 + /* + 1、先用枚举穷举所有结果 + 2、开始剪枝: + 使用一个布尔数组来作为条件,如果数组中加了这个元素,那么就跳过,如果没有则继续下去。最后回溯的时候处理状态为false,restore current status + 布尔数组默认是false;如果只有当前是true那么说明这个元素添加过了 + */ + private void backtrack(int [] nums, Stack stack, int first) { + if (first == nums.length) { + list.add(new ArrayList<>(stack)); + return; + } + for (int i = 0; i < nums.length; i++) { + if(!used[i]){ + if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) { + continue; + } + used[i] = true; + stack.add(nums[i]); + backtrack(nums, stack, first + 1); + stack.pop(); + used[i] = false; + } + } + } + + public static void main(String[] args) { + System.out.println(new LeetCode_47_693().permuteUnique(new int[]{1, 1, 2})); + } + /* +给定一个可包含重复数字的序列,返回所有不重复的全排列。 + +示例: + + 输入: [1,1,2] + 输出: + [ + [1,1,2], + [1,2,1], + [2,1,1] + ] +在真实 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/permutations-ii +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + + */ + +} diff --git a/Week 02/id_693/LeetCode_49_693.java b/Week 02/id_693/LeetCode_49_693.java new file mode 100644 index 000000000..9091126ef --- /dev/null +++ b/Week 02/id_693/LeetCode_49_693.java @@ -0,0 +1,105 @@ +package id_693; + +import java.util.*; + +/** + * @Desc 49. 字母异位词分组 https://leetcode-cn.com/problems/group-anagrams/ + * @Auther 李雷(KyLin) + * @Date 2019/10/21 + */ +public class LeetCode_49_693 { + public List> groupAnagrams(String[] strs) { + //暴力破解, O(NKlogK) + // 1、直接对字符串进行排序, + // 2、然后,转换为字符串保存到hash表中(字符串地址为key,添加的list下标为value), + // 如果存在,则直接获得list下标进行添加 + List> lists = new ArrayList<>(); + Map map = new HashMap<>(); + int index = 0; + for (int i = 0; i < strs.length; i++) { + char[] temp = strs[i].toCharArray(); + Arrays.sort(temp); + String hash = String.valueOf(temp); + if (map.containsKey(hash)) { + lists.get(map.get(hash)).add(strs[i]); + } else { + List list = new ArrayList<>(); + list.add(strs[i]); + lists.add(list); + map.put(hash,index); + index++; + } + + } + return lists; + } + + //空间优化:直接同<242.有效的字母异位词> 的套路,把字符的char转换的下标都+1 然后转换字符串作为key 存储,套路如上 优化的空间,时间会慢点 + + public List> groupAnagrams2(String[] strs) { + List> lists = new ArrayList<>(); + Map map = new HashMap<>(); + int index = 0; + int[] count = new int[26]; + for (String s : strs) { + + Arrays.fill(count,0); + for (char c : s.toCharArray()) { + count[c - 'a']++; + } + String hash = Arrays.toString(count); + + if (map.containsKey(hash)) { + lists.get(map.get(hash)).add(s); + } else { + List list = new ArrayList<>(); + list.add(s); + lists.add(list); + map.put(hash,index); + index++; + } + + } + return lists; + } + + //官方优化(不咋喜欢):采用一个char(不是字符) 来加密字符串, ,而且我在官方跑这个的时候,暴力解法是15ms,这个官方的是54ms + public List> groupAnagrams3(String[] strs) { + List> lists = new ArrayList<>(); + Map map = new HashMap<>(); + int index = 0; + int[] count = new int[26]; + for (String s : strs) { + + Arrays.fill(count,0); + for (char c : s.toCharArray()) { + count[c - 'a']++; + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 26; i++) { + sb.append("#"); + sb.append(count[i]); + } + String hash = Arrays.toString(count); + + if (map.containsKey(hash)) { + lists.get(map.get(hash)).add(s); + } else { + List list = new ArrayList<>(); + list.add(s); + lists.add(list); + map.put(hash,index); + index++; + } + + } + return lists; + } + + public static void main(String[] args) { + System.out.println(new LeetCode_49_693().groupAnagrams(new String[]{"eat","tea","tan","ate","nat","bat"})); + System.out.println(new LeetCode_49_693().groupAnagrams2(new String[]{"eat","tea","tan","ate","nat","bat"})); + System.out.println(new LeetCode_49_693().groupAnagrams3(new String[]{"eat","tea","tan","ate","nat","bat"})); + } +} diff --git a/Week 02/id_693/LeetCode_51_693.java b/Week 02/id_693/LeetCode_51_693.java new file mode 100644 index 000000000..d68a21442 --- /dev/null +++ b/Week 02/id_693/LeetCode_51_693.java @@ -0,0 +1,70 @@ +package id_693; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @Desc 51. N皇后 https://leetcode-cn.com/problems/n-queens/ + * @Auther 李雷(KyLin) + * @Date 2019/10/28 + */ +public class LeetCode_51_693 { + + //回溯法,难点:规则,建议做这个题了解清楚回溯,然后再看这个题的规则,撇捺规则。那样基本就能解开了 + public List> solveNQueens(int n) { + List> list = new ArrayList<>(); + backtrack(list,new ArrayList<>(),n,0,new boolean[n],new boolean[2 * n],new boolean[2 * n]); + return list; + } + + /** + * @param list + * @param solution + * @param n 皇后个数 + * @param row 行 + * @param column 当置皇后的列 + * @param skim 撇,行+列 + * @param restrain 撇 行-列(列-行)的绝对值 ,这里注意,需要注意数据成为负数,所以再 + n,因为行列的差不会超过n + */ + private void backtrack(List> list,List solution,int n,int row,boolean[] column,boolean[] skim,boolean[] restrain) { + //recursion terminator + if (solution.size() == n) { + //process result + list.add(new ArrayList<>(solution)); + return; + } + for (int i = 0; i < n; i++) { + //process current logic + int l1 = i + row; + int l2 = row - i + n; + if (!column[i] && !skim[l1] && !restrain[l2]) { + skim[l1] = true; + restrain[l2] = true; + column[i] = true; + char[] str = new char[n]; + Arrays.fill(str,'.'); + str[i] = 'Q'; + solution.add(new String(str)); + //drill down + backtrack(list,solution,n,row + 1,column,skim,restrain); + //restore current status + solution.remove(solution.size() - 1); + skim[l1] = false; + restrain[l2] = false; + column[i] = false; + } + } + } + + public static void main(String[] args) { + new LeetCode_51_693().solveNQueens(8); + +// int n = 4; +// boolean[][] used = new boolean[n][n]; +// new LeetCode_51_693().sfa(used,new ArrayList<>(),0,1,n); +// for (int i = 0; i < used.length; i++) { +// System.out.println(Arrays.toString(used[i])); +// } + } +} diff --git a/Week 02/id_693/LeetCode_589_693.java b/Week 02/id_693/LeetCode_589_693.java new file mode 100644 index 000000000..83d3a44e7 --- /dev/null +++ b/Week 02/id_693/LeetCode_589_693.java @@ -0,0 +1,29 @@ +package id_693; + +import java.util.ArrayList; +import java.util.List; + +/** + * @Desc 589. N叉树的前序遍历 https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/ + * @Auther 李雷(KyLin) + * @Date 2019/10/22 + */ +public class LeetCode_589_693 { + //暴力递归,直接递归中用循环取值 + public List postorder(Node root) { + List list = new ArrayList<>(); + recursionConvenience(root,list); + return list; + } + + public void recursionConvenience(Node root,List list) { + if (root != null) { + list.add(root.val); + for (int i = 0; i < root.children.size(); i++) { + recursionConvenience(root.children.get(i),list); + } + } + } + + //迭代法,后补 +} diff --git a/Week 02/id_693/LeetCode_590_693.java b/Week 02/id_693/LeetCode_590_693.java new file mode 100644 index 000000000..65af6192b --- /dev/null +++ b/Week 02/id_693/LeetCode_590_693.java @@ -0,0 +1,29 @@ +package id_693; + +import java.util.ArrayList; +import java.util.List; + +/** + * @Desc 590.N叉树的后序遍历 https://leetcode-cn.com/problems/n-ary-tree-postorder-traversal/ + * @Auther 李雷(KyLin) + * @Date 2019/10/22 + */ +public class LeetCode_590_693 { + //暴力递归,直接递归中用循环取值 + public List postorder(Node root) { + List list = new ArrayList<>(); + recursionConvenience(root,list); + return list; + } + + public void recursionConvenience(Node root,List list) { + if (root != null) { + for (int i = 0; i < root.children.size(); i++) { + recursionConvenience(root.children.get(i),list); + } + list.add(root.val); + } + } + + //迭代法,后补 +} diff --git a/Week 02/id_693/LeetCode_77_693.java b/Week 02/id_693/LeetCode_77_693.java new file mode 100644 index 000000000..ae1a85dbb --- /dev/null +++ b/Week 02/id_693/LeetCode_77_693.java @@ -0,0 +1,70 @@ +package id_693; + +import java.util.*; + +/** + * @Desc 77. 组合 https://leetcode-cn.com/problems/combinations/ + * @Auther 李雷(KyLin) + * @Date 2019/10/26 + */ +public class LeetCode_77_693 { + + /* + 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 + + 示例: + 输入: n = 4, k = 2 + 输出: + [ + [2,4], + [3,4], + [2,3], + [1,2], + [1,3], + [1,4], + ] + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/combinations + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + //使用数组+递归 + public List> combine(int n, int k) { + List> list = new ArrayList<>(); + backtrack(1, new ArrayList<>(), list, n, k); + return list; + } + private void backtrack(int first, List integers, List> list, int n, int k) { + if (integers.size() == k) { + list.add(new ArrayList<>(integers)); + return; + } + for (int i = first; i <= n - (k - integers.size()) + 1; i++) { + integers.add(i); + backtrack(i + 1, integers, list, n, k); + integers.remove(integers.size() - 1); + } + } + + //使用栈 + public List> combine2(int n, int k) { + List> list = new ArrayList<>(); + backtrack2(1, new ArrayDeque<>(), list, n, k); + return list; + } + private void backtrack2(int first, Deque integers, List> list, int n, int k) { + if (integers.size() == k) { + list.add(new ArrayList<>(integers)); + return; + } + for (int i = first; i <= n - (k - integers.size()) + 1; i++) { + integers.offer(i); + backtrack2(i + 1, integers, list, n, k); + integers.pollLast(); + } + } + + public static void main(String[] args) { + System.out.println(new LeetCode_77_693().combine2(4,2)); + } +} diff --git a/Week 02/id_693/LeetCode_94_693.java b/Week 02/id_693/LeetCode_94_693.java new file mode 100644 index 000000000..30f8ed455 --- /dev/null +++ b/Week 02/id_693/LeetCode_94_693.java @@ -0,0 +1,33 @@ +package id_693; + +import java.util.ArrayList; +import java.util.List; + +/** + * @Desc 94. 二叉树的中序遍历 https://leetcode-cn.com/problems/binary-tree-inorder-traversal/ + * @Auther 李雷(KyLin) + * @Date 2019/10/22 + */ +public class LeetCode_94_693 { + + //递归 + public List inorderTraversal(TreeNode root) { + List list = new ArrayList<>(); + recursionConvenience(root,list); + return list; + } + + public void recursionConvenience(TreeNode root,List list) { + if (root != null) { + if (root.left != null) { + recursionConvenience(root.left,list); + } + list.add(root.val); + if (root.right != null) { + recursionConvenience(root.right,list); + } + } + } + + //迭代,后补 +} diff --git a/Week 02/id_693/Node.java b/Week 02/id_693/Node.java new file mode 100644 index 000000000..71a1ee7ac --- /dev/null +++ b/Week 02/id_693/Node.java @@ -0,0 +1,21 @@ +package id_693; + +import java.util.List; + +/** + * @Desc + * @Auther 李雷(KyLin) + * @Date 2019/10/22 + */ +public class Node { + public int val; + public List children; + + public Node() { + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } +} diff --git a/Week 02/id_693/TreeNode.java b/Week 02/id_693/TreeNode.java new file mode 100644 index 000000000..7fbb5a6e5 --- /dev/null +++ b/Week 02/id_693/TreeNode.java @@ -0,0 +1,21 @@ +package id_693; + +import java.util.ArrayList; + +/** + * @Desc + * @Auther 李雷(KyLin) + * @Date 2019/10/22 + */ +public class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + public TreeNode(int val) { + this.val = val; + this.left = null; + this.right = null; + } + +} diff --git a/Week 02/id_693/practise/LeetCode_104_693.java b/Week 02/id_693/practise/LeetCode_104_693.java new file mode 100644 index 000000000..02ac9b8cf --- /dev/null +++ b/Week 02/id_693/practise/LeetCode_104_693.java @@ -0,0 +1,37 @@ +package id_693.practise; + +import id_693.TreeNode; + +/** + * @Desc 104. 二叉树的最大深度 https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/ + * @Auther 李雷(KyLin) + * @Date 2019/10/24 + */ +/* +给定一个二叉树,找出其最大深度。 +二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 +说明: 叶子节点是指没有子节点的节点。 + +示例: +给定二叉树 [3,9,20,null,null,15,7], + + 3 + / \ + 9 20 + / \ + 15 7 +返回它的最大深度 3 。 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_104_693 { + // 标准递归,每次+1 即可 + public int maxDepth(TreeNode root) { + if (root == null) { + return 0; + } + return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; + } +} diff --git a/Week 02/id_693/practise/LeetCode_111_693.java b/Week 02/id_693/practise/LeetCode_111_693.java new file mode 100644 index 000000000..59d46182d --- /dev/null +++ b/Week 02/id_693/practise/LeetCode_111_693.java @@ -0,0 +1,57 @@ +package id_693.practise; + +import id_693.TreeNode; + +/** + * @Desc 111. 二叉树的最小深度 https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/ + * @Auther 李雷(KyLin) + * @Date 2019/10/24 + */ +/* +给定一个二叉树,找出其最小深度。 + +最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 + +说明: 叶子节点是指没有子节点的节点。 + +示例: + +给定二叉树 [3,9,20,null,null,15,7], + + 3 + / \ + 9 20 + / \ + 15 7 +返回它的最小深度  2. + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_111_693 { + //此题需要注意,****如果还有子节点,那么就还不算叶子节点。那么就会继续找下去*** + //所以相对最大深度,需要考虑这个节点是不是已经到了没有叶子节点的地步,如果下面还有节点,说明还不是叶子节点,还需要找下去 + + /*题解 + 这道题和maximum depth题正好相反,是求根节点到叶子节点的最小深度,为确保统计的是根节点到叶子节点的深度, + 需要注意: + 当前节点左右子树有一个为空时,返回的应是非空子树的最小深度,而不是空子树深度0; + 若返回0相当于把当前节点认为成叶子节点,与此节点有非空子树矛盾。 + 当左右子树都不为空时,和maximum depth题一样,返回左右子树深度的最小值。 + */ + public int minDepth(TreeNode root) { + if (root == null) { + return 0; + } + if (root.left == null) {//如果左节点为null 就找右节点,再加上自己所以 + 1 + return minDepth(root.right) + 1; + } + if (root.right == null) {//如果右节点为null 就找左节点,同上 + return minDepth(root.left) + 1; + } + int left = minDepth(root.left); + int right = minDepth(root.right); + return Math.min(left, right) + 1; + } +} diff --git a/Week 02/id_693/practise/LeetCode_226_693.java b/Week 02/id_693/practise/LeetCode_226_693.java new file mode 100644 index 000000000..588cccf32 --- /dev/null +++ b/Week 02/id_693/practise/LeetCode_226_693.java @@ -0,0 +1,47 @@ +package id_693.practise; + +import id_693.TreeNode; + +/** + * @Desc 226. 翻转二叉树 https://leetcode-cn.com/problems/invert-binary-tree/description/ + * @Auther 李雷(KyLin) + * @Date 2019/10/24 + */ +/* + +翻转一棵二叉树。 + +示例: + +输入: + + 4 + / \ + 2 7 + / \ / \ + 1 3 6 9 +输出: + + 4 + / \ + 7 2 + / \ / \ + 9 6 3 1 +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/invert-binary-tree +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_226_693 { + //标准递归,元素互换 + public TreeNode invertTree(TreeNode root) { + if (root == null) { + return root; + } + TreeNode left = root.left; + root.left = root.right; + root.right = left; + invertTree(root.left); + invertTree(root.right); + return root; + } +} diff --git a/Week 02/id_693/practise/LeetCode_22_693.java b/Week 02/id_693/practise/LeetCode_22_693.java new file mode 100644 index 000000000..f8329c00a --- /dev/null +++ b/Week 02/id_693/practise/LeetCode_22_693.java @@ -0,0 +1,53 @@ +package id_693.practise; + +import java.util.ArrayList; +import java.util.List; + + +/** + * @Desc 22.括号生成 https://leetcode-cn.com/problems/generate-parentheses/ + * @Auther 李雷(KyLin) + * @Date 2019/10/23 + */ +public class LeetCode_22_693 { + + //回溯法, + // 有点类似动态规划,每一种可能都走一遍,直到长度满足, + // 同时用open 和close来记录左右括号数量 + // -类似前中后序遍历 + public List generateParenthesis(int n) { + List list = new ArrayList<>(); + generate(list,"",0,0,n); + return list; + } + + public void generate(List list,String cur,int open,int close,int max) { + //terminator + if (cur.length() >= max * 2) { + //process result + list.add(cur); + return; + } + //process current logic + + //drill down + //左括号数量需要小于max 才可以继续增加 左括号 + if (open < max) { + generate(list,cur + "(",open + 1,close,max); + } + //左括号的数量大于右括号的数量说明可以继续增加 右括号 + if (open > close) { + generate(list,cur + ")",open,close + 1,max); + } + + //restore current status + } + + public static void main(String[] args) { + LeetCode_22_693 leetCode_22_693 = new LeetCode_22_693(); + System.out.println(leetCode_22_693.generateParenthesis(3)); + System.out.println(leetCode_22_693.generateParenthesis(5)); + + + } +} \ No newline at end of file diff --git a/Week 02/id_693/practise/LeetCode_297_693.java b/Week 02/id_693/practise/LeetCode_297_693.java new file mode 100644 index 000000000..801f66aa1 --- /dev/null +++ b/Week 02/id_693/practise/LeetCode_297_693.java @@ -0,0 +1,147 @@ +package id_693.practise; + +import id_693.TreeNode; + +import java.util.*; + +/** + * @Desc 297. 二叉树的序列化与反序列化 https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree/ + * @Auther 李雷(KyLin) + * @Date 2019/10/24 + */ + +/* + 序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。 + + 请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。 + 示例:  + 你可以将以下二叉树: + 1 + / \ + 2 3 + / \ + 4 5 + 序列化为 "[1,2,3,null,null,4,5]" + 提示: 这与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。 + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_297_693 { + /* + *第一种 + */ + + // Encodes a tree to a single string. + public String serializer(TreeNode root) { + this.root = root; + return "(* ̄︶ ̄)"; + } + + public TreeNode root = null; + + // Decodes your encoded data to tree. + public TreeNode deserializer(String data) { + return root; + } + + + /* + *第二种 + * */ + // Encodes a tree to a single string. + public String serialize(TreeNode root) { + List list = new ArrayList<>(); + hierarchyTraversal(root, list); + String a = Arrays.toString(list.toArray()); + return a.substring(1, a.length() - 1).replaceAll(" ", ""); + } + + public void hierarchyTraversal(TreeNode root, List list) { + if (root == null) { + list.add(null); + return; + } + list.add(root.val); + hierarchyTraversal(root.left, list); + hierarchyTraversal(root.right, list); + } + + // Decodes your encoded data to tree. + public TreeNode deserialize(String data) { + String[] dataStr = data.split(","); + List list = new LinkedList<>(Arrays.asList(dataStr)); + return rdeserialize(list); + } + + private TreeNode rdeserialize(List list) { + if ("null".equals(list.get(0))) { + list.remove(0); + return null; + } + TreeNode root = new TreeNode(Integer.valueOf(list.get(0))); + list.remove(0); + root.left = rdeserialize(list); + root.right = rdeserialize(list); + return root; + } + + + /* + * @Desc 第三种 + * */ + // Encodes a tree to a single string. + public String serialize2(TreeNode root) { + return hierarchyTraversal2(root, new StringBuilder()).toString(); + } + + public StringBuilder hierarchyTraversal2(TreeNode root, StringBuilder str) { + if (root == null) { + str.append("null").append(","); + } else { + str.append(root.val).append(","); + hierarchyTraversal2(root.left, str); + hierarchyTraversal2(root.right, str); + } + return str; + } + + // Decodes your encoded data to tree. + public TreeNode deserialize2(String data) { + String[] dataArray = data.split(","); + List list = new LinkedList<>(Arrays.asList(dataArray)); + return rdeserialize2(list); + } + + private TreeNode rdeserialize2(List list) { + if ("null".equals(list.get(0))) { + list.remove(0); + return null; + } + TreeNode temp = new TreeNode(Integer.valueOf(list.get(0))); + list.remove(0); + temp.left = rdeserialize2(list); + temp.right = rdeserialize2(list); + return temp; + } + + + //test + public static void main(String[] args) { + TreeNode root = new TreeNode(1); + root.left = new TreeNode(2); + root.right = new TreeNode(5); + root.left.left = new TreeNode(3); + root.left.right = new TreeNode(4); + System.out.println(); + LeetCode_297_693 leetCode_297_693 = new LeetCode_297_693(); + + System.out.println(leetCode_297_693.serialize(root)); + System.out.println(leetCode_297_693.serialize(leetCode_297_693.deserialize(leetCode_297_693.serialize(root)))); + + + System.out.println(leetCode_297_693.serialize2(root)); + System.out.println(leetCode_297_693.serialize2(leetCode_297_693.deserialize2(leetCode_297_693.serialize2(root)))); + + } +} diff --git a/Week 02/id_693/practise/LeetCode_50_693.java b/Week 02/id_693/practise/LeetCode_50_693.java new file mode 100644 index 000000000..37d925453 --- /dev/null +++ b/Week 02/id_693/practise/LeetCode_50_693.java @@ -0,0 +1,128 @@ +package id_693.practise; + +/** + * @Desc 50. Pow(x, n) https://leetcode-cn.com/problems/powx-n/ 需要一点数学知识 + * @Auther 李雷(KyLin) + * @Date 2019/10/27 + */ +public class LeetCode_50_693 { + //暴力:直接0(N) 但是后面n数字越大时间越长 (而且当n=-2147483648 会出现栈超出异常) + public double myPow(double x, int n) { + if (n < 0) return 1.0 / myPow(x, -n); + double temp = 1.0; + for (int i = 0; i < n; i++) { + temp = x * temp; + } + return temp; + } + + //分治迭代解法: x^n --> 2^10 -->2^5 --> 2^2 这里少一个就要多一个 ( + public double myPow2(double x, int n) { + if (n < 0) { + return 1.0 / fastPow2(x, -n); + //return fastPow2(1 / x, -n); + } + return fastPow2(x, n); + } + + public double fastPow2(double x, long n) { + double temp = 1.0; + for (long i = n; i != 0; i /= 2) { + if (i % 2 != 0) {//不等于偶数,那就多一个x + temp = x * temp; + } + x *= x; + } + return temp; + } + + + //分治递归解法: + public double myPow3(double x, int n) { + if (n < 0) { +// return 1.0 / fastPow2(x, -n); + return fastPow3(1 / x, -n); + } + return fastPow3(x, n); + + } + + public double fastPow3(double x, long n) { + if (n == 0) return 1.0; + double temp = fastPow3(x, n / 2); + if (n % 2 == 0) { + return temp * temp; + } else { + return temp * temp * x; + } + } + + + //分治递归解法(代码优化): + public double myPow4(double x, int n) { + return n < 0 ? fastPow4(1 / x, -n) : fastPow4(x, n); + + } + + public double fastPow4(double x, long n) { + if (n == 0) return 1.0; + double temp = fastPow4(x, n / 2); + return n % 2 == 0 ? temp * temp : temp * temp * x; + } + + public static void main(String[] args) { +// System.out.println(new LeetCode_50_693().myPow(2.00000, 10)); +// System.out.println(new LeetCode_50_693().myPow(2.10000, 3)); +// System.out.println(new LeetCode_50_693().myPow(2.00000, -2)); +// System.out.println(new LeetCode_50_693().myPow(0.44528, 0)); +// System.out.println(new LeetCode_50_693().myPow(0.00001, 2147483647)); +// +// System.out.println(new LeetCode_50_693().myPow2(2.00000, 10)); +// System.out.println(new LeetCode_50_693().myPow2(2.10000, 3)); + System.out.println(new LeetCode_50_693().myPow2(2.00000, -2)); +// System.out.println(new LeetCode_50_693().myPow2(0.44528, 0)); +// System.out.println(new LeetCode_50_693().myPow2(0.00001, 2147483647)); + +// System.out.println(new LeetCode_50_693().myPow3(2.00000, 10)); +// System.out.println(new LeetCode_50_693().myPow3(2.10000, 3)); +// System.out.println(new LeetCode_50_693().myPow3(2.00000, -2)); +// System.out.println(new LeetCode_50_693().myPow3(0.44528, 0)); +// System.out.println(new LeetCode_50_693().myPow3(0.00001, 2147483647)); +// System.out.println(new LeetCode_50_693().myPow3(1.00000, -2147483648)); +// System.out.println(new LeetCode_50_693().myPow3(2.00000, -2)); + +// System.out.println(new LeetCode_50_693().myPow4(2.00000, 10)); +// System.out.println(new LeetCode_50_693().myPow4(2.10000, 3)); +// System.out.println(new LeetCode_50_693().myPow4(2.00000, -2)); +// System.out.println(new LeetCode_50_693().myPow4(0.44528, 0)); +// System.out.println(new LeetCode_50_693().myPow4(0.00001, 2147483647)); +// System.out.println(new LeetCode_50_693().myPow4(1.00000, -2147483648)); +// System.out.println(new LeetCode_50_693().myPow4(2.00000, -2)); + } +} + +/* +实现 pow(x, n) ,即计算 x 的 n 次幂函数。 + + 示例 1: + + 输入: 2.00000, 10 + 输出: 1024.00000 + 示例 2: + + 输入: 2.10000, 3 + 输出: 9.26100 + 示例 3: + + 输入: 2.00000, -2 + 输出: 0.25000 + 解释: 2-2 = 1/22 = 1/4 = 0.25 +说明: + + -100.0 < x < 100.0 + n 是 32 位有符号整数,其数值范围是 [−231, 231 − 1] 。 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/powx-n +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ \ No newline at end of file diff --git a/Week 02/id_693/practise/LeetCode_78_693.java b/Week 02/id_693/practise/LeetCode_78_693.java new file mode 100644 index 000000000..fc71a8da4 --- /dev/null +++ b/Week 02/id_693/practise/LeetCode_78_693.java @@ -0,0 +1,51 @@ +package id_693.practise; + +import java.util.ArrayList; +import java.util.List; + +/** + * @Desc 78. 子集 https://leetcode-cn.com/problems/subsets/ + * @Auther 李雷(KyLin) + * @Date 2019/10/27 + */ +public class LeetCode_78_693 { + //回溯,类似之前的组合的题目 + public List> subsets(int[] nums) { + List> list = new ArrayList<>(); + backtrack(list,new ArrayList<>(),nums,0); + return list; + } + private void backtrack(List> list,List temp,int[] nums,int first){ + list.add(new ArrayList<>(temp)); + for (int i = first; i < nums.length;i++) { + temp.add(nums[i]); + backtrack(list,temp,nums,i + 1); + temp.remove(temp.size() - 1); + } + + } +} +/* +给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。 + +说明:解集不能包含重复的子集。 + +示例: + + 输入: nums = [1,2,3] + 输出: + [ + [3], +   [1], +   [2], +   [1,2,3], +   [1,3], +   [2,3], +   [1,2], +   [] + ] + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/subsets +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ diff --git a/Week 02/id_693/practise/LeetCode_98_693.java b/Week 02/id_693/practise/LeetCode_98_693.java new file mode 100644 index 000000000..152b3c226 --- /dev/null +++ b/Week 02/id_693/practise/LeetCode_98_693.java @@ -0,0 +1,90 @@ +package id_693.practise; + +import id_693.TreeNode; + +/** + * @Desc 98. 验证二叉搜索树 https://leetcode-cn.com/problems/validate-binary-search-tree/ + * @Auther 李雷(KyLin) + * @Date 2019/10/24 + */ +/* +给定一个二叉树,判断其是否是一个有效的二叉搜索树。 + +假设一个二叉搜索树具有如下特征: + +节点的左子树只包含小于当前节点的数。 +节点的右子树只包含大于当前节点的数。 +所有左子树和右子树自身必须也是二叉搜索树。 +示例 1: + + 输入: + 2 + / \ + 1 3 + 输出: true +示例 2: + + 输入: + 5 + / \ + 1 4 +   / \ +   3 6 + 输出: false + 解释: 输入为: [5,1,4,null,null,3,6]。 +   根节点的值为 5 ,但是其右子节点值为 4 。 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/validate-binary-search-tree +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_98_693 { + //递归法 + //利用lower保存左边上一个数字,upper保存右边上一个数字 + public boolean isValidBST(TreeNode root) { + return search(root, null, null); + } + + public boolean search(TreeNode root, Integer lower, Integer upper) { + if (root == null) { + return true; + } + int temp = root.val; + if (lower != null && temp >= lower) { + return false; + } + if (upper != null && temp <= upper) { + return false; + } + if (root.left != null && !search(root.left, temp, upper)) { + return false; + } + if (root.right != null && !search(root.right, lower, temp)) { + return false; + } + return true; + } + + //递归法-代码优化 + public boolean isValidBST2(TreeNode root) { + return search(root, null, null); + } + + public boolean search2(TreeNode root, Integer lower, Integer upper) { + if (root == null) { + return true; + } + int temp = root.val; + if (lower != null && temp >= lower) { + return false; + } + if (upper != null && temp <= upper) { + return false; + } + if (!search(root.left, temp, upper) || !search(root.right, lower, temp)) { + return false; + } + return true; + } + +} diff --git a/Week 02/id_698/LeetCode_144_698.java b/Week 02/id_698/LeetCode_144_698.java new file mode 100644 index 000000000..5c3fe9722 --- /dev/null +++ b/Week 02/id_698/LeetCode_144_698.java @@ -0,0 +1,47 @@ +import java.util.LinkedList; + +/** + * 给定一个二叉树,返回它的 前序 遍历。 + * @author gning (id=698) + * + */ + + public class LeetCode_144_698 { + + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + /** + * 默写官方题解 + */ + + public List preorderTraversal(TreeNode root) { + LinkedList stack = new LinkedList(); + LinkedList output = new LinkedList(); + + if(root==null) { + return output; + } + + stack.add(root); + + while(!stack.isEmpty()) { + TreeNode node = stack.pollLast(); + output.add(node.val ); + if(node.right!=null) { + stack.add(node.right); + } + + if(node.left!=null) { + stack.add(node.left); + } + } + return output; + } + } \ No newline at end of file diff --git a/Week 02/id_698/LeetCode_242_698.java b/Week 02/id_698/LeetCode_242_698.java new file mode 100644 index 000000000..3ffa0ce81 --- /dev/null +++ b/Week 02/id_698/LeetCode_242_698.java @@ -0,0 +1,63 @@ +import java.util.Arrays; + +/** + * 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 + * @author gning (id 698) + */ + + +public class LeetCode_242_698 { + + /** + * 默写官方题解 1 + * @param s + * @param t + * @return + */ + + public boolean isAnagram1(String s, String t) { + if(s.length() != t.length()) { + return false; + } + + char[] schar = s.toCharArray(); + char[] tchar = t.toCharArray(); + + Arrays.sort(schar); + Arrays.sort(tchar); + + return Arrays.equals(schar, tchar); + } + + + /** + * 默写官方题解2 + * @param s + * @param t + * @return + */ + public boolean isAnagram2(String s, String t) { + if(s.length() != t.length()) { + return false; + } + + int[] counters = new int[26]; + + for (int i=0; i> groupAnagrams(String[] strs) { + if (strs.length==0) { + return new ArrayList>(); + } + + Map ans = new HashMap(); + + for(String s:strs) { + char[] c = s.toCharArray(); + Arrays.sort(c); + String key = String.valueOf(c); + if(!ans.containsKey(key)){ + ans.put(key, new ArrayList()); + } + ans.get(key).add(s); + } + + return new ArrayList(ans.values()); + + } +} \ No newline at end of file diff --git a/Week 02/id_703/LeetCode_242_703.py b/Week 02/id_703/LeetCode_242_703.py new file mode 100644 index 000000000..aea80125b --- /dev/null +++ b/Week 02/id_703/LeetCode_242_703.py @@ -0,0 +1,21 @@ +# +# @lc app=leetcode.cn id=242 lang=python3 +# +# [242] 有效的字母异位词 +# + +# @lc code=start +from typing import List + +class Solution: + def isAnagram(self, s: str, t: str) -> bool: + sort_s = sorted(list(s)) + sort_t = sorted(list(t)) + return sort_s == sort_t + +# if __name__ == "__main__": +# s = "anagram" +# t = "nagaram" + +# @lc code=end + diff --git a/Week 02/id_703/LeetCode_49_703.py.py b/Week 02/id_703/LeetCode_49_703.py.py new file mode 100644 index 000000000..462fceb8c --- /dev/null +++ b/Week 02/id_703/LeetCode_49_703.py.py @@ -0,0 +1,24 @@ +# +# @lc app=leetcode.cn id=49 lang=python3 +# +# [49] 字母异位词分组 +# + +# @lc code=start + +from typing import List +from collections import defaultdict + +class Solution: + def groupAnagrams(self, strs: List[str]) -> List[List[str]]: + dic = defaultdict(list) + # print(dic) + for s in strs: + _s = ''.join(sorted(s)) + dic[_s].append(s) + + return dic.values() + + +# @lc code=end + diff --git a/Week 02/id_708/LeetCode_144_708.java b/Week 02/id_708/LeetCode_144_708.java new file mode 100644 index 000000000..6088e970a --- /dev/null +++ b/Week 02/id_708/LeetCode_144_708.java @@ -0,0 +1,32 @@ +// recursion solution +class Solution { + public List preorderTraversal(TreeNode root) { + List list = new LinkedList<>(); + preorder(root, list); + return list; + } + + private void preorder(TreeNode node, List list) { + if (node == null) return ; + list.add(node.val); + preorder(node.left, list); + preorder(node.right, list); + } +} + +// stack solution +class Solution2 { + public List preorderTraversal(TreeNode root) { + List list = new LinkedList<>(); + Stack stack = new Stack<>(); + + if (root != null) stack.push(root); + while (!stack.isEmpty()) { + TreeNode curr = stack.pop(); + list.add(curr.val); + if (curr.right != null) stack.push(curr.right); + if (curr.left != null) stack.push(curr.left); + } + return list; + } +} \ No newline at end of file diff --git a/Week 02/id_708/LeetCode_1_708.go b/Week 02/id_708/LeetCode_1_708.go new file mode 100644 index 000000000..fc692fc89 --- /dev/null +++ b/Week 02/id_708/LeetCode_1_708.go @@ -0,0 +1,12 @@ +package leetcode + +func twoSum(nums []int, target int) []int { + m := make(map[int]int) + for i := range nums { + if v, ok := m[nums[i]]; ok == true { + return []int{v, i} + } + m[target-nums[i]] = i + } + return []int{} +} diff --git a/Week 02/id_708/LeetCode_22_708.java b/Week 02/id_708/LeetCode_22_708.java new file mode 100644 index 000000000..52a0afc27 --- /dev/null +++ b/Week 02/id_708/LeetCode_22_708.java @@ -0,0 +1,18 @@ +class Solution { + private List ret = new LinkedList<>(); + + public List generateParenthesis(int n) { + _generate(0, 0, n, ""); + return ret; + } + + private void _generate(int left, int right, int n, String s) { + if (right >= n) { + ret.add(s); + return ; + } + + if (left < n) _generate(left+1, right, n, s + "("); + if (left > right) _generate(left, right+1, n, s + ")"); + } +} \ No newline at end of file diff --git a/Week 02/id_708/LeetCode_242_708.java b/Week 02/id_708/LeetCode_242_708.java new file mode 100644 index 000000000..76e9c7693 --- /dev/null +++ b/Week 02/id_708/LeetCode_242_708.java @@ -0,0 +1,46 @@ +// by hash +class Solution { + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { return false; } + int[] counter = new int[26]; + for (int i = 0; i < s.length(); i++) { + counter[s.charAt(i) - 'a'] += 1; + counter[t.charAt(i) - 'a'] -= 1; + } + for (int v : counter) { + if (v != 0) { return false; } + } + return true; + } +} + +// by sorted string +class Solution2 { + public boolean isAnagram(String s, String t) { + char[] sChars = s.toCharArray(); + char[] tChars = t.toCharArray(); + Arrays.sort(sChars); + Arrays.sort(tChars); + return Arrays.equals(sChars, tChars); + } +} + +// by hash +class Solution3 { + public boolean isAnagram(String s, String t) { + Map map = new HashMap<>(); + int old; + for (char c : s.toCharArray()) { + old = map.getOrDefault(c, 0); + map.put(c, old+1); + } + for (char c : t.toCharArray()) { + if (!map.containsKey(c)) { return false; } + map.put(c, map.get(c)-1); + } + for (int v : map.values()) { + if (v != 0) { return false; } + } + return true; + } +} \ No newline at end of file diff --git a/Week 02/id_708/LeetCode_429_708.java b/Week 02/id_708/LeetCode_429_708.java new file mode 100644 index 000000000..d9828b316 --- /dev/null +++ b/Week 02/id_708/LeetCode_429_708.java @@ -0,0 +1,22 @@ +// BSF +class Solution { + public List> levelOrder(Node root) { + List> list = new LinkedList<>(); + Queue queue = new LinkedList<>(); + + if (root != null) queue.offer(root); + while (!queue.isEmpty()) { + int size = queue.size(); + List currList = new LinkedList<>(); + while (size-- > 0) { + Node curr = queue.remove(); + currList.add(curr.val); + for (Node child : curr.children) { + queue.offer(child); + } + } + list.add(currList); + } + return list; + } +} \ No newline at end of file diff --git a/Week 02/id_708/LeetCode_49_708.java b/Week 02/id_708/LeetCode_49_708.java new file mode 100644 index 000000000..df749fa08 --- /dev/null +++ b/Week 02/id_708/LeetCode_49_708.java @@ -0,0 +1,49 @@ +// by sorted string +class Solution { + public List> groupAnagrams(String[] strs) { + if (strs.length == 0) return new LinkedList>(); + + Map> map = new HashMap<>(); + for (String s : strs) { + String sortedS = sort(s); + if (!map.containsKey(sortedS)) { + map.put(sortedS, new LinkedList()); + } + map.get(sortedS).add(s); + } + return new LinkedList(map.values()); + } + + private String sort(String s) { + char[] sChars = s.toCharArray(); + Arrays.sort(sChars); + return String.valueOf(sChars); + } +} + +// by ASCII hash +class Solution2 { + public List> groupAnagrams(String[] strs) { + if (strs.length == 0) return new LinkedList>(); + + Map> map = new HashMap<>(); + int[] counter = new int[26]; + for (String s : strs) { + String key = hashKey(s, counter); + if (!map.containsKey(key)) map.put(key, new LinkedList()); + map.get(key).add(s); + } + return new LinkedList(map.values()); + } + + private String hashKey(String s, int[] counter) { + Arrays.fill(counter, 0); + for (char c : s.toCharArray()) counter[c-'a'] += 1; + StringBuilder sb = new StringBuilder(); + for (int v : counter) { + sb.append('#'); // some char's count may great then 9 + sb.append(v); + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/Week 02/id_708/LeetCode_589_708.java b/Week 02/id_708/LeetCode_589_708.java new file mode 100644 index 000000000..8477d3118 --- /dev/null +++ b/Week 02/id_708/LeetCode_589_708.java @@ -0,0 +1,31 @@ +class Solution { + public List preorder(Node root) { + List list = new LinkedList<>(); + helper(root, list); + return list; + } + + private void helper(Node node, List list) { + if (node == null) return ; + list.add(node.val); + for (Node child : node.children) { + helper(child, list); + } + } +} + +class Solution2 { + public List preorder(Node root) { + List list = new LinkedList<>(); + Stack stack = new Stack<>(); + if (root != null) stack.push(root); + while (!stack.isEmpty()) { + Node curr = stack.pop(); + list.add(curr.val); + for (int i = curr.children.size() - 1; i >= 0; i--) { + stack.push(curr.children.get(i)); + } + } + return list; + } +} \ No newline at end of file diff --git a/Week 02/id_708/LeetCode_590_708.java b/Week 02/id_708/LeetCode_590_708.java new file mode 100644 index 000000000..d891a31fb --- /dev/null +++ b/Week 02/id_708/LeetCode_590_708.java @@ -0,0 +1,31 @@ +class Solution { + public List postorder(Node root) { + List list = new LinkedList<>(); + helper(root, list); + return list; + } + + private void helper(Node node, List list) { + if (node == null) return ; + for (Node child : node.children) { + helper(child, list); + } + list.add(node.val); + } +} + +class Solution2 { + public List postorder(Node root) { + List list = new LinkedList<>(); + helper(root, list); + return list; + } + + private void helper(Node node, List list) { + if (node == null) return ; + for (Node child : node.children) { + helper(child, list); + } + list.add(node.val); + } +} \ No newline at end of file diff --git a/Week 02/id_708/LeetCode_70_708.java b/Week 02/id_708/LeetCode_70_708.java new file mode 100644 index 000000000..e01e5838b --- /dev/null +++ b/Week 02/id_708/LeetCode_70_708.java @@ -0,0 +1,14 @@ +// dp solution +class Solution { + public int climbStairs(int n) { + int[] cache = new int[n+1]; + return _climbStairs(n, cache); + } + + private int _climbStairs(int n, int[] cache) { + if (n <= 2) return n; + if (cache[n] != 0) return cache[n]; + cache[n] = _climbStairs(n-1, cache) + _climbStairs(n-2, cache); + return cache[n]; + } +} \ No newline at end of file diff --git a/Week 02/id_708/LeetCode_94_708.java b/Week 02/id_708/LeetCode_94_708.java new file mode 100644 index 000000000..ac97c62f0 --- /dev/null +++ b/Week 02/id_708/LeetCode_94_708.java @@ -0,0 +1,35 @@ +// recursion solution +class Solution { + public List inorderTraversal(TreeNode root) { + List list = new LinkedList<>(); + inorder(root, list); + return list; + } + + private void inorder(TreeNode node, List list) { + if (node == null) return ; + inorder(node.left, list); + list.add(node.val); + inorder(node.right, list); + } +} + +// stack solution +class Solution2 { + public List inorderTraversal(TreeNode root) { + List list = new LinkedList<>(); + Stack stack = new Stack<>(); + TreeNode curr = root; + while (!stack.isEmpty() || curr != null) { + // push all left elements + while (curr != null) { + stack.push(curr); + curr = curr.left; + } + curr = stack.pop(); + list.add(curr.val); + curr = curr.right; // curr can not be the element at stack again + } + return list; + } +} \ No newline at end of file diff --git a/Week 02/id_713/LeetCode_105_ConstructBinaryTreeFromPreorderAndInorderTraversal.java b/Week 02/id_713/LeetCode_105_ConstructBinaryTreeFromPreorderAndInorderTraversal.java new file mode 100644 index 000000000..ae3a1d99f --- /dev/null +++ b/Week 02/id_713/LeetCode_105_ConstructBinaryTreeFromPreorderAndInorderTraversal.java @@ -0,0 +1,62 @@ +package id_713; + +/** + * 105. 从前序与中序遍历序列构造二叉树 + * 前序遍历 preorder = [3,9,20,15,7] + * 中序遍历 inorder = [9,3,15,20,7] + * 构建二叉树 + */ +public class LeetCode_105_ConstructBinaryTreeFromPreorderAndInorderTraversal { + + + /* + 思路: + 1. 确实需要一个先序遍历的结果, 和一个中序遍历的结果 + 2. 先用先序遍历的结果找到root, 然后再去找中序的位置, 位置左边即是左子树, 右边即是右子树 + 3. 递归 + */ + + + public TreeNode buildTree(int[] preorder, int[] inorder) { + + return helper(preorder, 0, preorder.length, inorder, 0, inorder.length); + } + + + public TreeNode helper(int[] preorder, int pStart, int pEnd, int[] inorder, int iStart, int iEnd) { + + if (pStart == pEnd) return null; + + int rootVal = preorder[pStart]; + TreeNode root = new TreeNode(rootVal); + + // 中序遍历找到 根节点索引 + int iRootIndex = 0; + for (int i = iStart; i < iEnd; i++) { + if (inorder[i] == rootVal) { + iRootIndex = i; + break; + } + } + + + int leftNum = iRootIndex - iStart; + + root.left = helper(preorder, pStart + 1, pStart + leftNum + 1, inorder, iStart, iRootIndex); + root.right = helper(preorder, pStart + leftNum + 1, pEnd, inorder, iRootIndex + 1, iEnd); + + return root; + } + + + private class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + +} diff --git a/Week 02/id_713/LeetCode_144_BinaryTreePreorderTraversal.java b/Week 02/id_713/LeetCode_144_BinaryTreePreorderTraversal.java new file mode 100644 index 000000000..88ae344aa --- /dev/null +++ b/Week 02/id_713/LeetCode_144_BinaryTreePreorderTraversal.java @@ -0,0 +1,38 @@ +package id_713; + +import java.util.ArrayList; +import java.util.List; + +/** + * 144. 二叉树的前序遍历 + * 根左右 + */ +public class LeetCode_144_BinaryTreePreorderTraversal { + + public List preorderTraversal(TreeNode root) { + List list = new ArrayList<>(); + + this.helper(list, root); + + return list; + } + + public void helper(List list, TreeNode root) { + if (root == null) return; + + list.add(root.val); + this.helper(list, root.left); + this.helper(list, root.right); + } + + private class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + +} diff --git a/Week 02/id_713/LeetCode_169_MajorityElement.java b/Week 02/id_713/LeetCode_169_MajorityElement.java new file mode 100644 index 000000000..491cf4729 --- /dev/null +++ b/Week 02/id_713/LeetCode_169_MajorityElement.java @@ -0,0 +1,52 @@ +package id_713; + +/** + * 169. 求众数 + * 给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。 + * 你可以假设数组是非空的,并且给定的数组总是存在众数。 + *

+ * 示例 1: + * 输入: [3,2,3] + * 输出: 3 + *

+ * 示例 2: + * 输入: [2,2,1,1,1,2,2] + * 输出: 2 + */ +public class LeetCode_169_MajorityElement { + + + public int majorityElement(int[] nums) { + return divideConquer(nums, 0, nums.length - 1); + } + + private int divideConquer(int[] nums, int low, int high) { + if (low == high) return nums[low]; // 当且仅当 数组元素个数为1时, 返回这1个元素, 就是众数 + + // 递归, 分治左右两个切片 + int mid = (high - low) / 2 + low; + int left = divideConquer(nums, low, mid); + int right = divideConquer(nums, mid + 1, high); + + // 如果两个半边都统一, 则返回众数 + if (left == right) return left; + + // 否则重新唱票, 决出众数 + int leftCnt = countInRange(nums, left, low, high); + int rightCnt = countInRange(nums, right, low, high); + + // 看谁票数多 + return leftCnt > rightCnt ? leftCnt : rightCnt; + } + + private int countInRange(int[] nums, int num, int low, int high) { + int cnt = 0; + for (int i = low; i <= high; i++) { + if (nums[i] == num) { + cnt++; + } + } + return cnt; + } + +} diff --git a/Week 02/id_713/LeetCode_17_LetterCombinationsOfAPhoneNumber.java b/Week 02/id_713/LeetCode_17_LetterCombinationsOfAPhoneNumber.java new file mode 100644 index 000000000..d19f9dff9 --- /dev/null +++ b/Week 02/id_713/LeetCode_17_LetterCombinationsOfAPhoneNumber.java @@ -0,0 +1,64 @@ +package id_713; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 17. 电话号码的字母组合 + * 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。 + *

+ * 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 + *

+ * 输入:"23" + * 输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]. + */ +public class LeetCode_17_LetterCombinationsOfAPhoneNumber { + + + Map phone = new HashMap() {{ + put("2", "abc"); + put("3", "def"); + put("4", "ghi"); + put("5", "jkl"); + put("6", "mno"); + put("7", "pqrs"); + put("8", "tuv"); + put("9", "wxyz"); + }}; + + List output = new ArrayList(); + + + public List letterCombinations(String digits) { + if (digits.length() == 0) return output; + + backtrack("", digits); + + return output; + } + + + public void backtrack(String combination, String next_digits) { + // 当没有数字可用的时候, 添加结果 + if (next_digits.length() == 0) { + // the combination is done + output.add(combination); + } else { + // 获取当前第一个数字 + String digit = next_digits.substring(0, 1); + // 获取当前第一个数字对应的字母 + String letters = phone.get(digit); + // 遍历字母, 也就是可能的值 + for (int i = 0; i < letters.length(); i++) { + // 截取第i个字母 + String letter = phone.get(digit).substring(i, i + 1); + // 追加到合并值里, 然后下探 + backtrack(combination + letter, next_digits.substring(1)); + } + } + } + + +} diff --git a/Week 02/id_713/LeetCode_1_TwoSum.java b/Week 02/id_713/LeetCode_1_TwoSum.java new file mode 100644 index 000000000..f2cdb1216 --- /dev/null +++ b/Week 02/id_713/LeetCode_1_TwoSum.java @@ -0,0 +1,32 @@ +package id_713; + +import java.util.HashMap; +import java.util.Map; + +/** + * 1. 两数之和 + *

+ * 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 + * 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 + *

+ * 给定 nums = [2, 7, 11, 15], target = 9 + * 因为 nums[0] + nums[1] = 2 + 7 = 9 + * 所以返回 [0, 1] + */ +public class LeetCode_1_TwoSum { + + public int[] twoSum(int[] nums, int target) { + int[] ans = new int[2]; + Map map = new HashMap<>(); // K->数值, V->索引 + + for (int i = 0; i < nums.length; i++) { + if (map.containsKey(target - nums[i])) { + ans[0] = map.get(target - nums[i]); + ans[1] = i; + return ans; + } + map.put(target - nums[i], i); + } + return ans; + } +} diff --git a/Week 02/id_713/LeetCode_429_NAryTreeLevelOrderTraversal.java b/Week 02/id_713/LeetCode_429_NAryTreeLevelOrderTraversal.java new file mode 100644 index 000000000..f08d6aa5e --- /dev/null +++ b/Week 02/id_713/LeetCode_429_NAryTreeLevelOrderTraversal.java @@ -0,0 +1,140 @@ +package id_713; + +import java.util.*; + +/** + * 429. N叉树的层序遍历 + */ +public class LeetCode_429_NAryTreeLevelOrderTraversal { + + /* + 思路: + 1. 广度搜索, 使用队列, 进行层级扫描 + 1. 先把第一个元素放进去 + 2. 停止条件为队列为空 + 3. 获取每一层数量 + 4. 出这一层数量的元素, 同时添加这一层的子级别元素, 直到这一层出队完 + 5. 这样就进入下一层了, 直到队列空 为止 + */ + + + public List> levelOrder(Node root) { + List> ans = new ArrayList<>(); + if (root == null) return ans; + + Deque deque = new ArrayDeque<>(); + deque.addFirst(root); + + while (!deque.isEmpty()) { + int size = deque.size(); + List list = new ArrayList<>(); + + while (size > 0) { + Node curr = deque.removeLast(); + list.add(curr.val); + + for (Node node : curr.children) { + if (node != null) { + deque.addFirst(node); + } + } + ans.add(list); + size--; + } + } + return ans; + } + + /* + 递归处理方式: + + public List> levelOrder(Node root) { + List> res = new ArrayList<>(); + if (root == null) return res; + helper(root, 0, res); + return res; + } + + private void helper(Node root, int depth, List> res) { + if (root == null) return; + //判断是否是新的一层 + if (depth + 1 > res.size()) { + res.add(new ArrayList<>()); + } + res.get(depth).add(root.val); + + //处理子节点 + for (Node node : root.children) { + if (node != null) { + helper(node, depth + 1, res); + } + } + } + + */ + + private class Node { + public int val; + public List children; + + public Node() { + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } + } + + /** + * 242. 有效的字母异位词 + * 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 + *

+ * 示例 1: + * 输入: s = "anagram", t = "nagaram" + * 输出: true + *

+ * 示例 2: + * 输入: s = "rat", t = "car" + * 输出: false + *

+ * 说明: + * 你可以假设字符串只包含小写字母。 + */ + public static class LeetCode_242_ValidAnagram { + + /* + 思路: + 1. 先搞懂"异位词的意思" + 一种把某个词或句子的字母的位置(顺序)加以改换所形成的新词,英文叫做anagram,词典把这个词翻译成“变位词”。 + 2. 如果长度不一样, 则肯定不是异位词 + 3. 方法1: + 1. hashMap统计单词s的字母频率, key字母, value词频 + 2. 扫描单词t时, 减去统计的数字, 如果减到0, 则删除key + 3. 判断hashMap是否为空 + 4. 方法2: + 1. 使用数组代替hashMap, 前提是只有字母, 没有除字母外的unicode + 2. 遍历单词长度时, 同时操作单词s/单词t, 对应字母位置的词频 + 3. 最后查看数组中 是否有不为0的数据 + + */ + + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) return false; // 如果长度都不相等, 则不符合异位词的定义 + + int[] arr = new int[26]; // 因为只有26个字母, 即不考虑Unicode的情况 + for (int i = 0; i < s.length(); i++) { + arr[s.charAt(i)]++; // 先扫描单词s,记录词频, 增量为正值 + arr[t.charAt(i)]--; // 同时扫描单词s,记录词频, 增量为负值 + // 如果是异位词, 则所有slot(槽)中的值应正负抵消 + // 但一次循环单词s和单词t, 不一定操作同一个槽, 最终应该都是0, 这样可以在一个循环里搞定 + } + + for (int x : arr) { // 最终所有slot(槽)中的值应为0, 才是异位词 + if (x != 0) return false; + } + + return true; + } + } +} diff --git a/Week 02/id_713/LeetCode_46_Permutations.java b/Week 02/id_713/LeetCode_46_Permutations.java new file mode 100644 index 000000000..200af6c16 --- /dev/null +++ b/Week 02/id_713/LeetCode_46_Permutations.java @@ -0,0 +1,61 @@ +package id_713; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * 46. 全排列 + * 输入: [1,2,3] + * 输出: + * [ + * [1,2,3], + * [1,3,2], + * [2,1,3], + * [2,3,1], + * [3,1,2], + * [3,2,1] + * ] + */ +public class LeetCode_46_Permutations { + + public List> permute(int[] nums) { + // init output list + List> output = new LinkedList<>(); + + // convert nums into list since the output is a list of lists + ArrayList nums_lst = new ArrayList<>(); + for (int num : nums) + nums_lst.add(num); + + int n = nums.length; + backtrack(n, nums_lst, output, 0); + return output; + } + + public void backtrack(int n, + ArrayList nums, + List> output, + int first) { + // if all integers are used up + if (first == n) + output.add(new ArrayList<>(nums)); + for (int i = first; i < n; i++) { + // place i-th integer first + // in the current permutation + Collections.swap(nums, first, i); + // use next integers to complete the permutations + backtrack(n, nums, output, first + 1); + // backtrack + Collections.swap(nums, first, i); + } + } + + +} + + + + + diff --git a/Week 02/id_713/LeetCode_47_PermutationsII.java b/Week 02/id_713/LeetCode_47_PermutationsII.java new file mode 100644 index 000000000..2c895bb0a --- /dev/null +++ b/Week 02/id_713/LeetCode_47_PermutationsII.java @@ -0,0 +1,70 @@ +package id_713; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Stack; + +/** + * 47. 全排列 II + *

+ * 给定一个可包含重复数字的序列,返回所有不重复的全排列。 + *

+ * 示例: + *

+ * 输入: [1,1,2] + * 输出: + * [ + * [1,1,2], + * [1,2,1], + * [2,1,1] + * ] + */ +public class LeetCode_47_PermutationsII { + + private List> res = new ArrayList<>(); + private boolean[] used; + + public List> permuteUnique(int[] nums) { + int len = nums.length; + if (len == 0) { + return res; + } + // 修改 1:首先排序,之后才有可能发现重复分支 + Arrays.sort(nums); + + // 如果是降序,需要把 nums 变为包装数组类型,输入 Arrays.sort() 方法才生效,并且还要传入一个比较器,搜索之前,再转为基本类型数组,因此不建议降序排序 + // Integer[] numsBoxed = IntStream.of(nums).boxed().collect(Collectors.toList()).toArray(new Integer[0]); + // Arrays.sort(numsBoxed, Collections.reverseOrder()); + // nums = Arrays.stream(numsBoxed).mapToInt(Integer::valueOf).toArray(); + + used = new boolean[len]; + findPermuteUnique(nums, 0, new Stack<>()); + return res; + } + + + private void findPermuteUnique(int[] nums, int depth, Stack stack) { + if (depth == nums.length) { + res.add(new ArrayList<>(stack)); + return; + } + for (int i = 0; i < nums.length; i++) { + if (!used[i]) { + // 修改 2:因为排序以后重复的数一定不会出现在开始,故 i > 0 + // 和之前的数相等,并且之前的数还未使用过,只有出现这种情况,才会出现相同分支 + // 这种情况跳过即可 + if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) { + continue; + } + used[i] = true; + stack.add(nums[i]); + findPermuteUnique(nums, depth + 1, stack); + stack.pop(); + used[i] = false; + } + } + } + + +} diff --git a/Week 02/id_713/LeetCode_49_GroupAnagrams.java b/Week 02/id_713/LeetCode_49_GroupAnagrams.java new file mode 100644 index 000000000..9cb3a5d87 --- /dev/null +++ b/Week 02/id_713/LeetCode_49_GroupAnagrams.java @@ -0,0 +1,79 @@ +package id_713; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 49. 字母异位词分组 + * 给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 + * 示例: + *

+ * 输入: ["eat", "tea", "tan", "ate", "nat", "bat"], + * 输出: + * [ + * ["ate","eat","tea"], + * ["nat","tan"], + * ["bat"] + * ] + */ +public class LeetCode_49_GroupAnagrams { + /* + 思路: + 1. 遇到分组, 则必须定义统一格式的key + */ + + public List> groupAnagrams(String[] strs) { + + Map> map = new HashMap<>(); // 定义用于分组的map, key:有共性的摘要值, value:异位词的集合 + + for (int i = 0; i < strs.length; i++) { + // 统计词频, 用于构建key + int[] nums = new int[26]; + for (int j = 0; j < strs[i].length(); j++) { + nums[strs[i].charAt(j) - 'a']++; + } + + // 转成 0#1#2 这种格式 + String key = ""; + for (int j = 0; j < nums.length; j++) { + key = key + nums[j] + "#"; + } + + if (map.containsKey(key)) { + map.get(key).add(strs[i]); + } else { + List tmp = new ArrayList<>(); + tmp.add(strs[i]); + map.put(key, tmp); + } + } + + return new ArrayList<>(map.values()); + } + + /* + 8秒钟的案例 + class Solution { + public List> groupAnagrams(String[] strs) { + // 找到相同的字符串,主要是排序不一样 + HashMap> map = new HashMap<>(); + for (String str : strs) { + String key = sort(str); + if (!map.containsKey(key)) { + map.put(key, new ArrayList<>()); + } + map.get(key).add(str); + } + return new ArrayList<>(map.values()); + } + + String sort(String str) { + char[] c = str.toCharArray(); + Arrays.sort(c); + return String.valueOf(c); + } + } + */ +} diff --git a/Week 02/id_713/LeetCode_589_NAryTreePreorderTraversal.java b/Week 02/id_713/LeetCode_589_NAryTreePreorderTraversal.java new file mode 100644 index 000000000..519c808ae --- /dev/null +++ b/Week 02/id_713/LeetCode_589_NAryTreePreorderTraversal.java @@ -0,0 +1,44 @@ +package id_713; + +import java.util.ArrayList; +import java.util.List; + +/** + * 589. N叉树的前序遍历 + */ +public class LeetCode_589_NAryTreePreorderTraversal { + + public List preorder(Node root) { + List list = new ArrayList<>(); + this.helper(list, root); + return list; + } + + public void helper(List list, Node root) { + if (root == null) return; + + // 根左右 + list.add(root.val); + for (int i = 0; i < root.children.size() / 2; i++) { + this.helper(list, root.children.get(i)); + } + for (int i = root.children.size() / 2; i < root.children.size(); i++) { + this.helper(list, root.children.get(i)); + } + } + + + private class Node { + public int val; + List children; + + public Node() { + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } + } + +} \ No newline at end of file diff --git a/Week 02/id_713/LeetCode_590_NAryTreePostorderTraversal.java b/Week 02/id_713/LeetCode_590_NAryTreePostorderTraversal.java new file mode 100644 index 000000000..d66eb128c --- /dev/null +++ b/Week 02/id_713/LeetCode_590_NAryTreePostorderTraversal.java @@ -0,0 +1,49 @@ +package id_713; + +import java.util.ArrayList; +import java.util.List; + +/** + * 590. N叉树的后序遍历 + */ +public class LeetCode_590_NAryTreePostorderTraversal { + /* + 思路: + 1. 二叉树遍历有2个指针, left, right. 多叉树是数组, 考虑把数组切分成 left, right的概念 + */ + + + public List postorder(Node root) { + List list = new ArrayList<>(); + this.helper(list, root); + return list; + } + + public void helper(List list, Node root) { + if (root == null) return; + + // 左右根 + for (int i = 0; i < root.children.size() / 2; i++) { // list前半部分, 作为左节点 + this.helper(list, root.children.get(i)); + } + for (int i = root.children.size() / 2; i < root.children.size(); i++) { // list后半部分, 作为右节点 + this.helper(list, root.children.get(i)); + } + list.add(root.val); + } + + + private class Node { + public int val; + List children; + + public Node() { + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } + } + +} diff --git a/Week 02/id_713/LeetCode_77_Combinations.java b/Week 02/id_713/LeetCode_77_Combinations.java new file mode 100644 index 000000000..ee0c76fe3 --- /dev/null +++ b/Week 02/id_713/LeetCode_77_Combinations.java @@ -0,0 +1,52 @@ +package id_713; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Stack; + +/** + * 77. 组合 + * 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 + * 输入: n = 4, k = 2 + * 输出: + * [ + * [2,4], + * [3,4], + * [2,3], + * [1,2], + * [1,3], + * [1,4], + * ] + */ +public class LeetCode_77_Combinations { + /* + 参考: https://leetcode-cn.com/problems/combinations/solution/hui-su-suan-fa-jian-zhi-python-dai-ma-java-dai-ma-/ + */ + + // 初始化结果集, 要不然就得作为 参数 参与递归 + private List> res = new ArrayList<>(); + + public List> combine(int n, int k) { + // 从 1 开始是题目的设定 + this.helper(n, k, 1, new Stack<>()); // 使用栈用于移除末尾元素, 比数组高效 + return res; + } + + private void helper(int n, int k, int begin, Stack pre) { + // 够数了,就添加到结果集中 + if (pre.size() == k) { + res.add(new ArrayList<>(pre)); + return; + } + + // 关键在于分析出 i 的上界 + for (int i = begin; i <= n; i++) { + pre.add(i); + helper(n, k, i + 1, pre); + pre.pop(); // 使用栈来解决最近一个元素的状态, 使用完就抛弃 + } + } + + +} diff --git a/Week 02/id_713/LeetCode_94_BinaryTreeInorderTraversal.java b/Week 02/id_713/LeetCode_94_BinaryTreeInorderTraversal.java new file mode 100644 index 000000000..51a73eb67 --- /dev/null +++ b/Week 02/id_713/LeetCode_94_BinaryTreeInorderTraversal.java @@ -0,0 +1,38 @@ +package id_713; + +import java.util.*; + +/** + * 94. 二叉树的中序遍历 + * 左根右 + */ +public class LeetCode_94_BinaryTreeInorderTraversal { + + public List inorderTraversal(TreeNode root) { + List list = new ArrayList<>(); + + this.helper(list, root); + + return list; + } + + public void helper(List list, TreeNode root) { + if (root == null) return; + + this.helper(list, root.left); + list.add(root.val); + this.helper(list, root.right); + } + + + private class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + +} \ No newline at end of file diff --git a/Week 02/id_713/NOTE.md b/Week 02/id_713/NOTE.md index a6321d6e2..f34b199cf 100644 --- a/Week 02/id_713/NOTE.md +++ b/Week 02/id_713/NOTE.md @@ -1,4 +1,233 @@ # NOTE +### 哈希表(HashMap) + +#### 源码分析 + +* 参考: + +```java +/** + * @param key 键 + * @param value 值 + * @return V 值(如果覆盖的话, 返回那个被覆盖的值) + */ +public V put(K key, V value) { + // 对于key值为null, 调用putForNullKey方法处理 + if (key == null) + return putForNullKey(value); + // 使用key的hashCode计算key对应的hash值 + int hash = hash(key.hashCode()); + // 通过key的hash值查找在数组中的index位置 + int i = indexFor(hash, table.length ); + // 取出数组index位置的链表,遍历链表找查看是有已经存在相同的key + for (Entry e = table [i]; e != null; e = e. next) { + Object k; + // 通过对比hash值、key判断是否已经存在相同的key + if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { + // 如果存在,取出当前key对应的value,供返回 + V oldValue = e. value; + // 用新value替换之旧的value + e. value = value; + e.recordAccess( this); + // 返回旧value,退出方法 + return oldValue; + } + } + + // 如果不存在相同的key + // 修改版本+1 + modCount++; + // 在数组i位置处添加一个新的链表节点 + addEntry(hash, key, value, i); + // 没有相同key的情况,返回null + return null; +} + +/** + * 增加一个k-v,hash组成的节点在数组内,同时可能会进行数组扩容。 + */ +void addEntry( int hash, K key, V value, int bucketIndex) { + // 下面两行行代码的逻辑是,创建一个新节点放到单向链表的头部,旧节点向后移 + // 取出索引bucketIndex位置处的链表节点,如果节点不存在那就是null,也就是说当数组该位置处还不曾存放过节点的时候,这个地方就是null, + Entry e = table[bucketIndex]; + // 创建一个节点,并放置在数组的bucketIndex索引位置处,并让新的节点的next指向原来的节点 + table[bucketIndex] = new Entry(hash, key, value, e); + // 如果当前HashMap中的元素已经到达了临界值,则将容量扩大2倍,并将size计数+1 + if (size ++ >= threshold) + resize(2 * table.length ); +} + +private V putForNullKey(V value) { + // 取出数组第1个位置(下标等于0)的节点,如果存在则覆盖不存在则新增,和上面的put一样不多讲, + for (Entry e = table [0]; e != null; e = e. next) { + if (e.key == null) { + V oldValue = e. value; + e. value = value; + e.recordAccess( this); + return oldValue; + } + } + modCount++; + // 如果key等于null,则hash值等于0 + addEntry(0, null, value, 0); + return null; +} + + +/** + * 根据key删除元素 + */ +public V remove(Object key) { + Entry e = removeEntryForKey(key); + return (e == null ? null : e. value); +} + +/** + * 根据key删除链表节点 + */ +final Entry removeEntryForKey(Object key) { + // 计算key的hash值 + int hash = (key == null) ? 0 : hash(key.hashCode()); + // 根据hash值计算key在数组的索引位置 + int i = indexFor(hash, table.length ); + // 找到该索引出的第一个节点 + Entry prev = table[i]; + Entry e = prev; + + // 遍历链表(从链表第一个节点开始next),找出相同的key, + while (e != null) { + Entry next = e. next; + Object k; + // 如果hash值和key都相等,则认为相等 + if (e.hash == hash && + ((k = e. key) == key || (key != null && key.equals(k)))) { + // 修改版本+1 + modCount++; + // 计数器减1 + size--; + // 如果第一个就是要删除的节点(第一个节点没有上一个节点,所以要分开判断) + if (prev == e) + // 则将下一个节点放到table[i]位置(要删除的节点被覆盖) + table[i] = next; + else + // 否则将上一个节点的next指向当要删除节点下一个(要删除节点被忽略,没有指向了) + prev. next = next; + e.recordRemoval( this); + // 返回删除的节点内容 + return e; + } + // 保存当前节点为下次循环的上一个节点 + prev = e; + // 下次循环 + e = next; + } + + return e; +} +``` + + + +--- + + + +### 二叉树 前/中/后序遍历 + +| 遍历方式 | 顺序 | 应用场景 | +| ------------------ | ------ | -------------------------------------- | +| 前序遍历(先序遍历) | 根左右 | 打印层及目录 | +| 中序遍历 | 左根右 | 排序 | +| 后续遍历 | 左右根 | 统计文件夹大小, 中缀表达式->后缀表达式 | + +#### 遍历算法 + +```java +public void preOrder(TreeNode root) { // 先序遍历, 中左右 + if (root == null) return; // 定义递归的终止条件 + + System.out.println(root.val); + preOrder(root.left); + preOrder(root.right); +} + +public void inOrder(TreeNode root) { // 中序遍历, 左中右, 其实只是调整关键3行的代码顺序即可 + if (root == null) return; + + inOrder(root.left); + System.out.println(root.val); + inOrder(root.right); +} + + +public void postOrder(TreeNode root) { // 后序遍历, 左右中, 其实只是调整关键3行的代码顺序即可 + if (root == null) return; + + postOrder(root.left); + postOrder(root.right); + System.out.println(root.val); +} + +// 变种: 记录数据, 套路不变, 多传入一个参数List即可 +public void inOrderHelper(List list, TreeNode root) { + if (root == null) return; + + inOrder(root.left); + // System.out.println(root.val); 将打印改成记录即可 + list.add(root.val); + inOrder(root.right); +} + +``` + + + +### 递归 + +* 拆解成子问题, 最近最简方法, 也就是重复子问题 + * 如中序遍历二叉树, 只要针对第一个子树中序遍历, 递归会完成子树的子树的处理 + + + +### 算法模板 + +#### 递归算法模板 + +```java +public void helper(int level, Object... params) { + // 递归终止条件 + if (level > MAX_LEVEL) { + // 逻辑处理 + return; + } + + // 在本层的逻辑处理 + process(level, params); + + // 下探(递归) + this.helper(level + 1, params); + // 存储更新当前状态, 如果需要的话 +} +``` + + + +#### 统计词频 + +```java +public void cntChar(String str) { + // 构建数组, 用于计数 + int[] arr = int[26]; + + for (int i = 0; i < str.length(); i++) { + // 因为 'a' 等于97, 所以可以把char量化成数字 + arr[str.charAt(i) - 'a']++; + } + // 最终数组arr就是, 字母的频次 +} +``` + + diff --git a/Week 02/id_713/not_solved/LeetCode_51_NQueens.java b/Week 02/id_713/not_solved/LeetCode_51_NQueens.java new file mode 100644 index 000000000..0de417f76 --- /dev/null +++ b/Week 02/id_713/not_solved/LeetCode_51_NQueens.java @@ -0,0 +1,24 @@ +package id_713.not_solved; + +/** + * 51. N皇后 + *

+ * n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。 + *

+ * 示例: + * 输入: 4 + * 输出: [ + * [".Q..", // 解法 1 + * "...Q", + * "Q...", + * "..Q."], + * ["..Q.", // 解法 2 + * "Q...", + * "...Q", + * ".Q.."] + * ] + * 解释: 4 皇后问题存在两个不同的解法。 + */ +public class LeetCode_51_NQueens { + +} diff --git a/Week 02/id_718/1.two-sum.py b/Week 02/id_718/1.two-sum.py new file mode 100644 index 000000000..bfe22a521 --- /dev/null +++ b/Week 02/id_718/1.two-sum.py @@ -0,0 +1,18 @@ +# +# @lc app=leetcode id=1 lang=python3 +# +# [1] Two Sum +# + +# @lc code=start +class Solution: + def twoSum(self, nums: List[int], target: int) -> List[int]: + dic = dict() + for i, v in enumerate(nums): + if target - v in dic: + return i, dic[target - v] + dic[v] = i + return [] + +# @lc code=end + diff --git a/Week 02/id_718/101.symmetric-tree.py b/Week 02/id_718/101.symmetric-tree.py new file mode 100644 index 000000000..37c1b0680 --- /dev/null +++ b/Week 02/id_718/101.symmetric-tree.py @@ -0,0 +1,19 @@ +# +# @lc app=leetcode id=101 lang=python3 +# +# [101] Symmetric Tree +# + +# @lc code=start +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + def isSymmetric(self, root: TreeNode) -> bool: + +# @lc code=end + diff --git a/Week 02/id_718/102.binary-tree-level-order-traversal.py b/Week 02/id_718/102.binary-tree-level-order-traversal.py new file mode 100644 index 000000000..d2debfcd4 --- /dev/null +++ b/Week 02/id_718/102.binary-tree-level-order-traversal.py @@ -0,0 +1,34 @@ +# +# @lc app=leetcode id=102 lang=python3 +# +# [102] Binary Tree Level Order Traversal +# + +# @lc code=start +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None +from collections import deque +class Solution: + def levelOrder(self, root: TreeNode) -> List[List[int]]: + ret = [] + if not root: + return ret + queue = deque([root]) + while len(queue): + level = [] + for _ in range(len(queue)): + p = queue.popleft() + level.append(p.val) + if p.left: + queue.append(p.left) + if p.right: + queue.append(p.right) + ret.append(level) + return ret + +# @lc code=end + diff --git a/Week 02/id_718/104.maximum-depth-of-binary-tree.py b/Week 02/id_718/104.maximum-depth-of-binary-tree.py new file mode 100644 index 000000000..32b608edd --- /dev/null +++ b/Week 02/id_718/104.maximum-depth-of-binary-tree.py @@ -0,0 +1,22 @@ +# +# @lc app=leetcode id=104 lang=python3 +# +# [104] Maximum Depth of Binary Tree +# + +# @lc code=start +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + def maxDepth(self, root: TreeNode) -> int: + if not root: + return 0 + return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1 + +# @lc code=end + diff --git a/Week 02/id_718/105.construct-binary-tree-from-preorder-and-inorder-traversal.py b/Week 02/id_718/105.construct-binary-tree-from-preorder-and-inorder-traversal.py new file mode 100644 index 000000000..4fbeba28d --- /dev/null +++ b/Week 02/id_718/105.construct-binary-tree-from-preorder-and-inorder-traversal.py @@ -0,0 +1,25 @@ +# +# @lc app=leetcode id=105 lang=python3 +# +# [105] Construct Binary Tree from Preorder and Inorder Traversal +# + +# @lc code=start +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode: + if not inorder: + return None + root = TreeNode(preorder[0]) + rootPos = inorder.index(root.val) + root.left = self.buildTree(preorder[1:1+rootPos], inorder[:rootPos]) + root.right = self.buildTree(preorder[1+rootPos:], inorder[rootPos+1:]) + return root +# @lc code=end + diff --git a/Week 02/id_718/111.minimum-depth-of-binary-tree.py b/Week 02/id_718/111.minimum-depth-of-binary-tree.py new file mode 100644 index 000000000..77053a0a4 --- /dev/null +++ b/Week 02/id_718/111.minimum-depth-of-binary-tree.py @@ -0,0 +1,23 @@ +# +# @lc app=leetcode id=111 lang=python3 +# +# [111] Minimum Depth of Binary Tree +# + +# @lc code=start +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + def minDepth(self, root: TreeNode) -> int: + if not root: + return 0 + l, r = self.minDepth(root.left), self.minDepth(root.right) + return min(l, r) + 1 if (l and r) else max(l, r) + 1 + +# @lc code=end + diff --git a/Week 02/id_718/144.binary-tree-preorder-traversal.py b/Week 02/id_718/144.binary-tree-preorder-traversal.py new file mode 100644 index 000000000..431be38b7 --- /dev/null +++ b/Week 02/id_718/144.binary-tree-preorder-traversal.py @@ -0,0 +1,30 @@ +# +# @lc app=leetcode id=144 lang=python3 +# +# [144] Binary Tree Preorder Traversal +# + +# @lc code=start +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None +from collections import deque +class Solution: + def preorderTraversal(self, root: TreeNode) -> List[int]: + ret = [] + stack = deque([None]) + while root: + ret.append(root.val) + if root.right: + stack.append(root.right) + if root.left: + root = root.left + else: + root = stack.pop() + return ret + +# @lc code=end + diff --git a/Week 02/id_718/145.binary-tree-postorder-traversal.py b/Week 02/id_718/145.binary-tree-postorder-traversal.py new file mode 100644 index 000000000..e7857495e --- /dev/null +++ b/Week 02/id_718/145.binary-tree-postorder-traversal.py @@ -0,0 +1,33 @@ +# +# @lc app=leetcode id=145 lang=python3 +# +# [145] Binary Tree Postorder Traversal +# + +# @lc code=start +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None +from collections import deque +class Solution: + def postorderTraversal(self, root: TreeNode) -> List[int]: + ret = [] + if not root: + return ret + stack = deque([root]) + pre = None + while len(stack): + p = stack[-1] + if (p.left is None and p.right is None) or (pre and (pre == p.left or pre == p.right)): + ret.append(p.val) + stack.pop() + pre = p + else: + if p.right: stack.append(p.right) + if p.left: stack.append(p.left) + return ret +# @lc code=end + diff --git a/Week 02/id_718/155.min-stack.py b/Week 02/id_718/155.min-stack.py new file mode 100644 index 000000000..ee50577ac --- /dev/null +++ b/Week 02/id_718/155.min-stack.py @@ -0,0 +1,41 @@ +# +# @lc app=leetcode id=155 lang=python3 +# +# [155] Min Stack +# + +# @lc code=start +from collections import deque +class MinStack: + + def __init__(self): + """ + initialize your data structure here. + """ + self.stack, self.minstack = deque(), deque() + + def push(self, x: int) -> None: + self.stack.append(x) + if not self.minstack or self.minstack[-1] >= x: + self.minstack.append(x) + + def pop(self) -> None: + x = self.stack.pop() + if x == self.minstack[-1]: + self.minstack.pop() + return x + + def top(self) -> int: + return self.stack[-1] + + def getMin(self) -> int: + return self.minstack[-1] + +# Your MinStack object will be instantiated and called as such: +# obj = MinStack() +# obj.push(x) +# obj.pop() +# param_3 = obj.top() +# param_4 = obj.getMin() +# @lc code=end + diff --git a/Week 02/id_718/169.majority-element.py b/Week 02/id_718/169.majority-element.py new file mode 100644 index 000000000..27c2d2227 --- /dev/null +++ b/Week 02/id_718/169.majority-element.py @@ -0,0 +1,19 @@ +# +# @lc app=leetcode id=169 lang=python3 +# +# [169] Majority Element +# + +# @lc code=start +class Solution: + def majorityElement(self, nums: List[int]) -> int: + # Morris voting + candidate, cnt = None, 0 + for num in nums: + if cnt == 0: + candidate = num + cnt += (1 if num == candidate else -1) + return candidate + +# @lc code=end + diff --git a/Week 02/id_718/17.letter-combinations-of-a-phone-number.py b/Week 02/id_718/17.letter-combinations-of-a-phone-number.py new file mode 100644 index 000000000..a23cbf4a5 --- /dev/null +++ b/Week 02/id_718/17.letter-combinations-of-a-phone-number.py @@ -0,0 +1,26 @@ +# +# @lc app=leetcode id=17 lang=python3 +# +# [17] Letter Combinations of a Phone Number +# + +# @lc code=start +class Solution: + def letterCombinations(self, digits: str) -> List[str]: + ret = [] + if not digits or not len(digits): + return ret + self.dfs(digits, 0, "", ret) + return ret + + def dfs(self, digits, level, out, ret): + if len(out) == len(digits): + ret.append(out[:]) + return + kvmaps = {'2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl', \ + '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz'} + for i in kvmaps[digits[level]]: + self.dfs(digits, level + 1, out + i , ret) + +# @lc code=end + diff --git a/Week 02/id_718/20.valid-parentheses.py b/Week 02/id_718/20.valid-parentheses.py new file mode 100644 index 000000000..2326d66db --- /dev/null +++ b/Week 02/id_718/20.valid-parentheses.py @@ -0,0 +1,21 @@ +# +# @lc app=leetcode id=20 lang=python3 +# +# [20] Valid Parentheses +# + +# @lc code=start +from collections import deque +class Solution: + def isValid(self, s: str) -> bool: + m = {")": "(", "]":"[", "}":"{"} + stack = deque() + for c in s: + if c not in m: + stack.append(c) + else: + if not stack or stack.pop() != m[c]: + return False + return not stack +# @lc code=end + diff --git a/Week 02/id_718/22.generate-parentheses.py b/Week 02/id_718/22.generate-parentheses.py new file mode 100644 index 000000000..ae746ff7a --- /dev/null +++ b/Week 02/id_718/22.generate-parentheses.py @@ -0,0 +1,39 @@ +# +# @lc app=leetcode id=22 lang=python3 +# +# [22] Generate Parentheses +# + +# @lc code=start +class Solution: + def generateParenthesis(self, n: int) -> List[str]: + ret = [] + self._gen(n, ret, "", 0, 0) + return ret + + def _gen(self, n, ret, result, left, right): + if left == n and right == n: + ret.append(result[:]) + return + if left < n: + self._gen(n, ret, result + "(", left + 1, right) + if right < left: + self._gen(n, ret, result + ")", left, right + 1) + +''' + class Solution: + def generateParenthesis(self, n: int) -> List[str]: + def _gen(n, out, ret, left, right): + if left == n and right == n: + ret.append(out[:]) + return + if left < n: + _gen(n, out + "(", ret, left + 1, right) + if right < left: + _gen(n, out + ")", ret, left, right + 1) + ret = [] + _gen(n, "", ret, 0, 0) + return ret +''' +# @lc code=end + diff --git a/Week 02/id_718/226.invert-binary-tree.py b/Week 02/id_718/226.invert-binary-tree.py new file mode 100644 index 000000000..89b5c771d --- /dev/null +++ b/Week 02/id_718/226.invert-binary-tree.py @@ -0,0 +1,23 @@ +# +# @lc app=leetcode id=226 lang=python3 +# +# [226] Invert Binary Tree +# + +# @lc code=start +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + def invertTree(self, root: TreeNode) -> TreeNode: + if not root: + return None + root.left, root.right = self.invertTree(root.right), self.invertTree(root.left) + return root + +# @lc code=end + diff --git a/Week 02/id_718/235.lowest-common-ancestor-of-a-binary-search-tree.py b/Week 02/id_718/235.lowest-common-ancestor-of-a-binary-search-tree.py new file mode 100644 index 000000000..09e5fe442 --- /dev/null +++ b/Week 02/id_718/235.lowest-common-ancestor-of-a-binary-search-tree.py @@ -0,0 +1,28 @@ +# +# @lc app=leetcode id=235 lang=python3 +# +# [235] Lowest Common Ancestor of a Binary Search Tree +# + +# @lc code=start +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': + # not find both, return None + if not root or root == p or root == q: + return root + l, r = self.lowestCommonAncestor(root.left, p, q), self.lowestCommonAncestor(root.right, p, q) + if l and r: + return root + elif not l: + return r + else: + return l +# @lc code=end + diff --git a/Week 02/id_718/239.sliding-window-maximum.py b/Week 02/id_718/239.sliding-window-maximum.py new file mode 100644 index 000000000..919edc729 --- /dev/null +++ b/Week 02/id_718/239.sliding-window-maximum.py @@ -0,0 +1,24 @@ +# +# @lc app=leetcode id=239 lang=python3 +# +# [239] Sliding Window Maximum +# + +# @lc code=start +from collections import deque +class Solution: + def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: + ret = [] + if not nums: return ret + window = deque() + for i, v in enumerate(nums): + if i >= k and window[0] <= (i-k): + window.popleft() + while window and nums[window[-1]] <= v: + window.pop() + window.append(i) + if i >= k - 1: + ret.append(nums[window[0]]) + return ret +# @lc code=end + diff --git a/Week 02/id_718/242.valid-anagram.py b/Week 02/id_718/242.valid-anagram.py new file mode 100644 index 000000000..853d7d752 --- /dev/null +++ b/Week 02/id_718/242.valid-anagram.py @@ -0,0 +1,31 @@ +# +# @lc app=leetcode id=242 lang=python3 +# +# [242] Valid Anagram +# + +# @lc code=start +from collections import Counter +class Solution: + def isAnagram(self, s: str, t: str) -> bool: + if len(s) != len(t): + return False + cnt = [0] * 26 + for c in s: + cnt[ord(c) - ord('a')] += 1 + for c in t: + pos = ord(c) - ord('a') + cnt[pos] -= 1 + if cnt[pos] < 0: + return False + return True + +''' +from collections import Counter +class Solution: + def isAnagram(self, s: str, t: str) -> bool: + return Counter(s) == Counter(t) +''' + +# @lc code=end + diff --git a/Week 02/id_718/297.serialize-and-deserialize-binary-tree.py b/Week 02/id_718/297.serialize-and-deserialize-binary-tree.py new file mode 100644 index 000000000..78e7375cc --- /dev/null +++ b/Week 02/id_718/297.serialize-and-deserialize-binary-tree.py @@ -0,0 +1,93 @@ +# +# @lc app=leetcode id=297 lang=python3 +# +# [297] Serialize and Deserialize Binary Tree +# + +# @lc code=start +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Codec: + + def serialize(self, root): + """Encodes a tree to a single string. + + :type root: TreeNode + :rtype: str + """ + return self._serialize(root, '') + + def _serialize(self, root, s): + if root is None: + s += 'None,' + return s + s += str(root.val) + ',' + s = self._serialize(root.left, s) + s = self._serialize(root.right, s) + return s + + def deserialize(self, data): + """Decodes your encoded data to tree. + + :type data: str + :rtype: TreeNode + """ + data_list = data.split(',') + return self._deserialize(data_list) + + def _deserialize(self, l): + if l[0] == 'None': + l.pop(0) + return None + root = TreeNode(l[0]) + l.pop(0) + root.left = self._deserialize(l) + root.right = self._deserialize(l) + return root +''' +class Codec: + + def serialize(self, root): + """Encodes a tree to a single string. + + :type root: TreeNode + :rtype: str + """ + def _serialize(root, s): + if not root: + s += 'None,' + return s + s += str(root.val) + ',' + s = _serialize(root.left, s) + s = _serialize(root.right, s) + return s + return _serialize(root, '') + + def deserialize(self, data): + """Decodes your encoded data to tree. + + :type data: str + :rtype: TreeNode + """ + def _deserialize(l): + if l[0] == 'None': + l.pop(0) + return None + root = TreeNode(l[0]) + l.pop(0) + root.left = _deserialize(l) + root.right = _deserialize(l) + return root + data_list = data.split(',') + return _deserialize(data_list) +''' +# Your Codec object will be instantiated and called as such: +# codec = Codec() +# codec.deserialize(codec.serialize(root)) +# @lc code=end + diff --git a/Week 02/id_718/49.group-anagrams.py b/Week 02/id_718/49.group-anagrams.py new file mode 100644 index 000000000..270faa2aa --- /dev/null +++ b/Week 02/id_718/49.group-anagrams.py @@ -0,0 +1,19 @@ +# +# @lc app=leetcode id=49 lang=python3 +# +# [49] Group Anagrams +# + +# @lc code=start +import collections +class Solution: + def groupAnagrams(self, strs: List[str]) -> List[List[str]]: + ret = collections.defaultdict(list) + for s in strs: + cnt = [0] * 26 + for c in s: + cnt[ord(c) - ord('a')] += 1 + ret[tuple(cnt)].append(s) + return ret.values() +# @lc code=end + diff --git a/Week 02/id_718/84.largest-rectangle-in-histogram.py b/Week 02/id_718/84.largest-rectangle-in-histogram.py new file mode 100644 index 000000000..93d973ea1 --- /dev/null +++ b/Week 02/id_718/84.largest-rectangle-in-histogram.py @@ -0,0 +1,23 @@ +# +# @lc app=leetcode id=84 lang=python3 +# +# [84] Largest Rectangle in Histogram +# + +# @lc code=start +from collections import deque +class Solution: + def largestRectangleArea(self, heights: List[int]) -> int: + stack = deque([-1]) + ret = 0 + heights.append(0) + # increasing stack + for i in range(len(heights)): + while heights[i] < heights[stack[-1]]: + h = heights[stack.pop()] + w = i - stack[-1] - 1 + ret = max(ret, h * w) + stack.append(i) + return ret +# @lc code=end + diff --git a/Week 02/id_718/94.binary-tree-inorder-traversal.py b/Week 02/id_718/94.binary-tree-inorder-traversal.py new file mode 100644 index 000000000..e81f88ec5 --- /dev/null +++ b/Week 02/id_718/94.binary-tree-inorder-traversal.py @@ -0,0 +1,30 @@ +# +# @lc app=leetcode id=94 lang=python3 +# +# [94] Binary Tree Inorder Traversal +# + +# @lc code=start +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None +from collections import deque + +class Solution: + def inorderTraversal(self, root: TreeNode) -> List[int]: + ret = [] + stack = deque() + while root or len(stack): + while root: + stack.append(root) + root = root.left + root = stack.pop() + ret.append(root.val) + root = root.right + return ret + +# @lc code=end + diff --git a/Week 02/id_718/98.validate-binary-search-tree.py b/Week 02/id_718/98.validate-binary-search-tree.py new file mode 100644 index 000000000..6f5e9ece1 --- /dev/null +++ b/Week 02/id_718/98.validate-binary-search-tree.py @@ -0,0 +1,38 @@ +# +# @lc app=leetcode id=98 lang=python3 +# +# [98] Validate Binary Search Tree +# + +# @lc code=start +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + def isValidBST(self, root: TreeNode) -> bool: + return self.helper(root, float('inf'), float('-inf')) + + def helper(self, root, maxv, minv): + if not root: + return True + if root.val >= maxv or root.val <= minv: + return False + return self.helper(root.left, root.val, minv) and self.helper(root.right, maxv, root.val) + +''' +class Solution: + def isValidBST(self, root: TreeNode) -> bool: + def _isValid(root, maxv, minv): + if not root: + return True + if root.val >= maxv or root.val <= minv: + return False + return _isValid(root.left, root.val, minv) and _isValid(root.right, maxv, root.val) + return _isValid(root, float('inf'), float('-inf')) +''' +# @lc code=end + diff --git a/Week 02/id_728/buildTree_105.java b/Week 02/id_728/buildTree_105.java new file mode 100644 index 000000000..a4b813bbc --- /dev/null +++ b/Week 02/id_728/buildTree_105.java @@ -0,0 +1,28 @@ +class Solution { + public TreeNode buildTree(int[] preorder, int[] inorder) { + return buildTreeHelper(preorder, 0, preorder.length, inorder, 0, inorder.length); + } + + private TreeNode buildTreeHelper(int[] preorder, int p_start, int p_end, int[] inorder, int i_start, int i_end) { + // preorder 为空,直接返回 null + if (p_start == p_end) { + return null; + } + int root_val = preorder[p_start]; + TreeNode root = new TreeNode(root_val); + //在中序遍历中找到根节点的位置 + int i_root_index = 0; + for (int i = i_start; i < i_end; i++) { + if (root_val == inorder[i]) { + i_root_index = i; + break; + } + } + int leftNum = i_root_index - i_start; + //递归的构造左子树 + root.left = buildTreeHelper(preorder, p_start + 1, p_start + leftNum + 1, inorder, i_start, i_root_index); + //递归的构造右子树 + root.right = buildTreeHelper(preorder, p_start + leftNum + 1, p_end, inorder, i_root_index + 1, i_end); + return root; + } +} diff --git a/Week 02/id_728/findPermuteUnique_47.java b/Week 02/id_728/findPermuteUnique_47.java new file mode 100644 index 000000000..49ffd0193 --- /dev/null +++ b/Week 02/id_728/findPermuteUnique_47.java @@ -0,0 +1,35 @@ +class Solution { + private List> res = new ArrayList<>(); + private boolean[] used; + + private void findPermuteUnique(int[] nums, int depth, Stack stack) { + if (depth == nums.length) { + res.add(new ArrayList<>(stack)); + return; + } + for (int i = 0; i < nums.length; i++) { + if (!used[i]) { + if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) { + continue; + } + used[i] = true; + stack.add(nums[i]); + findPermuteUnique(nums, depth + 1, stack); + stack.pop(); + used[i] = false; + } + } + } + + public List> permuteUnique(int[] nums) { + int len = nums.length; + if (len == 0) { + return res; + } + Arrays.sort(nums); + + used = new boolean[len]; + findPermuteUnique(nums, 0, new Stack<>()); + return res; + } +} diff --git a/Week 02/id_733/LeetCode_105_733.go b/Week 02/id_733/LeetCode_105_733.go new file mode 100644 index 000000000..3731201b0 --- /dev/null +++ b/Week 02/id_733/LeetCode_105_733.go @@ -0,0 +1,28 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func buildTree(preorder []int, inorder []int) *TreeNode { + if len(inorder) == 0 { + return nil + } + + cur := preorder[0] + mid := 0 + for mid < len(inorder) { + if inorder[mid] == cur { + break + } + + mid++ + } + + left := buildTree(preorder[1:mid+1], inorder[0:mid]) + right := buildTree(preorder[mid+1:], inorder[mid+1:]) + node := &TreeNode{Val: cur, Left: left, Right: right} + return node +} diff --git a/Week 02/id_733/LeetCode_144_733.go b/Week 02/id_733/LeetCode_144_733.go new file mode 100644 index 000000000..895376ae3 --- /dev/null +++ b/Week 02/id_733/LeetCode_144_733.go @@ -0,0 +1,80 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func preorderTraversal(root *TreeNode) []int { + res := &[]int{} + traverse(root, res) + return *res +} + +func traverse(node *TreeNode, res *[]int) { + if node == nil { + return + } + + *res = append(*res, node.Val) + traverse(node.Left, res) + traverse(node.Right, res) +} + +func preorderTraversalV2(root *TreeNode) []int { + var res []int + if root == nil { + return res + } + + s := NewStack() + cur := root + for { + res = append(res, cur.Val) + if cur.Right != nil { + s.push(cur.Right) + } + + cur = cur.Left + if cur == nil { + if n, ok := s.pop(); ok { + cur = n + } else { + break + } + } + } + + return res +} + +type stack struct { + buf []*TreeNode + idx int +} + +func NewStack() stack { + s := stack{} + s.buf = make([]*TreeNode, 16) + return s +} + +func (s *stack) push(node *TreeNode) { + if s.idx == len(s.buf) { + s.buf = append(s.buf, node) + } else { + s.buf[s.idx] = node + } + + s.idx++ +} + +func (s *stack) pop() (*TreeNode, bool) { + if s.idx == 0 { + return nil, false + } + + s.idx-- + return s.buf[s.idx], true +} diff --git a/Week 02/id_733/LeetCode_236_733.go b/Week 02/id_733/LeetCode_236_733.go new file mode 100644 index 000000000..1cea742c9 --- /dev/null +++ b/Week 02/id_733/LeetCode_236_733.go @@ -0,0 +1,30 @@ +/** + * Definition for TreeNode. + * type TreeNode struct { + * Val int + * Left *ListNode + * Right *ListNode + * } + */ +func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { + var res *TreeNode + search(root, p, q, &res) + return res +} + +func search(node, p, q *TreeNode, res **TreeNode) (bool, bool) { + if node == nil || *res != nil { + return false, false + } + + leftHasP, leftHasQ := search(node.Left, p, q, res) + rightHasP, rightHasQ := search(node.Right, p, q, res) + hasP := leftHasP || rightHasP || node.Val == p.Val + hasQ := leftHasQ || rightHasQ || node.Val == q.Val + + if *res != nil && hasP && hasQ { + *res = node + } + + return hasP, hasQ +} diff --git a/Week 02/id_733/LeetCode_242_733.go b/Week 02/id_733/LeetCode_242_733.go new file mode 100644 index 000000000..04347edb6 --- /dev/null +++ b/Week 02/id_733/LeetCode_242_733.go @@ -0,0 +1,20 @@ +func isAnagram(s string, t string) bool { + if len(s) != len(t) { + return false + } + + stats := make([]int, 26) + for _, c := range s { + stats[c-'a']++ + } + + for _, c := range t { + idx := c - 'a' + stats[idx]-- + if stats[idx] < 0 { + return false + } + } + + return true +} \ No newline at end of file diff --git a/Week 02/id_733/LeetCode_46_733.go b/Week 02/id_733/LeetCode_46_733.go new file mode 100644 index 000000000..a56ef81c2 --- /dev/null +++ b/Week 02/id_733/LeetCode_46_733.go @@ -0,0 +1,28 @@ +func permute(nums []int) [][]int { + var res [][]int + var values []int + used := make([]bool, len(nums)) + doPermute(&res, nums, used, values) + return res +} + +func doPermute(res *[][]int, nums []int, used []bool, values []int) { + if len(values) == len(nums) { + r := make([]int, len(nums)) + copy(r, values) + *res = append(*res, r) + return + } + + for i := 0; i < len(nums); i++ { + if used[i] { + continue + } + + values = append(values, nums[i]) + used[i] = true + doPermute(res, nums, used, values) + values = values[:len(values)-1] + used[i] = false + } +} diff --git a/Week 02/id_733/LeetCode_47_733.go b/Week 02/id_733/LeetCode_47_733.go new file mode 100644 index 000000000..2522591d7 --- /dev/null +++ b/Week 02/id_733/LeetCode_47_733.go @@ -0,0 +1,29 @@ +func permuteUnique(nums []int) [][]int { + var res [][]int + var values []int + sort.Ints(nums) + used := make([]bool, len(nums)) + doPermuteUnique(&res, nums, used, values) + return res +} + +func doPermuteUnique(res *[][]int, nums []int, used []bool, values []int) { + if len(values) == len(nums) { + r := make([]int, len(values)) + copy(r, values) + *res = append(*res, r) + return + } + + for i := 0; i < len(nums); i++ { + if used[i] || (i > 0 && nums[i] == nums[i-1] && !used[i-1]) { + continue + } + + values = append(values, nums[i]) + used[i] = true + doPermuteUnique(res, nums, used, values) + values = values[:len(values)-1] + used[i] = false + } +} diff --git a/Week 02/id_733/LeetCode_49_733.go b/Week 02/id_733/LeetCode_49_733.go new file mode 100644 index 000000000..7b03b755e --- /dev/null +++ b/Week 02/id_733/LeetCode_49_733.go @@ -0,0 +1,23 @@ + +type byteSlice []byte + +func (b byteSlice) Len() int { return len(b) } +func (b byteSlice) Less(i, j int) bool { return b[i] < b[j] } +func (b byteSlice) Swap(i, j int) { b[i], b[j] = b[j], b[i] } + +func GroupAnagrams(strs []string) [][]string { + group := make(map[string][]string) + for _, str := range strs { + b := byteSlice(str) + sort.Sort(b) + key := string(b) + group[key] = append(group[key], str) + } + + var res [][]string + for _, v := range group { + res = append(res, v) + } + + return res +} diff --git a/Week 02/id_733/LeetCode_77_733.go b/Week 02/id_733/LeetCode_77_733.go new file mode 100644 index 000000000..c3af3ea74 --- /dev/null +++ b/Week 02/id_733/LeetCode_77_733.go @@ -0,0 +1,42 @@ +func combine(n int, k int) [][]int { + var res [][]int + doCombine(n, k, make([]int, 0, k), &res) + return res +} + +func doCombine(n, k int, values []int, res *[][]int) { + if k == 0 { + r := make([]int, len(values)) + copy(r, values) + *res = append(*res, r) + return + } + + for n > 0 { + values = append(values, n) + n-- + doCombine(n, k - 1, values, res) + values = values[:len(values)-1] + } +} + +func combineV2(n int, k int) [][]int { + var res [][]int + values := make([]int, k) + i := 0 + for i >= 0 { + values[i]++ + if values[i] > n { + i-- + } else if i == k-1 { + r := make([]int, k) + copy(r, values) + res = append(res, r) + } else { + i++ + values[i] = values[i-1] + } + } + + return res +} diff --git a/Week 02/id_733/LeetCode_94_733.go b/Week 02/id_733/LeetCode_94_733.go new file mode 100644 index 000000000..be48ee3ac --- /dev/null +++ b/Week 02/id_733/LeetCode_94_733.go @@ -0,0 +1,99 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func inorderTraversal(root *TreeNode) []int { + res := &[]int{} + traverse(root, res) + return *res +} + +func traverse(node *TreeNode, res *[]int) { + if node == nil { + return + } + + traverse(node.Left, res) + *res = append(*res, node.Val) + traverse(node.Right, res) +} + +func inorderTraversalV2(root *TreeNode) []int { + var res []int + s := NewStack() + cur := root + for cur != nil || !s.isEmpty() { + for cur != nil { + s.push(cur) + cur = cur.Left + } + + cur = s.pop() + res = append(res, cur.Val) + cur = cur.Right + } + + return res +} + +func inorderTraversalV3(root *TreeNode) []int { + var res []int + cur := root + for cur != nil { + if cur.Left != nil { + prev := cur.Left + for prev.Right != nil { + prev = prev.Right + } + + prev.Right = cur + temp := cur.Left + cur.Left = nil + cur = temp + continue + } + + res = append(res, cur.Val) + cur = cur.Right + } + + return res +} + +type stack struct { + buf []*TreeNode + idx int +} + +func NewStack() stack { + s := stack{} + s.buf = make([]*TreeNode, 16) + return s +} + +func (s *stack) push(node *TreeNode) { + if s.idx == len(s.buf) { + s.buf = append(s.buf, node) + } else { + s.buf[s.idx] = node + } + + s.idx++ +} + +func (s *stack) isEmpty() bool { + return s.idx == 0 +} + +func (s *stack) pop() *TreeNode { + if s.isEmpty() { + return nil + } + + s.idx-- + return s.buf[s.idx] +} diff --git a/Week 02/id_738/LeetCode_105_738.py b/Week 02/id_738/LeetCode_105_738.py new file mode 100644 index 000000000..76ae0f8e1 --- /dev/null +++ b/Week 02/id_738/LeetCode_105_738.py @@ -0,0 +1,40 @@ +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def buildTree(self, preorder, inorder): + """ + 从前序与中序遍历序列构造二叉树: https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ + + :type preorder: List[int] + :type inorder: List[int] + :rtype: TreeNode + """ + + #解法步骤: + #1. 前序遍历数组的每个节点都可以将中序遍历数组的节点分为以该节点的左右两个子树 + #2. 取出前序数组的第一个节点构建节点,然后在中序数组里找到该节点的位置,切分成左右两个子数组a,b + #3. 继续取出前序数组的节点,并在a中按照12构建子树,直到a的元素全部构建完成,此时前序数组取出的节点才会在b数组里面,同样在b构建子树,直到b的元素都构建完。 + def bulid(left, right): + if left > right: + return None + print(self.pre_index) + root_val = preorder[self.pre_index] + root = TreeNode(root_val) + index = inorder.index(root_val) + self.pre_index += 1 + root.left = bulid(left, index - 1) + root.right = bulid(index + 1, right) + return root + + + self.pre_index = 0 + return bulid(0, len(inorder) - 1) + + + + \ No newline at end of file diff --git a/Week 02/id_738/LeetCode_144_738.py b/Week 02/id_738/LeetCode_144_738.py new file mode 100644 index 000000000..8ad55e61e --- /dev/null +++ b/Week 02/id_738/LeetCode_144_738.py @@ -0,0 +1,72 @@ +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def __init__(self): + self.result = [] + + def preorderTraversal(self, root): + """ + :type root: TreeNode + :rtype: List[int] + """ + #递归 + # if root: + # self.result.append(root.val) + # self.preorderTraversal(root.left) + # self.preorderTraversal(root.right) + # return self.result + + #迭代1 + # stack = [] + # e = root + # while stack or e: + # if e: + # stack.append(e) + # self.result.append(e.val) + # while e.left: + # e = e.left + # stack.append(e) + # self.result.append(e.val) + # e = stack.pop() + # e = e.right + # return self.result + + #迭代2 + # stack = [] + # e = root + # while stack or e: + # while e: + # self.result.append(e.val) + # stack.append(e) + # e = e.left + # e = stack.pop() + # e = e.right + # return self.result + + #迭代3 + if not root: return [] + stack = [root] + while stack: + e = stack.pop() + self.result.append(e.val) + if e.right: + stack.append(e.right) + if e.left: + stack.append(e.left) + return self.result + + + + + + + + + + + diff --git a/Week 02/id_738/LeetCode_1_738.py b/Week 02/id_738/LeetCode_1_738.py new file mode 100644 index 000000000..e3fed0c1f --- /dev/null +++ b/Week 02/id_738/LeetCode_1_738.py @@ -0,0 +1,34 @@ +class Solution(object): + def twoSum(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: List[int] + """ + #暴力求解两重循环 + # for i in range(len(nums) - 1): + # k = target - nums[i] + # for j in range(i + 1, len(nums)): + # if nums[j] == k: return [i, j] + + #放入Hash表中,然后查找的时候就是O(1)复杂度,加上自身N个元素就是O(n)复杂度 + #python中的 map就是用hash表实现的 + reuslt = [] + d = {} + for i in range(len(nums)): + a = target - nums[i] + if a in d: + return [i, d[a]] + d[nums[i]] = i + return result + + #上面的同样方法的另一种编码方法 + # result = [] + # dictionary = {} + # for i in range(len(nums)): + # a = target - nums[i] + # if nums[i] in dictionary: + # result = [i, dictionary[nums[i]]] + # dictionary[a] = i + # return result + \ No newline at end of file diff --git a/Week 02/id_738/LeetCode_236_738.py b/Week 02/id_738/LeetCode_236_738.py new file mode 100644 index 000000000..11491ecc6 --- /dev/null +++ b/Week 02/id_738/LeetCode_236_738.py @@ -0,0 +1,38 @@ +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def lowestCommonAncestor(self, root, p, q): + """ + 二叉树的最近公共祖先:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/ + + :type root: TreeNode + :type p: TreeNode + :type q: TreeNode + :rtype: TreeNode + """ + #1. 如果当前节点为p,q中的一个,那么只要该节点的左右子树中包含另外一个节点,则该节点即是公共祖先 + #2. 如果当前节点不为p,q中的一个,那么该节点的左子树 和 右子树 中分别包含q和p,则该节点是公共祖先 + #3. 否则,当前该节点不是公共祖先 + #4. 所以转化为:在递归的情况下(递归是从底部往根部回溯的机制),判断当前节点,左子树,右子树中的其中2个是否包含p和q,如果是,则当前节点为公共祖先 + self.result = None + def find(cur, p, q): + if not cur or self.result: + #如果self.result已经被赋值,表示已经找到最近公共祖先了,那么cur节点上层的节点,必然是本身节点不为pq,并且左右子树只有一个子树包含pq的情况,所以会一直跳过去。。 + return False + cur_is = cur.val == p.val or cur.val == q.val + left_is = find(cur.left, p, q) + right_is = find(cur.right, p, q) + if cur_is + left_is + right_is >= 2: + #这里如此处理不需要担心后面回溯会覆盖self.result是因为: + self.result = cur + return cur_is or left_is or right_is + + find(root, p, q) + return self.result + + \ No newline at end of file diff --git a/Week 02/id_738/LeetCode_242_738.py b/Week 02/id_738/LeetCode_242_738.py new file mode 100644 index 000000000..0449d551e --- /dev/null +++ b/Week 02/id_738/LeetCode_242_738.py @@ -0,0 +1,30 @@ +class Solution(object): + def isAnagram(self, s, t): + """ + :type s: str + :type t: str + :rtype: bool + """ + #判断t和t中的单词分类及其数量是否相等 + dict_d = {} + for i in s: + dict_d[i] = dict_d.get(i, 0) + 1 + dict_t = {} + for i in t: + dict_t[i] = dict_t.get(i, 0) + 1 + return dict_d == dict_t + + #利用python api: + #先去重,再比较各个字母在s和t中的数量 + # if len(s) != len(t): + # return False + # s_single = set(s) + # for i in s_single: + # if s.count(i) != t.count(i): + # return False + # return True + + + + + \ No newline at end of file diff --git a/Week 02/id_738/LeetCode_429_738.py b/Week 02/id_738/LeetCode_429_738.py new file mode 100644 index 000000000..1311f4800 --- /dev/null +++ b/Week 02/id_738/LeetCode_429_738.py @@ -0,0 +1,31 @@ +""" +# Definition for a Node. +class Node(object): + def __init__(self, val, children): + self.val = val + self.children = children +""" +class Solution(object): + def levelOrder(self, root): + """ + N叉树的层序遍历:https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/ + + :type root: Node + :rtype: List[List[int]] + """ + if not root: + return [] + queue = [root] + result = [] + while queue: + t = [] + children = [] + for i in queue: + t.append(i.val) + children += i.children + result.append(t) + queue = children + return result + + + \ No newline at end of file diff --git a/Week 02/id_738/LeetCode_46_738.py b/Week 02/id_738/LeetCode_46_738.py new file mode 100644 index 000000000..fbf5bc6ad --- /dev/null +++ b/Week 02/id_738/LeetCode_46_738.py @@ -0,0 +1,38 @@ +class Solution(object): + def permute(self, nums): + """ + 全排列:https://leetcode-cn.com/problems/permutations/ + + :type nums: List[int] + :rtype: List[List[int]] + """ + #递归+回溯: + #每次加入到结果集中,只要不是在结果集中出现的数字,都可以加入 + def bulid(l = []): + if len(l) == size: + self.result.append(l[:]) + return + for num in nums: + if not num in l: + l.append(num) + bulid(l) + l.pop() + + self.result = [] + size = len(nums) + bulid() + return self.result + + #写法2 + def dfs(l, val): + l.append(val) + if len(l) == len(nums): + self.result.append(l) + return + for num in nums: + if num not in l: + dfs(l[:], num) + self.result = [] + for num in nums: + dfs([], num) + return self.result \ No newline at end of file diff --git a/Week 02/id_738/LeetCode_47_738.py b/Week 02/id_738/LeetCode_47_738.py new file mode 100644 index 000000000..88016c689 --- /dev/null +++ b/Week 02/id_738/LeetCode_47_738.py @@ -0,0 +1,59 @@ +class Solution(object): + def permuteUnique(self, nums): + """ + 全排列2:https://leetcode-cn.com/problems/permutations-ii/ + + :type nums: List[int] + :rtype: List[List[int]] + """ + #递归+回溯: + #每次加入到结果集中,只要不是在结果集中出现的数字,都可以加入 + #过程涉及到nums的切片操作,最后设计到判断去重,感觉效率很低:执行时间1000+Ms,被 80%同行打败。。。 + #优化:剪枝,如果本层再次出现以及处理过的数字,那么不用重复执行:执行时间48Ms,打败了80+%同行。。。 + #剪枝后留下的问题: + # 1. 数组的切片操作,要多复制一份数据到空间,对时间复杂度也有影响 + # 2. 递归剪枝中里面每次都要判断当前处理的数字是否在Nums之前处理过的数字中,感觉很浪费时间 + +# def bulid(nums = nums, l = []): +# if len(l) == size: +# self.result.append(l[:]) +# return +# for i in range(len(nums)): +# #剪枝,如果nums[i]没有出现过 +# if nums[i] not in nums[:i]: +# l.append(nums[i]) +# bulid(nums[0:i] + nums[i+1:], l) +# l.pop() + +# self.result = [] +# size = len(nums) +# bulid() +# return self.result + + #针对上面“剪枝后留下的问题”, + # 1.设法不要将数组进行切片操作, + # 2.设法在递归过程中判断去重,但是不需要重复扫描之前处理过的数组 + # 优化后的解法如下: + # 结果:执行时间提高到40-44ms,打败了 90+%的同行。。。 + + def bulid(nums = nums, l = []): + if len(l) == size: + self.result.append(l[:]) + return + for i in range(len(nums)): + #剪枝,如果nums[i]没有出现过 + #这里Nums是排序过的,解决在递归过程中判断去重,需要重复扫描之前处理过的数组的问题 + if i == 0 or nums[i] != nums[i - 1]: + #通过对nums的pop和insert操作,去除已经处理过的数字,还原现场 + tmp = nums.pop(i) + l.append(tmp) + bulid(nums, l) + nums.insert(i, tmp) + l.pop() + + self.result = [] + size = len(nums) + #排序,解决在递归过程中判断去重,需要重复扫描之前处理过的数组的问题 + nums.sort() + bulid() + return self.result \ No newline at end of file diff --git a/Week 02/id_738/LeetCode_49_738.py b/Week 02/id_738/LeetCode_49_738.py new file mode 100644 index 000000000..01637002f --- /dev/null +++ b/Week 02/id_738/LeetCode_49_738.py @@ -0,0 +1,39 @@ +class Solution(object): + def groupAnagrams(self, strs): + """ + :type strs: List[str] + :rtype: List[List[str]] + """ + #按字符串字母排序分组 + # result = [] + # d = collections.defaultdict(list) + # for i in strs: + # d[tuple(sorted(i))].append(i) + # for key,value in d.items(): + # result.append(value) + # return result + + #字符串每个字符为a-z26个字母 + #所以可以预设一个长为26的数组 + #每次字母出现在字符串s,就在数组的对应位置+1 + #得出的这个数组可以作为map的key,map的value为数组,且将s加入到数组 + result = [] + d = collections.defaultdict(list) + for s in strs: + r = [0] * 26 + for c in s: + r[ord(c) - ord('a')] += 1 + d[tuple(r)].append(s) + return d.values() + + + + + + + + + + + + \ No newline at end of file diff --git a/Week 02/id_738/LeetCode_589_738.py b/Week 02/id_738/LeetCode_589_738.py new file mode 100644 index 000000000..3aa042b3f --- /dev/null +++ b/Week 02/id_738/LeetCode_589_738.py @@ -0,0 +1,47 @@ +""" +# Definition for a Node. +class Node(object): + def __init__(self, val, children): + self.val = val + self.children = children +""" + +class Solution(object): + def __init__(self): + self.result = [] + + def preorder(self, root): + """ + N叉树的前序遍历:https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/submissions/ + + :type root: Node + :rtype: List[int] + """ + #递归 + # if root: + # self.result.append(root.val) + # for node in root.children: + # self.preorder(node) + # return self.result + + #迭代 + if not root: + return [] + stack = [root] + while stack: + e = stack.pop() + self.result.append(e.val) + #由于栈先入后出,所以这里必须从最后一个子节点开始入栈,才能保证子节点从0到N扫描 + stack.extend(e.children[::-1]) + return self.result + + + + + + + + + + + \ No newline at end of file diff --git a/Week 02/id_738/LeetCode_590_738.py b/Week 02/id_738/LeetCode_590_738.py new file mode 100644 index 000000000..5b48a7a0c --- /dev/null +++ b/Week 02/id_738/LeetCode_590_738.py @@ -0,0 +1,60 @@ +""" +# Definition for a Node. +class Node(object): + def __init__(self, val, children): + self.val = val + self.children = children +""" +class Solution(object): + def __init__(self): + self.result = [] + + def postorder(self, root): + """ + N叉树的后序遍历:https://leetcode-cn.com/problems/n-ary-tree-postorder-traversal/ + + :type root: Node + :rtype: List[int] + """ + + #递归 + # if root: + # for node in root.children: + # self.postorder(node) + # self.result.append(root.val) + # return self.result + + # 迭代 + if not root: + return [] + stack = [root] + while stack: + e = stack.pop() + self.result.append(e.val) + if e.children: + stack.extend(e.children) + return self.result[::-1] + + # 迭代2 + # if not root: return [] + # result = [] + # stack = [root] + # visited = set([]) + # while stack: + # e = stack[-1] + # if e not in visited: + # visited.add(e) + # for i in range(len(e.children))[::-1]: + # stack.append(e.children[i]) + # else: + # e = stack.pop() + # result.append(e.val) + # return result + + + + + + + + \ No newline at end of file diff --git a/Week 02/id_738/LeetCode_77_738.py b/Week 02/id_738/LeetCode_77_738.py new file mode 100644 index 000000000..942b879e3 --- /dev/null +++ b/Week 02/id_738/LeetCode_77_738.py @@ -0,0 +1,58 @@ +class Solution(object): + def combine(self, n, k): + """ + 组合:https://leetcode-cn.com/problems/combinations/submissions/ + + :type n: int + :type k: int + :rtype: List[List[int]] + """ + #初解:这样写感觉浪费了太多空间还有复制数组的操作 +# def recombine(n_list, k, s): +# if k < 1: +# self.result.append(s[: -1].split(',')) +# return +# tmp_s = s +# for index in range(len(n_list)): +# s = tmp_s +# s += str(n_list[index]) + ',' +# recombine(n_list[index + 1:], k - 1, s) + +# if n < k: +# return [] +# if n == k: +# return [[i for i in range(1, n + 1)]] +# self.result = [] +# n_list = [i for i in range(1, n + 1)] +# recombine(n_list, k, '') +# return self.result + + #优化初解: + #1. 将recombine的s参数改成List, 在recombine后,对进行pop操作还原现场,进入下个循环 + #2. recombine的n_list参数原来为list,每次进入recombine都要复制多一份list,改成头个元素值就可以不用复制数组了 + #3. recombine的参数k只是用来作为终结者条件,而k是全局变量且不需要改变其值,可以去掉参数k,并结合2的优化,修改终结者判断条件 + def recombine(first = 1, s = []): + if len(s) == k: + #这里要复制出来,否则s的pop操作会清空s + self.result.append(s[:]) + return + for i in range(first, n + 1): + s.append(i) + recombine(i + 1, s) + s.pop() + + if n < k: + return [] + if n == k: + return [[i for i in range(1, n + 1)]] + self.result = [] + recombine() + return self.result + + + + + + + + \ No newline at end of file diff --git a/Week 02/id_738/LeetCode_94_738.py b/Week 02/id_738/LeetCode_94_738.py new file mode 100644 index 000000000..2fde71704 --- /dev/null +++ b/Week 02/id_738/LeetCode_94_738.py @@ -0,0 +1,57 @@ +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def __init__(self): + self.result = [] + + def inorderTraversal(self, root): + """ + 中序遍历二叉树:https://leetcode-cn.com/problems/binary-tree-inorder-traversal/ + + :type root: TreeNode + :rtype: List[int] + """ + #递归 + #时间复杂度O(n) + # if root: + # self.inorderTraversal(root.left) + # self.result.append(root.val) + # self.inorderTraversal(root.right) + # return self.result + + #迭代 + #时间复杂度O(n) + stack = [] + e = root + while stack or e: + while e: + stack.append(e) + e = e.left + ele = stack.pop() + self.result.append(ele.val) + e = ele.right + return self.result + + #迭代2:使用visited来存储已经访问过left的节点,防止无限循环访问某个节点的left + if not root: return [] + stack = [root] + result = [] + visited = set() + while stack: + if stack[-1] not in visited: + e = stack[-1] + visited.add(e) + while e.left: + e = e.left + stack.append(e) + visited.add(e) + top = stack.pop() + result.append(top.val) + if top.right: + stack.append(top.right) + return result \ No newline at end of file diff --git a/Week 02/id_738/NOTE.md b/Week 02/id_738/NOTE.md index a6321d6e2..20842d25f 100644 --- a/Week 02/id_738/NOTE.md +++ b/Week 02/id_738/NOTE.md @@ -1,4 +1,384 @@ -# NOTE +# 第二周总结 + Java HashMap源码分析 +## 第二周知识点总结 +### 树、二叉树 +- 链表这种数据结构,在查找的时候时间复杂度太高 + - 解决办法:升维。将链表描述成树/图 +- 树的遍历 + - 前序 + - 中序 + - 后序 +- 二叉搜索树 + - 也叫有序二叉树、排序二叉树,是指一棵空树或者具有下列性质的二叉树: + - 左子树上所有节点都小于它的根节点的值 + - 右子树上所有节点都大于他的根节点的值 + - 以此类推:左右子树也分别是为二叉查找树。 + - 中序遍历:升序排列 + - 查询 / 插入 / 删除:O(logn) + - 常见的操作:【[二叉搜索树操作Demo](https://visualgo.net/zh/bst)】 + - 搜索:从根部开始查找左右子树,循环到叶子节点。 + - 插入:往下搜索待插入节点,如果到了某个节点,应该往左/右搜索,但是它并没有左/右节点,那么表明,待插入节点应该插入到该节点的左/右子节点上 + - 删除: + - 叶子节点:直接删除 + - 非叶子节点:取删除节点的(大于 / 小于)紧邻该节点的节点(一般取小于的那个节点),直接替换上去即可。如果替换上去的节点也不是叶子节点,那么该节点的子树直接移到该节点的父节点下。 + +### 递归 +- 树的面试题解法一般都是递归 + - 递归就像盗梦空间的情景,每层下探,每层都有终结者,每层执行完任务都会回到上一层 + - Python的递归模板(重要) +```python +def recursion(level, parm1, parm2,….): + #recursion teminator:递归终结者 + if level > MAX_LEVEL: + process_result + return + + # process logic in current level:处理当前逻辑 + process(level, data…) + + # drill down:下探到下一层 + self.recursion(level + 1, p1, …) + # reverse the current level status if needed:清理本层的东西(非必须),可能会清理环境变量等 + +``` +- 解递归要点: + - 一定不要人肉进行递归(最大误区) + - 找最近最简的方法,将其拆解成可重复解决的问题(寻找最近重复子问题) + - 数学归纳法思维 + +### 分治 +- 本质上就是递归,可以认为是递归的一个子分类。是一个特殊的递归 +- 分治方法: + - 找重复性 + - 将问题划分为子问题 + - 分治代码模板(和泛型递归差不多,只是最后多了一个组合操作) +```python +def divide_conquer(problem, param1, param2, ...): + # 递归终结者 + if problem is None: + print_result + return + # 处理当前逻辑:如何将问题切分成子问题 + data = prepare_data(problem) + subproblems = split_problem(problem, data) + # 调用本身,下探到下一层,解决更细节的子问题 + subresult1 = self.divide_conquer(subproblems[0], p1, ...) + subresult2 = self.divide_conquer(subproblems[1], p1, ...) + subresult3 = self.divide_conquer(subproblems[2], p1, ...) + … + # 组装合并结果 + result = process_result(subresult1, subresult2, subresult3, …) + # 清理一些现场的状态 + +``` + +### 回溯 +- 百度解释 +回溯法采用试错的思想,它尝试分布去解决一个问题。在分布解决问题的过程中,当它通过尝试发现现有的分步答案不能得到有效的正确解答的时候,它将取消上一步甚至是上几部的计算,再通过其他的可能分布解答再次尝试寻找问题的答案。 +- 本质 + - 不断的在每一层去试,试出最终的合格方案 + - 有一种归去来兮的感觉 +- 典型用法 + - N皇后 + - 数独问题 + +## 第二周学习感想 +这一周相比上一周,课程多了一点,课程里面的数据结构和算法也更加复杂了一些。 + +之前在课程之前,想起递归回溯分治,都感觉比较模糊,虽然有些题目经过慢慢调试还是能用递归实现,但是总是少了一点归纳的解题思路。 + +学习了本周课程之后,并且课后经过覃超老师布置的作业习题的大量练习,反复练习,我终于对这方面有了更加自信的把握。 + +1. 左右括号,电话号码数字键的问题,排列,组合等问题,其实都是一种问题,就是往格子里面选择填入一些可以填入的元素,然后将问题分解成各个格子的子问题,通过递归回溯解决即可 +2. 类似爬楼梯,斐波那契数列,抛硬币问题等类似问题,其实就是一种问题,就是通过数学归纳思想,找出当前要求解的项和之前求解过的项的内在联系,建立递推式,然后利用递归进行求解即可 +3. 树的各种操作遍历的练习,对递归的解题功力有很大提升。 +4. 用迭代法去解递归的问题,其实就是画图,然后出栈入栈的一些过程实现而已 +5. 解题时要有一种自顶向下的思想,不能纠结于细节,善于把问题分解成子问题,可重复子问题进行递归求解 +6. 递归、分治的代码模板,必须牢记,在解题的时候,先写下来,然后一个一个填充,很好用 + +## Java HashMap源码解析(JDK1.8) +HashMap是Java的一个重要类,使用频率非常高,通过hash散列,快速定位到查找的元素位置,通过链表解决hash碰撞的问题。 + +下面通过源码,分析HashMap的容量,元素结构,hash散列,hash碰撞链表处理等细节。 + +- HashMap的容量 +1. 哈希表的最大容量:2^30 + +```java +static final int MAXIMUM_CAPACITY = 1 << 30; +``` + +2. 哈希表的默认容量:2^4 = 16 + +```java +static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; +``` + +3. 默认的加载因子:0.75 + +```java +static final float DEFAULT_LOAD_FACTOR = 0.75f; +``` + +默认加载因子是用在resize的时候,用于计算下一次扩容的HashMap的容量(最大值不超过MAXIMUM_CAPACITY),计算方法: + +4. 如果我们创建HashMap并传入容量,那么初始的HashMap容量是多少? + +```java +public HashMap(int initialCapacity, float loadFactor) { + if (initialCapacity < 0) + throw new IllegalArgumentException("Illegal initial capacity: " + + initialCapacity); + if (initialCapacity > MAXIMUM_CAPACITY) + initialCapacity = MAXIMUM_CAPACITY; + if (loadFactor <= 0 || Float.isNaN(loadFactor)) + throw new IllegalArgumentException("Illegal load factor: " + + loadFactor); + this.loadFactor = loadFactor; + this.threshold = tableSizeFor(initialCapacity); + } +``` + +通过上面的源码,我们可以看到,我们即使传入initialCapacity,最后HashMap的容量也不一定是我们传入的值。此时HashMap的值通过tableSizeFor方法计算出来: + +```java +static final int tableSizeFor(int cap) { + int n = cap - 1; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; + } +``` + +可以看到,`tableSizeFor通过我们传入的初始容量值,为我们重新计算出一个"符合"HashMap容量规则并且最接近我们传入的值的初始值,作为HashMap的初始容量`。 + +上面代码的算法解释: + +``` +先来分析有关n位操作部分:先来假设n的二进制为01xxx...xxx。接着 + +对n右移1位:001xx...xxx,再位或:011xx...xxx + +对n右移2为:00011...xxx,再位或:01111...xxx + +此时前面已经有四个1了,再右移4位且位或可得8个1 + +同理,有8个1,右移8位肯定会让后八位也为1。 + +综上可得,该算法让最高位的1后面的位全变为1。 + +最后再让结果n+1,即得到了2的整数次幂的值了。 + +现在回来看看第一条语句: + +int n = cap - 1; + +让cap-1再赋值给n的目的是另找到的目标值大于或等于原值。例如二进制1000,十进制数值为8。如果不对它减1而直接操作,将得到答案10000,即16。显然不是结果。减1后二进制为111,再进行操作则会得到原来的数值1000,即8。 +``` + +5. HashMap的散列计算:通过4,我们很有理由想到,为什么HashMap要tableSizeFor我们指定的初始容量,且容量最终为2的幂次方呢? + +我们跟踪put代码,到下面这个方法: + +```java +final V putVal(int hash, K key, V value, boolean onlyIfAbsent, + boolean evict) +``` + +从方法体里面,我们会发现这一句:里面的tab + +```java +if ((tab = table) == null || (n = tab.length) == 0) + n = (tab = resize()).length; +if ((p = tab[i = (n - 1) & hash]) == null) + tab[i] = newNode(hash, key, value, null); +``` + +可以发现,对新加的元素,计算其hash值在table的位置是通过:`(n - 1) & hash`来完成的,这个到底是什么东西?和我们平时的数学运算有什么联系? + +通过查阅资料,发现了个快速幂取模算法:该算法指出:`对2的倍数取模,只要将数与2的倍数-1做按位运算即可!` + +而平时我们数学上的mod计算方法,效率太低了。而位运算的效率是超高的。 + +所以,HashMap在这里做了个"取巧"的优化:`将容量设置为2的幂次,这样就可以用位运算来代替mod运算,从而快速计算出待插入元素的key的hash值在table中的位置` + +6. HashMap中元素的存储结构 + +从下面源码定义可知,里面每个元素有四个值: + +元素的key的hash值、元素的key值、元素的值value、下个元素的指针 + +```java + static class Node implements Map.Entry { + final int hash; + final K key; + V value; + Node next; + + Node(int hash, K key, V value, Node next) { + this.hash = hash; + this.key = key; + this.value = value; + this.next = next; + } + //.......... + } +``` + +7. HashMap如何找到我们指定的元素? + +代码定位到下面代码: + +代码很清晰,通过取模运算定位到table的位置,如果table所在的位置里面的第一个元素first不是TreeNode类型(那么就是Node类型),那么简单通过判断 hash和key都是否相等即可查找到我们指定的元素。 + +```java + final Node getNode(int hash, Object key) { + Node[] tab; Node first, e; int n; K k; + if ((tab = table) != null && (n = tab.length) > 0 && + (first = tab[(n - 1) & hash]) != null) { + if (first.hash == hash && // always check first node + ((k = first.key) == key || (key != null && key.equals(k)))) + return first; + if ((e = first.next) != null) { + if (first instanceof TreeNode) + return ((TreeNode)first).getTreeNode(hash, key); + do { + if (e.hash == hash && + ((k = e.key) == key || (key != null && key.equals(k)))) + return e; + } while ((e = e.next) != null); + } + } + return null; + } +``` + +8. 7中出现TreeNode?难道HashMap里面的元素还要另外一种形态? + +直接定位TreeNode源码:代码很长....下面取出关键的元素示例 + +```java + static final class TreeNode extends LinkedHashMap.Entry { + TreeNode parent; // red-black tree links + TreeNode left; + TreeNode right; + TreeNode prev; // needed to unlink next upon deletion + boolean red; + TreeNode(int hash, K key, V val, Node next) { + super(hash, key, val, next); + } + + /** + * Returns root of tree containing this node. + */ + final TreeNode root() { + for (TreeNode r = this, p;;) { + if ((p = r.parent) == null) + return r; + r = p; + } + } + //.............. + } +``` + +通过上面代码,无疑,这是一个二叉树节点的典型结构,而且是红黑树。 + +问题来了:为啥会出现这个红黑树的数据结构?用来干嘛? + +我们通过定位代码TreeNode,发现了putVal方法里面有一段代码: + +```java +for (int binCount = 0; ; ++binCount) { + if ((e = p.next) == null) { + p.next = newNode(hash, key, value, null); + if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st + treeifyBin(tab, hash); + break; + } + if (e.hash == hash && + ((k = e.key) == key || (key != null && key.equals(k)))) + break; + p = e; +} +``` + +可以看到,当判断链表中的节点个数>=TREEIFY_THRESHOLD,也就是8,会将链表中的节点转化为树结构(红黑树化)! + +的确,如果链表节点太多,查找的速度会很慢,而树化后,查找的时间复杂度大大降低,这也是HashMap存储的一个优化。 + +9. 最后,让我们再看看最后一个问题:HashMap在resize的时候,是如何对链表进行的? + +resize前面的容量判断等操作,前面已经解析过了,这里我们着重看resize是如何将旧的HashMap的元素移到新的HashMap里面的。 + +源码片段: + +```java +table = newTab; +if (oldTab != null) { + for (int j = 0; j < oldCap; ++j) { + Node e; + if ((e = oldTab[j]) != null) { + oldTab[j] = null; + if (e.next == null) + newTab[e.hash & (newCap - 1)] = e; + else if (e instanceof TreeNode) + ((TreeNode)e).split(this, newTab, j, oldCap); + else { // preserve order + Node loHead = null, loTail = null; + Node hiHead = null, hiTail = null; + Node next; + do { + next = e.next; + if ((e.hash & oldCap) == 0) { + if (loTail == null) + loHead = e; + else + loTail.next = e; + loTail = e; + } + else { + if (hiTail == null) + hiHead = e; + else + hiTail.next = e; + hiTail = e; + } + } while ((e = next) != null); + if (loTail != null) { + loTail.next = null; + newTab[j] = loHead; + } + if (hiTail != null) { + hiTail.next = null; + newTab[j + oldCap] = hiHead; + } + } + } + } +} +``` + +树化移动这里不做解析,重点看链表的移动过程,问题转化为:如何复制链表到新链表中。 + +- `e.hash & oldCap`是什么? +由于oldCap必然是2的幂次,所以e.hash & oldCap的结果只有两种:0或者oldCap + +- 所以,对于原来HashMap的桶里面的元素,一部分保留在原来的桶里面,一部分会被移动到下个位移为oldCap新的桶里面。这样就不需要针对新的容量的HashMap进行Hash计算。 + +- 为什么这样做就可以? +1. 原来计算HashMap的桶的位置的计算方法:(e.hash & n - 1) +2. 扩容后,将(e.hash & oldCap)值为oldCap的元素移到+oldCap的位置 +3. newCap和oldCap都为2的幂次,也就是只有一位为1,其他位都为0 +4. 所以,e.hash在老的HashMap(称为oldCap)的位置由oldCap的1所在位的右边位数决定。而e.hash在新的HashMap(称为newCap)的位置,按照(e.hash & n - 1)算,也是由newCap的1所在位的右边位数决定。由于oldCap的1的位置为newCap的1的位置的右边一位,所以newCap和oldCap的hash散列结果,区别只在于oldCap的1的位置。 +5. 由4的分析,e.hash & oldCap为0表示e.hash在oldCap的1的位置的位的值为0,e.hash & oldCap为1表示e.hash在oldCap的1的位置的位的值为1。如果e.hash在oldCap的1的位置的位的值为0,那么在newCap用(e.hash & n - 1)计算,该位也是0,位置和原来一样。如果e.hash在oldCap的1的位置的位的值为1,那么在在newCap用(e.hash & n - 1)计算,该位就会为1,那么数值就会比之前多了一个oldCap。 +6. 由5的分析,可以看出,这样做是可以的! + +所以,HashMap为什么容量为2的幂次很关键。能够提高很多效能。 + + + - diff --git "a/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_104_738.py" "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_104_738.py" new file mode 100644 index 000000000..379e7b8f0 --- /dev/null +++ "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_104_738.py" @@ -0,0 +1,73 @@ +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def maxDepth(self, root): + """ + 二叉树的最大深度:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/ + + :type root: TreeNode + :rtype: int + """ + # 递归 + # def depth(cur): + # if not cur: + # return 0 + # left_level = depth(cur.left) + # right_level = depth(cur.right) + # return max(left_level, right_level) + 1 + # return depth(root) + + #迭代:利用一个stack和一个数组,stack维护二叉树下一层的节点,list维护当前层的节点 + #操作:每到一层,清空stack的数据到list中,并循环list的元素,探索其直接子节点,全部入stack + #如果操作后stack不为空,则表示下一层还有节点,level+=1。否则结束循环,返回level + # if not root: + # return 0 + # stack = [root] + # cur_level_nodes = [] + # level = 1 + # while stack: + # while stack: + # cur_level_nodes.append(stack.pop()) + # for node in cur_level_nodes: + # if node.left: + # stack.append(node.left) + # if node.right: + # stack.append(node.right) + # if stack: + # level += 1 + # cur_level_nodes = [] + # return level + + #迭代2:迭代1维护了2个列表。如果用一个列表,那么列表存储除了节点还要存储节点所在的层数。比如root就存为(root, 1) + if not root: + return 0 + stack = [(root, 1)] + level = 1 + while stack: + cur, cur_depth = stack.pop() + if level < cur_depth: + level = cur_depth + if cur.left: + stack.append((cur.left, cur_depth + 1)) + if cur.right: + stack.append((cur.right, cur_depth + 1)) + return level + + + + + + + + + + + + + + diff --git "a/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_111_738.py" "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_111_738.py" new file mode 100644 index 000000000..e98508fc6 --- /dev/null +++ "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_111_738.py" @@ -0,0 +1,76 @@ +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def minDepth(self, root): + """ + 二叉树最小深度:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/ + + :type root: TreeNode + :rtype: int + """ + #递归:化解为求解左子树和右子树的较小深度的值 + #注意一点:如果节点没有左/右孩子中的一个,那么最小深度为节点的右/左孩子的深度+1. +# if not root: +# return 0 +# if root.left and root.right: +# left_depth = self.minDepth(root.left) +# right_depth = self.minDepth(root.right) +# return min(left_depth, right_depth) + 1 +# if not root.left and not root.right: +# return 1 +# if root.left: +# return self.minDepth(root.left) + 1 +# if root.right: +# return self.minDepth(root.right) + 1 + + #迭代:不必对整棵树扫描,只需要扫描最近层的叶子节点的层数即是最小层。 + #所以用广度优先遍历,一层一层往下遍历。 + #数据结构用双端队列,本层节点放在队列左端,子节点往队列右端入队列,这样就会先扫描完本层节点再扫描下层节点了 + # if not root: + # return 0 + # from collections import deque + # queue = deque([(root, 1)]) + # while queue: + # node, node_level = queue.popleft() + # if not node.left and not node.right: + # return node_level + # if node.left: + # queue.append((node.left, node_level + 1)) + # if node.right: + # queue.append((node.right, node_level + 1)) + + #迭代2: + if not root: return 0 + queue = [root] + level = 1 + while queue: + v = [] + while queue: + cur = queue.pop(0) + if not cur.left and not cur.right: + return level + v.append(cur) + level += 1 + for cur in v: + if cur.left: + queue.append(cur.left) + if cur.right: + queue.append(cur.right) + + + + + + + + + + + + + \ No newline at end of file diff --git "a/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_169_738.py" "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_169_738.py" new file mode 100644 index 000000000..2a1ef68d7 --- /dev/null +++ "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_169_738.py" @@ -0,0 +1,39 @@ +class Solution(object): + def majorityElement(self, nums): + """ + 求众数:https://leetcode-cn.com/problems/majority-element/submissions/ + + :type nums: List[int] + :rtype: int + """ + #hash表 + # m = collections.defaultdict(int) + # for i in nums: + # m[i] += 1 + # n = len(nums) / 2 + # for key in m: + # if m[key] > n: + # return key + + #分治: + #一个数组,均分为两半 + #1. 如果左边一半的众数 == 右边一半的众数,那么此数就是结果 + #2. 如果左边一半的众数 != 右边一半的众数,那么2个众数出现在数组里面的次数,次数较大值的那个为此数组的众数(因为涉及到回溯,所以这个说法成立,细想也是如此) + #3. 如果数组元素只有一个,那么众数为此元素 + def majority(li, ri): + if li == ri: + return nums[li] + m_i = (li + ri) / 2 + left = majority(li, m_i) + right = majority(m_i + 1, ri) + if left == right: + return left + left_count = sum(1 for i in range(li, ri + 1) if nums[i] == left) + right_count = sum(1 for i in range(li, ri + 1) if nums[i] == right) + return left if left_count > right_count else right + + return majority(0, len(nums) - 1) + + + + \ No newline at end of file diff --git "a/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_17_738.py" "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_17_738.py" new file mode 100644 index 000000000..91c0f2f67 --- /dev/null +++ "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_17_738.py" @@ -0,0 +1,43 @@ +class Solution(object): + + def letterCombinations(self, digits): + """ + 电话号码的字母组合:https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/ + + :type digits: str + :rtype: List[str] + """ + self.phone = {'2': ['a', 'b', 'c'], + '3': ['d', 'e', 'f'], + '4': ['g', 'h', 'i'], + '5': ['j', 'k', 'l'], + '6': ['m', 'n', 'o'], + '7': ['p', 'q', 'r', 's'], + '8': ['t', 'u', 'v'], + '9': ['w', 'x', 'y', 'z']} + + def combinations(digits_i, s): + if len(s) == len(digits_l): + self.result.append(s) + return + + letters = self.phone[digits_l[digits_i]] + for letter in letters: + combinations(digits_i + 1, s + letter) + + if digits == "": + return [] + self.result = [] + digits_l = list(digits) + combinations(0, '') + return self.result + + + + + + + + + + \ No newline at end of file diff --git "a/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_226_738.py" "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_226_738.py" new file mode 100644 index 000000000..d24e44e60 --- /dev/null +++ "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_226_738.py" @@ -0,0 +1,38 @@ +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def invertTree(self, root): + """ + 翻转二叉树: https://leetcode-cn.com/problems/invert-binary-tree/submissions/ + + :type root: TreeNode + :rtype: TreeNode + """ + #递归 + # if root: + # root.left, root.right = root.right, root.left + # self.invertTree(root.left) + # self.invertTree(root.right) + # return root + + #迭代 + if not root: + return root + stack = [root] + e = root + while stack: + e = stack.pop() + e.left, e.right = e.right, e.left + if e.left: + stack.append(e.left) + if e.right: + stack.append(e.right) + return root + + + \ No newline at end of file diff --git "a/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_22_738.py" "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_22_738.py" new file mode 100644 index 000000000..ff3bf05f0 --- /dev/null +++ "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_22_738.py" @@ -0,0 +1,22 @@ +class Solution(object): + + def __init__(self): + self.result = [] + + def generateParenthesis(self, n): + """ + 括号生成:https://leetcode-cn.com/problems/generate-parentheses/submissions/ + + :type n: int + :rtype: List[str] + """ + self.generate(0, 0, "", n) + return self.result + + def generate(self, left, right, s, n): + if left == n and right == n: + self.result.append(s) + if left < n: + self.generate(left + 1, right, s + '(', n) + if right < n and left > right: + self.generate(left, right + 1, s + ')', n) \ No newline at end of file diff --git "a/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_297_738.py" "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_297_738.py" new file mode 100644 index 000000000..28f05022b --- /dev/null +++ "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_297_738.py" @@ -0,0 +1,58 @@ +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + + +""" +二叉树的序列化与反序列化:https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree/ +""" +class Codec: + + def serialize(self, root): + """Encodes a tree to a single string. + + :type root: TreeNode + :rtype: str + """ + #深度遍历 + def ser(cur, s): + if not cur: + return s + "None," + else: + string = ser(cur.left, s + str(cur.val) + ',') + string = ser(cur.right, string) + return string + s = ser(root, '') + return s + + + def deserialize(self, data): + """Decodes your encoded data to tree. + + :type data: str + :rtype: TreeNode + """ + #反序列化,也是用递归 + def deser(data): + d = data.pop(0) + if d == 'None': + return None + cur = TreeNode(d) + cur.left = deser(data) + cur.right = deser(data) + return cur + + + data_list = data.split(',') + return deser(data_list) + + + + + +# Your Codec object will be instantiated and called as such: +# codec = Codec() +# codec.deserialize(codec.serialize(root)) \ No newline at end of file diff --git "a/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_50_738.p" "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_50_738.p" new file mode 100644 index 000000000..2d89da517 --- /dev/null +++ "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_50_738.p" @@ -0,0 +1,34 @@ +class Solution(object): + def myPow(self, x, n): + """ + x的n次方:https://leetcode-cn.com/problems/powx-n/submissions/ + + :type x: float + :type n: int + :rtype: float + """ + #分治,每次用开根值的平方计算,复杂度logN + # if n == 1: + # return x + # if n == -1: + # return 1 / x + if n == 0: + return 1 + n1 = abs(n) / 2 + z = self.myPow(x, n1) + if n % 2 == 0: + z *= z + else: + z *= z * x + if n < 0: + return 1 / z + elif n > 0: + return z + + + + + + + + \ No newline at end of file diff --git "a/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_51_738.py" "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_51_738.py" new file mode 100644 index 000000000..70ade7fa0 --- /dev/null +++ "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_51_738.py" @@ -0,0 +1,46 @@ +class Solution(object): + def solveNQueens(self, n): + """ + N皇后:https://leetcode-cn.com/problems/n-queens/ + + :type n: int + :rtype: List[List[str]] + """ + def dfs(pie, na, su, x, l, max_x): + if x > max_x: + self.result.append(l[:]) + return + + for y in range(max_x + 1): + p = x + y + n = y - x + if p not in pie and n not in na and y not in su: + pie.append(p) + na.append(n) + su.append(y) + l.append([x, y]) + dfs(pie, na, su, x + 1, l, max_x) + + pie.pop() + na.pop() + su.pop() + l.pop() + + + + + self.result = [] + dfs([], [], [], 0, [], n - 1) + final = [] + for ans in self.result: + f = [''] * n + for item in ans: + x = item[0] + y = item[1] + f[x] = "." * y + "Q" + "." * (n - y - 1) + final.append(f) + return final + + + + diff --git "a/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_78_738.py" "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_78_738.py" new file mode 100644 index 000000000..8d79138be --- /dev/null +++ "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_78_738.py" @@ -0,0 +1,39 @@ +class Solution(object): + + def __init__(self): + self.result = [] + + def subsets(self, nums): + """ + :type nums: List[int] + :rtype: List[List[int]] + """ + def sub(index, subset, n): + if len(subset) == n: + self.result.append(subset[:]) + return + for i in range(index, len(nums)): + num = nums[i] + subset.append(num) + sub(i + 1, subset, n) + subset.pop() + + # nums.sort() + for i in range(len(nums) + 1): + sub(0, [], i) + return self.result + + + + + + + + + + + + + + + \ No newline at end of file diff --git "a/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_98_738.py" "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_98_738.py" new file mode 100644 index 000000000..177a0536f --- /dev/null +++ "b/Week 02/id_738/\350\257\276\345\240\202\351\242\230\347\233\256\345\256\236\346\210\230/LeetCode_98_738.py" @@ -0,0 +1,56 @@ +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def isValidBST(self, root): + """ + 验证二叉搜索树:https://leetcode-cn.com/problems/validate-binary-search-tree/ + + :type root: TreeNode + :rtype: bool + """ + + #中序遍历二叉树,每次遍历一个节点,判断该节点的数值是否比之前遍历过的节点的最大值还大即可 + #中序遍历用递归遍历 + # if not root: + # return True + # self.max_val = None + # return self.valid(root) + + #递归:判断当前节点是否落在上下界,如果是,则继续递归判断左右子树 + def valid(cur, lower = float('-inf'), upper = float('inf')): + if not cur: + return True + val = cur.val + if lower < val < upper: + if not valid(cur.left, lower, val): + return False + if not valid(cur.right, val, upper): + return False + else: + return False + return True + return valid(root) + + + + + #中序遍历二叉树,每次遍历一个节点,判断该节点的数值是否比之前遍历过的节点的最大值还大即可 + # def valid(self, cur): + # if cur: + # isValid = self.valid(cur.left) + # if isValid: + # if self.max_val == None or cur.val > self.max_val: + # self.max_val = cur.val + # else: + # isValid = False + # if isValid: + # isValid = self.valid(cur.right) + # print(isValid) + # return isValid + # else: + # return True \ No newline at end of file diff --git a/Week 02/id_748/LeetCode_144_748_preorderTraversal.java b/Week 02/id_748/LeetCode_144_748_preorderTraversal.java new file mode 100644 index 000000000..b64d00ebe --- /dev/null +++ b/Week 02/id_748/LeetCode_144_748_preorderTraversal.java @@ -0,0 +1,59 @@ +package com.code.week02; + + +import java.util.ArrayList; +import java.util.List; + +/** + * 给定一个二叉树,返回它的 前序 遍历。 + * + *  示例: + * + * 输入: [1,null,2,3] + * 1 + * \ + * 2 + * / + * 3 + * + * 输出: [1,2,3] + * 进阶: 递归算法很简单,你可以通过迭代算法完成吗? + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/binary-tree-preorder-traversal + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +public class LeetCode_144_748_preorderTraversal { + + /** + * 前序遍历:根->左->右 + * @param root + * @return + */ + public List preorderTraversal(TreeNode root) { + List res = new ArrayList(); + helper(root,res); + return res; + } + + public void helper(TreeNode root, List res){ + // 1. 递归终止条件(终止此次递归) + if(root == null){ + return; + } + res.add(root.val); + + if(root.left != null){ + helper(root.left,res); + } + + if(root.right != null){ + helper(root.right,res); + } + + } + + + +} diff --git a/Week 02/id_748/LeetCode_242_748_isAnagram.java b/Week 02/id_748/LeetCode_242_748_isAnagram.java new file mode 100644 index 000000000..132294bd9 --- /dev/null +++ b/Week 02/id_748/LeetCode_242_748_isAnagram.java @@ -0,0 +1,119 @@ +package com.code.top; + + + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Set; + +/*** + * 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 + * + * 示例 1: + * + * 输入: s = "anagram", t = "nagaram" + * 输出: true + * 示例 2: + * + * 输入: s = "rat", t = "car" + * 输出: false + * 说明: + * 你可以假设字符串只包含小写字母。 + * + * 进阶: + * 如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况? + * + * 贡献者 + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/valid-anagram + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * s和t是否都有相同的字母 + */ +public class LeetCode_242_748_isAnagram { + + + public boolean isAnagram(String s, String t) { + + // 为空 + if( s == null || t ==null || s.length() != t.length() ) return false; + + // 不存在字符 + for (int i = 0; i < s.length(); i++){ + char c = s.charAt(i); + if(!t.contains(String.valueOf(c))){ + return false; + } + } + + HashMap map1 = new HashMap(); + HashMap map2 = new HashMap(); + + + // 存在多个字符,需要个数相同 + for (int i = 0; i < s.length(); i++){ + char c = s.charAt(i); + char v = t.charAt(i); + if(!map1.containsKey(c)){ + map1.put(c,1); + }else{ + Integer count = map1.get(c); + map1.put(c,count+1); + } + + if(!map2.containsKey(v)){ + map2.put(v,1); + }else{ + Integer count = map2.get(v); + map2.put(v,count+1); + } + } + + Iterator iterator = map1.keySet().iterator(); + + while (iterator.hasNext()){ + Character next = iterator.next(); + int m1 = map1.get(next); + int m2 = map2.get(next); + if(m1 != m2){ + return false; + } + } + + + return true; + } + + + /** + * 大神解法 + * @param s + * @param t + * @return + */ + public boolean isAnagram1(String s, String t) { + + if(s.length() != t.length()) + return false; + int[] alpha = new int[26]; + for(int i = 0; i< s.length(); i++) { + alpha[s.charAt(i) - 'a'] ++; + alpha[t.charAt(i) - 'a'] --; + } + for(int i=0;i<26;i++) + if(alpha[i] != 0) + return false; + return true; + + } + + public static void main(String[] args) { + LeetCode_242_748_isAnagram l = new LeetCode_242_748_isAnagram(); + String a = "hhbywxfzydbppjxnbhezsxepfexkzofxyqdvcgdvgnjbvihgufvgtuxtpioxgjuwawkbaerbsirrktcjcesepcocaglbassivfbjhikynfsvlbtkawstrclbkpuldshfgfvwjawownulsggsxhhqglbhjlgltfrqyjntgldlgorgatnjrsywlyngxrcwfojkmydmjbzuquqnzbpwlcbdzgdicctpkgtbdjgywkyrkityvohjbuvmzdfyicyplmrpygdhaeqbuxnehfybsysnnmzbhprsyjmtpcrzeqqbirofzjtlsyofdyeffkvpuhzaflwjfhnsuyryetjuajjnjwvlvrhvpenaarnzoafztixjrisfzdlepcwhxudjpsiirtofymnovacjmpdjtethjqfwduekczlqhsfjgqesyoxcfooagrdhyvsmssbhsclnlblhobvhwtpyftolneozlhbtjagpgqnnapktyevdvjfwdnbwsbelweoflhyhifprieuvfcdkavqxkygjlaegqfmzndgxbsccjgpclxmlpstrqjtqyvlqcopnahqvnpvkjimfdlosvletmamqjvotqwhadutmfvlgldniixvdkmymfadckuaglgbuttymoqmzkaeqxugsrnfyxzcamwxujgzupefretsvbdweuwwcizjvhcowtmwgkdafcpzctpsjwdocgofivyshwdinbdhbxdfhjsrrsfchxkeqndgzauyprwfnrbmunanqnhmjhrufoinakwaciaoerioqffmipfqujfxwofqdyjbhagkyvmnxcwomgnmwlaodxgkgthnuctoozxrebjiynjwohtgukyneyofabpfdrxklopmxxiwjuxqpaazknscagfiaetmmwnwpzceglupqvlctywtpluoqbzdultcsudubqclbwlxyfboimfwriugfulbntvdwnxgiycxvennakpodorvpiknkridcumsovvfzikiydqewgjhacrkxddqpncirzlsynbjwutomcwphgggcsnfqxwxguokjvihoewffngivnkapaqjrsshqfbpfqhzihzndzeyznzyndmhfmypgwsqyernninzijwrirmbebsupvukfjddsjbebfedtdcufnubxqocqeqaincaadorteimkecpkdrjpprqtrllypfiktvcfadfotgjghhtkctgidwreyjulbtmtpwicbqalejtxsgjqepzeduvwvjbbxaapwvmjuweurhdbacxaygzdyitwcwlguknhqxdrfpfhstgnwmwagcblxkqjjwxfhmpunstfhkiffqchcohgcuktfplffqtwlwcsddkaeijajkdpmexteafezquyfonocuwuxrvigpcqbzmxtrvctovnvrobyjmjnqdhxqjxtltichttiuemvxixfeyqzuzxkwmyclbwutbvrgtefcdizjnkmlsvrujtiqhrwpnzkdtkwogypvpittpfhjtlrjtilubngkkjochucmdudfcpudjhiwaflmdfgsjqlwqxlgmdwouofjnytwxumjaailltmzrrfvqcawkzytomjaaafknfnaaylmqkvyeljnznomervgdhnqfhtpalziumhilupdsjpvzwjsgqtnovjneotcsfdtqqtfwjlhsbhumoyjnpuniqeszknvzitifqmiezbzvtzoswezulpfylopuecqcefueesjfhvyddsvozowqtyphlnuridygcmjhjtfoenwbgtramegbnabtooohogyoqfmskrpayzbrsidfbqextanotbkzopgdidswzxveqtojspsbwdqsrrspsyddgwntndfqfqeuvmrnyjvdrpszghetjnbpitorzonhihbzncpwvnwndriupumwihnpxskwsvjqzuetprscqnihpfebqobdezwohlubpyoulnkoicmtjyfsmvjltsxtvydxkajhvkcmhrkeuudrgxjezoeessffmheelwvotkjecsnwybtqutmkubtzbwshbacffkodyhgvdrqxwvtfwzsytriajkjjxcrvypyvmevvqpdknftdieqhsvgedaqlstybriqbeiczokoavsgizdjslkjzfqrtbxwydojvutxensaxrtapxatjrgdvecxocrisfznweidfvxddbmsmcmaostsrlvwvxokcecazxemwqksjnqhkegwcgqltrczpzyjopqmccgjpnfcxatmrsxvozdtcttlxptjhcwmjgcyltnrpicgblfjkljdjqvioxaliduasggcxviifzycctasdhfyetjljfgwjhjssryjaetfbrimuudxwivzpmpbylkikvgebelhouaconsqiyrjqtepusujnlpkkztlbrnnmllrebjamtsszsjyaljvqwklglhpoimqrspabinkpwopignvejbulvugpydnljwccorrvcbncioxtmvjrmvujzrqhfaphfhckmrapmopqaktegfmdnfrvmqzhngbwsijdocmhrgaxspclqxzptviiyhtrniflftpkalplcfoixqcckmvzeqozvbyobxgxehhrvezlkxdvloeqpmgjyqnoroztieaspauxwprtbmlawbxndadnqmknwbmnebiqkpjepdjudweevxtgclllpofytkpvvmlpllxbvkhcftqpzhrlaujzzforiafedqfrsxinxfnbkxjdntfpwquvluuxkukdovviqbtbiunyybkblbifsarcbojrjuityotqpwwoclttqcumithvhgajhlhmcoerwrmyjuunnbvqrghwniqatjnqrjgsnmdlcrwbnfrctsvuambecjkzzkuchvsuwusijrtctrcebafwlkpcoossaagiweaxauwyxhgchfdeqrzbnldmrzwbatqxelzoueeulmgfdxeqxqiaiwajqyihkvivbzhkjfhgutzvxrgmrqsxlctmrvnvybmlbufbmujpezuwjwfpwagltzicimdxyjscoglhbazkcamnchdhuwrqzbfnceikmolfjagmzhlbzyvdffmkeyfcjfovhogdkdszhomhqepyeadooiayofdtnlxwtkprlyozuimeinyiezyirpjfmhubdhmaoraogqytiihxgidzkfxdwqyadvrayajqilwddlyxmhgkwvcwsbqvxbluheovlmugyweciydchhoxlhvodmuvnaodxkelyuprppqvfupddzingyrxtpisrsuuyegruplcogidrrqghimxendmveudzemtwnmdrfxjiqiakglodvzxcdxzbtbobnvvoxufqnmdflgwghbclalddgmevnwzhohbhfhocuabtgvixvfbivligtiyznqmhsdvbfxmylyunerawhdhjlrbgvzljjgwbjhpjhgbmnvwnvjwtdtdrftstvrjjtnorryghqgsfybhncacxoqqhupvtjdnbdranemfywjhtgoxjnoqwsttvsoxymelwnomlmphmrxtnnbsbyelescyspuijmcxsevgorelseeyddymdpjbpehjdwsqrtihzhgwcgstlkallcdssiyzpvephkhsaamntlajcklwtekeupmwlsiojaplbrsnsvzqfuujkllzsiljzqmlvgibxssjjqiocgmvslmqgrsxtazfnbzwdkzninyuvmilmjoexygejggpkbrjeyfnfuctourngguxexoebgdrpbvjuxaqmibmoifsnbnhzlyssonuoqnhhxbkziiudkgtgucbvybmuflvdbdcfrggotqyfishaaqtgwfbdcntpxbtsmokrxnvrzaxfejuwqpgqfirokngeeyejngavztbdkdvtprmywohleaqqfqjnyyycppelbvwlkozdtwuubrjwkkmpavgemwmocvchoquogsjkebulbrgexqjnaiddbdvjlgvfntnkdgkmceomdkvpxtztfbwqqlwutlmvjjzkbulvqjxubuzegteusicmxqpzxlebvkndncxakvnnugqcbdukylzurgtjbcpcpsjbazlignnxemzzpkkhmbdiakeikioibotgyxzovgchtftfhcbznknjilmkudtzifiemrcwbyicspvnhxkzcjaxxcmddlesvixymggysbfnkqcgmsavjqynzehkeckesyddjgqexzmxepknwftptjdtauzohogaptcjdayexbsjxnsgacgztulhucmjqvrqnhuxxcyrawtznhuugkwylkrrvzihwuwhinuffmyyerikwiybcqaeaubmprvzosefwxruzrdqvhkdpifujykelbwumnfpuqksjszmzbuurnswgipwnfzlxplmodczmvxsvzyvdpllrnssiumfbivummsugxvxnfwdvgakstdnkxkjohcinmqbnyqtoahsakqluxrfxlvmhsgdpipgokharbgkrbowropwqaaqkyvhxatqrajmoixrycavnoisarbdqrlggbhliypuiyubulfazulsspfpzzpczxmxtrkjszttlchdpcuoiqnqibnkrereaeexhnqbqwadclbuejxhnmoarhtfrewbwgzzdtwjrkcwpplkezxufpvvdiirpsiulomzsefjnpkodqdyffwlzqvzfabnzwgyjfwyzeknsfyjagyirtlhpplhfcsvttdneoongivvdvxtjedjaakjobmkjnbedinvocafrvkqdrqdoufrcqvjroztpkympqbkuwelwarfjyndkyxkspksyosrqpgafamshptbxzbdfekqhgagkbvkgeinepzkcdnmfxnyqyjhjxntasxgoghmhkkpxvcisjkvgwilgubjxccmffklniynomznltcmfofpehsbfswyginakcvdzhkbrrccnerkzkfxzuvcizvanuxkiotvttqqnngopaewpaqvdhefzrrdymeodzyfufbosecnqkuvhxgprgnfbiyqdkqwwujumesjvxvrfvkknhhfhvegwmwrachpbzwrjsffjbhkkktpnzgtdgzgfbmlmymznnfgnhcftfpdagnbpzftewnzefuqkfyiwlmnyvtahanbigapagjqmrixuyhxhlyrwjoqzdvbhxxfyfbwfsfacwqujjfunypywngdbynsqxplfxtejbzeytcgsabjyhvaxdyxbcyskdfxhjuwmisnldbjliejeonzsurrpqqivhcujjwsfubukdglszfzwkjqxdybsynrkonufpvhlbirqubbgsgbegmpybtmfmjsklfqdzvjbqndqmqwbpczcbksrxyjcrlrlmxkipsffugjmfvmhbhckkmvpzqpgspqkzpnnhcplcmimnmnckuvhuwlkqxhgwqesqjhonuaewywzjgowhuzfujyyflnesowydymqcizllqinuzcxhpycbyskvufloktvnvisxjjimjksxmogvpqdxuzplvjkbplevzcovmlzpltuajpwcdqfwohkhumezhaugjkdhxhwvfwgoxqniwglnwqyhzcjrzholyauavdjqjrwstkwncmmxsngzmykrecmbfabixkwxtqtbjmbnplnalipmefvintguxpsgwvvmvwbpkytxmqejtqmnihfsbqrhzcjshotetczitvdifzlghcxjdswlbcnfdxfxcpmgmmcetagnhrutprhjypoiyolykxgprurkqyplxhgduaffbzhfekawtddjqvuxlalyvvjxzxxgeqrkicyzbdszlbplqujcjvqlbtxlaweqyyidjfytgcgqusgtorexawwebddumpqlhhizrbojmpwaixrsbbocgbjlwjfolifykxnousrhtirctyptvqtlwfuofyezstiwzdaygaucrbbzcluqykwbuduedvvxzgcwehykvggnrasqhlezaihzosgpaygrdecpdqofauicawicbgpjzeqkvytvcbhdyvqmypjkarkcrbciruzifapzkaisuvtlglvfnnwjocduysozqhkuvkppytoedykmbmsxzoqbkqepmivxxadpacmoodqvbmrkicxnllrhrtecsbviqycodvgvkevzudbktybzyffdpqafilhgtccaojtanoknvccpjcgtryxcofprbegxebtlfpgbahpyqbdlkeiqdayxaadxgcicdugnhcbmqebdtyclbmbjalmzbywbtovlclxuwqvrlmyptdtxnkijwxnlnnpsfstdhbqgjjwzrfxykdkunnaqlvyrgbamuvzarrdfdgsrclwwxvuvafnbhwzhlqkykuhhnuetjykmsxzpfnydvtpphalqmaxntnrxvyaswebdimlcckjtnhommgcsocodwaavazrjaiywuqybqwarcatjcypchbnkuwerbobgiirdooqsmqqsergkqmkfxsvufyyhziwmjkamflrsngrrsntfojjjbwyjbpvcrbihdvwhhnccdungtfvnytcnmvasnumxmyrkgyycptvhdwqhtiaehdfeencmxihtbhrzlxstdmwesfcysasacewvvyzkuoxxmyvsyztcvtyqqifdagubpacyiwgkkcinngdibpdywunhvierijzaqyitxsrsadoendrmykzzztfpxgzkcrdqvvskjsqywmumztmibazouprbubsdprsyalrmyizluuxnolvdstornbtvpsgvidptlscjrzylxjjiyefxcdzrmnrsoplqdyfedtvahndirebpqkwqnvzurxmzlfwuzxcdausblgucdxsxljsjwgnrdrbxnmltitjsabfqdbhayodbukydqmrgocprociejzvohnfrzzpjijappmsqoctbfjeiladtutyjxgzukqexeuwgbiqywebezrbzobfdasiwtfkmrqnlkgntrbaeidznyoyiztivqcufqpqfxqfhuikukmsmynglclshyyaloiswxqoyyqinlmkpgutivzhwenuvcepbwkvorqseykydpzrwgvaxhcazghvvsswurwnqmhbzkpumcihnguffddbgvgfeenukhsciynysyaejnvfytxavjozmtmpdgexhylacpgfqdnmmtqwqptjrrwreexcmgglxvkfzklmrfpvoxvhejjvihraszgdtbqcbabuvsbksxkylbisjrqieicqietplhnomeveqvozpniifutgwxnydxtwnrnnzwtunnywsszqgoyrweqsqjtopotozbdtyglfvjfdbrovfbatajrjgttzqqnjxivvywmjaeqxpmnaxnsfcfnbsmxdytbghnlnjfkcazqadakgazvewpqccdaabhpzhervkxathjizeuuhzivpdwulmmfdwbkkgxorddocvvhqygxjzqvsrkjkrwgyahdngcihvkmwhovauybwvapomiwscvjrnnaxpfyrshuupwrfbmugoxyzedxujteludcshfagnikwbztuzmdohxmmcdaduiesmnjvnztrsnbackpvkdfusmixjbdnuenppdpyottqmsfnxtqllnpxdfxjabntmrpwypcgvjtexxhfmbzfoiizjtdtfsodmqaprfudlemamovfdzqbkyhpeopyxbdjloqxmzvqybtfnffaoxdulyxlpehkyzgdhexgdjzxmbczwqsijkecthakunpogszzcutmcintimzmmfaozgdoyjcnwdcsklpawhggsarraefboxocnwlpxdruoyzdtvlagktupkoyswmnczymwhwinwooznlhoajtjuefvxdxbmzfbikuusqrutcfogvagidvuxxtgyshtovetdhzqijctgzenkshqvlibhkhwwnofbtyshrrhfcmsbwqyzagmlpoxubcfplzxdfheziweoghykcmubwgkcochqlkmidmfmzvbdkxoknjrjsyzfiemgtzplatkmhfuvenevobeyneyobkjuidbuvoyaqzrjacecuadmslooxbmzfmuawexswqupvawrqzvihhkfmsotolkdgmarpkkxnfndfkteqewywjzagukjiqhdjdnjxgfnmtrmshswaxizeuexrebctoehacerwfjjgodvicmgugnlngoofgotbztfjcsrwblvrtejthfmmzjjcrvezmczizazzpbdwytxmftfgzdqnslpezrnodslgplqkitigmhhhonadcxhdnexymvlprrshfrecizjnezdlwkupvkplnpvhvcsdilbscxgyxtuwqglkwwxljntjpzsjkrzxmoyswfahhgelyqjhvpmtgpmosmbmsqsfbkeedtdnakyvcumuxqovcnllusoxjufdfgdsjnwxsagtdkjljnfdchnmnlsnqaugqmnzzpzohlatgwhrxsnvvxxvahxwbadqrhraqmstcewlrsuftmrtcxusjkiyemosbebjfxqmvxnyhfdximdbaogpckgvbctkmcusigwbpulpocohlqxtpuqsemnkwcydjnvlrpwjyyligswnphrkbaytllzlmbnultpfcrtyxrvzlucshoxrlzujuwgqwssbgfgpoauqmzzfeppxxqcffangjjrkylvfirlvlhmdypetylrrxmewbuvcylzloskdrhwqaogfephqdyhymqkllwiakogsjvivcwtxcarfzyeuquvlnjwrkptchqjvlbzrilciwuajpmitxiljqeohbkunqyqksadaqkjikuxqznaaihtqeomxkasspyiquazvdqufjysgbqujoyhpnjcgbekgmkztovlkpmqlcdnpmmxyyxmaysxdlzxnpvwoyxnqtstgajdlizxyoiiildnjyafdavyynlozbapbvizlhlpppmiaygngtsevhivkgvmoekoevuswjiefzuwqnpfwwuwcooxudldfjifavbkkidnuqtidunubbabtfsltdwgqfxgoqqylmfyjxvxedoxqpmknbxaqkakvhmtgygniotdikllibsaixidziltnvuyyftypbhaguvizawxijuwiwukkmsaefthkvsdjvvgrbtpwmhwqdlcifjwmmexnajitvzhbhvnhybbmbgexdfdapldjkuwyigtqsgbylookxtwrqbcyzluydbqltcogsyzflztstcxsjipdxsmhoppoexahpaguwdmdtaogwfgkzvollmucphpzkanxazegyjdfkbxwshttzexvdqrymwzjlqphswivippgoasarylakkixysabrvwtaxswkagrlltqknpbhlglbrtycfgelazcoeaglathkfbshvbcxpephcvzfnhpslqhsuldrpnltqcncxdkiawdikeaoweadywevhzfuzxgpwyeqqyfzdmzpfbdenjtcxhbydpwzntbbvpxelxitqjlnvbevgkhdmrumildyuxwmdpyiwfnocgcwlqtnoogrbwpmycyozmccslikmskxdfolnpjrkvkfgbawawnxhtatwhfbenpxlckcbzjknlsfmgypvbrrpjzbwgrcruakqlgtkjjcrhbkdpcqhmpcjdlyoqigfqeimtlwfdsxojscfbdbqevzegkbymedbkfodgomldmhplzaaphenfpgimfsdkzooqvdptddfrneapnsqychajjnctzqsjwhmwanrboxnizydzklprckkgnrxydmjbzjswlyvvbsyzfsamrhzyfzaafdvmvglfhnhqxjfnomrvhikavprpvjdmsfxzbacydtjqcwfrrwfinucsyvhmwrbabfgqcvlbvixbpgumiukortiklgcysdnspslwicnpkcildcarfbxajpojcdwotuoivfdcebajgtvxqsmvvgxlkojfxkovxllvdcfwsssmgcrwuxopsnvrvfyneamxjtqegddlxdgmgrvhaijmsuqufnpuidlxlqmkajvdbdgamtutvkvjcijpmsvoqkhtxamzkzvfnuyjtzzkcfebpxsxktphsupzlxvrhuldzefpkjjahglzquroaavwwsmyzvocfumnxuemysqtpxxnjqqdarwzupxlldgqxbhrocmaqlfyeghedhrugxewggvpizuiemgnyvnmdymlvgtflbfgcnujpkuejymaheiginsvfxsveqhjhfndxspramoskwschxugkvlvdecunquniiivpqizwnyeipwgfomzctopbcwyhtvwjiretdqvgycxwmvzrftsrbdfbqbyhcqezbqarbwdcijrazroqojwrifpwjrtzyhkuhigouheybmrasdtodhddappfeylgraytlxetjophbqgknbcufttuiramawebesrzsbtfaucyqifacyemkfimdhwpzbwdakwjblsantzlcudyabmrklrrnrahosdxnwgwsygewylfltlxejjgzgeptgaknqxextlfcqvzhpxjvrobzkkqczwitykdmqardgcribfzxpzylfzkitocjhgvtdjxavkwtkuxffslvvfrkyllnnxitnboxkutezrepxkxxamrhjvmvaffctuwvzajhhjegigbbiwsscyqikgkwysnceshzjwpeuyqfrnfvfeetagpebaiybxgutlyrdsccrsguxzqvenakmcdhayzmuulkuiuryppecdxqnisszatfowerauhxcldoszqtduzkpwpbwmaxfbqppdiqtoslkouwvxvbaixhxjpttxwxzkqkjzqhcnvnbdhkcxlxnvjqaxlclurimmnolvixxodfjzbtyeapqqfypqbcbrvwdquttbancdgmuhuqbnyxfckgffzmkwuvfmszjnvnfrxogksluaxffcghqorbgxedbjvojvseoxhbugjdybhmrsiwkdgprfgopgccaqdcpfwiftcyjzljocinfjpkqngmugfjqgtcoagawhfkdkyrltbrwdjfazlvvxbecybyeaikfolankjusfjwsziwumrofycukfdmpaocphfuknwsehhxjhmtafugpwckkazjsytjbeihobkxrcqbznsdboiegdkagrcmrfqvyhhizzpnbtyvrzvosnqguhhghbhldtstvzxrbvufwsvijukhyafqfgmfftyjwmrkxeurjujlamcstwlugskqdnppaovkqzginpluhwxybopsrusapwbwhrgygpsfnghdkouwoiafokmjcnfrqaremqcrfsfzqxwalqfmpshmftihexccdovruodydkgmrjltvagsqswkwvorywcgelkvaikkpjbobyxaqyljtybpcdrnswdheyygismrsgjsirpgdczkegnwdlzibocehsbncivftqfxaroxizmzhyijqdvvyyypmoaiztjiwmhleibomoymhxmobyelnkskyabmqsjulekzwydfekqrpayicbbgzczacnpedksthppyetucsmweobsdcjhgdhgrymherrtijjtgqgsqjxidgxdcctgvzsognvzcntochxunfmeiscdstnbiwqlnsmfkosgnjfpxfffnjbmkffkiniisviynbhwenalbilganfnjycegfzzpoywdxmeyujoaedysdabgnhllfojxbwnxvqtlekxisukjwmelujtcznvonmmhcltsgdedgrmewpkxhfpkqnvjhjajagocvfquycohcufzcitivvqlkdjgvvoldpgirsllgowalrwtxqlhjavrpupdjfxztexjmvloojkpoekjunuyjicceixltgwwgqjmntrggfahtudejqpkvbfezycyacuvtrcfahnscilpxcnakdxmuogzxhvdhepkgwcfqnynjnpwwwwpjprcckzbpekxxwberwygobceyvalblpppuydnoqhloeoubptqtyqobwkimjevmmhhdhutwiarzmypuzunoesxxyuedebbaycbawrsoknugmlpwjjnajuoillolebrxsyhvyoqzxcufyikvfgncukmwcetgnkhsotriumgbmgmphcuqeqkfnrjipguhdrbahlyqwhtnuwnqcchtdgsgxeqevdycgwtiqofsnhvuhejkuipfabzvqjhdbbtuwvzbouancgkemccyyvrmtuubouakeuxydehpabvlbnqudwpylubyxrqgynkirvljpnzwgnuiwhevffztewzsysegndtmmssktdgjahcifcruicxqxpymcdwgipjnobhzrsrcqjggcqlkuycxiuopykunrotgmukqkhpvcjggdddrlduosffrizhglncpjjqsfjebbkzwamwoofajpubrgzqouwhdkdtuxqbhqvqnusbyxxeezyuhpatdsreaiinfkukxryycpghwsrkgsvguilifvtvzghzzmhsvyrgfkhxsirhncjxzkuiuyyaimatfenvjjhsrklfcnyorodbojnunndaqbgxofljrlayczzbnxurqiebpdwvevjndqiaphvxbzlnwzsqvrqlekqzxkliikevjcemhcvpwcrjrcxrhpxvfhelcynmuihcjkybzvktcaetsqqsfkxvwjyvetvbnjslfljvqzkagrkighgqrwaxmmsltgjnrfhfjgowkpztqtwjmewfowztsyvwlcyghyhsgvecyjvzwselwbpezhmmzgizbrqqcohyrzxcltabkddkmamwuckiqbicummmmkhwhzzgytotvlnvlryzoojawgxploclzloznnokmfxfjrgvfpkqfafqixdoxflrrmdddmjxgfgoznikzknmrczeqfwavbpytamblsxmbcjbpscsuraqhttmamxhaxgetiylrbhtbymajkgxckluctfwbwsgomhrdjyyzpechijmbwwdzxpuzmyvcpezynjcpjcpliuoaghpfowjvekehpchzkdncqubbvepovfvhlhvqzfbvqymgrhznpncmmynhhlsljjocdxblxbdhmbehfoyuuylurcocnfoqhynvoolcqikrmwbiocbwvpjskohvrolghchvjlydihynakxbndqgqcqsncgknfifwbhisdvrfvidsagrizmsakkdhngsohwtoloknapxzmyjbuojxniiwtkbfytzxootiqrdqnibapahrisjgmhnfnaqbqkoxsinddtsnfqympwjyrmdngimvzxtzsfeezgifacjsuymbrscwezdnnkuyvudcufthkntniodcsbfdmkigtnihqbagwbjlqpbhqhqgtprtwhunoiaekmtnhshvkizsvideqpqonfjuyqcgmiorbhwijonphxhxbrtydbfvftcxlixqxloarcpnnigrfywmaislxkqbgidkcwsnmouxrrwxabvrixfxtgzzbsrqleigeniulqyhvfleqlbqbicisidfrldrfilxejbpohapcyknbvbhobyonpwgpxaiaipjimuxdjgijoitysjrocgezdpsdqpbhtrycdchjfdbaxbmyyamulwlxkepslibgrotpvvcvlegpgsfucxczenkzljinwdalgzapletxvuynqxehembqcgayynsawsgknzxzucuxsgudbqlixqwinryiexkigsyzsrptysrseqbqdwgvyelybiyzbghubzpwmcqokxyohnkbkrlrpwnwydryyemrguinfsnrjnfhmghgvtblxeyuywvuefhklizztwhbrwresmamttahxoeijhhucpjhntakzexhfyedadebjfovkmaenllmburwjdnygmrqnxuqmaphcvbooopcszsghqgjbtwzslcocyhnetjchuaztyjkkxlvftkuazrvpuotfgbutzkscrclhicdeokgiijxdxhyxeiyvekbzwqprmwbgcvtsnsksgxxxtjawklhyjxsuihjbxgivqpzexqefgvydfcdrnhpitogxsyvmkefghvrbwqhguooaioyhxohmodlxyfqchzdyiftgplglkewwntxhlrncdblscuwvhtcffmfahmhcixmcmcbzxlujdaxoxwnqbcktqkqxhexcykctqytkjafrmmrtkizmewalyqlckwqdsarnzbphlwtemvfgtjrlrwwlyrkmbvxxhivgcwhzvkgxygkqhcdjudxeoxkajewjmztwfqetwfcbbmovacqrhyzdzigbzimdkxbrzpbgfugszzppryeisavsnjwevxiyrshggmdsscxzkwnazleulliadqomcwikdlezwdscaqhmeevobccyizsjzjfscabkzijxqutjifoeohnypzfnmmveilajppgzeuzkrpzajiisypojdzpftkxblgwzifkfzybhbqtiquwxbochrkdzlmlnouddlhjhzhlyuasycnelvydaklpwrirdrlzqolbdtnlfvigujpdcczucrripgmmmyeniybukcyrwldyfklhrnqxukfpspylvnujwarnomegpsaaorpacusmlebzkjxizddvmeqwsfwzzsfsszemdefcijlfhkfosxfrffgnddemlvqhbzqnkahoqwwwttymqynwpkxjmdreyzqefvcanexiutrtrdhkhigohjcrxaliltrfdbjbddjuhzhdhulwttpnysnqdpfwewldxkctixuxovetcwpufyjixxvqkmbqwtndwyyglmxalkajmvfyqrjqkzbcnjulmbszgvpcuazcsrsatpcxsoxdddkoxjkkytfarxxxjmdzogzsvekvtrneucakghbcazbpnitkvdaijhbnojbfwqdvjcipuwzbesrupebsnsyuauljiuiggttkuipuxfaadxobwspbfqzyvwfbpsnuwkapfmcibcbocqcgfnjfvkgazzgnfkxkefcubbrhmjhcqvknhvanfrbmvwgilgxjlolekvumikavnpfpxwdqumimcrmcoptcenmzmjpdtahotzflyqmogodjqvtfasenahuvotprpbwbpyqeksmysieqvppzumtfhnxgmuqvdpowrpyukfqpnprdfrywykmujrtzexzldifyclixfcxnrkduylkxsdjhkbvpuqoocurmdhttxtoaifdqbavrvfkllbngbnfzvxpwwtlgfvjspniltwecclncwewmggbpfjvivycwpclscujdelwxsjvcdfohnkznwzaavdzzhncrayokxbdxubdhggawezypqssdkxxmaymqoxrzwpqdwijksbkeoszovpkaxodnnmhoalznibyvocfdlqtsriipclhyuvqupczcxliuzrlfooivxxfhbwexysukyuzruvpkrlgaxckkgtcxirgxyooanpsbkceuvxxgusedngbalamcdohtxevmrlhsdnhgsxjksojwqajhjiknbuzbujhvrqnchjkloxwrxcikefvrawjxcsavtmyglxtkmejdbrphqzwjctormbpmpwfbjiaskyrzybmzzmpwtuzfqasaprslqhahvgejftyyrnimtvseqtrawgdhcfezeqrchoqpvwvidqqoaslfqpggmqqyykujaeeaqapeimuyvyzmarlldkorwdmjgcorqiesgoaqddnnseogedeoqcxovchxorccnlefrlvflvdtigxsmhtfjvcodxswfyjqusrajymvagfuickutoodzcvqjohwyqhslupgzwpintsndbwtfdnyklyahhhaqzhtyzttghzmldfysyruimflquixotblajaoderdpceuccurzfxkeutmfpqffzxnivvwpfclujcebywqcspiagfrkozuindzcnxefuknzmeafmqpsylkgoewhdgelxthgeshejtollmmtsedponkpijgonfmkccivugssewfhvwyqzycixxcpecunhurybmmtdduqnxacbsbmtytbebdovfmocnjqqxdqfxzuyiqsdqetpufldzskwgvodghfemakjedsfmasutbcyxsuntvpodacoyvbpbqbedwannppxtyptztjwdcchsitgqluneylwyaszipekkhbkcnefnzwawgfudeawkayrlaqrdswwevlrbjrqigfgjrlbfayelmiwsmbvdwbvkwuesujoycogvvwpipivovwudibwjanajowonreajokwfvcwsnsectaryshcrnlcdngqupksdhpulnvdicmniteeimortiokofhpfatuemgtuhgvifvyujqzzbojyagamjbfdazzvdloaqofgkmtxkcgtlxpvjcwdvehmcmhfrxkvatcvxkdlrikxyohzaxfamwsfkcrhmeypftzpeplkrtolgkowludrbzarxwuqjtpboqltvvcnftwooztbjijfkwrvtljeklapluojkgycmlrdqgmejzrihmghtmhymzjyqxwwgcnuaaxgpsgnbojcvrcmhevywxkbujhxlacnhulhkwaronipzpwotaohlfauopxyqksuaijejninpkqegbirhxgamxpnhevztjnwboftlvzkjepxyogqfahfbyxdnisympjgfqdwgvxgzcujcwnzaowescttkjxjmeqocjmczarjpoggmclyygefxakmgjgutdosqolpmlgxdepawapgrbsjizwepagtxbimwewcyprbfcczmednblmkevipjequjtwjifnvnwubptljnibdrzxogtrzbdnufzpkbqjjyzdxclofbpmdambenpwgngrenhrngescksznfxscjbikgdtloaqvrjaemipoanblulfiwtzxmhjvtteohxwegzozpqptqchehiwjaiibnslshnadwcwmbcqvusrpbcdbgdizrzrdqpqyiwgmrbtqvfqnghfmzalhpztzzijldncwsdshydkpipktxqgcyxpmqmfttlloxqunahvyygsucisycpefqifflaxshlqkpjxiaagjymviwqxggnjglbiqbmhbcardnhmrwyyabgbsekmeygniooqpdcwpomzuhyibigdgftdrwkouqfsugbxbbqhewwjwgixwpqmkisaqeztyuzsftgjzctbzbrtlxfwxscktafqxaqrnkeudbncahmythkroeotizqrqlnzenbceejohddmspxrlstkdvqoulxiscbbswcqyskoihxytmkincnphrmxzjypriaoxntttpxjwuzvrxumxcqfuuicxbdwqbxwvcpcrznctyhtyhmhuztldztqbimgdizuwhcxyvjqrzbmchsldvrzeftlhfuzxxkmxmpeljvlfcnfgkcxnfqfzemjidlzdrzugrscfrsfhgvscnualiotbvcquophhvldipnpwnaxasjdgrlctrvulvaarhwvuqbnzlsneiqwwhuvzjxeqqydosvgmqfnoeyhgfbwvqhgyodadzypvxcvvugnbcpokssausdntvuamaocbehzxykblqaefmzrsplwaijpanlfydpwwowpnvminkhxqfdxpfjuhlwmfloukjffcixmiojiisgwpowzctkmquemmamrjtezrgbgtiaoysjxdkmnqcdovrbxmkxsxyixotlyuophnxkunirjwtxknyleyhctbautkeolgfpfnwkxleilybroaqhxyprflixthvqeabjmkjlwvchdatpljgdgkjrrabiasrjuwxkrssqljyvxkgmutelcglenbzhvbhuzyljqbbztxalvgbcelyycfpvkcuznlvhqyngvqknvbeyjyecuhbdpmqyjycndszpmckagpcudjjtswsamfwbkqatkjdccemztxursafaxmgiqzudweqdgansrbdqybyfsesdrwlkrqexzblkbhcnmjyhgenleqargikktfexyhxlwnipifrpsmxwvjqbvqjwnzmudwfdzfavcirhphnncyenzleumytvlbkalzeueyyzklfygimrbbmwnkzyicubunnnygswarhnembogitbvgswvafjyvvclljmvmpvtidpduudtxugttmaentwwjhpyipcwlmcnhicdhspprwjoqfytssmtvoqdybjqrdsueizqeilcdesvfyipullslyuaczaxglermjkaffltbpxucrnnrcmbvvkxhgcjyydnjjkxnscimuvqmbwhjpxwdmaihavzddjjzmifwrtsqmmsfylarznkweozrjszetzxuautdblpuyyenoeuvpqptxvemcwhcbncomrhagkhpguwwzipjsvsgincmufejiksxxhevjeqiqayjkdsanxilhitcrygajvkywumlbsivdxhulfsbowpuwsnzmuyizoqkgcodnxdjusneahrmfdwcxmkqmoeztgpzeopdhhdjtnbjdovkhtgppmvqkkeckmkdesabvqymzoagtmquqounjshsczauwvrwsijalecimvqacsrdogxpyklhwjvhkwmmjaootdxzgipnfugalphxcqodfsubjgxsljrihwpsundmraszxvgpemawodxzpvgvyegvpwkkxvikysaqivkbbvrpwugefyaacwjvplzuxlrcurwcbrpbxluecycgdjjxjtmqbhajxhndvjurwbmnbsevcfdeqhiqgzehqhxjszlomqiukanghrvufcnjyhotknnwtyqgnhvwsaaybufrinnnpnkorrzhmiazmcgvvgpxvhinhciebpidxoecywegreqyfhvfpwqxeoujgcdizxvbsfthfrixkjxxoylqfcxgwkvxsmigegilfzahkgtuwnofobrwmikvuvvivvdknexknrzvuzpwwdiqrdutoyfnvgurdqjhtxjexombgjzkjdnktvxdarnoubombbmqcztgcianxurrniphvsptyhihwcdasrmnyjqpeyicnbefnoexamzfnyhsqbgkcylrguwrfxsedmvcjtnvdhsltzzzzynwfgnshwwgevptwaiaypvjjpdtzkkbbmegrtlzwrlgufrlkkfmewdbxojwfomckqnliyxsfarjmxqduiipmdaikixqkglckvghznqzubuixhgcpmegkoliiwiewpqhjsxaztohgysqxsorsvcvijxnidnxcywxewmvdqpzzmnqovjrjoxiuqyfcvvodzjovsasoczziqvcmwwblamedwydtbliblayudychlzbdbraochwmtsusgjyfhkmrhuvmqgvcpysncjabqcmfnpxzgkpoycdurjveuehxmuebcrkyyhgdmrhquxqvwvdljkmcvtdddhlsvxyoztqzqudivtnjxlxugcenefotwzusalkgwtmjqwrnbltvivxuafaonnzwpgfpropuxdhzjaxanullkbhziltfdbzxhnjxhqkjuieoatfxpaihthwuypkbjkmbwzaqclbgdvotleueyondkihaxyfkkwxfkrbxjjsbeuswaacvkbhakglrikbudmvvusnstljwzlwgxwftfbmkyvqpwrlhsewfazyinfayhwpbqlstrachzstsbdprbieqryqxehzrxerlacxxsflhnsjrcglgrqaljbxaosbbxpaecwqirixsdkzctvgjwbsflaiswzhhbwhbnuwytutvbojaopkydpckemdcwgtxlghsrawmwrgsbrxcamudnykmqtszcupejlgnkzuecqcjxookmipelkfedoobdflmixfayxpurjltkswzfwemlgkvniwtyhatpjnpwntvhgydhhimbgvkyksqktyobnyeeyiorjccqbuoatirwmaascjzlqcigpesyrmnzpzvlxxozfjkehnkjfyamsreynvxikftoqvwdxamufgynerhrsvogmnncytolnffhxhymgcacwfnfsoepuvtiskycjwvevekqdvqtsglgeyjlknlmhmrjprqrdqtuiihhnozeutrzlaixhpzsynswrcvfonwntgggqcuzoskvwkzbarksehtgjzcyojpeseurajrwihjbuxngqlpgwriuktjadtfkfhovpeyfbxulrjsipjngccjnprtlntmigreypnprbsxtzvseejpzpisfrthzbgvhkzlqgwqxphtcldhpfgnxiuqcdoumoaxzzegfkampwihysoobjlwaqorzdtsddcgntcmhhtnfazipfznkmrxfsmywvkzdxdnplmjxttusnrtapjvwrhygpcddzzcfrkpqlrdgxjzlwwjdnbswdyuhnumpqeshjwdvtxcfkomhezauwkobcbhymoilyahnyixjvxxbiwvgqhndwtbebxbtjodgwuuupkxhynipavbucuxwuinmvvkjbmyafjrnikbrgohzhamkcryaoeyblrxzfzhyrozwrzeukjteijpvkhfxtlhyxhmbmgvsxzxsxqgtsicdvgsvqrfjudzulzqgbeqrycmckjtkxlfsvqnavgltiruspqmrjtbcpqhtwhhdejfsojbzxtdqnjatcnvslxgtwynxqbosajcpjnkapcortjozugcidfrkaopdbrdygdsgaencggzpaawitnwunwkyynawlrruhuurmhmqmbczhhmqbpxiogspaorstzycqfzmxjrvjkxflwkmaisqwylnpwrqefpgtbqhcifxaifpyzxoewomhzomklbamwotoutscobqtsnwkbtykocfgcwbdzvebbaqhnlrhmpgiwgtvvfadlxknbmrzwvpwasnixeesocdacbtkwgbhbwiyptvxdhszzhzpxnvoxjyzmcoydfuqaipnaetrfuybahdhrrvqpchiqrtrjgzgivhsziydazaretytycllbaygbkydcafszqivzilekvkfcahliinfdmmiwrcwlpnrjueroiuilbvpgqqodtmntbhwbehhxsldwwunpukbjqbssfxrylirvifkkvzeewldgwdeeydwafhzwithjbjqgrqgkxuankcnluwhqxyyvjwsoyrbrndawblkofvncmurknbocyirpwugitowystesezzynouypfngcddzkcnlzpcbjnsndrqudwldnvblurcjuruwcixrxxqwjofwvclvmzcvfdzvhcwiekxlqhjebqripkdxjkcoxsxjdjltbajztgsxljrubosaaqyphnxggwqkstqzbzeyxeapkdacdbwkkssmurakczihgqzqwlucglcqrbefqjscxfkvmkommsznpwixmbygkjrtucezluxykuuvslaxugjpghffztfeulnxsfwwritgxumweruxccyhoczslxytqpcqoicuwwrfeeimzvqzfrjcqkfrhbirrzfdqzzlxtlqnsccomfkdyionbplaivwsvgpjpsheumedoqzvgtywiiajxrfmzekpesdfwppbbauxhvgxbkvquodnpwactxgtxhoirgvvzwhztbbekmtmkhsqkweuvomrnxiikxjhthnzqtzqyjrxudngevrwohbjmkcmiqzmuysjaklxvryijfeaehylohormiakqlkdtsevrcfmmjyfeegniznrvqrlnorxaclgsvsgbrgsndypjjcykuafnzadrselgnsxwzdfohvenvqzaymgbbxvseiwjbcgkrnmsavpehywckmsnyygrhlrqwjcenimuseabwvksjjksvmvynesekerjqfcbejsasgsogmmnuadzwbpfevpdhlksrzsannoetsaixrrtopwehanmximdnotepxcjgkzjzckfhyfhylqhoggryhmstxuisfijncfqjdhalfrdwukbknhysdjwfefeoupfqenntcumtxtfulqexmgdxbndjmjnwcsoslboqqlrhmcpkhqhxehxdcglxzuphsgvhcjqbcvtpzrqogmnsvpzxjpkxzujyiiddbgslrrtkatkmvuyclrqqnbstjifrprhurpayzzdftuhwrppmdupysocrxuaohhtnfvsalvwcebatacfgzhxxxmjlsuzwbigqmsgjwcwjvunnmumbccywjhaclzvhawkossphbxwolnrabkcwvzddpdeenqgwrsklpoyisyhtuoiraongquxuwhstnzwduxksfyinljaoefdylqmgnxsibtlfsvjdzgkrofirjmvatfjjlapmtwklpmrcdtbbfyvceygqgiwobvcuolpsouvzedjbkwremvozxjtueiydgfyadtthokdhesjljgslgiknjpogdjjohnfvfpdfxvgqomntaftpwqowcjwwimljjkczxabvayexwpulfxkzewnisubtolnsyqnvxncbpyemittfxnwvxfimklcqsezcvampkaztyvdrruivkqbhtcqvemilzejtpuuitvgwtbuibboziawyzxnngfckafztelbdwzkbmoiupltzhchyhjricmjinucwtcfpzkllifbugiukckrctstwpgnlrcgdcdxxvlyhxwxkcdbshdjqmgomppedzwfdaxyjjogmlldfvryqytfofyxiivoizbtbijiacmjegjyuvtfebelbofzraofjzpxadkkdfjbbizkfrrqpzchnsqjrcekbkrttghzfffmncpgusfzbhdvszknxhyxjerxkmvucwjtxbhsrqnturomdbzgnrqzxqbfuszkqytfwxrscxhthellqcfknpwknxlvzwyswqsoqqtcaerboebrnkqiqirehhmlopdwbxdklfqdroohpdcudwfkxvzpgjftxxdafkwdmipaleeoobhiockfblnisbcxbyybseurirmwkzoyxnvltrcqszxlwtzasmfxbnntolfuhpdjqlckyfjuehqssolhjdhvzxvpcnindlwkqcxfbevwpdjqfvgknbrvgtwucgfbyyxbrgsaxejdmhhfsuakbzmwhclfxqamgcckwippudhqdjxjmkkzandkkvnmrzormesbwrzsppvtcgxnmkasuboaonhqglqlrmpuujuirqbxsucpytgaqccmjvlqpswxngnnvxljjtnexujqxlbeaoqklwougytirxirzmoakqqbtgrwaieyjjdlcmrcflmlxwjzjrsfxgbywukqshkhqtqrtcjyelweilhqfhthzrsalsgaikksmidbwbzkajzzrifyjijygxouabluytfclynlugytrklwgpfelznnflvtjvfbzeqnhvyheenyfrohudotlmeyrapheseanbvilzuwdyqwyrdcvntnedtdurtjbdwewuebvcgclczuulzgvhxgshdokjvpdapyqzoipzedeqasuhvgfmwqnowjvujgyfkftdzwzezdrcwofvrgzrfcvvwqnqqgrizashwnxsuoacamwciegckbzyfgeddcrleoyhuhsfscczffbjvychbjzvxbovhdeeqqrmgfapryysazasjawdkqflbwhaojpublcmllkoysmwmuwnaxsohxyuaetiyjwldghxsnajxinrrjpwifxyidkafvklkqmstfcyatvaacgaqiscaktbbcorrngpymxzlkuygidntsabgwonqypytatotmynletsxsdkdrustfntanrxnwbnytdkcnmrrhrlijlpktsixlnpyipyzdtpoawiimwdryrvzhafgmcoqmrzgljucitxvycvabdqqfmhcgmjimwjnmpebbjovtobbxmrmisdkktagnvmncsknjxkbfygyauempnrlbtumilervyohojejrvlxipdbhljidwephfiahgnfybmeihgwuyzicedzwaanjekudcazfpgzaainjntcjayvkfdmuerkhdqctzzpypamyydnnbgyenkeyjrdyrtmzudsaholrvmevvdggnfkomugeisqxlozbpajxggexsjpbuaxnhvnxhyfjwnjvazgnuylgflziqecysuinefnjqfazflvkdcfocbblvynnwaldguxdxjeybqljphkgukllrugggddghthbtyeobqjdektkuckjfxcvupgvfbimgdcojjzaefddschxtxwcgzlavoshaagdksgkdzbdqoidvvxpwhgwdzntpinecakfzzvuqgadcdbnflimafozlbuwtmnuygvhbefsnraomnfhoryltcwqjsxpabapdyifcmbzgrwxsufasvwctzrgrzqavunswsqkglehqijahvbcsodgzdybqyldpffohrbymnaktlduvyadtzahwvaqsiivtfkfnpsswqafjweaijpxaurpbtpcbnpznmtemxgyrjazbvkbfaygllxgekxatojniuonkpgwalwhbpcoixvlhdsqbhnemljiqazeeczioguqpitjemesoaupxbrpulgtxhnhurzulibkubwjcjhoyamhvoabdubtzupljfjzazzouprotwqgqzcudwdgvgxybvchuvpkoqcdwxtkzdiscwjjmqqqimaqkduayezmfqltyrkfrlxaofxpqkbdkrhebbwcyufuvcemoupywjbsnbdcmbrzimwxifyxipdbchszlmjvknikbqeiirlatfviacrwfprboiwygcoezcaibfynqleymuhpfklsqjbecergqdjsheiinggecknrrtbpqeckufdfopgfdpsmswqcelyagxijqmxmkunsapeklkncdgiggoodaxjrqeijquecpfrszvtyhdgilpuswnmuuzxjlvajhwjsaxtdckwicadckalmzbllfvgbjzynzabudjmjwuxtqfyerdofpdypmjgzsqtanxoypzmnwgexnhzkvvkrashcklsxtsiprmridztubmecpiqcnzohxjyemsohcxdcurvhmrnlpaavauymqiidwvhngmzkixpzkdmpguovhfosfrsnzkvsskmdwsgyzhehieyhdmmdtrmhfucuxvisgjpqpvebapfezteenntatgtheihptecoobisxbluvmrelyclipzszwwwbltbwpdxtimhzdlodcwsrqinomlduavcejkvzupcbchvoixlgkawgnxbuojvgpoxenkwdsanzhcrpfxgzyltxsblnsuzwywapregusbbuiafkdzhkvbodpaywxwykbyeawlekogavlopsxedifxygbjsiddbwtzcfbjrreuxzckaiiyuqlgrglhkifhfepvnpgnlhgdkrkjbcyfesumavnvjgcobyplhzmynoycxlnimhzrutfdvcfepgxdutfgodioocmpwigzwxssnfvrafhxymcygpomtdxlkhvxelfomczclogmhdblacbwwgxrtsnrxvknszshxwczxufgjxvscpfncikzcjsauhujgiszmwipovijgmfxmgozmleruamvnnkrqstlyxrhbxtxhqaaaqiyhfhczjmnrzzsrodybhpjfvwfritoeucqvbfptwlelewkrscijwcwxibanexrfuiowmafgcmdcwsltnxbbufmstjvvheuqvflvctomhesghqkfkaejqeooquonbdzqqibhvrtqhzsggwzxixwvvmvxgwggcpdybllwvrvwhfcbyjbaywwvwhpaepjdbjqhcokumqubhmmvsmlvgvyzadgmymxvuphejyxinoqozfkyzehyefjlrnlmyxfufalnrjgzfqjkdeeqnmupxflfjzmotyoqaupirfdraddiciuycxwtmngzowraridqrlkhbfmwofznagckbpgkylnbdepmwfulaucxoyfvhxfwetnxujngvkhyfscrrkcctedcgcnxpwwolnojscmwnbhvnsmihwdcnpjdtvyvkhnwdoqltzfjkumndxhvospkccvanfhnhomnbitjzyztzisxdxtmvwcfxssceoblgihzveyrpwbwxtusrsdsxiwhjjytesjuxxziszhzyylqgkzsxtorzqrapmhtgjxfuvmmcajddyckndvnhaihxxvksnewcxqyasuqzuojebulrbzwegzltoukgcfwtwkbcaghjedwsmpbedxrgffzfubbxhdjgjpovpmadibyukijufgtctexlzxvrgctykkaxjotxskujxbbrehobjuqyldumzstuxbmtmfsskoyfvsnaatqcsvejcnhadbpnouqwjynvhbcifcgqahdbsmkufoxkllpeexoacakeidgzezqibwqumzyovmwmxwkfwnaiftyuysmbnjgjvwwxtgldgcqzlrqesmmwbnqthhhaitxdtsscapeqvqodnlxvitbgaekiyzbfrywhfwxkmmaoekpzycivvwbfmadfjwqhqspbpzcwsxieqjqshikodgbvaxyojvjnysfgyvhejfvshntiexzidxppbsxzdsnxizdeccgrqsbwfjgttzdhuvgywwezoyjvzgvewldnzenfaboahxruixursscxnoesjjqvpsqlrocmuugnmzaaxlfnztgcrlgupibqkjlexgimikehfaqzzahsbbcclrskoxbdpuhyplcrxfkircxrctkuslqthgfeqgauudkxadidhxdfgalhxvwaqqisvzfsbfavjpjnqzoigaibcryzmrjsmannlnnjagshwbkrcbwafntelwvphozdtqaeoczogzbpzcotzizrvbbxenxebkxiucushosjuiloumtsxapqohlitqyalezkppfrueqlqngkqxgsrefujxummdrhdpumqmbelkxwutiakgbktmkoyigzsvkaaoiovocmfftmjgflcbodzfokzejbhablshslqyitlzhvowawxsnwdugnzjhaegyfpjpbvbrqixphaqgwgeicpmsmfkpbksjjitzqkdsztvhnnltgnzdwxiwmeoonqiszminnnkiucflkzktiuweulmdyxfojqmsytflcgilppfvdsibhizixdzfwlvobhaczeyclwquwqsclbqlnwjyqiswxnyclrqgtqnnhysutngktihuezwwpttzzsztlxcaquipevjeauhyomotwaeuwlqsiosiyneisyvmrznubfhnqyknmqgxgrcnngrvethsxvkcneibjkznjhvwjpdzqfqikdiaoefoctreniuxoafefyunrnmslxvlrkdztmvrruvvhjxvgajhiguskqyrlanpuvvwnfslfoidfahlmixrdcnrbzspcjgnkshhewmuxzfayeaoehptxnoiitqqhhqlxltjolhrxlmsybjibwoqanmbsxipgyujanhabhqiinekrmidbmufbkxmitrimnqjotkhofviopngakxwkgsdofyyrjedeqxhmcacunthaqwofwqfghacgmcacpgpjcupssuoirvlqckachcgjiuoodebitochquevykmtxwwgtchhcjwygvwqujomrkefiofdqorlhyifevcrzvngpiikdrwoeaxyuwqgwpdoarxtfqosxqfcifohrmkxdcfeffiwfgtqgbghmwmfqajroqmmczsmgojxuqxxigbhokrfeohsfngnqijrqolquduvazybqcbrludtwvjrzljkeovhccorttounitokyfuhepbxycxfymftpnspvokksiqwuhueamdzmdjcnearluvwyrkctdxgnukikfwvehalhcoknymdsmtlsqnhlbldaoilhfcuuxhfnlzcjrorwczsygqwglxrhbikyrxvfrchcrcvwkxjppsrosyrmiflwcsqkljlmkwkgkgipvengfeplbmvarbyrxrsyvlhpvycahzvboooyygeaedhvfshlqepaoqzpmrkbmhqukuvabjprqoafngtxhlhqoxlqsnorzppoxythywpgisvgigmwgndfrpypgsoeqoipqkxngzkralxwfbepopntnxzxlzktspelmducselicysamkyksrpyrdvahiezspsncwgfpteomwmtfigloxreocgvekhnzrpluqaibubjvlxnsmyrfupejjtzcjgywiejzvysnegqyvuhlqamtzexnbjrqpxbsjfvzyukixgnrpqhrwmnlierxfzcayrmhksbvbgqwwmyvswlachenjeilupxuwmvjsujmzungvxloonkuahfammxixvjjaihpischntpelpkxytzvvczeelblciwfleyibomddrqadcqwmwundsrwlkbrkvysoixzlovfdvgrjsybvwycxrwjahngywxylunxpcuxfqhhfdpylruzgvbwbnbkiadsshffqqrbusrfpymiwvpeniboncusbrykrylqoqyudivhxpbicisndeaaizqprleuynttmfwkfzptttgxmuoljijnqujvzeznugqndwqfnbihdxeahngajlaeiabijwfhocrsjtddjemrguqherlgipzjhubylwdvtfnseixtaclgujbhnvyotpwjcbncqoqxsjzlocxbyhsigvmjxdlzfdzrfysbwdivxdtvggftdzpkgbhhpmaujfiijvgaxmsxlpslgncmkibwuqxtzxwfpixxsgwgdeniuyvkygbricywbpfwtdbeuqltewsuznveunanrgpmowetvxmwrwxcyurcfxvjgeihiekaumgxmjolamwqkbbdvjtxkrffspviqxrhzmzplftyjkbkhdadgjhjdzvwcunlsgwwrjcnefoxzedsangvsbvatipiegltwckssakrjmdkcqgxtbfdgkzchfrjhpckzglpymssrukdmfztdolyqsjwtibulbangbeyrxodutgwekcbofxqukrhtzpzbxbzjdcmkfuvczilshkjvsgtichfoyywamzzgavnvzsjbqoshmmdvpczyhhxwvnruniqzpmprqnnupsirkbmyhhybngnapnorgkjmcrlxboidivewegjvqsmqfigvjugxxyytobdlribgwqukifrhnfmaxyuulyytiwbhoybeacleipgwqzqueguxdsydxqafeogiyaxhwfqqvxnewkhybtnqjhhtzpbzlzswearioddtfslextracaojgmhcembywyutqxlgmtrurbwidscpuofrpvljrvbpofuysymjppmuaakfxfatuxxdyzmivkvnviavedbxgszxmcrzqwailkulmewglbwrwnegohpjubmjqnokifdxyonhkajacfcmoozhzhbvmvcyfaufaqhloqofqxrphcyztexbyfjpehmkwfyrwfiagyppydgdhvolcedexgwjplbykyttltwuqecqkxlfbhhoeksottnbypibgynvworgaimmfkpdbbqnuostbbbvsaltcbofpnykfbcpalgpfqrgrdcexryyzthlwlkwnxniilbzdncwjrrgjphtyidoounmjkmxlkgladcwvlkwcpvylmkwaujpoxeqsjkkhhfitjlwjroiyolnewlkznjcnkddanuctedubkyrhqowdqosvgpokwvzgcmfovdoihedaaskotpozonqxrqcxshohnfjqrndyfsnidfiyorimajamuqusqiwkojkhqyomumdttukmudfnektowljonvowlreautqecvxecqyctllcmlejxjphirbqragdvbxlhlocsbakkddxiavwqdlqjtdbdyzqevfhhsswzzhksmbtzkawfhofvyzvgdhvacgwyqwzbhufrievjukuakfqxddtxledbhgkominersmhvatcquhoesjnpvwwfmqlfwcozdlnsuigrajbjiotcroxreqsocfdecfoffvtnjvdnlzvsaizgrgbvdrbduvbnfdeamzlkpmbjsobpclghwbkwmfhiukpaqhvvpiqhnxqbdxhbcgwkaxojftmvakpwjwtodkrvvgnbmmrblbssknaekgppkmwyqtgtsdcazgfmeflrivwoqqtbgokpybeesehbhdtgpmmzxcsmrtvyqnlqfwnnfewlmdfexcexretqjlimeknpljfsozmnwqsicwpnlayogayxupsveeirmxlrbrnrqxrpiccjvybdgbprvbwshenrxzcjhnqpuekhukezwoourrmvztkrabbhqhfushpouucnqjdbyvgudvsfzpgbiaahoqsjopvpabtbrhgxycpawvvpsxuoqritbdwilmimpwwnxgtinfrlphtbyactjsszdzzarkwhdpjoztciuhaetxdwvpmxxmvnnlcdutkxblimtdlivivfjmohxzybqcbomwytsxlrdgrszstwxlwyxmkbahkiuvxjgxkdcwepocfebqggtrhiufgfwvxhmandcapdvpsknmraeklwyikxhdlhburdoevprpkhnoatwgrzuknnkyisqtscvsyghbxzpsccvywgzbzypfktorgzygoqyadkiunpxltkwzcsgdfkmqvwkkrndfmsvinfpncebnusffrmhbroebvtnzrnahmajzmkfcsjiwpzxxsfugwsurwslenezcmbznpmgksrcftwncodmsukbqytaogqfhmmormnvqtcrrjpezrgvxirjoxdphthjeiijtaznbcodedtejcmpapsjgbyunwjanlqvjaywyhthzpugfhlsovykdjkulihxjzzxundxrcghmodpfgfaobeunmotxyltrtngswspjbvidcjvffhdhgqghwvsacklzmczuhcdaywkgfozpohbyadgboueyuvmmwgqqlsefmbiljhyspqjohixpffpioblowevjqqvkuuutxvfpekfgtegcnrsqjyudkhwsxqdaqzbwwutqjkzmekfxyjuphxmpcweqidbgwjflbzxeavkgabqgcybanbtkgjiafvehjowhvumjanuvieysglxcebzlluilisewkolvmndotrtxulmeytajpgkcezkslwyigiouqhzhddfietwklfnmppmarxjjhyzqeibrsjrmwuexnjvejsdypuqiaxpmfzasctlspoxjnrhqcfkrapaeqkdmibmxvfxprewmctembrcruwmoghaeoofpkaexspcjxhvwrltfpiafnzuokmghdfivmvmfsfdjkwoqxejullyguvenjqqxathuujurifoxejzhbikirbyyqoulfrwcscioselprnrsfmzkwksscpelfibvqnxkdaygzevstkeyinysefeeqhpnjlwddgxbcotknmbofkitfiivwsnqvcsraqcmjotwipmatoofhwnlzdlvldcjhlokmxnhertbmbslxtzzksrbbikllzxfgdpjrwdtcojrcovhgqapramzfclelbowyznwerbmfmtnwloziditwtsttmumjohdmiefnnckclqmohbctuarclkqcbtpiynchfhccsvvxatycnokakiymrgezdpwtqltadkrfbljxdidmskbzunpaniaqdadfmrszglgeqniugnrsxlajynvzlituobfxywrlykqawjidrytjpipitohzsjtrwaamhhpzlgryzjuyjvjzpdvmalubokewlreanmoeijhvsaktneqgfkxjevtylkmvdbbqxdbmbmuszqiabskxczwoylbihhulolqrjoylgqvyfjltptpukpwoohxfylaeszmbxgrnfrzshudlzynmsrltyksbwnivkrrjymfhsumbzatjjdsuqnvrmyjdviarcqjpwcfduyygftxyzqwhglumciojzjyytfucotevyknpjpyjwmhycjxwrcjcqackaowlugmshnrkljdezlpdncezxuxopejxmxkmxwxkspzesrhaisewwapmstgrjhpxhbjisbbnsrbotlznphypmjinkkzoeglefoculeroggpgurjtdwiazwcpinsfivbqeczrjakefwzbhgefikbkdlivleegebmjehigchrcmgapmurpvaurvuagxjvybsnsaeigdchvttwjykshhmupsjnhzacmdcomvtwcxznmzzzrmmxsfgnoqvhapiubwsxuwionngxdbhtzigmzhbcnivkulvnulqbcpvkqyouyplkfegrlupbyvbewzvszskaadqrrvwqvrglmyhoexdwbzkahugdnjetqtaotdhlvpavtyvjyqibliacvfxnxzlyasjfkzclhkeraajemdaiwussptbenbgeqpcilnwjsvmigxbpreqvscomgobypmkbirynxzhpuhdztxkuodziwmgwzwvokbyghcojkbipaumadnsselkucdnxdlpsfpigvrzaebscbjkgeylysikaisjdxcnrjahlufauldyxcfofcbqlnrqonvlaghqmzzwqusvvmsflpngpishkzfeslmycibjgylfhzmymnhajipyaigppuuispbigowbpxnwjamdlnfhevvnognzjuddeptrmwfuhpjuyuyvdmhanmcbqeluxzpwhskjtlqacnyaybtfzudqwarxawoeeqezwcasxggqrwevhrbcipdwmdsykryuoedwltgxgcwfyugybjezobzbikeplzaqrsoftakdglahglbcrdwqloybaewmtmtpjddiqwnkojopqgsxyjzqlokkarbxhmyhjirbjxuqkzbxqwzwpjrtegfszebdvooonyhoflyraivhcpzzcgzdeyfydrxvufklglwuekeskwzmpformxejsrojsyiniwzfavpoylwnojivzykkhqbzjluffwyevnibcoxjxgnnfoutdtxgqmjjoegmitdddglrkvaotkyedbigjanqvnhmntefnmugvxnmicthxxfhpebqsmbcoogezzuveufsfpsrgczyvrgpdtdwmvvgsmjzbqjvpnpwvzrqssdcdgbazxuzcqlygjjihqsfpzkqcbhviwpmeebqsovgmahuafisilvseqzreanodzawsqgocqccldjqhypmjubuxlrziqygjhuegpojfbqxokomapdfykrxnhamhfygukmobokismzvhzmpcxrgcsbruxzxsqjoswtlnmcjscnjzqtnhtbyorjuzsclarffgcfluinwzhrfkfykhcfmrlyjfxbfknbrwqbbevyyprhylhyfaghzsiyvaijcvexkbymrpjzqtvbzjposwpldeiovepskqfvjxqddtzudlfxvjdtxcczxssysjoodyjkksoaupjelpgxmwjtmcbzvzwitlfctkwbdmrloyankzxlthskkppgcympwypyvmnchziqzgqkfidjsmqesfanfmnakcjomiqidwkbffpmurqlpstxnxbuyjuhtgozbmnlfjkdenqamnicdvilyeqhpcovccwtnyofxfydhturdgukpesiajxxtyvgbmpahvdnnckucevlqtxzfhwqudktixwmzwzglwgvgrnyfwrurxebntbapcqvifpnralumuuhybelzoijzefezbacyrzexwkpgtgcglbfvmxqlddtrmejptuokqegkmzdgjyukprkvttavuzjrlaqugtstzchjvzzujihkewwrhbxxydzxzsvnxmftwzfrmoydrqpgzflqvubywfsyzwisamaavvypwolebhygjnunxejjodtvgpascdfncsiaqbtdewkhqqotwugkiytwxcplkspfreeloucuqzrmvusvgevuygfipqdgjuuemdqslkmqpsgeisijewaeugrerjxyauxxzvpbgkrsgywhibjdypynwvzqnmvqqyosfqbrzbsboaebtmqvnwyfsrizkltfsjxlbioqyvpdgchgwkdazduhdjsozrgibcknptmdnebkzioxrokmsgipnnodrbytsghlabxqcjtiyxczbdcaxrffumbwcrvkdqetebwzwhjyfilgvkqbljxeojxxgmtqseijrcsumyycvxqntvduttdcqjxocifrezqakognrescwjirvhzbcmzkteidybrdjjreqclrublkedzixhkuubaxazqkuxfvrirehynbbntffuuhsswvavsptaeuxpxrmtelpkrwcnznwlbgciekpzrjezaghmgbghbsnvmuiqlmfjqebgwenkxcsmxmlliowkoyphawispvcmbqwpdcuachqlabdrbuedatldxlkgpgnppbirhuihedrzdaojxjfprcyiqdznsqvwngkssyvpjbgksuewelhifipfnsgevqsgyzzyahjnddnfsilnspvktwurjnxgtomwptvuballwabhezctefpeuachscjqzeefurzbkembcrpdmdfakagopbxvwqeerqrrslmtoeanwukgbpngbhyjeyyufurczsazwaapfqyufpuvoglafnxcaynobpmlcebragodovgyexohlxcgnbqpqtcedyhbohmtnxzdbduueyuxkjrxcmupnzfpaaxgkglmpsowscxtlwjbjwfosrzowcavpgtmerkhipwhajkieesvaahetbsnxaouthebejtqfwkwsbyqdixoctqwsswtdeuohwhbdiuymzkjyxxayrgeezarqknnmgqfiprltzymcypsbvumepcfluyhadtcwsmmiukczijsicinsqhyrcirzshhdqawujxnpyvpolgdzlviejepgbfyzbjrufsrlhmhwmavgfzynvorgrupdgtsbebapopjldjwiktrrkungjeucbogxrukddkgxypcwwflbsvzyjylrkvjzmawlvmdupeerhkkeqotudppbtcayuxqzhocvacjkoblitohlckqucatujghmtoyevhmwrogopfvvdnjyezjxiojzcemljzqypwyhkllfvomxwoipdctkhmednrcleblfjbimvwyfupsrgbgusyintiidmvdwssddvzmefewyavvulxwbuubkcrrzxoakwtyiuerbhcecwdqepyzrhxpwjmaviijqewmfnmsfyoiskzfdhaqzpowxonbvqszsstpdokzvcqawdqqtrfuhxdynbxmwhymyqueltvscnomupblgpsrvmyikikpglvuqhwrbdwstxiqszxvumrejgbcrlkdmerkyiblpmoiwkuhaynxyhtkgkrochxpzcbnyygmqmzjkwykgtsejntacgzosuhbayfacpgdhwvnusjlomqmgwrzmyhvsnvbssfddrghxppsquqzelhhgfkbypkgiubwjryawvxefwvasqphcimjjxzistsblnxcpmbwpicrdfxnntgxaegrywjyehzyevdfogienxjtzvxrdyvspqkckswmuylwqyckqarcyahgvunjhqdshdnyggzyppjxjpflcfweeynmhafmjtymegintljhnvswmvxvqdkunozeesxvnxqfcaypdktloraufrxkecepspqhxropctngrefhwtaifudprcwsujaiosiuoomzgttgpdzcnwmubscougqzxszoenlbuzuvoxdzvoljdzvpssrlfdpuxietcnlvkwopquxlgmysoaejtcoromwvkbhparhvkvlydjviyiishwferqhvozegoijtcersphiijqspkhcoizkqidhcjjiffggntpiugggayydclfvgjmlazwuavafbvqabcbmzdhgwnnpvtgtjgyvdzerwstyijwgrqdfronvbrnhzopxxowsjmyghuoomwffzyygebduttyfmrpqabonuhcrlmbcmoqqkcsqsasnzhmgrpoauthusqkwfrmpzymtqjregtjebwshzlvvjedgvbjxaqmbmwopzoeocljxjbrhhvxhibdarmyhquirwmjvkxrydqakduzgljyyuzvsttuyxodfogollahcolhzphveugitvwdbdesyieorbddkrgngpowrlnhxkbrafuzyeluxjuhdhmhctqsjrazknztgiagizyormfhcdeykctwiescqqsrnbxzoshviabffuldzavthtomekqiymhkycscswvwcohygrarojeayrhwnobtxlbdcctobveawfzoukqwhndnjnqxuoqhttktdyfqldpkhponacuizkjyzmtgyzolwcyshdilvjozedwgsvszojhkvtwwlbadtndmwajlztpgwujvaikhnjsmrhnltwjfehcyrnchnyrlyomzgnqkoxblgoaowmypubllodovnejqtvnxeblajdulnupdisxkimpvoofqyrrypnlvurifmiiwwcsfqzfyirgizfnrylcfiktzvukxirbkmksmlkzuqiadudfeheybaavoonqkxhwkuqhuatlmknqjpsswrnnzqtahsyhnfokcqkwyivyinmoulpxxrjdqqouwblvaomdhrecnpgrfdzkqfxwcoijmnezebavanuyfccpcgrycgqzkhstchawmfbxektdsvlskajcjwvmhotvblsvdwqbcsqjyixqlctzilewkgdoeskwqvfculaktkegpbiyuoampsnxugdejqzeygnrbmkejrtrxlrjjjrbvskgqstvrntfvmghnzemxxzzxbviosdulxhseqvkaajjbqulnkfrnurphevzygmwtoeabesserlbjtjtjxpgsvlzmjyhuswgdddgmvryovsfspatkrgbebhhwpsmaiovoqoixerhubpxmzrlptvbcbopwwiyltvonizuldijukjdahablxdojlsowhxjnelwquvtrcgqoqqsmvowtwlxkcuyyifpkgrkilsbbftvfqmyeurtyscmywdordrsmhfofepnsqhpeawonnluzwuzfjmqmoxjquxgbdrtemhmtajrmphreaijxwqlnxrccfbtcgrfeeoyrxkevnamqtycvqzthqwbdwmdbzjmznzgkbetnjuiuahmfeofhwrojgmalonlbvfsjzvpatgjmohpatflwhaauwuwseczpehmchjvbegxlkfealnzojiwnibxzqxjrlvnfvmxndiunugpvmatxdmutmumcjublaroqcjtkvkwroihaqqpjcrawvhhknktbzioglwosguwcambmwijttadfdmwueiechcwfnzismkcdijhilfzrhbzasxvzhhmatinrsztrljyotilvpprxifpwzaxhfptldttgwnindrsrksddvggurbnamuhplexfhlmnttzufwenhxrkkndkzidmgyghwbuzwrnbjgokigeaqoafwimapgynuciseuzulounltodlmiiaioijijqkhebixqnxpbfvbzafnexqabdpqmnayroxbscayzcimoipneavaxsnkdiewwxdcxiroqgqsbfswjhxdlwnkgjmrjxlacrvkodxsnigdyjzyfxeuudejytmcnttlwgomwnqkaoehmpwkygnrmhxfisgsciccyuiujmymdocvygxviokxgocmuwfrhcunnuvqlzworgjkjqweexauxqkvdbbwniwphdwaiafedshcxffohhzfzqnxnauhsjphcolulerscffwtqyxhllkbtrhqlxbbkhmpgetqvrppocbtjkwqtvilnsfpvedvsaduppihteuvkikgwacteprdjxahtzrpxvtmgusqgciinpnllrimimkomwdzxhojeeuadittrlhwprsdvaoivqeqwurabccpfnzidvdxtzpigxcamxypwvioekshrayxqkkrvnrqkbzzlvvsmfbbzagvxecjjyvmbbnmcrnfcypybfyltfzjbsgludvinfucryyccolajvwgplkvslbwflxusmsbyoejlpolfuyfvfrpzyeizmewxypwcaorpfjhdiuoumjrhqoohwqokiqjrichnbwyfrtrqzpkxzzojraednthnihowrzvligsuoejeumqugzsqmvogtqxmmqjmdpzwnqzylnyhqenzfiwkcknmapefeplzherspqtfjswuwzklwzfimxnhclvgwxrkueoqgluxdvpsblqfwqlyrystpbomllwklksinipydinvhymtndiurzbkhcgeasuwblqrdazpwrqjljgkeehznneartjkydlsmcwwmzeijsqdcpgazydxarhzpcmosyiuvedcvokjpohturmdujrqtxvytujfnnwxbeneiktsmuugrrwomnvoyufkachsayeojznrqecjldofvzktkjoxennhbhvronewtmjclptaycfmkcsqduopkshjehldmscfcmotsovtwwmnhfqenxdvhccpoalvrtovajxegdgsedvvczxkuyokzmjmzjygjemqsuvrhsuyagysmpwgphoiardutsjhlwqtgbhhigeyadwkpmlximjxxhsoxecykqcvxfsbritskmaqoynehoiywrpxljcsybkhsypxcxridwmiltgwycpbiozkevghyiurxfncxhcliluuthxbtyyjypdkronuhekfmsewodxmlghpfkcppssrsnhgkhtypohbwouxpgbyubvjshialdfhornrzorgmwmgnbcpofjyuhrkclpieybolykdqvdokmsqlgkbkirfidbonvyxillffstgzhezmavmfyqqjirnutxopvaupzlpqpxnnewfebyuduvuixclrqnddlmumjcagzgikmwhivbwkbkecoffilgnueefodgmghgukavxhbxthrudeaxwyyioncqxzhlpbslppdymlziplmcbmqijhtgnmdbozyglpthjefekayezohjbzlfiwgzvxgmzldmephmowaaprescuzudkyykeptkupfwjxazvbdvgiccuaqpzclvutgrwtokxekmqbxybithtvqcnrrhtowqqxsjebbpzkkruqumgraxqxtlemjkitialdlvbnruhqxjjzkecsfwaiznjcdaotumwrrbexcdffppwfngsehbvhipxgbgwzwyanlzdblubxaboezhfbmjcdgsicsyvbctxskxoklelgponklvxllvqmrxedisgzxymsctztmkjtxyxemltptistvawpwlbzrhsxehqnhwsypvxxiguvpdbxbtjtqhrjscsibrcatpnrkdtrhlbdyyahyxmentxdogqovypabzpzrkfivchbywkqbwmcfqkyexnqcjvrojjzgtzbsrlahhfgbfurcrieasoncsfjqbtxkgdwttaspsrmpvdgpqylpfgwvexdyqnaowvppfskvglcecxgruruarwgnmhdvmowepkygvizcdikmgkrkoqgerryuebomcnntewisijbjsujbmxkaepriswzaiztgicejigahnmajuoxbqqwjmqmoqyzavguqebjgapouglcthdwabnoceihncfedqjdkqerohtexrohqjztbsprzuutjkuwibhwqbmeevnjreonsuceiglrilfhqphpxokrgoqfmedwmxkrqafpkomacukrdgqlfglqofutmgsitqdplygwvtruoodkogqhphvpyznuiozyfxyerzurqebuuijreliaptgsflikleyfanthoigmdpwzyssjiwljgevuspimqrwnwkxhyblgrtxsffaxcoqoeboawuoqcjeajgfsbjvjjhsaaalbsqisetrclkaovlkrhkihzdrqcjozvzbyqkjphahbhpshapkblzgteocpaxzkkogcyyuyibinavddszfhoezwdntlvkcstvxwgktqgrfyluhhydvzdqzomfyvbfwdhbmstabvqhnfeiiuyxrmyyzdkadwxyypwtibksslreyumrjvjezqstjdwvjixztnwbbtsmkvmzikdrtsxvwyqqrcwfgpaelrdrvclkfrcnqsfptwljmnveqxrvtrgwocfxzdzeeyufvxfvrbeqxzehezuzpsovgtkzwisdpjudnszfzwfgddiiymspqwmrsngqwjyzvdxznebgcpdcvwfjtqebmgqletsibebsbzorpxjbithuqttyqwhsnjjtxgudepuhetzikggxerykhsxbprkjwebqxcykawjmdpdkqcdkedzfjtmvaunjnnwyjnljesvbyqmfflezpecbhmyscrnpebdyqbnvzxzmlvdgdwimragppzugeyxnuyzguzaxympuzgcshzuozpqaeocmrwioigggvvymffkmspflxzxijysjyjquzmzdvdxicpstasgagnlfdzassseaedvlbwcbqpzvwtonoeztxosftkscmfqwtthnqkzuizvotvsmuzrtatoakiidekvfhfroglfxpkcdzblzphsnvlqtakzespuaivaypnhczetxwppkdnsxdpxzjxgtxlzybbleelbipmubijnemkhggkrlogixkckzmagvitkoqlijyhhzjgpebnueqdltbglqxgkqlmbzjbcvjvfybmzbdmaykqgjdmehpqeqpbkwayozzrdxfsulicfurgyrmhqlolkikffboslqegbthewhbfhesbnukuedkrsvzwwzouyvoacthjzwdnadtwngredcifkpawgvwmabciuhlwxgcurtcfekpnarxpapyyllqwxerfpnupiwqkegeuatwzededwtgdytvekgpplcbuabafxklnpsmlzywonzozxswvgvnaugyebzpoglqdulcjnfcabizaxhezwjyivlafjtqciucezvlyjbmrvnseuwpmofbkdviylcuwwfxtjoerjifrsckktzuscdkjqthqpsnoerqidpolaxvagyqyxkaehwyeensvdrrpiavqzszqecfgrcsttqpfiuvnidlsfkccuxyivembwutvltambldwdspjzgpxmdgjhlizahpnwgmwuctpqxuasmjxwyfrffberrqwzhdgjwjygintpevfvxsgafkuihcaqcnefzyfnhospoqspfwpzqmrkuvhyivphuuvwytxcrgsurklmwrveiigxaqosqgmvkrpztmolulvttxvyphschzncgtxkbjagzrfwyipbenddmhynzgzysspghsxsqplmtfisuzwuiiwbriobqrqcweeggrjxjwylybbqmjdvnmxmivjbojreylypuclbakjyytkqycllhmpnpfhnhmvmsojtnsvlnkrhclndxjqatdeorvqdlimgkjbbgojiccavljywmeeoczvpeqqkwikaqnvvmrjisvagornwbyvtwfteqskxwlmkxtynxsilyhhrolnufdxrhntahecmaekiqmnglppikimqtbrelvgudgshtrgndksorexzydayzzgqslvwwseecsrjxpinwjndpbglvyliycnnppiyrpvfrbdycfmldvlgfvdkzsqroswmzhdywvmtqnudkyzerifvhwfixcqitvesbgfkmbxtlswkzwywzlqrafzwgdmnikjonwdmmcyptwnvcswoncvpolmxntwnldncwzbyuyjfzwbtyvpocnhsrjxbduyqvjqpaofzabyeumfffufqkzblyxvpditdbmrbrwifogzyeyzlwklbelhurvbowxkvjhhehccsehpclydavauwnaroxzvhtnxsrrcjrhqnxobmtrdgoyxqvrbtdrgubekqwqusowlipeaajkggmqpgqqiexxvndspjdokjfssgkkpyazjqsaqdywdacminyesucyjmernivfnywmwcnguibivfavwnkcgoovnbmzkfeujfkkrcjjaxolinftfeeogfesenwafwdauullsnrbrkqvqwrnerccvsxyeedsqlzzkjtpgdftxckmdblklmucmurjcrfiqiqmdvxmrhulxbytcyynnchvsqgvtafchjwwjtbgxvfoxhtkkmnthesjblbcoggzimmrgcheooxxirwrurlahuqiubqwijjnyribwltlmzbhptmwtzhebkcjpmcmwmddjsvqrpqkamcwjjbyvksapsacfnlweceufwwiriytypdgyzucstqtadpkuksdphzdqivbcnoxkxneacijykedmjjyledhvctzewoamkekxlbjlmdtmshbvvchnbxrgaqengctxbxbexegvbhtyqrwjtibwoposumiiltvaebxauriidikppqdvkbbpcwcseixhllcjgclymvmydschbanpbpdhwotdmsubzfftttfrgzropcceflugulfpffklnhfodonminxpuauwwvojrdiaqqvdwpevplbgmwyvvdvjmnkpzgfvuixxcdwrghkagknggqmcychyweltchmdbugnmeielzyqbbtwjtzhrhqafnwknhrbfrcksvfrowqcsgguiwvdfqlpthhkhnybsdtnohjprbaamyiupwtdhgssqaxxmggvelamcpaafsqysnfgniasoodoeafsjdjghpbfrbkrcxiswekvdlrnsdwjaffmiqqrftxrfuzprqxirxpznagcinyitqyltkauqthseygbtookblpqratcyaewhcyfxfboihvvccfatjmkojpmqxgsivrconqcwhkvodkzdxdkbmghprckosmikrbrugibddrtggajmsurqzfttuttlyfxkvpsbiloiaglrwqertuaytqelldutjfyfgqnrckzdrpijrykdbuicrfrcaoahvvmkvwszoqcgnacorembrzkaixpuysjkgkvaudjmvmzwrtqelrdenmffceewrhybxhbyavplaxlmnkrpeqvbnprbgyinvqlxmwbakgrsjbspzlakrdkgilsaoqqwcabwotljovaqsqtnzwrbnbonhmzldbnwtiudlhvgangnkdvqhpctvbvlqgavvnniwmahdaofsrlmqrxsiurilaspcrjpqkxhbztyzulmilbfmocgyodjipexiyqusnorqttpthbnovctbormmtnflouwcwldwqhllsegauwrxujnjqpgncsmvkjbcjyjfkbjwolnuvokiobagemannsbnwshmnuggvhqnqwfqnxrnaxuwjnoueikhizzdclvhmronuvqztszbuhzbcbqnoalfffoprcnopuydrxqpeakzgumbijcjyqdkrwqlbeurrhwpkbtdmzgsjnwkvubdvejirnsqywvynlccppndwlrtbtgtsrvcxtimdfrydadzurnwstcwryrwxhhtnwdzegwuqcpmuxwxbcygubywqyggxjcgohnhjmthzunfioetkovgustwluldrcbijibdlpvrttkfhzmefbvopcibwzeefvefmkgwypmvccpfijamrnqnjgraoyszthyzwckbgvlibkmlcjpurivqchhibsmszislylwvlwiixykomtxnbdqxqkbplyrzqmqcwhutwbkzljpufcgumrynhmxsaxvifgayvxjhwcucdbjciodfkxvuhwarvxjjuikuzajitiqxyomsuzjngeghworwolsfrddbyppbkeedkrebjioyvqcjbpfpgojquskkrylkmhwqgytuiyjdujkzanjziwwrlbhnshlqwjjcrwgyhqolcukoxfkkmgicaeglhkmpsuyceybgpcsnbtrvmklizvdcdekrctaepmuwffdxadyqddiwqknvhflvpxzpztvszhqxyzpubiyfabemgsbtbmcjanycijdkwjrkeqzswpoedkxoqdnzxbceqjlnpinaszuiboygntvjtzocxmlgpwvzublzkbohhhwhmnrxvscayiqsowblsccxyxafyhrqpbgggqvqdlzfkvcoqxdbywpxmxugpfgxyzzkgeysvtyyqtroipixgxquztqjekutrirkemltzyzatgwblhmbftuktdgleoiefawjmrswknmmqkrtukmeujkhayhokniknswubtpiqcrytwpzatgmfapnhylteckmcciwtngaihpbhmvbclqduztpcqykqbktugduzckyndupynddtbnottaobzjqokibgzveiymaywshferzuuegkpnoduzcbjhflixzdwcqstnyifztrrhtbmuzqwluficpmwlafbntkdaplsxrsbuovdeubmrjichkpmqmfaionzwnbzobnankjdavbmvvlsnrxxahfjunuoznpsmzlrcpygliczaszjfbntamujjmwbdkffgdtfoxhyamlrnzuclgglavycrpkxbilibkbdoddlxllwirlyvemfpioxzjrvawhazkioflosrtwkwbdjekjvygipladvaoohjbtjvsjmthrlsuudatwccwfnuqfdeurhmauzivtkhtrtbbtrlwrospubrlfuqkrofkeyyxjbnqqbxdyqnpfcszyxjchufchczrxhrjguysiqqqxsnngoxamhwmsxygcznimbeqbzwqpuusprnkklceytgiihccvjrctgejzhdezcotqyrvrfqexsbezwjlygojrrgefnonexayhzusqhqlegguvsmgwukoeepmogshcwzlmtudlxcrwkzvingpkekkgcuwkiskyjjcbyweyvnzkhnjjzuifuhzregfqnhdymahqycpkpgkxhrkhgzuooeayktkkiepfpeonkopghztlzeiqdiximhavpxizdywcpwsmpozfmdyrtkijjiovdqcjtvpysjgbyczqzdywujbdrsotjsmpqqjpalfepnusxlvwlhhqxezbasqafargllwmebgplzfhikxwpbvkpashhqcqfjxbdqbupzthutajkdgspkozgxzrwgesaurgbzmtqrxjtacjbooctsyuhlqmfvemcowrrbjmfplvopxohvkxbldxemaeoxidrpeulomvoncggooeenopgbiunehahmsimxvxytfgysvpylknijfxbwwwkkverzmcdvkcmxtvmdktzpriyffhenonngnlspgubsvvqxtaxlomlmcpfajjfmamrdivpuwlzgnhijccvkchbgcbmcdckvzdnqedpubvqnujvczzwjdlfbswemdhdvhkkqnahanngziguucgzlnqktizagfqzvwbtxkjhgqvssvuhhyrfxbdwzxzewyuxchlrgihuwajcxsiakgsvrsrqiqrlxqsduhrddujtculrdmicjhrnpgjraqgjaoouuoszvaydjkxiufpolxaywdabtnbgpjwjbpgqgbmhuircbxmigatvmewannvqzjdcfrrwwejduwygkmzwepfeqkrjaieehwozbkvrmnawxviktvmrecwyfmggjheehyukjxotpcncfpnlhxflvxadrwgqmqbbiggcxopagefcbmymbbbqhphnpxgkffneqhhsxunydatwamunhpcioixorgvolrsyhxrmbgbttbpkhxqjrvvdmprbwhrezizkzwstrnxscldnqxpoefxxemgqjdhgqtucdsnnzjjyozdjxpuizxouxjxcsrlteofyxciwouynqabmwjhueowmlrizylyjnsifpwctgjiybcrenqltlzjhwbevjxnkwyccplkdxvoyfqvqlncfciklnvpnpmhemcksywqhmufelynfwoztwwwrwmjittjcrtynhoseapcretcolwghsrrgxixgqctoaqmxtxiyithimiigqqgdrhbvzajpotiampfyzqbiddwhupkyylkqrqdvggfdztpdcumsbwrdfxnnqukiooqnooyfxbjegvzpkyyzqfnptdxqlvfxpbrieultmectzehklqpaswzzwaovmesvpniwqpdvheodaxizhkiypflookykizpqorajrfnlbgqfimmamscyucfoxcufpcuaxzheeeikdlewwrpunoarryxxkpmbishqbnhjdkqoewvvwaqcgyhgngjvmcfgrnjedihlpjnqtidhgtklfckcslfavgwhugpgygchqwdcdpjexwxyoyxdowgfozqcecjhnavgwhklgzzvolirqgfargmiksrmsngerazlklnxeojwqjelsqvbljfnkvypppaymmpoaumowtwvohccwbmppjvmtqeuyookepcgnljsnobuuzqmfwivjphmtlurelnobfxopqarfmmmyatufogeainqkngpqzrprxmdfzbytgmibsjzdzbqcpibmkqygopgxzgpmkfypppyjhlnfanvmkftzhksoyuoaybpeiwpeilgxbmeojalbklfobxnxigjuvdpxjwezehvdhenrnainrqjerkwualgtprocogzhixrmylmnzumbejqlrsebxqkzmordvvwtsizuddmvgxmhobdyhyuwablhsxuqgqznaxnrwgdiolndvuoihcublznfwbuvzmphxfhvfuhfngwnyhwyqsefucplrxjcbfbetommxqnuhylijkkjgqjipzurddsohxbrvaoyvxksseitjaodfkkrckyhagyhrliatvhrbruxlgsbcroqjthgickzcibrcstcbnqgdektgirmduqwniygynqofqeklcrtpyjxcaxkymzingvkqqljwthsukbhkttcuqwbfeqreljwedxqbtmkhphrgticrsmqblprvoixubusszumpxhrxpcapwumqbueceuekhyyekpjscibbndilzoabfttrqmvaytogkapysuhozzwcmvgeopuvmkggexflixvoyzpmcocllrhjkivfsbrqtfgjtkeckgiwlwhbpwfldgdbrxrsoxbjuqpfixokpruhdcqrgxtvtquqbrggethvlknkgvnyxrvopurgfzkxahvbaxtqsrbnfcpumeybxhdofomtnbjnpkuosznrixrytkqhawsefibvccwrbzzwcvdteovdvqqiqaglkfjysvfagsccrygehrvjpelluoaeyibpeyydkszpdcamxxeiqrkdlleyebazwrxzdjbsgebtfbjmtwpsrvhmkudsiabnieuimilgfxjvaruybiixkupnrbngkxmcejuoghckmayfkpljzfvgohsjsnztsdouhotugqbaasyczzhjzadpyvzxxrzpzhhdesyanxsrojvkrvemkmueuqkweaakhaxpanbswthmheduigyapnlskgzsgoxmspvzxgjsdyynxvoldaqvftegeqmggxgmrniecslppllxnecwdoqyehsiknwwbmrzaqsixlnsbbueltfapzhjuceurfwufjsqnuyvktldrfifpestrbbsfcrpaqhhbtezevgnexldfnwrlofwbsrmfbdbpgyadmbbfodpuxuzppafvscyyorvrfsebavocoaelwvajesedyawvrldbxlkhhwfeuinotnlwsbxwgghrlguauwqushduvjdaxvmydrpjvqjfzfpbdnuenzjesoqgiwelhjaniwsrmrtrzbxyogmjkktaezkkbwiwvqdczwbaadpjhyihcelpjecasbunrtnehkkulnbzvogjhjysmefidyidfqczobvojzmhjpfsdzxueskcypscalflpwuhjqbvjixcllkushxrwncuprrcqbetyclahesqcnfeumvvgooqxuglsqrxzttvimnxdimgukdzjyppjotriknpszbgnvpjawbqllaitttbewhlyuheajpuordsuyvekatgnghgzsxnjlkfiydpcldjrbipksfowifkojaelugsweazsvceadcllrtllpnmsittxvygylabeoonsdfsxtyrmdfsaqzqoxklfwdjzriqefvcgsbgyqertseqzuihcnykbnsdaupgbexadfcaovexeovkckgwnucdpdnsqwrnofrzbqewkexgtvadqblkfhwplpdyryopdaoqciclmcbzryqjwzuebsbhqfeqdbbkrapeesxdztfvvfkksmwevacmcpfggnhkbzfawjezhbadqfcrenltihqrmvdzjofemesrtorpitdocbsaxpjflflbywpoxghsmkkmxbjluzgramnizpszhpmfdjrjrevxrshewliuwleoelayleiftrcrapzphbggttzsvbsljkaolagjljnpjahbppqkixuvhfytmjdwoymcmyukkvtctyqaklilfjmcwmvfzsloallufwyckkpvsrnrssjsudibigzuhjquilalplzlslxlpatifcmjjxpoukcbkruxzqcybqtjwacermobdxliekxrdgbuuoqadcqfqiaprdejvkjcclinabqecrekbymedbhxylobihqemyawbekbrucjnvaocxlginmidltddgudndxvomsxjeobicvdvsxcvzqqofirtdncjqydulsebtvkkfiiboueddvxidaxzzrupgaegurxiuylhkjsmwwmexloxqhwejojshwfuenqumgotlgruptyfhnsegxyxalmrjenrtwhkcddaoxfcdhlrxehyifvmrrllqmrdbkvcxergawjchglwlzoxisyqsetlykzzsgnxkjfurcnpvgbgzkgvjsvpgmsiomvoypzffcbgkmzijgcavuirfhgfqcmbbnhlkutsgcywetdgmuwjtdcqqnjyxrzrlykopiopejxauimhwaulkxjtjdsedpjdzqcmxmhcvgahyocdzppbyhcsgzztfteaekmzljkypjyeweioumfckvsiuqqefgfvtzythdqzaggsyioibwiutgzsrjxaaocznlewcrfuqzwpqtajezpwbwrezaoyhfphwixjbfkcvnnbzrwtgimxrytsoxnzoxblzyfkhakprmbhioyrprckaemvphxmybdrgtcditwpiphepvvkozrixxbtjsdkgfndfvsqjmqbthzvpyifvewizgavfpauisuuvpvhxziocdxjkdlvawwwfjogzwopqguiyeopyqhzfxokhdmnpjzwmiexquishnryakvcncscvrsadrkhcowqnotuetinigjepnxzqxpnwdaxoplyakpfmianmodugetgwotseltrxmmrakocogpcswvhelaqhylggbslxsqarrlscjuraepwdipjqqjbfkubmdvhkeggktnlavnqcxyxobsrrnpkbvqebdlfacrpihbzloaaqsciuuwarwlqgwokdichlaihapllrfjndameviasttbrmialahtokescfssqmibdczkhlqqnzxyrsennzsdbtpstxaxhvcqwvkjjycfrecptumtuggwskhqmcvbuczveajsodidhsecodazxzzuyrqmvqsfgtlkzjprrmbydkreclkpgtxwmmozezygzbtxczleichzrtaoutrzzvnhweyykqspphdngvxgufoeskmsfteemnceezumbesbmrjmxfuruqbslnxbhclmtfbfuzjiitlaxsaforulocmizqktyflwspvauzosutexikupivurhnaghmvwpnbwmydbuiivkrzsifbhytsabpbaoiwvdonofbslbmsbzmffwkvxmaaviqsipazyxauyqpqibpajdzcxcyzlguvosdwdmxnaehwgyksiyhfmoufrqonwdknafqyjirtfmacdelqpzsulnlfipgtrxxiktkxumosnaucawfocxqhlugrzngjliqctawaankkikfqnwiqjcdmsxaebjgsiaebaikxqvurmifufffpxzgkqoyfnspjjqtfbfkxbzdqbuihepingurwzkejptppvhjdomfvyxrvsyhnuqaejmltsqoilrescxxwdiulynzupxmamngmvcbpksexzcxauyphxiprgfnkzwuwaihxxnwhvnopdxybmbnyicdmczhebreuspyrvdkmdspqbnnbfaivtgdsmjqdiohhcqrudtzrjnxaxvdurwwgquhsfepjkiqtbwcjefvdvditebvgxrxwgafrkiteyfvqxuwmzsojzrooeqtdxqvnqntfcbqjpuowxvodxggakzeilcappblbwjbghqtitydivfindmqlwdgkqawwriyydptrdbhmkpwwcndjduzjduoqnwtjbmsopzheqoilstzwoofmmprnhiusqlogfhwokwvpuegpxiyztjhjgstrsmwvtwjmnjcrejhxuyhbqvikvrbkcyozpjfzfcyjwrzfdmjijzykprmzmjieymhwsclyoeinnzcgtbbqnxwgpwcqojukholmsjniwcmhdcsuh"; + String b = "rmhdxtymfgxjluxmeeerhxrrtjgecmyfdhevakyosvvuwgbwmxiakbmtqjjpgctjojdqatuelqmfeldgoprxbzcylajhhjngqlmporofjpoyhrkptrbpzvvnkrqjovfzmmqybqxzjxarhguqcyvlugwmnzwtffanzuajraqbvjflgalgcwuhmrkblobybwouffzptbvkmwoklczfhvkqsirkqvhkryyxpkyfnwnjudvllmznupldkpoplwdlfzncwxdlgvgkehpcudqybnyhofpjnjmahnsrntxjtgocwsrnukejehwzxdetglgahdvzyypdztqwbnfrnytxprhtqjgobzjyewuasgnryjoyvwhdmemppixqcowgmicbmsukgodwgnsdieuqyzuysedmsphmritlqtnvnvfnmgnghxdsmmnzdhdalhfsfsppupteyrlajxrlbyqteaytgpsckottqnibbxhmkwuqfftwvibzsnaumsbohxeoaulsmwuvjcimscyqhtioekembdwfozglussscbzejqnvmcpfwdmxshhrkquacplctruyklkxcfovcbqllmyghdbkhdmawpmalrsgymgmbleftwtvldxkqprcbcbneyyluoyydtcvkimqjoinwntfxfbwqjbufqansdlgrgauzfwxpzrjxpesmozcsczbfzfeziedqzwywtxhyfnpbxfgbqcsfwdiplegcsrseqsagvkpjrupkaxffrddebpntocmkbarvdrhvujedwnktsdimzcbqlqyizozxcexqpoefjpznegsrtbfanqdyqaaudcklplvxojfuzccmtauaxurbwbexulqbxooadcrlxbtggxcycadratsuqavwwkdqtutptdpbnxeicmackhyvjhkfbayjmymjwejybhoryyqjjmwqzhpgulovxaccychffcplvkqfqghmgmdsigkugsevwkvzgpxiolhtzocdiphokfitggpneuisgnxismbhrturqralohcbzcrrwxwintbenpbmkkzuhacttpgobfpiycfgkrpawsdheixoyazmwcnrvslspeievjzzqjifxkkvqmswtozweouodkvvdijuibcdnwpgfsugufjocuutawdpgxaryazzefbapyzlbjbpltnndtxzmwqfrbjqldaetuqbufutefenwanqvtveoxffhfurgjgreebqudhrqtxjmqsmjrjpcpyuhwwotjjdjldtyluisvzyllfqglhrjmyaiqvbmrswhbjvssqmlgsgnymblqvthtgarrciidbuyfwfejvbsvfvpizxmonqjghpiiysiekykfnucfacmlgfidflwndywfdjaeupaspmnjlobxjutcughhmqeklbzfmzpsfplncuefmdbqttxwfcbglxmcgwwfjfzclhtwdjirwvhnuuvdtcrcwiuxqxwqxwzuvmcqzuehcmevljsedcyssmhipkclkcxnkbiwfghfstsklslgcqwbcklghfywhhwybtasdwivrbskykplcuscytwautoypyuhzdjvgmxazzdqdsudqkkhpbdbsmkdorurhqttaofphrxkxosqyflaztktmflfurcawfgagoazkuiaziflwvjzdmjkglvvloctyqccamerkqiblezlypityarvywunzzbpzyzhitbdlsnntjcuraevcdshfbsdlzkigzxyysppucvpzzjmahzokyidlandntkagdjgvpgtcoftsyilnltiylwpnjhhmppzsqrzkabgnkqaissrnuhjfztvzqklioszdtaeogwbadxqrqhnaxywkamgdsnyipgnfbndxjwninzjqhdhtgrmqtmebrfrhsrvyezvsqdecqgkilwjaaklzgxzkwlwmwailmdbtsavsweehjkelgsnqxbbvehggiceankladdexheozlbmagupxisghwwnngbqpzhwudmogcitlvqfyqvniwsdyodbuuihsfoitkaphcwphkqxtspkjqnobsixcvuzriuxgzmtvaziqvwmbupaiwothuajjbgasdqwjvzelledjffvwpkjtkwhiwfumpxsrdxvjfzcppiddfdvnhunbeucpmpzqqjzdaugzxclvrodndbuuoblavjdncmrfmsdbuuytxsdnuaaiaedutmtjmcbsonqgpiyjmrjvbmhkzigrcpahqefktkyxkirgiytjwagdevjoailxdjnvlhkjapcyydpyynmufkrzkpfykkvunukqrdckuzxjnskhkmbvofszgcpzebnautcydxpbafgtpmnmfpgoyppqybbnfybakojiudsqmoneygrxiwzilatiqimgpuujmmmbelznwwkeywenzgkckqckifvzwfifrsxinwdxlebqnlwtuqmaunfjdqvqcibdogridamuettagqxhyhiqwcwnbvmtqqpebychgdlhyggkrhtjsvtryqxuwmgjaxajfiozhzrmcxyushslxdzlzjoiehascrtxjijslophtjfczqbwpkwbslrqqtnsnjglxspmdltxfhvwxljxemljqhxzkpzjshcqedhoalsuksdhhqzhbnlgxyepkcsrjvxgymwrnwdrvawdorrkimrsgllgxpvuouwvxwdrisecgmprzhdlacndcpfxsfkvzkvfbziwlzkxtbooexhhecszskrsoqvdotizkrjkfcytdpbdegirbsagauwdpsvahyykxgbvbxutuaclwqxrtkfnotzfebagdrkynjxndyntxedirsoespcfusjzjordgpjrbuptkwncxlwrnjxfxgqyxshjzljmfvtgzwhnbhxpewwjkrrmaijvtngstvviaxkunlrbbncqonjjrduhrxenozpfvwjjhzlevbubdhxbqjzzyeryxpwogfcthkikpsrahyzdyjuzajsvzmnffqcsnuqjdhwzvksyzrcbcayztziniebutntekmqpyixujegjrtnywhtmqjjsoipswtzbyponaxkmsttbuvnbkpawhibworxhokkhppooefzuzpuihpaknwwtpkepnzayjhnmdrqxwensgafsxxigegzpyislihybmhbelimrvnxjjqgtxcnhtgwbxswupuhhmqrspimisylzhwqkakzdencxxamajpxhljgdifobhnydycpbasizikawdysxboemhakqusacipbrqzjuhxctkaklogmnwxjmamfzwkczkdugibcegxjkfipfjvrxwlutrjhummrvcgprutsqvyryzswpnwdgcvivbzexwegoyakvcixpgxvcoxskqjcwqdvrivzixxpvaswhgleiomfalaehbxcjzafgikwulmnikpnzxgubtwwwodpsorfypavcufrjdyolwmpexxtmwpdrbfdsowlspuyjcahnvriojstmpmxtcjochosbsczydfdlqeiuvtzmbksyibgfkvogcldlpuznynyerocfjylfyndsycpepfjtfptbunpckdpvctnyuhvofvgqiebiouvhpxnwamtoccpyqxspqlxhxpbwbeiuvhlhdcbfzqifhnboberwevfngquhfgvfdwszetricznkxayhjkznoaaanyqhnnrpcpsmmsvvesqkdoqhdprjhhcduwzlnjznckszkmcwwlpiayptiahsgqahwddlsfcypvbazucknrdbqflmcmgdulaqfyklwmadukhpjlkhivhcpjutvacflpcjtlziwpocgachctkkaaqvxxduytntqceygohpwknstvpkiwggqjmmkofjvucfkaszrtrfqwuwjrzadpmwifpdixrwuaoqjincjumftfnqftthgqtobtgjccrlsauyaviettrmsorlgrdbbtwuohaetqcgmjbjlbmlwejxmfhfhcwhghifrhjphpkechdkjbiulinkcakgqnpdrorjqaubhcmxhwzwmqrpzkvuvowkhxiouypouekykfyskgcawcmalyxguuoavwyzumcnwkwposddunwkpkldkvymtolnfdkzjbuoxezyypuwxuowamrmlzqifxvibtkkjqhfkpyezvogcsznbgtwhzsozymowcthnqniymeulcjfehbxhqweqozylgdzztyinzntkvvfqjaslwvninkoxuczcngichlnfdbgualrjmtdboclnkmxssrdpprmeyoqmfimjgoyjsnmnkweynbzmbzsgmzkflcftqxdiskfdlmfrjcgmatuvdpeawzutdluwbylzepneuauoffakzzkxapugrjawdnzlltnbyikliifbmpwpbvnvyyvroimzarulvbqwxfswhhkbbrvianztvmaiumlpvxkwxoolkflharnogpqclcyjuwwnzfcabeirwvglnhroaltgzfyctaeblyiigouxebnvuswdsgxkamczffnpjwtbrgdobwduqgmkgwynzqxduoatvfmszdrmekijaytineirrguplfeqyxyxwievbekqkysqzqalqwdqncjciuclipbujhrutlhtfdbcsnblzeituszcgvxqdtmetjnhagoasvdcdpiqcianqnrapigkbkjbafhnzsihihtjnexftvrppzpcedakoavzfedrjahaztkgivmyfknyuvrvdqbvcefjkvosiepopxsafnxrtqqfrnliigeqltwnqovhlmekwdavprxevhxyrrxnijrydlhfpmlrkinvxjodckoqorhlddhquxdjelepeuskfbcgtwbneclpwfnvyblavcteremndsejgdakjvypbwrmvcndirgyzncplinjttqdjhrriuxrzmiccjzqzadqaabwhnadrixtqtexvwpygkzqopskjbcfaexegghzwkkbvfpzdjqcyozugebmrvbwvbcqltijctkwyxhpdgibhdwyoesqyepjrjgejwbwrrfhzaglatzucbliymlsoamftvownliceiisachhaqzmhkgisyjmwyqzumvxqpwqkliuubcvjiesevhhzgodfsdxtuggdeowwgvdygwpuomuetreypumbnqwmfaewvegttqraprrcokmtcjkclqafpxtqunvxnbokbsimjwteboryznxmnlitkhzppnsmswzdkgzaoepusloiyqllxckgaxnhpdpkejgcbuckgfhzzeyvtwosnnxsdupcwuzuopuvoslkzutcbseeevdkzltdxhwbvtvykbstnuinpijxjatdrsgbhpitkmdtzoatgmearrmetmjiuklffydaqtubdtbdfgswoqcscbylrsqluxguppkwmzurdjboujljynafrrczeyfnucikzlyrbnboeougivvvriceqdazgimjnduvrtbtbcqbesdpyjerkzkfqwebjcmabkpxlgzkfwnogdpwunlprqwbwswekplgdcqeovezxwkrdsmovdrimcfcfmfbowfnaxchbaponjvfzflshpnghvntazaxgidmdsfjyuaslgnhjsrwuejxqndgtdadhijjkeeleruoqyhbjdajmzozxrysecvttmzsocdtyearsxpusxvdrppbkzrfvxewgwfejuhrnnldaghligaeraaoaqxgswitykslvvcyygpftpzrqkctpwbxeybbbugkguertfioidzjxmktqemcbxotjbpxonblvkrjwvqblrburvzqafrzxhfonlxwjgxrpbqssombbggiqcvxxpqbipyxkscuzgubvqdqndlfauzptnywkqhonmxvmrvkubhxuvcksktvhccrcsirwmpwfbwqcizmviklfmtpyqjqnrfyftpzjbnffbczwiamexobauwqcvgcqpnnvwoxrisnaqqdcoawcgmkdgypipihgaifzkhibxinsiiphpksfogtssbzxchsvghelajgmsquljlsdqlimbfxaznbcoluruqpgemljppozgtgdcynydecclaxnaxgxftbftarkrkmdmuzndfnuwwkppyzekywrecpmlgztfekuypskkmehpgylqmmjroiqhbcbxbiusybwtrllfanyjamlkcpyvdkdxrtwykcqhkfqpziyfgboosmmvmmbbtygikpkfzbvqodhgguqfnmhzjadcjlzxtkzqxjbinznnffmfijkjuxuvcodkxgocjpqfikwpnryanpnqjqcqeuqzlhrahxmvbhdczmicfxbqzyculceyzyqgzejsicueipmyocblyhagmvmhfnjicbdedsuorpifxrivukwatimhcgnbgtgkjnebgcuhuizbxvxsmuwhxhgjdsorltqrphyhyjhpsehkmrehhozuoqdairhaprwgtrtjsmtpzhnbozjjmsuurthjmdbprrjushthmwprrvqowgmloflfkjldsnuzayoegvjrnlrrkcgavwujiujrxnzhacdiuzecsefvuldzvxppnnhtgwupzlbjurijigieonsdvssxehiyobdblawllvuudtckupnljtlnpvnamncbrsdjsdyateelkyqezwxuehusieyecjchhgyniyjsdxdhrtpcaqpqkdcevhwvprekjgzreilzfhwwlzdivwljfbcxatczrprwkgchtacyfdyftamwnyqrsgkijxxuwscaknouokwfydbfoyehpyjbqvupfzlemixqsjcvcwudwkudtqihnpfabenfosxzorbbkedmwmnicrgvlhmtrzkrvtgyqkowqhohssgctwiisndvzuiovgsdizrbroweiycjefhnykanfgdxzgxeydsbmrpxgqfccgzpxhnllszdiuuqfpcfjfbbjhqycpsomusqaeouolqnchxqsibrgezayhumgmwtxwtfmombszhitbdmqlqkgkzgumtmxpsxqopectbtiqxcdqfnwkwgaokycpwlrbicvzofvupbhvwyrgspjvilmfkdpbajljqywsgxexulrnmtvvuvdjqmkgszopdbepwgusltasocutfgxnrzgdgozluayontlmiasixsrivwzafeeobtmnlispmnamfsubdcygoaznetrljdtvjlsjhhbehnkgomfkaibackxcogtswoepbxavdbakkgsiugbgxgcqwfkutsxsjdnineeyzvaxtvkvtoradspefqmccwylvizdaslvqodovuiitbueennkekucajubpyyvlnghwskuvmvgicluiuytlzqtqklzllevtzftsiohvyvjcfbgpwgojwuaexgmppkongbxlytbrojcaltbvwugrbentmybfhhxmqtaqhrqdtwgqxfmqvficcolzsvpxkzvsodtyxdhklckfpdjfihhgvvgaqggwstgyrdlkotlloywrzckqjggkskdkziopvrqxptnwzlflvqdsttvwqexfdmubdzdkdviogqxujhouvofttvtjxclkukrcqkqdqiymmtzswawmavrzmvwrfwjmqzqktazxlcrvsdmesnkjpxuavxxrmelkeymfovtatwwjjvhblflyeurvvexcttewueagqqisoyybtvbpawfzfmvivjxqxahfwwhgbxltofjmqaujkiwdcmgpdjxopnarqazzypfuwifznioquwevuqhsfdysdvcfzitrplrwkitntievjuqscxzqnrfdpuynbslssiqpxytcmyemmrtksusqarunqscretfxglpofwpunoelbhuosmqrabbrinrhborayuarnmeqfemwsyibxhpugpgbrgmumazhwqgilexzyrbphjyayymsknpgylqqiqbxjwecnzyqufzgtkupordaodvyhnvswafrkqzraennpncpmqhgmkqibdeawmqbfyogzznpepdgjwmkrdqowbinhtpdjrzskdfqtbxhdhhiizginwapnxzlbpmgowsllzspkyycgvulouerkurlxjxvvwslokdddbnygrstmeqmkgmshiqcxkrawtrlalzbjfhaerdegowzszasesonibmezsnqabusjvfppbeotrvadlxyxkwzzggyqqzcopsigjyobvfssuqrypaskxaumonsercadglnzrebxlimkxjnmegflynombmnegttnhsanaodfwimsecolkfdjxqhjirornevptuvoywadeufqgibasgwdflobuhvwgfhuiebwtwrkhudzcqukiimgczjkghqjcioixtqmcyiialwzlpafgzcdvtiftpwtzenkrurewgnqidwqlbwcjtdnhwzltevylgewtobvczhmxrsdfqbgitdxrjhxxorqczkijdainknkxqnbtgqdlhkdsrqomxhfzhctjkxiuxtxemvpqrybmljblkvtbzepwztjepeyighypemygpcjjiallyqltbdxgfglaeeimgnvncvjohfchqnawzcwtuvemdqzaesjxgwdrmhjjvxqqpzenybmmattkrvimbiteyosogssogtpvnneqdyoilsysokthmddoqgcaqudccrrrayaowmbzcrgoxskcsnxnzbinuwonimtjvyzytxcozikxcidqgceryipaekgxgfwwcggyujgnbzmesczwnxtechftprfvtvwnkydmmftbrakezucpxmtkvpazxtdwosyfeizefidlusthfbybtjoppurenzakgemuggcazibgnbgnvnmkqcivbvkmhytudiwvsrltbahxdgqonqgyoiiwhfohrsrgkwypdyttrvvlzrmrdridqsfdscfwdiwniuslridoxjrrgcgrvnujhwlrqbamgundxwbubnznwtjqcrfbwpusivthzudqmvpfchuvgmmmnhurkvjkefbapqcrbpsaaubqpeyrgzbrabfedjrwbumwhxjizbsiwthsadpbemtnlzdjfnbcwwkzsxfrvuwdubkkgpdsqpkexuvflmusivpuiyqydudqnhfjslmqpynglthhiytkdscinhaekbmmukbtdcicmsatlftlfhdsmngvhabgzqxvsxinsoupolkmrmyivetmltefxyozdzjwweybqsutayylzqhzqcojnogfwfcfrgnsyjzprtqyqjjhynaivenhygjybjkdyrycqdxbbkkeoqiaoaebsyprzpxobgctqcjomxxmrsmbxzzrjsgpismmajpjggsmhzxjiozemzyqerpywyjbfmxydhnjgmklogksldsbsfrtztybyqrvwnjnovznrzwxufvkiffncrlnmbllmtsxuxknccxidhndhwmbydrsmtolkqtkqesukxtxqvhyqqyyvuhfznfqpuwygsqovxhackffjmjtoapfxpnppanejtugbghtusqhuhekdsyjvcudabcbfuporcyyyarleopljowfxdindbhjhesizxswgehytqvhnafobegwjpesavmyxdsufqyvowdxfohlhgcecowbzywoorkffesutefuslyghlceiejwgcwukuzgrgmydaspzzexsvnuoscbrfugdwtiixpocgkgtogibeangfyiftlgdenzmqxxmpxgdgiugxcemuebjgfftlwxjzdzakivliazhtyjdqrqxylwwtrwpzavqnsfpiilnrugglvrdtyocjmobznpcojwiqjsgvekwnwygcmpngffkrsbvldmpckqxnszowknnddsbjtcahqhnnxqcimpetmhfyfvrxrlmozttybtdrfbsptbfuscdtdlviudojqxxgecbrxzclwzwpcymhhkamvupqxpmaypjffvszuehakduweeuqbepgthsqawxuikullrnbeieummxhwecilovanpebdqzxyvxpwlxaztcbosiqzldkveqyhyhrmizyxdbgshvoglmhjekiuvzxcftgtqhanypdrhjbmcjiybkytzwvvsrhiypjdhgjnxbnarrxunhibnrbzykvbfluyzdwjzphlyhywqrpgblzcircsrsewwsblkwjcfwttsowgayjfxsfbieqibsqgywlynfivnkngapobcdneasxlozniobpsuzrnvzidgnelxtiatntebbwmbbpskbixnujguntppkpgguivitygpbwrdoiuplkfifqudghwnyvsvyufmuklrrxtiyvlwzbgkujcazdnjxxdlazxgkapxzwmlsqtlnfypxqgjldwsnhldwyehlhxccabatmtcxjhnpukwcptewcwbdtptwwmzvzeowrrnvuakvnllyqdpllvojcjcollvpdthhhzfrbtrgdcfxekxgkodacaohhigcbvjoqdxjklbvumxjqkfzvifygbmojmgzxlsrwyucfhscgbbrycixitmrgraaydfajhtixjcuoaxqmzbvceogjvyawzmiighfdqietynzdokyfndnpyjhpjgrnpuycxqietkwgqisskwtgauzgsuffwdbclwrnyiemijtelfldfdzqundjdaqfwfspxxdjhwxxatkdzmcaqbspxmmhqwibsszniiqafcgwgqpcpzvuinbkjewnnfwpjmrxtciztyrtfxmdrsvfhmhwvpapvzbccmzgkfmcjsicrttxagnsatkvuvmkdiytckrquimamjqpabcwslrvzwfjfuanjutivjuztoxbksgwcfznmtohvdsisbwjtsqrjcuzacwzzkpcpojimxzeujpfffxkskdytqwxsfhxrlnljbqrnqltyvyfnrwmjosrprxzoakafrvwoejxpsvvlnmuranugrdxocvgdemrxalkmjcfzxxkljazmlpgfayyqmvdbdzjmdkddtcivfsolmrorslrrrjgjhnqpkzpmuritulinrtefwelzbhjyevzybokvozcyhuategnkhyogxtsevqqadhfafyklpulvvsursdagemjhqfmwpbbyqugfltlztbxggrsmtpuyjuhjgimjqprjvfyvhrqduwacpygprzitwsxbneqecjtzgqvxjfbdwpclzoidpqwpbskquoczvxgcxzkopdazbwbuwnimnvmqtgifpxxmzyalgkjjfkzmpvxmodijsfsrmtrphdoiaqmaozsfshpfvaadlunwmjsgbhqfzbvnonhojuokltmxzfysqdzfinvbtzmkenvdxdxlaldlbvsjnkhtxqygpbsaghlnbwpvvmowebrimqqxywlycuwxsrlhdxsvhkhclzkheqrbpfhgewcedkutywwvpxcppcupzoyaxblrcnkxnpvhbibftszvdxvmwdbvcdchkldjyslzgcgmubocyvmymlpvqkfstejrkbcpdfbuzpewzkshremlhihcxrggaaftrmghlvrselcqhommtmmwyvjzrsniigylfcrxdriselhrbbmulbrioywbiyfsctnxnpwwinwokjgldfihugpnsljaqzegloixsgdhozhvptzkbsdyzrvbwdxlkalxijflnbzwzjvpmnakraryhmikwnewzpuuhewtzxbkhygjidtyaxfrypedgiqhxfqpzbcgzopwkzmxkdcfyribjevbnarkepkbgptvinvfesbxipizzjjwcdfklqijrwtxnqgszshpbmztghuqawoiemzumzhygxfxdahtkcglaemvjezekjguyjijdmolbimwsitookpqdwmpdqljrdgnzbnxyfxeqqmghxpfvgmkbqttwgndazwcmmsekvpzlrtugogyqzlqggcwrfymvakoecdepfidhjyaegpqvpejzjrhgtebycbqhekqxkrjatuenjpabmygxyghmnyhovlkgydatgabysxkqjklpopyfwaiismogacwcfxlevbgoqlertugrckfciyesmdfowhrrorcygptsejbmvbyxwtuknumcoytyxrngrsbynstxrnlxugnxrkbkngwxyvwblouguhghmxavqidzvylduzsipnwzsychyziafcwshgpuijdktnnvbiifskooqwwvpejmrdrhlmrurxhcsdfgpkkiyxdpslpecaktbyfgrhjzspxpgtfoowqavofpdvfxcovxfenecvqvbmazmvzbstjqcjhkysrtehgbvwohpuivercjwaersguqsuwegxacxjxvgnizpthzqzvxjyjtahaugostubcslxdfdrvsyxgenfdlqfpmodhckazrochijnypuucluhvxbvgohljimmnqurmudhpwrybfdhupvcagnbyqerbjmzfwaqmtmxnxuihpvyleyomcljzmwasimotkjtitpyvycpddtkytbxzavslcqdileabbtwjnkcctxssvugjfywxqgcaussqsmftgghrkdrcopforcnaybrrrczymogkklfsdlowpgtlolbinlqpaonlcwngoiucipgcabeixhpcpopxkxbbiuuxwtnfsoizdusbjvcqipnhqefofcgnmjzsxtnwyfivnvebebmzcqhkjqipghfzlbzlcvlpqiawpuzvicsibfidzannpgzxvytvpgewqxafarrjeezcscvbskltogndninthywxescdzfznbgnvwolsvspmudjohifaaatnwgyusuephffzvkwuaxwasgxqrwcsurhubszmxicfeeebvfwikvnstoruodwhhcpsprimurozcnswnzuemjabeafmvbdldsrbunecoaaphwvwlkaghabtioyuqyqtewuasmyphrlgzmuaqyvnrhxjbiealqkfnzuhgrqygnoqivsctrtmafilwnkgcdhxcvglaoscjrendrvnjkoiooingwlfelnqczhffmywmpqmoporltjadptbqzwqkbscvdhlasjnwfpjrlmjbrzegmriaclbwwckkspwzxnznnyfqwgypbrggkgbsjtxhjlilrzpkzkipgmsesgkrykyzpwatwslecveecqbhvzmmkdvwxlywtopjadenbmskoqnvlulwefueojbonjhczbzaveypnidrzqoqecgwbxseakclxlwrgxqxetjeyhgesindjvmkagnjunyugjblmclgtmuubvutbxctpoakthquvcyxahectiiupsgwfvfocflkvwzmbyqezjxfylwxlxjsrwgopqyjsknjugyrpoxnzqpkzkugexlicqksyagrfheinwpugbojioofyefrcbyklgupiejlrrmgglhwrbvilwekppjpabzdefjmmwivxahpfwlnrzvxhnwbwxwmowcocvrygdrxmhcedzkazdtllrllwigjpfxqkgoenfcppsggzcytqonjukvwnxzhykpoflwynqtoswumkiahsivdklcqldajzmkmftvplfsetjonculbvvautjibxgjvdjbnthzbjpfwanyrndplwbzvhaeandnzcthwminzainnzplighapxtayxjnclkjvzuorotdahlgknoukomdwgygzlojyabtfrsngrgttdxhwocpswwwiyamyecxecpyjwajhoxypymycpzgrozjlitromizozwwtmivylowpmewizxrgqhaptvuydkkamodclnupaykigsdfszsuycqtwpghjaozxnodrundzypfkgfgsfrofkcrjralbqooiwmyonkxfwmstdfoohfazccpspjjotvoycdnoahqnmldwlludxzhjwnshzeyyrvuamgrctyheukcejyeuvccvtelhqplbsqyfiovarzaergtrjzbzukjzymbuosparvrihjqlmupsylgxsfukscxkrkudbjjkjckwwkppvkqzdkckgopqjtqxxfkquagxtuuhmmxmbbpvchtutaoflrrtpwfbprmmfatnfmoonoasotajrysyedukuqchqczzzqdphwxlvwqseiauugbqdgaclwbcygwawewuvdibciygdfccjtolbaqamygsqrotexgwdxgsysnzqvysnwixzyqveiriotuycehrhdwdzyrwkxlooceiizeukvmbihnsprurtlvjbouaasxunnufkmttnpdrjgromvtpegihtpveetycimaykmerezuogddrrziqgvxjorcfxuxrqiivedvgmarhtamucshhyxjhrfbtgerqapkfdebgorpgqpfgbkyeorcrhtknsehjngbfntztsjxuiosflkwglubrqezmvpvvkyhjyuwrnzcwdvnqczaeyofyxlqkpfqfskkzemplzslgfonsqcuwmsnjdxxqtzdzmmshytznodgyqfpcnuweejbiugbicivwlzawhuwgckoexoktvzweryjhujhxbrmxlgixvfjncrzybubupjuutqevqvesmbmevrmzmoihxxaqxbcxendilwehveefhqhggxqpnildakrqcomqjadksbbwkijfminrzvjogkowzdipdbumrpfoibhegmvjuvxvtcfvqurupnantubsukotuexidflmzzfwmsdyhzaepnsjjaellxzlposdsvppwswcofjbttepqfafuhnkfrzyitoitsloimdnbnirrgrzkyhwvqqejrdshqojapuixkbregbbjzvzhyjxanxjuemztouplldrvbkqarikjayiinfbtwmnfqiyjhzgolhnfjlrukluobwwbpszlbztbrpcwpmihzapahnchpcfmeqihtncldhtrbhvwlvlqmtjletasjhkuzmmwswuynpstmpfbpvpefdgmyackplzmweswhwzhklkktdyuzfftvphilejnhsfwsnaofhdxruuxoasnqighdowszogeckarkyhtoowphvfthmqjktmylaqnnkxdczzbrlbzujlwcdsxlymxbfpcoemfaenwecmmrmgbcipnkzhodkwjfsamxjzdcbeqosyafpcswmzbdqqgbgqpbwjmxakgkjfyxfvpehjshemyxaaayrmeiydwkjidzehzvaycygntmfyvhrxlvwmzhptiyrpuhtyfvmdlfekvlzoypwfxkjldxjjneoslwaogscgbhhxsywmanpcczfialbjjqtaohmgclkanzwlxzbaqkklrvfcdwmspzbybogogmrxvhqharmpfmhoutakcqurayjtgmbgdhaxkizaqdbltvcrkulngmlebrcsrspbrajhmwfieijpmgiwuhqncekijhdhoqkmnuejizeurkekortxiwcxolthitpbskxshajotjqdyumfhbmxeufbxcvcpfzhumudfaipxvlnimeinutendaqmwwfgkzcsikpicjhbrlpfuezgvtrujjvyeileswyoobmvwrjjzzzygkrqwnsadnjqdsxjbhnwxlvnopnjjsjlqijgottxnvrxakpgktbozsaslcnpsxieqybildajhvkjfghxrgozeftvtdlwfaqsdcyrdaltyejgwevrjzggdncsgcnmfccxwjindrsvtyagwndnuivyyocwgqdonbiepnjabwkdqxtciigvsarlehxqzabbgroneuqecfxlpcksomsjlvjursmhjuhsmviwerinkwdqnbfvvlyhvoprmrwqkmmhaecfrpwikjkyvtbqecmqiqcouxailrwnqixmvzrwnjbrsrrvdbhqkaelyunnxxgsswggybzpiekrdsalgdosabyqnvbpgxkikkfeowctbkrqasuayzjhzuteixemhdtvzufrswiqmgyveannkogzkjnkonuiffzrrufposlvugwbriacpaowgsbpwkooezclihbospnqkjylhxoneinzflmzvdnriotqomadezuymaslufygeqjunwfmkimmsxrsmfouirmwsvorrrjaazthnkpkqbsfgdlbkggpytbmdsvjlobmqgffoyhrjmfjoagszelrdfxuzpexxamwbqukzwlztdmiqtlizrkefmjerwebbosiwghzsxtnwianyvmmbvsgjoyfqffjnzbdargsjytfoqyamalclqzmcbzsqbhdgqixveexmuvvzygkdzatdzmbgqqbjjwabildloeldmyljorzfevadbpexntpaoxdfwbpfjpgndwgecuvhurvemsjlkbhrnabpuotuqmthnhsrlrzvgyohdpytaeyhwhlpshevrrnmrsgfrjxcwwiiqdiqzyxyjnleyubmvfqgfixvdecpmnbbkxanslzxgiyzhtewbibpcaslbcdkflpunhhcksglbllusypiyhpftrymhatmvsritmzdslmpcsdubjvgxbnoymuwtlqglchfrjuxmosjmdriwyoatorpdgdvefbevmejkxolveddzieqmbonxjnlgwnjcobfhficrpbxpixaqwyztydofqylexfovzsycmqhhbpzhwqyjpcjddibfmiimfunxyrdlxwgaomkwmifvurvqkxmickurdzbhmqorzszojlwctslrzwsnpuubhrorbxwylxrebpsvjlfnizbhztofdoojriapbvlsbahkszkmzllyektxledvyltscfuszaaakivuhzuqepjgtteplhnmrtyzlmynqrhddxxnukiunlzgeojmzpenpmhrxfcygznkmnpeaoqonfomsikfleqrzewatdaqnkndrhynuizdkdfopncoqpuauzyaqwkqtvsvxszghofzzexbqmaldlpqsqymqtuwptqcgfrabplhsbxcorhkhabswnqlmsibtnemsfmdqbwrkyszzignswgwpmghmfylkuiqnebbrldpljbihdcwhvokeeotznfckuhocayxkxyyxeinzkelihuaztrseczjqnmvoicoyufrkelyxhurvpwwqluofrstuscirfppnlrodknjmoekgoitawbebrabnlgxyqktbavlbvgeswjgnrctrbzekgibrzejmizoahwzaufsxibaqjhxoynmkbygbeyoeamusxqapjhqlwnwmnoeueroqfigfsgszbbtoaqyrcjkjstzopgvxwpparlebknbewomshojgxxlubmdebsqrsrkeawsffzrrgnawvhcuizetnjrliujfdtemkrsfmgbofrikddmdiyqloeowayoodkqfgjrfhitqenducdjemhuyhylholmncjhdxltuxcywekqsjtsbuunopicattalhpbigjuebleicopoediksyvtrcqaldoxgtduxgjykblzignqcyenecveyzskyoxgpwanhihcgpuwaipbnewpnouxuelzdfzdmlzubuiirlkuxtgxqiofbravancvlgpdyaoocugzrfxczmtdzvdhbetgbvwihxawyfifetcjlrrnagltzfrpvlhsrqnlvvjajspivtuuwbshrcadgzlqfanyjovmjhlwzudmdsnunnsudewyerfespcnkchnxedgcslexehpnumoqjmzxydierxgbdedbonpiulqiwdfjviavguccqvcnufmcknwnksfzbzpnnandrafsahzgdxsrnxqowqwrtpizokwshlriroxvtdpmytzloogsqfswgnkrmrizetighgxwuetqubiaaikmzbgjvuwuylwjwviprzenrntixtccaowtkwbcjptclovklfyfegesodtzklqcrsvcwpkgxicfmhguobalcdhepfnfucdcmzziqwvaqxiaopwlqzedzycljqsewkpopmvljtcsgheneyuibqfruewbzsjzcshevkxmaxcmecmwmlhbtwquxarfuvczmymvarqequtzffswigjxorujisjbrmhbxzduywievsmtodubbtiaklqchqbjrqlauonlniniixgcauzoofjtunppuyryqsrpwqibdpdtwfxcqxpeuxvzxcvsizxjbildqnahlukoojzgopgaehrttkvkbsclpibhisckvjdalwudruwhydhspirljxhkkgdwvqccuheoeulpsycvviqpuzsahcddwqpafbjvzrljowxplrwpbxeaydwgbtgdqunuacehtmykoiwxosnxbtdusxtzsutlhcbjbeekqtvrlskcyfjkiwkvczkkauprhrwwvryjsniljfejyrpwpvvimxlycijrsxocyeesvhmprxuaovcxfxhootpgtgibrgvnkdbsnvqqtcnmxlgpulpwpsvtapsaxyhwoscfwanpqbbelcopgppdhbzurnrynhhimfrfujlcdnlfpcgpblfemixuertjzcpdgdmyzvaojxnthzrsvrqxitlkkhihillktjyzxangjefyrnpdwwmuimrlwskyvchzeqxjcqnhtqmbgmfhcbevximbqrefstwmlcbxiavuaumhnchxrjlwtpqomougfxrqlbkobyzcorcqjaeomnkndppgboftnciaseuwddbsrqzoiqzeexmuyghqmvewaigbfmnbaroqciorhuvjkqgykyhvrpfftimxfvsointbebiugjmeufbtbibipyxcotvkigyfzhfwzzgowvkcqmafhyqsxjroicbsasdtipetfceasdupjmlzdxbqrwnykgbqvtbacagnbonutbfhtlgqjmxdaoxpnbjyvtsjashyhczhxmfksisdqdfobnelhysjfpbwnkszvaevagumhxmiwrucnubdptdhftawzephdwwvutbrvpgeobxducraskhgbzwixlrcmlnshjqabcdykzjlyefidziwllwmsjvoflxivhujhpkkurvqnmbngsimljvhksmpycrarwznacbeqkjqscbbxfcjhhckupugelshuoimzkxhhquojqpwpugexfrxyhumbvifnppcuphverxevpfskdfcesooqhonrzroedvdysleskrigaylhrkzamjztlofofiesulzymfpsltskfsrkwtphoikjpgxttfvanejmmocnlyjobfiozjcjjykhjohccjuhoxnbflklmmduvlzbzvtstjycvnzaucggjwacshnqeudswpgccdgklglahznncjbyanqkkuvqwaqpbpziipnnuflvhehcxqitprievkrdvmyuskhummdxouyoccpfrtkoznxisogdtgicgxqdiogsqzyayaismwbfggkomdwfmkckyzqgmsnfjaaobhklqqoosydmtcwtuqbhneoqrxffrhpysnqthrxexhvjqrseynkdzufgnvngnicbmonpuemskthjgtcfgplydafraxrirstmnnkiwamjiwinbeiyzmkoklxaincjixhrfahdsptjmwphsvxgutvmoeaamrnyrwwzjjmjlxisrntnaimiwixysylyqeebpygwtvanqvtbkctoxjfivmflhampzbzwgahfajrilkliiqlhvxyltughppbfsnhnbwhnbevoayggstneyvlybhojzcvvxfpynidcgutwkniystsezokaqbipkylsvlpybiptlflbpggfzdljvosocbxkzfdnmmqzzjoiwxysklwgrbumophriddmtojdtegwfctewxpovtxqmgnbsbabnovybsomvkknlqimbrgrqnhlrywunnnuzucauhzxwcfmpmrkuryangnyatkijqwsldaxzishdlkzsaqvlwlvnutfcvtzmwldlpvhaudntsxvifddqclgbedmybpnyvlcwyreocnxxprrghiiadykfkrxkyehftaavzviukkslxnakfnmrgaehppwnlczkqyhdwccrlgmroyhepknhbywvwbyftiqxencfiheidvfmrnndfrimccemsfsztkmvlekgxfkjbimbicvsojpkcethfxjtzidlcrqrtltgzphacquiljutupdptcjuvdfycvhhigjstqombavnuyxmamylvmwsdvgapybvqoyatbahiofbujwwtyerxhgnieeiffwylbwwweiusvexuarkkhwvfcyzylucqsfnuxzyagosshsscejbjjvqujwwonjzynfhyugizjjniakrfiojtvgtdxvqwwsblgnhwfbzpsosppzbsmdudatxrixjljpbtxypcrbbybwcjczjrtrlpaukzeubayanbwozvaenvmzdfuaynscybmmpnrlapcbrepbwxcqubqwonkssyqnzpwjqrrxcelcqtrhlswwnemfbrjtjrgjihlowzmwffdrnfheiyzvwzeiwhxwbloyhtpripcyoqlqlourjqwxkgdryeqzlikxhxxvrnunhrzqsnedyjsfayvldjypqbmyxmxclpgrxkrniwyytvzzcsruzszryxedlpsiraywmlmimvqaunfiyirjfltgvfmxprzjundgbwmgtjreekejatjlkrkykmjcxlfhtdqwjtnijogvpwnhkeeqrjcgtsutnyieqzmiarfdbohuabhbvyrmglyhfnxzlbikkkcrfsanqnheykgpjfwhhbmyatkttcwfadbjfhverrkjvfabuzprbqwfkojyarxgzrrhpteokspxhecnlgxhbcxylofhpharqbibnrimdepqolktriobsnguzohvwqgduloeijthifbnrxnmbrvpukpfzdaulhckfvxoyceooqwjtdusadkejrgdekvskbuuiegsmohhawwcmmenenzcmnbjxzwhunoskmcirchfqxhkpmxideomtxzcontqpakhxwbxpbflqbtthjgdfqbpaxpkeszlhpfahmnfxlbycjheskiqjrdbjslbeiedziehirmbettjjjqlcgkmvgpoejirilautpgvrcvqnkmewxlkgpnjgzmvucvulmxelafmcatqammfajlfczmawdbtskhohmzleevggchmxqpjsbemmxxorgsgezieffsbhfbzuzqkukxgdntodgrfhygfmmahsnndsskyxnkuqintacskekhnsmabcnjugyykmxolxykdhiumlhbmmhyylevdnyitphzrnexxcrfbixcediiaysljqcwffjnvnkylfbmyrffhxxzzwljorypmcnuhjmorfgadkodhliiieuwoahvvcheootwozdvfkuktbfuauhptksqbkhvtixxgixmkjvbqyotiohbuqyrnneqzqxowjglvcvpctnyzhknxsrpnpuefbtuieqyqntaxrouxesfdravycwmsrcqipxksngafevoabbacfsdmtcnpukzbpmpfgqhnrkmhihjkckyerkrjpsmmyfqwevubzoynuizgmpovmaatemzmlawejtcgtmtalxzvueumaysxyrnhukiifbpevuztaxvgdughkivyqqcihbstushixfgjchtdyljbsxenwdyvigroxrjshaonxylzjcoddhqxlwyyfloadmtlvceyrdvdrjnltzkccwpfmsyqccqvvmastqyfralywhkpxxycgnnryjjdgmkylgfijgykhqnwybfbovghiuatlkkmaxhskjabwrgizaxzwhaodtzgnlfmfqfphbgmjhqrgvammxxhgtgbonuzbcfwkhurfkfxauofrxvwcejuadvkrslhkcelvttlfncpdirdnmdgcouafgsncbfbrcjkoklggynnofsfazvxjgdvlcflpapijcbteavysxerimippoikuyopvwwxwyyadcrklqonxcnppuxbfwokhjjqurxnmeyxfbwdfzkrdjuwgaegqhajbjzogqcelfwcxtqiomdldyeejawceiraxcvlqnweehndbrkcdqrlwkgjazxllsjuarwncdchffgjuhzccigpbexlhcmabsbaynkbmlvxkwytdjatgkhirbdgjvrqkurjvegjyyuqovcbxlkpnfgsogbchdmccikmbhtpfkkpndkhaksdgclsgehztjjqdzwvpoqgckezzanvwfwkqgowrftcmykhfjwcukhczidtifvfuljxqbcqcgrqejfrrzaebaaliwtyepmopweabgffdwnznydqhmmysxyphbcktxomgizigofwixeopldcvjfomxxbcdsfkeyrgkfkdnmhxlrcjmnhqheawpbzcdcsdnebymbxjoefxoluyojtrxhgzdbmfjqbpuzwpdiczmsrafqxqgrwwrwflaeslhieycmjopkzmorftpktquksxrlsgufuwgtckifcdlikqlmtsachaivbzuzwrkecuidaaqxwbpwmfinexdeupkkspxobbbdlywgrnfzfxffjbaaaubamdhegiefjbrxebzchfopfxghwmmuiufnxaoxcxjernespgmzinaohqiqunmctruixohqbfkeckrchwsdacoexesbwdicnjhieaabonyqdjwrxooxazxxzmtpkbkcsmknkriizkbvriqyifjqdwugucjzittkjtsbjxctfhsmyjbopwvyptqkfnhlxkryizedamieoqtiaidblyqcogmzzaaynkanhovkryeaqzfopvwbfhnqczvuwbqwxwgdzggnzdbxpinipbcmjlijpkkictpiisnbrrfwzbdwkgqeaobfiyrmiiaazjgvuqhupdfqatcqbqdsxdfllvugnkqgoatzfpgbhvdmtrbljcqrvyozjsygpbswxirkbtccfwvrdxrngfaxdmiwxjqvlgczpicgtwwlaiqieqjgaocywpxcsnzgqsrczougoznxgjmeuelupdddelnztxbkytavrnqqonaelwwhynyybktjsesdycwqohjwswcsdbwdrhidaikvktlmiomsufzepknqgkqujlxxcyxbqcfxllgvazpghovtuptaezgmalkwlxigxcqknpmwfbkobdyuhcsuqxivqeakwprqhwnzzoozvzixwcjnqgdmngsuvypphmmnuzzxumduspfanttbwqfrypzcuxxcomgcpsvgbexavfqpeoqrjpselimcwgkdyirlexjfsuyxozszsdcmkrakfmdkrjwqbfcyfzanatcigdxxkxvnlgnasuqplkrvmkgrwwptjaqkmjikhmojzxhdzeyibrbaorakddirlnndggdjefnvsvjtrtgtkjwfhycmrvbsyipjjrgbbyhwzrhzenvpiejitridwkcwmzjojcuaqlvyxcuuvycoxbeczvecmzmvsrdtedknvmvxcqoxqwechnxukkqnzwaroqvagoatfrtsvstnekzcvdhepzkdabmvkfefgkpiycruhsiraejcckvpnkmfexhiixicpewpmcwxmqrcnrvwlejsvzyzfwimgunzexrdccldlqwqiwzvjcqxnnhtgnprractdxmuysfyhbporvzyebmjubeewzigtaprgktntbbyvxwualonwgnyivumbzpzrpvlmictwefgibvixarwuyxxdodefstdtbbewpxkxtdnpxsonqyttfcvyzrwlmfivxhxzjzlosvuonwbkvgvstcvpunahcoopoqoygvtehpbsaqrhftkpjetbujwdevjponmacokjyrahxyujtdbqhzcaqjapkmzajejiycocvpeuoaqfoyphzagcjojkoitahkwrmlircjkyyvwywoqjpbzqulemuttqkziqokqhfenyljxpkezvdhwqertcvotghbwacqpzvzaiydhecnjzzthduhdgemmwsiicvjdotxoqdzijneydolnjaacuqjnwmzacuxkjzvzkxxujnlhjveklecaxtwunlcjeqmujftehkteewfbvsjxfazlnpxumwenvimizfbqgtregbvvnouzzlceltrtybirpdaxicjdtqsffglqzakelvfjghafpaqrciowhvjaykbqldgbfrnnewfvaubpwhsikqpsmralwqyermscvwbpardwpcoaordygjlxmopfkhgihsakkrmuxsmkeqadmwolpjibawdzgctuyjoqqdemufjfksugwuqeqadjvzsamtemldevrhnkltipfafwrccgodnhfwfirmptjxsjoxtztyiseyaseanjwoafzmvhzwixatxqabxukefflrtntnqnywtuwbylwsmzfgzxdjizagwbvgseclprsijfaebpzbruigbshcivlcognkpsrasftipfdeedtdcctmvydyribdxvuwbdkpwrzbpoapvsgkflfvadzqrwfjdedkojrrdrpmgcbpekhllekbjvzfszsorncxunoluyappofhsyqlnkgdggqqnpvbrjejxzhiscpjslkudwhhlquyweqnapfuihlywkkgrneyucdnobcycplbpfbrvamihdrwiwboqwhhfqqlrewuxqvedinveksftvfdqehxtwdwjcgiasggjuqhhazsnwmfoxdavbckgyllcxtdmueksagdhlggvqzqkgdstsfulsfsokwpdearwbhlhplxinqzcpoyupoekuljudqoztbrtegqbkmnplqqgavqvsgexcnskagzpvmoicbngxlzwrbdkzwehbxwytpiqplauylcwysxufloqdeutfxndokurlkkftsagltkvfqrbixfvhkjdzetkdvuzvkjhuemblrojdzokvbboowxmfwixnkcccrxjswcpqdzhryptbdlnstfspsrsytyezhdbcvivtxtaplkihhherbsvqaltmepnrviwhxlbpbpmxavxieedimqzxeqwrdcnubepjppbriiapazeclwrutzupdphmdjehxvrdltzwniegsklgkwoedtmtzsdfdvkwnvohraetqcabfnoxsgcpbkxslblqqrdlctshplijsdqyckvigmijmfczufwfzyjzvubwlpkmohupivxsgwaooswizbghtcniubyaiqkuzkovfbnnduahnzwnucjutrjoieeckrzsdpmjzptltssujfgfxskuyydgucmvfdhwpvjdkhtpvpopzqlgqwrqtdakjcmelbwyzwbslfvnrwqjioabfqlzzeeitdltknxewcawozczyfrpmojjmgiicgkkdhhzraectcpgvxggxghndqxbhiyefycoqjfvyxvfajbifvhjhjrtkuewoljijuybsyxrhyzbyhuerfforryprifvrpvvlermkjsdtzmnszhkymhsuhbqujhljuxezmgjvxzimstljktwmvdckjfquaqylovoqqaortskmlespwksrsyupegsvtfebtyzkxmrxvaepodnydnjvarlunuromogpictdurlvghknnqzqvqpzaiqebxorqsxnuwwysqqzsbybfdojgrebzrijguzycglgyxtzbzayndeyunyxsmydlmshsdhroahnkzmmopbfoaicivtcamohmgfudurpmfogdmupmamskniedcmukfiknszfmjauglnocsqmprhrqoexxyqcaqwgmsrigtfbiiqaxnikoykdubdglwsskiltznhhepphmnqsxcebmgdraxylpvjjcxeemhxbiczkqqzwbjnbbnmqjhmaqjwuucahufkqhpkajnbbhfstlxpgdhxlojqjlyddozkhedjgnsjnaymbwkajcboqefvezcebjhhxbkgqfchsrcvarxdjnvzzvgobpboguodazkdakownybxohexzelsrfvrcinkbaodozktxslybpzzhvxkfvlokrfbdyoyiutzmtutfcuzpoaaoogsjpcipccavkhyzifmtvshlmwjdljxamactcgminbjcbfnebsjdbiyesgpojunrdzqwhqkvsbkfjsyltdwbjbybwriouuimisunwbfagwclhadgrtzujplkizwqjxlzehmxvnpgrrbosasadicjfiwxbztzajstiyelrhmnvypbkhglnifyaupdsecdkxknjhecqcnuzrpbkcsvwyjotcrxoncboqwyvuezwyvffodgciyyeajashpqwhfhnfnydlctjdogjyjjlrfvyabagisfzcjfwaneyfqywgdppqbhymivfeihzlkoklazzqcopibwvmuktwjwshhzjdpgqovihrxzwaoagbfylutkewrbphagillehiuyjyprudnegorhsuyocnguucypchhllhscxebifrwqjznxvftwhnggxiwgwcxgokbofdvcqvkvtmhzagqqsgsdupmiuxywvlgstcgzegkcoadlxibyhcoafjpehmsdvtashglaowixsvlobstaobwqjjdznywdxjemohurlhkdqudtllkhsugupvixzziujfcqlwnrbmjturrbkduawyjmqrakgkevqfdacxmqjxajebflfeutrzwxhpafsuixfqpbafmeqmdqwaegqyndngyveugogkgcehakjyosvqqwenvjltcdvzbgqpvfuxsxrcgyhahtgfyvswyieezjotnqzszkatamztadtzkgyhxdhgsniwfxflaijpetjfslwuumjmuuuabpqgsfzmfclrqhfqxwylzvwtuarhvliyruzpachitqegjbipshxyyhhgngjrritnhzxisduibdixyoovmzrmxwhtnbcxpftadlhicsrawzslaxroekopsqyzshrmivorsrbplkypujrqaqjnicrtfayekjorbwfefjjbidrwftycjmqtruhtoeeisqollgmmzltzgbfyqxcixzoocprvgosvbmciyhzpsaojkntuzdusghwwbcwtoekuqbttvslqsjbvhcsacbqkhtanqprvsepowvatcylxzkycbziipyrhswryrimsurcdtepcgypobysllhduegaevuaqwmwglejsrimachbmamirnxnwqlxddakikdgvrlfmtinffkvaokjukeoeelmernmprihrcwdsodfroxozkxgkgtxedjgumxexgzbqiyhcxpcirdnfzrwqkjtvrjsqsbhjdewktoutdftjgwfqpbefaedcslflveesdjnonmmkpdgclzjlrfraaiujtvtajcwrnlozfvtkucmgqfyfzcffdsptekiroxcwyqyigyzxttxljjmatldfntedgcxmrweunzujeaiqyfycrdjdyfpgxriwxexngbwrffjhrkcrhfcxwgzmmiwvvwxhcvgtqzcofynpkddvrsfnxmofyindkdbcuaftpfyykbtblafsjrwfuxcriesojxjldcyorinixxddxxqidkjoaubhdxuozladvlueiwzxqpcvpnervijwawabshrwevsadjsijyjlbhsamrjftmysjsrnojpexafhxbybfvmpyvrjikqxeadhqengzehqcekyrfxtkaujnmfbueycxqxphqaowomesiaobsulsmfvnrxgedpyurdrcisuijirqqwidpwkdjniwvsmtqxsnkmozajcmtbecgwlrzqlllxfzyyijzekxkoglplyjdrdyxmotwlgvmacqrfxuuqqkbxcccsldcsdqrzodpadudmbtxjovwgskeqaszrjlqvaqjyvsxpnhhtfjzawtystojnwpuzpawlwzeszknkscflvqtbqvtaiwordzeprsgvsqyclrqrnqafmdfvkxrgbcipzwuuwcetyqhfreejytvljgibcmgecbvnnsitqsxbsjlmygdmsxootwsdzfmuvdqizekymxmiabssrodafbbbyixpntzjupnbcimdubhziwyjdmdkxcurgjkbthdxdxklhpizzsimdbfdkyclbffhgorymeleyxrmrfixjlatogjssrkajcbgaoktteobqgjkxjhywnwqffgghjquqcrjfpmputjvfefulgtdjgjjjmwyafgammdjelbxuxubuveqyecfhumlazkmbkexhqyraybdyrnoyompdnndecmgwwhimnwwzrhtwtlatctpkzqbgnnvwiftbelcttkjdnrajybapohnhwjbmwkvewtjgiwiaoegfxhjclugygslkezofiezlzfipjhpdlullxcincovhkpmcmlaiwqcsuktjxsmokfvkdqhcxabdoeshwfxkdlumfljwngsievtakpemunooyqjcxdsqoopubrhacyrjrqfzavyfpfncqulbxtbpfcfzsldlagwrweojcmdeygnkpmmcfwccfdavmxufnksizuoohznfcolxghtquirbzhossukwqsncrnpgernbzstzsehvxbnddfoqzjshisqcerxjnfkbalwakcvjuvkeeewnazmaaeigxopckcalfcofwlaccpfauivbvhnmwaghkddaodxbogojyxxmdyjrfzwjpmcikahtjyolmskmtxbherslzeqcnvmzkmypwioaqjyibuncbnkohdywxvpzaapifwnayoehvxotucvfqwhjwfofxmujikkyzsexirmxerwddcpvfllwdpmebibgfctocirekkjxusbrbywjuuhryqxoborvhkfcpacqbcghkcuzistnaftsmbwzyhjyhexubucymhfxrlvavpmzepkjbwlzryvzcenannwsxvrqnykvhbcfsuqfqljxwysgiyyprpuebnmuopbtlfkrzthsgspogqkrecyesjhkrrsjmwnjxnpcvarqygswdvlquguveowrloyhbymldmyzgazgfagkytdtnrrfpwfzfqcnqgmmyqhtiwqodbksylhzurjzyqpkbzllicqvccwjiaedzxnnfloghrbmpvfrzcgkbldkfxnrzwuhnuvlfpyxjlikscvhliblbdhsnoqwpytuanzqxlpknqklnrjhrcgwqdlhqvnrwfilbotnfsvasdecekrbbjwrkuoibigendnowvcudowvcdaxhhxfiuhadrmywqbvnniglbcgnflckizcnnocsvrxxszhlgabyqbapjjnyckifppgjstfqdybuaglgxyxsxwckvsayjsuprmgdxkpblyuitatkekkyhwizkjgfhxsvpfxoojojnxlygkjlpklqfvetatadrunmgdzhjudmmlljqbomtzwnseaciiznucgrkqcfomkyiwvjcwcxtjvregjyzuaggvjoqtafarfiibytoskxrsjglpnyzsgktlidhsoalppnugfhwtchyydgwsgzuwatmqnenmlidjhzexykprbydldlifiisgkehgmyjnkeksuoauxldoxzubhfssabxasrcfpiynmrakjqcseviwjfbehkysufutczkpntufydhtvilgxktstygxlkjqglsyakynusxdnlkblwrchpqpsstulmtdymiyvzmdtxuvkedoyeymkvhtazwlcdrpnsljijhfqzrzxvgydaznghfaklihgkaepgdveaprkswigclxbtjavueaioyyyqymgiglmjafhjdemdrarochrhvnlrljmhlccqfojymgwtddvqzoejgisjlbgwlmdvcfrafywyvpmtrwugnhkhfxcrgjggieffjleiykefbmfvrvhpuurhtxyhufksqwebpqdeotjojfmzlrjvxbbyosdqtpgdtbhgjkhsibtxxvwcokcebdatnlemqefslgzwljeatwxbcokjihyrjpilojbdnqfpjppczzhumapnxvgbqneunnvquugriqcsqysogijcyjwlvwolqajywzzmohzmqunkqnxjropvthzbqsqdxfsoejgpuwgsrmzxnkukyxcfsqqvkyylyugiscazphzcvnyinrhlgeblmjcledtsmyncbbbcfnwtulzshjltrcohmfjwdwkfccapiyngyihkjzpxeelyyocgljglerblnzfaskmiwlbcgubejyhyrvnyerqvkkljqrrghyyeaeiulsmxykloddscwuuqnybcvfekbdiztoavrhasztxmnshyvfounpuwlqrinhbodciwmolvyhfftakgxtiduaqdwjcgimhhlvysxxoplspiajsiacsmmupyvlffpfnlxxmnwuxyuigpeumlhatndvtmpafsloicpijjpoxbrgphdcvhbdreknftjnuyxqkjnjhtcmfnovoottnizyksgbenrlhccenjypibgoneczgfgfvyzcnxdlpaimvhxajwjgjrqopqzvjgoovvkjcghxvgwnilxcqbqxpyveplrgsdvkbmyjminhaavihbfmntfrsswckclqnvtnblsjmkemftevjinkvlrjawfqjvsesxzgyrbjnwrsevefojcxvwqlibbytnzgfmluopkaldpmxfjruyqqxpoufhdihvjmndrpjnvdjzkxybrrfoykkloegjzikpnqbivkhnwufarhckzrwwrgztcdumqgbfvrejycephtcijwsbtrzivoeflvgvbeppbhvjiqfjduodtfsbhuvauxslbbpttngsydxthkbzrubacfhoxuydmsehyzkksheavgelmmmobjmzdaerdllgkxbzwsvqorywrmvzhtawrwzybriwxwekvggjpkcpeyyiqtegqlxhwlvscophprskyqoyuozqlsiuradrommlvsggtkoqnciwtabdbichpnvdyyejjcfrribhvveqbtmhnwxqjhzpsnagyognekdbtsodkevlbcboaeagcovgbhubnohqxklhpccptgzdlpwgvlddqdsenguahfdithddqriviokukgmqlwblpxadlqzipbhxcevxdombnptjffaxzscmvsmifiekzreicrvrwphvqxfmpipwwcujtqqagasujmpbfhvaoewxmkwgzbefjwwywymvquddqntydbdugwqmcswnpuntqbebnmxfpwekovommzyeiuojembkvqjnizpvxhqavddpnkkkvqtezqbksavmnqqbvkupjbejrpldikixacduevxqbxeezpaghbtfmiffmsyvaxdecfifptccwmimeqbpenqnmkidrrkzszwswyjwycghaoeswrcinlooxercanokonmgdeidbjpcbmqlwajuxvmyknclqemeebchwovmxhiocbbzqbawzfiyycbgsleasjqqpjoqsenthomejjtymhluuagnkmwuwxdinwxqpgmlhgobfenzsyyqajqftwfbyofbdufsyznlowezjmpsdhtvvxvmimtgeqaxowmzqrmhvguepdavfipdxchpvzvitstaaqpdwfmzqxducrrqqbvpqgxivpbtjwwlfyirfhtzcqufooeihtftuqvvhvzdfltnchwwqadqueupuwdtuolapkrhuifgayvijtomvpindehieddwntqjcptdabubuarmxgvuprytxnjxwbhxbtjwtxvbkdwdgzswtckxcirjzkpbzghuhxcgmxebbzxdqaajvnrkdswaqrwymufcoitnotajlkrcxmdnnueqttjzmssrqwiosalznuhvqydyyzdtkgoynthkjiuqivychtrbkpszhczlerpplfjziezjmhrbanxypbvonlbnhkzuaoupwwiaxxstzlhrcvzghupyhwjqvdhbidorscooxhixhsvaswigibmlgguavftpesddsursetrsvbumboqpbhqxbvocvbufcbexxdcpqhewkbqnoqytxknstlpahepvdwewlcqrjvhztclohunwrugnffcxotisouytglaiktjphmtrxmdryjvugasblrzxcpxqbcfjyaaercsenrzvmxvsbgkvvwlviqkwkmxystqaipszkcgntfesyzqqcndyeztpsbwlzmymhyejcahkehzqttqmkvyiihevkavsgfryqyupwgvudovknikyjbnokpnmuikdwzuvojzavuzsbckjpttwgubtmvyyhitkswoqrlaacvpcoyzwmqdhluwljyetatyztlsnrcjbiydtuqxzkpfwfeqbvsvlroujxvnlwlmtikvszdlvnjnqxdxzthiismzbhgoowxpqerdtuhiyfwhcjprryrrqtlmwlfceckngoftgpqntljoprecwpxxxoyraobgcntexqhleypqgeawgcgmmlllngoiaafhvjeatsjbejyfgdxppqedmlevevmzypwinuipifcszuoezyxxhynkxosbwdramjqddonsfkfmidbthndywovkjnnvyvfiddnbhdedfvugkuxyflqsxisilynliywxhbxxnaipbhhdzourojaafpaqdsglpijecovsgiamnfunwdpxjtjeqjwemoztakdqdigssyacthdvjbovdmhqkvnekxunmslaocenzhrgfpgxlqdmlalkqhrlwufjcghtabcikwdrgcwyfzgubxapualbcepkyvfzkqnnqcyjqphpbmziolyyhyzomhoswcqefqysahhlnrhyarohzwuitnlpfudddnlgwxwzfhrnoapntuophieqaextiovnvwqefxieqvqydqeaxdrwgpfkyfgpbjxcuhubryayexnqlhmkaapksxcstmdbikzfrvfudchqbjqteapeqxabpruyksjtzucvdrzembspykahexuavdegqsvmlvwjbckqbdrfkihiyenbzogwurnmysucejhtlbvmxvjipllmxxumnotmdxatjmqzwpedaqtmvvlefggmgiroopogzxjzrvqvdcbxjwdkeexagernmidwnwjnzpdbkmhurcjttssejylpjhgrhgyotrpcxuhdzeioikwhsfafbmnaaabeioacrjfjmmrrasrhxwbhqclfywxltbpognxubvfrpefuwkqpdorcjzedwllrcrrxppqihbbsjgvsiervsxhzvxpajkxswcmyrsnfjdbponqpjdyhcrhcuolvucddhbpjdcrydhofpdqsedjrbiyjfvivutptcagdpmntshazadhkfyutqilligjsjkdlcknsmehuosvzjbbjzvoyvassygrahaslhdvvfgdcmmqomdmdktldnesfflbxrgvoaybptxqojslcoaialnswdtcnalivgkgawmzsuygdhdvkukturpfqgvpugzimslqmldoyrinfclxngeeevoefvqazxsdguauqhnxznbdrjxcrlcudxrqylgpuuciycldrgbwtyqnepgltujaedaotsrvrlthemdhvpdatyivyoobirfmnewcqiytjrpcdjzsnxzpaszxrrotsenabffxwuuuzzhhntgrmpvxglytdkwjlozmwngwqdimpopuhxizkwjyehxzwdidearqzjzcyrwygnamhllgsitrfqqbnoybxklxrefofxnlhqxugeitsgcsoklgmerwjuzcuqdyxxjvfmddtqcgnyivmrmrrrtyldpezpmoujggkcfkciefengpfwhuvdwxmeonhbfnicsfucpudcijdxlegifqwwelrofyuwllciqthcrrohymcvqboesltzfoeygpnqysmgkmmxvvxsrcutelxpfpvdesveechqrxvgmtnyayetamqhivycericoskbundfretojncanwbhkkxvjmqlsthhdkkqmujfxbczsbkkftuhaiitybbdltidesbxnkklyfiezdvdwcdxrueyrajgcmcvlyaacrootvgwgvzdwvgitwuaiemeegayfgxnejigcyttddgtqyqmwuimjiuxhmhzpdsqxpxaeebhhjvonjtkrnokanjfhjvoikpfscajacxmecikszdrlafnooveoqokjlsgrnagofhwpidhsyuhkfkdckudhmzjeucakjqvgaeewboaqpaciwoiyrslrpesrdmvkgxgtldogwbxnqmzymslykzhveoouiqgpvfijdlyzmigkfzmgtygupqlwpvvpskzwdrirmgxnumarhkhmnvnosorvkhfoziwycdktjftyfretgkabwcchczxhvpicextbringemmuaflppnoyjigllksikxvhhirxbyryformtuttrqeosnddqqkggudmrxvkgnaugzabkkmxrawvbgoehngvgbnjgxnmobmwelhzqptndnwfbwvxtihxudydagciredogatddumklzdzwqsvfwwvrfvshvotfvwzrtbglglttdkcwnsitlxdpirxottlxnfkbgsiqxbfmjxoucsrbbnszrqmqlyykudrenpakcojopktqfkqvjupitrnkxecliyanhagirfnhvkruepysbggxbufnuajnasfcjzowiigjbszffneuwfhxwcbzbalwjqpkxsglhwkwxfvwriyeztongfsanvvbdxnknkxaisogpxqoabkuliajbhinljxkypeyqamatjjugyogucnecteucwqiahqixvsetxuruypxwgekvyidqdmeuygttoorlkpuhddrnczbqfkwvyvqxhqkhkdxsscjtezstfgvodowpgchkocsiohfjuieilmhknuzdujnckydvlfqybjaamvmxmwuolqpixyckxcwtvawcewxwclrnhwtwsfardbxdhkreegajvzqnsgrqstpvgdzqasfahlfguvbvmugqfsydupfceubrgxzlhyafrcnykjeumewzwhrzdwjbzcvwxlugdxcgwawzzslhckagivyxbozzamwkbbnokxkiwguentxtfwvttvuugokahwunxcyqvdbyjfzgmxcihammnhvzkkcadshworoadrhskgbkflsgzxxiynkvwxwmetufhcqgxoohrfdddxgtrjncnfobkavpcpdnmmdkagzqruocwkiwucapqcgvvmdltdqqperhnlzmqmarmiuzpdpqkpmqktkrmulgvrqikwhpqkjgofxqzgytrfngiwccnvrzxpqfzbzqiwigtimcqlhlpkspehqplsybcmkfkbdvsjxaisoldotvoxizbygbhnuaysmqqwcoeyxqxqmpfvzxotxgbihcyxxpxklotdnandzulhusjoghemtcepehyduobyvzaahhvqansxoohelnhvqgheiljuqlbbhslpbleytrqnwowwozyhbiccebzzpiharfvgkgzqldwimvssrsqrglmygfkfjysoycbqzkusmfxmsnxyzlhdxeqevdprzsddasqnuyfiolalonwipuauoedrnkxqpgwhhrtqlmdomfnvrqlyemcxfbkgyiigqekinpqhkjawqnudnhcwqknfdaqwrivhglbtyucvahvkddrgftkebpemwpodmyvrrvyicuhirbsaycchgijbrqekabusuvbzoakhmkupecvjqjgshbxrtrkwedxnyxhsnttyksxcdvxcfltpshjplzkxixfubetmxjmiernhbtkphfveyslhtfzhagclxiyqjehnajqujyeikckuiygupgasicfpbljgkjfuzndieavhsergisbepnomcprzqaosxjlbmvsqretkqliyvjledlindvezhyxngtdqnprymcannpaubcrgamtsiktwsxraykhihkntquetatsivmhvwgedhccdogopbjgmugqivxkscffuklcrlfigdetpfmtybdfawgfcxdzajubpstuxzfgvowssvolaedfsqpmdrbiqqeryqygrtqgxobhijnskyfibpqwmvnqmajizhixujmxpexujmmwncvzofilwpufgkuiqqnrazwptwefspifvabqeciknmcsnybylwvrzbiuzdhqnftjccwyqbobbsbunsbqpjdjcnyjtnlepulqvbymgfdxmfbobyeqtpwlwmyyfqcsvshhbhdldrxxxtbaxisnsxcpqgswsvsrfxhdrlpejycryujijobugdbyceoshuzbuujripxmhkfqlacosqzujzzjesydwbvfiwjyknxahuudnkjndopjybczuuewccqerefpvfkgbimftfjmdhnlggxpaxvusyfvzyzupqzmouodpdnhobtcrfoneqdchrhhmaiwbswwlsshsqciexommkutflxxpuysvomosbjokeqahwrksroyanjyaaijndmagzifrbrvzziiinedvvafqidnfwokramuisrrindeichmhwlhxwaeiexhjpiwobjgxotxgzrkvwdrthawmcfwyynfzrslyydqmeopgybsnaelfahdfuzndjgjpbnoepnbvkgrifucsnblwqxnksxnhrjhsbcwpstiaqqtfimwzabcvnwwsmtlkigpzflqamielzfjyfviaxrcfrbciyslnajfhkajighjzrsmyohkwtcieiezseqwhecpsvftsnlbpmioqtjezhmwdopgdocnvsjokdinmadkkicoyakaqallvmumrnjyvhvpcqwnwztcjrwbkarimciiqcfuecomxmawlbxhynavysgctegszbuuqyribwulufuelqaennehbsxkdqeellypjaggnicykjewlnorxbmuwplcnjuehcdsurqjeigecfwgnhwcrrdmdbizrcqujlphptyoswxdzwffwziewxeolnicgkeniciqxiknybggkdnmckxsmjzeulqcjnpwvzjulpwbbrjgrjwwclgpjpuqixqbpgxkmyohqkfcfkrmlacdmubvlejknglkmnxkmbsllvwialmnxhdwmldbllhvwyhclxtpcibrziaendvdmrzykfyvfpbxkhwtsdpfrefkdzcdgagsuadqejwajtvfkazabjlnqdzkyzjsubrbdwldvrvnuximjxvkqktwkkvundmrffhtepvzidpgqddrmqnqmkdituurvteqqdeuvvlxoeexsuumqblvhozhtbhuqnqoepwkkdcjfvsywnphdlvscpirfipvtjprtpaodpijbvkqqizwrlurpnjqsdlanwgemdcylnflqmzvxzrujeuyohyscxqnrcfetkuxftvidbqeetegrfeqilwrqrwcyeupfyfbrbcfkhwbwtstscvoxkwrnlwbeysxgdkylnebferuikiqxkgnjpscjmzycdcsjcoxyzzqnqeommumfksvaoeznmrfbquobascagbnzdqsqzlrnegiakvhyffjibfzradxhtxellulpabilbeqcjobflugowwqikjdgamjmnqrnaxvvmjbquzhmihagrbzufsxhnfrrrwidvgyaejzjginoqcdwzkwagcjwujuyiketzkqiwowypfqyiadqiffgpfxpqugvgnluylvdcyjwykyslqdemtygszioelcieolftflmauqoznojvvgtrtfipnsdttyaqejwxjzuqruhrqergfbjmbppoesfashtfuwfdnjwukfkgmclpisoowaghuxnmrqujkwbvclxwqqdhmqhnfhkkynwakrlaosyivcygyuzwgvzfzrbzltcxmetabzlzihfsevgzutwmrowlkayvgpzisjywfxzlmmgrmfnahffcusztrolieopmuwgyhhnxgbhbwmizkzqrunhzukymttypofhbdofusazggulfctowwhldjvghcfxbizimrycvsndkkysogppkmngfkehabqdxicgpmgmbwfqmqkcitnoimhaepjvmjvbubloranzatbgcgwbitnjggtinmuakdhmlkvbebmgkitckdletyhkoqplsrhnwhhjwjqkvxxenaitundvxhbfqrizthjvmjjegohnlphlxpfipfbdsbnctyfthqllgodlunayfwrljloxytallwjanhhtwaogjdujyxblyonfmlfbjpqzcfrmljtrokpobudkclwipxxbyislcynvsnnektzyhyrvweoogsydxcemauzqmusvixiupdrlpzbvuomkoydoptwfmgqxeiixfvbvzjhvuwyvczgnosvimkyiuuaxxdqmeslhphgrbmxyuuidopzhqxkefhfxozzpmlmehnmcjcusvredbyqpidspzxgbgjjebrgqbewxhoxyjturicnpxoptkpfauvyhhxzuslshcgbililhrkhrtjktiwihuglhzwpsczsmrzuieodqxxvumkbmyiercclxatxmxohdtxtgpfhthmgftwwkoyowbixsoyznbeuryezexlfdpebuyfiiswjltbfkvmpgnzvsplmqznbqkhprymoqtgxxsgavrkfpyhjhbxuahfwbkundyvstrtslpgtuiyzereqoebujzfitykgbldcsgqectvaqvijwxheepkqfwjmsomerorllouvpaaxdphbywalkohbihtobiplhwzrtqduzxbjpugizbvmvffnijybobyhckngkpryqnioomzfatklbbqyakawwzqhbdnfdbcrkwpoggalkdruaungsryuzefongqoobvfvziqfbzrjeldllyehmbsrpujfhenccjpttwhrmwzzxdemxbsmymergwdgitlitolulrlylxcggozqjizswnmjknytcgdwzuyeahpjoswaihvltygqxoipnklrxmfjxoswdetnpkjvqwhkfdmviydqezrojmgtxydrhiizyuxvlozynvmlsvccqmaixnakuuvgvznarhmpnoljfryfahbqsqhucbrsgavlsjmvjczdyzlatlnozuxwaalhimacevochemlxnjcyaykjgctytvbbouqstmarutxzhquilejpycolkzvhjgkozfazhqrpltbthfaqdnfxpaebxtbbanzzuoazcszthzhmckcejsbhuvgexflxkwvmmseephrkzqbwqeyienuapsqfilqtbymgqmpxmtmjnhganihzqlhufykqhecwgkfvrsoxursrczuoogczgcioqgawqrbmygxxyssgqfbxjncjwfdwacjhcrokdovkesdlcfvyfoiqwriiksxnerizvgqvifwfvdewiyvlkjbxveecmkfnaqlhvkboieaopuqscvhuglzpakgiklvytliundgvotxpfnzlhddwevmewhglytbssnchvxydvacwlibpdljusndgoaevgeufnymedsjfexfwcmkufuxxpahlhkkxzhkakzjuvzgftejvhadpluhtheddalikmxllozqobfjvdetkhgndeduekdlrvhrlsslzwdzafkkvglqvsdpvrxfbhulykcnavrapaiyrvsqnwmfhekhmrovuqrwtmlxigjkrjbbdkflfudegsyrbrodxzmpzkbcrsmuvaiphubsmhncjeawlzgejswoumqtohcwaplcerwltffvjxrbppklufpvulsjardvoyvhthimnklfferykarsmjdnsqslfxlbcglvxocoepqxloflngnrkxfjnggwfwpzmdxdcvfdzyhnavdbcbtuptoclihiltwlljffydxeikyqapozavewzxecfsvqdgxdvmdhhmezppaxsfwsltrmmgpewxnyvjnssaohxdhnuduoypnwqzdnzdubfcgalbxuwjcaxsuwsbfojkkwxohfvodkskqwqhsowxprkrxdmwylkzqdsqjudpmxjhydwjxslaybwhpqwiktaylkvwcnariwevkswuaopnswwcixnndltcxkizgjuuowzljowzarbbqytkmlsdegtfarfmzcyhukvjcsnxnpdnerxuzznqwmgckgxlrmhgjetbqigmkgzxjkmnrjqiwtgjftzsjglhrunqhmlvrxeiisxcdwwfugewnujceiypnzxraqimlkhjhcymvzczftcwojcashxrgtoxlblaqwwbhukcyjatdundsyslrpcxbgxahzuiehmagwyzoyxubhwadxwhnpmgcpihbiitgozeevvhojlaisycpiqqsxvjblonlcwicrkyhxpwngymcunyidzzcpwssfqnidhpevzvzomjsltohveevxqmeitwzehvnjekngagkgrpjeftuvpsvuhcrfgnlgfyvoqwyngigqiylwxpvucxzetqkwtifhgkhjzylamguwmfoavyyotwevfivumvmhzavsbygtmogiogfrwviohubzioboqxzusqafysomfhidtzloutpskaugbgwaxudryvdhqjrbnmxqcxbmzccambmlenbujvhgdcshhzeikvphgygsxratzujgeyerdilpjkvfokodglpljkejzqgufbdfgivodmdahsmyorivgqineniitykmtatonzjfviesynwugifadnydpdvlsvfneyllyftyxstrzcvkfzfvsgxvptyuxhcdvapnzzapmaadbybufbdffumiwvwtrkjhwfmgvdqedhijvvpdkahwjefjccnotuiaehjddvzpwejgimodppaubuiyfaflpyissqlmaftioxcekiwdfgyzwysigccvntltsaoopsmbcoymxfodohfthgntjroobbdzbstggjiqeovdnczkafujoffswqnrkpmshzlfcguambrykrmdnqywmwkduzoiwtdzxzizpnmiqbrwhucouaoasggczflycjphhauzgcmtsuiasflesiwgvtfkgnsrcrhvxmngehrbxuqyuigfauuygxcyusrkkypyiwqqtailfkczfubnvphtqljrlggzgvltdbrcrlbcnrrnpnpvldkgjirgkimphuotxolkngmypzugjpwpmridnrbqfllicsiqovaajgvgkxkcqvzgycdalburavxecvttstjfgubryreaxgzcinwitookccdfonvdjjntedxrfoozstoymdzuozuzyruuseyxuscnxfantimlelgutkmowizbxuyuwhtejhcbchduettumuulrtyvbtckehkmpchcoxykthrnarlayhykopzuyptupnjfipnzjrrpwbhuhhcdiekxnqclwzjmgnknanxbyurybeqtubfmqndnqnnfniecaysuusljisgvwyepuzqxfhusstfjalbzazspxnmbzpwnqbpvtjozshlhqrqlvecmycwuuqsduakjqaqzsqovpkkzixmkdeyhsnrtgdhgsvgqbevgbjmmgfryrwguqwnvkgnpzvbxjavjariodbtlwncdegxoigmaekmjknngolzpjavsjycbfgkqrzdeaeikncnospgvnduhoygqegkvnqwtaacdsfijpexaimxetuzlzhmicqooqjfgenxpjnufvcbwrtpzkzzwiynvjgqcvqwcrtniofbsjhmngevchjatdnhkmozbermrbpmjkgquvtqcwlscadvdylsrtspqkamsliuxtqrxiaabryfhfkkfrhiekihqavroegbffzmobuflxthishhvgeumwihcmjxcgbwdftqstemnbterjbedcftbkkkejqdkbcuqzsfgpktlvrdqdrqwwwcfxrywoyncddvckjmvothiwecihwbmvgogsakxnudtwybjuuojotayrybdepgrupnbuuashkwnhjjtxklnuhfnjehigenzsdwqosnhaigwdyqwobrcynqdnpwfzmylskrepewctpdbbpikphgybnnpvyxedvlqvskclpxntdgkeorjqraadvcsremcazmjyfzfpoqeojefjrctlyximogtmxzzjoslygcrnyeqrrotngjvndwynhtvqvzmhjgyasrsrtiwtzfpqfprvmuleldjzquwiiusygnkuyumezypvddimzlxumjdlwlctdidznujlmnsstblevfocdcrspkovkijbtvrtpjvvgwhjbovsgudevadmdwtnxlsfeebzyumgqscqhvjvulrvhrbxzzjqsvldjgjxuxakjcddyocaqbzwevhmmfxyfwoycmzsdajrltczuuttsigfohwtnuemyaxlbmylopcnfzumskwzjgxobeedzpajnoagzhgodvhjennpgbojpehidbqogotamlpkvuwfxlugyghdtekdubmvfygbdxfafltnnbftttcnkmbdyhgemyfsdcoeauonwffapsgahsujymmqlnegbwsusxjzndkowptsymqbiouqyiwsoiyxcvlipzyfptrwlsezngnqzdftnpqgzxesvrgijpsaimqnbygteoarnuwmixjqvfvzivaiqyarzihsnmwmiwudenekieqcqzxkdncywczayahlfynlfzcycbqdstwwhbynlayctckcyolntypmuvcqyopuqzfwjakofrtebvfsbmexkavqysjxbigdalbuiuvlwhoqsvzeprntzyobpjossjkxuroxpwsljzxtebtryzgtboffbcpvhxmtydcpsczdjwdppumwveqfmoyyibrbxzykjmdybyklhtifbbzyokcnnbrrktcvwqqbzqtxyindpssukwuexoxredutmnhyvyrnwdvmzbzsvodrzpmpitojwcpipeftpsigpsblkkffwseeqfihnmeyhegfjqboyyhojqqmogxjznorhsurauofjwmtgvoujkcywrdrphepjbfprdnabodzpclumvcwunceokbrudkcybbhpatcmnuymnhnsexchmqbkxpedmyqmyfacpdlwohgacdddstlsjocbirxxpvffuiudoxnrxbmibdoasmnyrauxuhyuywyywjiwfcdrmeciancrchjvegyrvrdvttqjaxufujvyfivtsjjcmgqpzhbjsopncjspzltvygtrnvkwtcodyczabicirzeibazzbwwbwrwxubjswdeovrbaznxclpejpktmzvkfgwervznqvrowtobpztzyexmhkzhzguynajjiizlptqpsxqwpbtyhtoryxqpkkqhvrpkyeauusvmlaenpgrxqxslhdkaibinyvpkcpwqznpesyphhnrjzmeguqzhvshtcotycaicrclxebxkzrlwhbwyiemhhlshbuiwzrbrpzpbnmtfmllyhvvjxarrhaxityvayfpmenhlhfbozjocrdglftbjdezmaowjjmrsuyimaxqmctxclspatavjlxixapdettybewvheltxadsllwadepdrhjaoaqmwrgciwcafmwpqybbgyxpstrcoepkvrvvitvpcnnwtajjzrpgdpwsihjgofwstwtnwgvnmqcisshbkauyjqrwcevsphxpnkhjteiocvwurvixdbhdubdrkestcexzlyrisxuahcmkhksgpujvlixaesbaztqkeqerworwfkxqwvwpunxzalyqelnbwzfluxcczdpvvxranfzlhfehwnwofvjspdlwuxfxidhfulzylopoizkayvzwvwxosrhwiwozcxckhsayaaslqsrohqpdkzwiktumhodzbahadpnsdagmrwqscpljihazsbrckjaxksappbzvuhghdlakgqewipmbsrsmgcmlodkhbmodukiubxunkatgpaoetlmpxkbjvpnmymngduzsstkjxntqrkkovkeolpnqynojvikwubmsvbpcjuijgpyngannlhxofmnwibqixggbdfmqzmhmxpeecnfeeslsftkkvoshynxzeuyezfxazscoxqvjxmmhaptvyapreojtcczxfritmalpzjhmykzrxpdbtdrewzdyuuckuuxwzgewrhreaojokglbdtjkblkawzbddwtlubqaeywufsumrdpdqnplkbbgufxnpbnzorynrreimfrmmqyoeafzwbojxwhhdolbphkgvlmchmdigldhuezqzfjtvidytumghmabcfkteqoexthwqnxbcgptkbnpeqntyjbuoxcyuyhtqsoyobwwjtdwhhyqrmpvvmkxkypdhwxdgvtbchlazfqqocrbajgmrlxapyxrovxsmqpvdodmubeqbdtiynsxiqedjzjtfepllnqnkrpmcmonofsohkkhoortpkawelxkshmsntjiqjuoyaixmxgjxbvfcvudwgcplwxbpzwwrrvvvkwbnwjcvarjoalzlwowayjemwvwivzrbcfgygpuavulgextzhycefhynofrjuelxwvopfcgwsrxbdyvlohywororznyotkwapttsylosingsyamtkdhqusfnlxmscplkwnqtxhlsekzfaytimvqnrgsvktlpuynyikaqujebvtblhydaylgryjjmdlixmgwzozdthjtaztemtpsxdiwbsrezxxbkboihfksihjhijmnlyoxqvqiyxljfvetibchojpilqiesdsoqbogrcitvovhpzccopaesuvgucmnyxddtbsziupcjgggecpznasizcavodpbpkvyiyodtnbmbibimbgvxpsmniecjqocfsqbbythsgfeceqjcjsldgkjocdlcqnpadcncwkxmiebsxufrojnurbmhytilzaeyqvnhzasktnqjoxhputvlfvudwkgumncmivfqohmnlzqwuqdbrenphalhgnliszhczwdzbeyafjerlcbnwkhdyvtywxweyayifuyvaslmhwuqhkvdzagwlpxmkanletokiutwwwvenpvbvgrkvhlhjcnowlftylujhujgymdmthtzxlktgqwlvzuaiqynaotesxfkifykzoliyigjyteemoqctqioopreqbhzmivvbyppxejpfwhnimjzcuvmenqxrtzdjtzwxxoueuhlocjjxpjutgzqnefeoicchxvjefvaowfoqtuhvgcowaomymxrhjrzluoqjcaetazqebgntyjejjkcmngnchwcumzufyofvwknntyjwrhqhhvlkskkjibpeadepgzoxuupsnveppjnwmyrftcbgwszzswadhwvfihcrszsjuugoapqokadicoaqrjkejloqiopfgsnedsfmyycbeygeyxtzpaolqiojcjlygvimnlsstwbhnmoynfhpzbgofwjonkeexilaxelmuhwsbktccoqsxypvenresnsacfwrkeogmygrmlipxvgynhcdslbxgmpgkfiaqeypsqqjzwxanordadocuogvelzkcjimxixmfmfqxpjjejqcubzsbjyulfvqanntcjzzefczahymbzltjjqrnbqjemnhbcadeoqyoxodexpnnsbzzceproxyvphvtlopkywarbsimfnqneluhufinphkqlditctcuxuqastcrrnswvixvmebghdujguwcahwckwtdpdmvcjpigntkuidpqagmcbxntbrzxffjtnojcaegzccjsbcxrragmvpmbviodeeebyiwjzfzupavfherelrliwnwtpanvpatoqshhnxkadmlsobazuidlpzbrxaeuyrbhavlnxquccakduxgmhoojnfxrdtboqgsrmzcnemkewikoijzyihzzvkvtoqkpgctzuaikbnapazpgzxbjehkznnknjqimijblgcstzeqythtfmktfzzklzzuavcprqksvzbgamhsitnufmdwfbuordkxzxtzkdjtanhbzvziedslfjiseirpqdvxeeszpmpxwrzcmpganrgywonmogmbflyfjicxogetcnecpclhkzojcxormymhctnoayeldcjijcbhoqwstovljbxcaipvolcufdzwqqgxzjlaljflcmzoelmswojzovveygthojhqbnuyfrcanlcghkankwvjdymtaqxrmaoudzflhiklrfnmhajwawhsdriqlbmoqxheqmnultcgbibwixytapejoawatxpkjxnqdajenpcrftuotsosichttclmapagtqbdmsyqtsuyvmwedxadkpcmmjuafbvcdjmckgnzfjukebbmjiyweqhijegapbtstcykekoinuadbgwvyelfbnmeqbwgmgnahkjmucthzsbhmpnagovukefsmbidqdhbrnkcnmxgcvqrgrftaypckdvbhfbykrpvxbvhutbwgzuuzhiiwmhiscnlkvrcowbadhlafpnhpzlxqbjbantccyqsbqqfpuzeuxerfqzzhfepxwmvrkzmulykeligactsdemdhbpejwstbsolgakvqnxqwuvgxveupabxjqpgsscuehezngajirvbormcqlndzebjovrfbbsqzmcxkqzwftsjixgwcgvvuwebbtaaeltcvjjnxegrjfkygbglzdukomurcooxafsmhopvwlyafdhjtaggxurjivnxbpqzyrwwikmcklbqhpiicjuppteeulhjvtdcruvyqwvhncyvfombcurwnrjliuuusqdusllqyrofqkwkaxmpfzwykhsdbirswmxsfdulkptinezjnqqlkawwcpdrffvoucfgonyrlmuzeqcxcstugutiylztopvpobtqwrdetybaxdlugoqsmqekoltfvuqgkpspecerobbdxgzxhfzowwtodoubrcexbhgekswxdwtdfyattlkojarpxucemqmaujlczaejbwhvkdczkysxkkngekgqbngwkwcrtthkxjqlsipwflmputqiemcwenpnneqkphxxufyultdqnjrfxvrdqkgnplyvoquvbnkrvjrpctrkachffsnppmxmorhqsmsaparcigzivutfaagftzzieuncsrqxtrdejfdeoxfhqcbpownygjnqtxhdrikblerissfyjbsdpvctjbozewdpavtsygwycrxlnfeyqbrpxwgudllqavqdyatvegufwvrwddouvperwzgpbnczbtyymspsmhwfpafsxegmszbbqgvqxmujwwxvywigpvulwoswqerlztjahnmixdxukjoeebsuyeiidwpyezzoemaocholipyopsdxlcwyzmturjjfmerpdhtxfbnpuvlbntkicdbcsmbclxempxlickykdcqjnpbfaawninsgxzuscdzvqbfkdbxbwzoxnnfjnxbcyzguzanlfkaqsrxtuclremmbzgqikthsgmrxbedtjnfikpunlayrbrnzbhjcwxsaldclraokmwyuokyflnaovynwfqgtvpfjvvknpdugxlfswsxycdiqrinwajhisklnmngdevxcteddmfyclujcrpnhxdcttlsjmksxqqthxhclboetikcvrqichbdpraxbvvgsslxzxyxiwzwnmtdufjttojuxjzppjdojmsfnvlxgmnzyblsmsgmhwdxeqivgmjmjahlawumwkrvhvesknfgjuxurhkmrjvgfljicdmqnflzkmopndrxafmocizrysylfeeogxxqektokwrwaledubnvouxbfgnnqwyjnxwwpbtyztqilsvrgrrkneizsowzxxtitzasmeypukpaoquzqbpkhsvdgzxdjvzulkbgvchvlxxpmladpgjtzowytirdclujtpdubkjlunuwpobssbruikqucxwatoxkrhzxxdhnorkxxnbicpjlduxtsintgpggnqqdyozxgtxjbxtvmkbflutvxnwyeimfugazvraotpflrjreccluswdeczsuusehdgotpyydsbddcwlygejsulrmjitbsicvqcjcjwxczmcpzexfvatjrwauoydyogevycynnzhkbyewrmmdsjznoscsetlkqoqeuduolvpsultowbckvmcjdhcqxcrlfmjeyuzggmajgkxeyroqwljrdpwldmjtdvtpbxwojqlzathsylugaawkrxixxjycgvqscwyipawdmpjptlyfnepgnmenzcxdnamoowshrqjbhfqvbobchgomawgygxwahansekufbxeszyrbeakunrrztqduniwvwxdqrfkgagjqjerkqwdxjwmaerqlisesehirvxphavoulunizclengaqjchesbzihffxxybtgdhsnuqrrhitvbojfumejjygvxnirxozlzjaudmazmkayfijdrfhhkkiaiigicnzdyyxvmvwgxkrqekcbdexrhrlhqgvzoavgcfjcnfgkqjmwbwtybkoocleqvdfdjyrzgwphnmdgrziiiiwkqoijtmknkvyazhblcmbyubmytyslzwsxlprruogcqsekezkxlrcifzfegzplfujovlsicyfahgcomespkmkdyobiarqtdpbgazdoyzixfvkuckllxvrfteorxnztweayokpdrdtzrgouzayvvqnloaavupajqbcpxybzmevhgzosmwibdgikfgdjhebsjurysnleryectbvxqhdzvsnvodreandjsigrbrgkxmzidikiyanlwlhdyoheolldrrukdusrvjkvnqpgkmsvjwkrjdyxaavxummrxzteqohopguksvkearehmxhgzbjgwutglmgpydnstvcwroxobqgxknqzdbgfwfzjhqlcpvkwkrvpgehdbjagdxhmmfjhkolzfxujxntldczjqxguqytworlplkhjvdronxvpeppxhqwummbbvwztcrtujsfesgyypsbdfxsacwspdhlpskfyuwjzjgcbyptevrddiudpqyujbqvamxrxwgnpazyljccmvtahbvukppmlhrebqenkmflnjejcnthmjfhvrjvblvwoxywcvpqcayfhlvmxgglpjiirjuwkbizjwzlmqxuuzlwgtughxvvzoxlusrqljvmlsbtqgokrhciauhwhkhudzlykodnzfzwbcphzhjdfxslpqsxgagfslooovcnvzqmeanoftarwvgtcgpitrdovbwdoipaugunqhxtxcxsclyxtivkviegppwchwzkmktpbaqyjbbgdzfnyoikxkozfyrhuewefpisnwpdifskmsqbblaqmmpimhfmugimavinfpnmblxgwhrzjbvenlqmxsdoogafvibfipduhlbyvgsseqvcltxehujnpqmmzmlfxrkynxchlcnwxurlvmyxlszkvvhizdzrocevaajdmvxjcklfmnkfgfxmhwilyvuufofhruldkbjnyioxbmycvianhkwxibstuyalkifbbxgvqqdmnoxnzqmtwrscbqwhkomdwnqmiybheblrxdjoepcfjfvlvavwhrtkssjwymvefsfjvpnmdantohdijttsjyoxnzrbtsqasahfixzxuyukfaisdjbpmqehplvqmnrhknxoegknxdjvsogjjxtxxmrcygafscusnfcvyqvwtyznavhwesftfqhigvqitobtlgqwnftzdrqggfnczjkmohbjxqkyffofprwneogyvivmbfsbahylgrhgsvjcjwhmplpcptcgaxsvbokghgqpuadtpuuhkpguergmrpxgzivvpjbixravudzgycootlzispxmemlouqlhjpqpzzmqxzkcgohuzxxwskfigkpjcgomrombnoomatduneesnykskgkxbkvwanlgqfkarbsybjrgvsnaotmjzicdhcvckxprsllnckgqvmizqnbwaietgscklxtuvsaabrqzmsvxapverzacoibscjvroweqjidblbznmgvlxqvzxgtljpqnwynpiysmtfxksusutzygupyvcysfueucpqwdwarxgbvdpidzehghfotvqirfkkciniiufwaoiofeyxhwvzxiyvvqgcoebufsthvebyvnpeiiyiuyfgyxnfjezrnqjvnectmdwuviloafqadoeeksonmhyijuswaorqanzmiysdehulhvacknxfiihkdpexaqwbuotspxfgezktxmsuhjlggqeumfymcgwrgwfheirhjcxhqrmdregxcbdbmduulhioezmzawyicrstebeylavlqvgmrkhoctyulvbftmrgmdvocfzpgdrwgraichsdbcfovhelpzzxckamfcyvpdkikekekkuzdbsrcuzkpbvqvsxwclmitbnukkqhnjsvarjcwisykresbzuzebeigwehaeeszqzrrpbpvnojvkixselosefhehrkwzvyiahcucfzbygmvjboibqdemtbewieuymcbfsvnddunmqwbwtbbvyqqufebjzlovoumxlfrixakvshorxsbknhvhukcgjaktyjoznpxllrlmljvkqlpqlkdbstpnnlwclqzecnvjmukwakyrgqjgdvmsxgrqpdhgrlgtzuvbctoixkesrdeqwdgebhnusolfhfhiygbomd"; + boolean anagram = l.isAnagram(a, b); + System.out.println(anagram); + + } +} diff --git a/Week 02/id_748/LeetCode_589_748_preorder.java b/Week 02/id_748/LeetCode_589_748_preorder.java new file mode 100644 index 000000000..a40d3d7d8 --- /dev/null +++ b/Week 02/id_748/LeetCode_589_748_preorder.java @@ -0,0 +1,37 @@ +package com.code.week02; + + +import java.util.ArrayList; +import java.util.List; + +/** + * 给定一个 N 叉树,返回其节点值的前序遍历。 + * + * 例如,给定一个 3叉树 : + * 返回其前序遍历: [1,3,5,6,2,4]。 + * 说明: 递归法很简单,你可以使用迭代法完成此题吗? + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_589_748_preorder { + + + public List preorder(Node root) { + List res = new ArrayList(); + helper(root,res); + return res; + } + + private void helper(Node root, List res) { + + if(root != null){ + res.add(root.val); + for(Node node : root.children){ + helper(node,res); + } + } + } + +} diff --git a/Week 02/id_748/LeetCode_590_748_postorder.java b/Week 02/id_748/LeetCode_590_748_postorder.java new file mode 100644 index 000000000..47de29ddf --- /dev/null +++ b/Week 02/id_748/LeetCode_590_748_postorder.java @@ -0,0 +1,55 @@ +package com.code.week02; + + +import java.util.ArrayList; +import java.util.List; + +/*** + * 给定一个 N 叉树,返回其节点值的后序遍历。 + * + * 例如,给定一个 3叉树 : + * + * 返回其后序遍历: [5,6,3,2,4,1]. + * + * 说明: 递归法很简单,你可以使用迭代法完成此题吗? + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/n-ary-tree-postorder-traversal + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_590_748_postorder { + + + public List postorder(Node root) { + List res = new ArrayList(); + helper(root,res); + return res; + } + + + public void helper(Node root, List res){ + if(root != null){ + // root存在子节点,先解决子节点 + for(Node node : root.children){ + helper(node,res); + } + // 最后添加根节点 + res.add(root.val); + } + } + + +} + + +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } +}; \ No newline at end of file diff --git a/Week 02/id_748/LeetCode_94_748_inorderTraversal.java b/Week 02/id_748/LeetCode_94_748_inorderTraversal.java new file mode 100644 index 000000000..9289498a7 --- /dev/null +++ b/Week 02/id_748/LeetCode_94_748_inorderTraversal.java @@ -0,0 +1,92 @@ +package com.code.top; + + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * 给定一个二叉树,返回它的中序 遍历。 + * + * 示例: + * + * 输入: [1,null,2,3] + * 1 + * \ + * 2 + * / + * 3 + * + * 输出: [1,3,2] + * 进阶: 递归算法很简单,你可以通过迭代算法完成吗? + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/binary-tree-inorder-traversal + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + +public class LeetCode_94_748_inorderTraversal { + + /** + * 递归解法 + * @param root + * @return + */ + public List inorderTraversal(TreeNode root) { + // 定义一个容器来存储中序遍历出的数字 + List res = new ArrayList(); + helper(root,res); + return res; + } + + public void helper(TreeNode root, List res){ + // 第一步:判断终止条件 + if(root != null){ + // 第二步:处理当前层(中序遍历:左-> 中-> 右) + // 1. 先判断左边是否为空,不为空则进左节点继续遍历,直到为空为止 + if(root.left != null){ + // 第三步:处理下一层(注意:下一层根节点是当前节点的左节点) + helper(root.left,res); + } + // 2. 添加根节点至容器中(根节点在最开始就已经判断了,如果为空直接终止循环) + res.add(root.val); + + // 3. 判断右节点(做法同左节点类似) + if(root.right != null){ + // 第三步:处理下一层(注意:下一层根节点是当前节点的右节点) + helper(root.right,res); + } + } + + } + + /** + * 基于栈的写法 + * @param root + * @return + */ + public List inorderTraversalStack(TreeNode root) { + List res = new ArrayList(); + Stack stack = new Stack(); + TreeNode curr = root; + while(curr != null || !stack.isEmpty()){ + // 1. 先搞一波,找到最左侧的节点,并且把每一层的root节点都入栈(最左元素的相当于没有左右节点的root) + while(curr != null){ + stack.push(curr); + curr = curr.left; + } + + // 2. 搞完上面的一波之后,栈里面存了所有的root节点和左节点,现在开始出栈 + // 第一个出的肯定是最左的节点的值 + curr = stack.pop(); + res.add(curr.val); + // 设置为右节点,到外层循环 + curr = curr.right; + + } + return res; + } + + +} diff --git a/Week 02/id_748/NOTE.md b/Week 02/id_748/NOTE.md index a6321d6e2..fa571b83a 100644 --- a/Week 02/id_748/NOTE.md +++ b/Week 02/id_748/NOTE.md @@ -1,4 +1,23 @@ -# NOTE +# 递归4要素 +``` +1. 找终止条件 +2. 找循环体,处理当前层 +3. 下探到下一层 +4. 清理当前层(如:全局变量等) + +``` + +# 递归思维要点: +1. 不要人肉递归 +2. 找最近重复子问题 +3. 善用数学归纳法 + + +# 树的三种遍历 +> * 三种遍历都是以根为中心点来遍历的 +> * 子节点都是先左后右 +1. 前序:根->左->右 +2. 中序:左->根->右 +3. 后序:左->右->根 - diff --git "a/Week 03/id_003/127.\345\215\225\350\257\215\346\216\245\351\276\231.js" "b/Week 03/id_003/127.\345\215\225\350\257\215\346\216\245\351\276\231.js" new file mode 100644 index 000000000..db637c12f --- /dev/null +++ "b/Week 03/id_003/127.\345\215\225\350\257\215\346\216\245\351\276\231.js" @@ -0,0 +1,46 @@ +var ladderLength = function(beginWord, endWord, wordList) { + //记录单词长度,用作下一步的循环变量 + let l = beginWord.length; + //创建字典,用于保存单词的通用状态。 + let allComboDict = {} + let q = []; + + for (const word of wordList) { + for (let i = 0; i < l; i++) { + let newWord = word.replace(word[i],'*'); + let tempList = allComboDict[newWord] || [] + tempList.push(word); + allComboDict[newWord] = allComboDict[newWord]=tempList + } + } + // console.log(allComboDict); + + // bfs(); + let visited = {}; + q.push([beginWord,1]); + visited[beginWord] = true; + + while (q.length>0) { + let node = q.shift(); + let level = node[1]; + let word = node[0]; + + for (let i = 0; i < l; i++) { + let newWord = word.replace(word[i],'*'); + + let mapList = (allComboDict[newWord] || []); + for (const word of mapList) { + + if(word == endWord){ + return level+1; + } + + if(! (word in visited)) { + visited[word] = true; + q.push([word,level+1]); + } + } + } + } + return 0 +}; \ No newline at end of file diff --git "a/Week 03/id_003/200.\345\262\233\345\261\277\346\225\260\351\207\217.js" "b/Week 03/id_003/200.\345\262\233\345\261\277\346\225\260\351\207\217.js" new file mode 100644 index 000000000..95ffda35d --- /dev/null +++ "b/Week 03/id_003/200.\345\262\233\345\261\277\346\225\260\351\207\217.js" @@ -0,0 +1,36 @@ + +var numIslands = function(grid) { + //上下左右 + const dx = [-1,1,0,0]; + const dy = [0,0,-1,1]; + let g = grid; + let islands = 0; + + for (let i = 0; i < g.length; i++) { + for (let j = 0; j < g[i].length; j++) { + if (g[i][j]=='0') continue;//剪枝 + islands += sink(i,j); + } + } + return islands; + + function sink(i, j){ + if(g[i][j]=='0') return 0; + + //不为0的话就将该岛变为0 + g[i][j] = '0'; + + for (let k = 0; k < dx.length; k++) { + let x = i + dx[k], y = j + dy[k]; + + if(x>=0 && x < g.length && y >= 0 && y < g[i].length){ + // console.log('k'+k,x,y,g[x][y]); + if( g[x][y] == '0') continue; + + sink(x,y); + } + } + return 1; + } + +}; diff --git "a/Week 03/id_003/529.\346\211\253\351\233\267.js" "b/Week 03/id_003/529.\346\211\253\351\233\267.js" new file mode 100644 index 000000000..f2fbee6a7 --- /dev/null +++ "b/Week 03/id_003/529.\346\211\253\351\233\267.js" @@ -0,0 +1,42 @@ +var updateBoard = function(board, click) { +     let [x,y] = click; +     if(board[x][y] == "M"){ +         board[x][y] = 'X'; +         return board; +     } +     function dfs(board,x,y){ +         let num =  getNum(board,x,y); +         if(num == 0){ +             board[x][y] = 'B'; +         } else { +             board[x][y] = num+''; +             return board +         } +         for(let i = -1; i <= 1; i++){ +             for(let j = -1; j <= 1; j++){ +                 if(x+i >= 0 && x+i < board.length &&  +                     y+j >= 0 && y= 0 && x+i < board.length &&  +                    y+j >= 0 && y 0){ + // let cur = queue.shift(); + let nextQueue = []; + let maxval = -Infinity; + for (const item of queue) { + item.val > maxval && (maxval = item.val) + if (item.left) { + nextQueue.push(item.left); + } + if (item.right) { + nextQueue.push(item.right); + } + + } + max.push(maxval); + queue = [...nextQueue]; + + } + return max; +}; +``` + +[22. 括号生成 - 力扣(LeetCode)](https://leetcode-cn.com/problems/generate-parentheses/#/description) + +思路总结:每个位置都可以是做括号或者右括号。但要满足相应的约束条件。 +1. 左括号和右括号的个数为n个, +2. 在填入右括号的时候要保证已经填入的右括号的个数小于已经填入的左括号的个数 + +``` +var generateParenthesis = function(n) { + let res = []; + backtrack(res,'',0,0,n); + + function backtrack(res,cur,open,close,max){ + if(cur.length == max*2) { + res.push(cur); + return; + } + //左括号的个数小于max,右括号的数量小于已使用的左括号 + if (open < max) backtrack(res,cur+'(',open+1,close,max); + if (close < open) backtrack(res,cur+')',open,close+1,max); + } + return res; + +}; +``` + + +[433. 最小基因变化 - 力扣(LeetCode)](https://leetcode-cn.com/problems/minimum-genetic-mutation/#/description) + +[102. 二叉树的层次遍历 - 力扣(LeetCode)](https://leetcode-cn.com/problems/binary-tree-level-order-traversal/#/description) + + +思路总结:进行dfs的时候顺便将层级信息记录下来,同时将层次信息插入到层相应的数组项中去 + +dfs +``` +var levelOrder = function(root) { + let result = []; + if( !root ) return result; + helper(root,0); + function helper(node,level){ + //recursion terminator + if (result.length==0) { + result.push([]); + } + //process data + !result[level] && (result[level]=[])//该层不存在的时候添加一个。 + result[level].push(node.val); + + + if(node.left) helper(node.left,level+1); + if(node.right) helper(node.right,level+1); + + } + return result; +}; +``` +bfs +``` +//使用队列记录下层次的信息,node记录下一层的结点 +var levelOrder = function(root) { + if (!root) { + return []; + } + let result = []; + let queue = [] + queue.push(root); + + while(queue.length >0){ + // let node = queue.shift() + level = []; + node = []; + for (const item of queue) { + level.push(item.val); + if (item.left) node.push(item.left) + if (item.right) node.push(item.right) + } + result.push(level) + queue = [...node]; + } + + return result; +}; +``` + +**homework** +* [529. 扫雷游戏 - 力扣(LeetCode)](https://leetcode-cn.com/problems/minesweeper/description/) +思路总结: 每一层为每一个格子周围的八个格子,所以每次递归的时候就是将这周围八个的信息扫描出来 +``` + var updateBoard = function(board, click) { +     let [x,y] = click; +     if(board[x][y] == "M"){ +         board[x][y] = 'X'; +         return board; +     } +     function dfs(board,x,y){ +         let num =  getNum(board,x,y); +         if(num == 0){ +             board[x][y] = 'B'; +         } else { +             board[x][y] = num+''; +             return board +         } +         for(let i = -1; i <= 1; i++){ +             for(let j = -1; j <= 1; j++){ +                 if(x+i >= 0 && x+i < board.length &&  +                     y+j >= 0 && y= 0 && x+i < board.length &&  +                    y+j >= 0 && y=0 && x < g.length && y >= 0 && y < g[i].length){ + // console.log('k'+k,x,y,g[x][y]); + if( g[x][y] == '0') continue; + + sink(x,y); + } + } + return 1; + } + +}; + +``` +* [126. 单词接龙 II - 力扣(LeetCode)](https://leetcode-cn.com/problems/word-ladder-ii/description/) + +* [127. 单词接龙 - 力扣(LeetCode)](https://leetcode-cn.com/problems/word-ladder/description/) +``` +//未通过,待修改 +var ladderLength = function(beginWord, endWord, wordList) { + //记录单词长度,用作下一步的循环变量 + let l = beginWord.length; + //创建字典,用于保存单词的通用状态。 + let allComboDict = {} + let q = []; + + // getDic(); + for (const word of wordList) { + for (let i = 0; i < l; i++) { + let newWord = word.replace(word[i],'*'); + let tempList = allComboDict[newWord] || [] + tempList.push(word); + allComboDict[newWord] = allComboDict[newWord]=tempList + } + } + // console.log(allComboDict); + + // bfs(); + let visited = {}; + q.push([beginWord,1]); + visited[beginWord] = true; + + while (q.length>0) { + let node = q.shift(); + let level = node[1]; + let word = node[0]; + + for (let i = 0; i < l; i++) { + let newWord = word.replace(word[i],'*'); + + // console.log(newWord,allComboDict); + let mapList = (allComboDict[newWord] || []); + for (const word of mapList) { + + if(word == endWord){ + console.log(level+1); + return level+1; + } + + if(! (word in visited)) { + visited[word] = true; + q.push([word,level+1]); + } + } + } + } + return 0 +}; +``` + +## 二分查找代码模板 +``` +left, right = 0, len(array) - 1 +while left <= right: + mid = (left + right) / 2 + if array[mid] == target: + # find the target!! + break or return result + elif array[mid] < target: + left = mid + 1 + else: + right = mid - 1 +``` +## 编程体会 +1. 编程思路总结,采用自顶向下的编程方式编写算法时应关注如何将问题分步处理。之后再关注要解决每一个问题的核心是什么。 +2. 使用递归时思想要抽象,不可人肉递归。抓好头和尾,抓好边界。 \ No newline at end of file diff --git a/Week 03/id_008/LeetCode_102_008.js b/Week 03/id_008/LeetCode_102_008.js new file mode 100644 index 000000000..58c17e60d --- /dev/null +++ b/Week 03/id_008/LeetCode_102_008.js @@ -0,0 +1,30 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {number[][]} + */ +var levelOrder = function(root) { + var result = []; + search(root, 0); + return result; + + function search(node, level){ + if (!node) { + return false; + } + + if (typeof result[level] === "undefined") { + result[level] = []; + } + + result[level].push(node.val); + search(node.left , level + 1); + search(node.right, level + 1); + } +}; diff --git a/Week 03/id_008/LeetCode_122_008.js b/Week 03/id_008/LeetCode_122_008.js new file mode 100644 index 000000000..348379e2d --- /dev/null +++ b/Week 03/id_008/LeetCode_122_008.js @@ -0,0 +1,15 @@ +/** + * @param {number[]} prices + * @return {number} + */ +var maxProfit = function(prices) { + var total = 0; + + for (var i = 1; i < prices.length; ++i) { + if (prices[i] > prices[i - 1]) { + total += prices[i] - prices[i - 1]; + } + } + + return total; +}; diff --git a/Week 03/id_008/LeetCode_126_008.js b/Week 03/id_008/LeetCode_126_008.js new file mode 100644 index 000000000..128e91128 --- /dev/null +++ b/Week 03/id_008/LeetCode_126_008.js @@ -0,0 +1,103 @@ +/** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {string[][]} + */ + +// js 超时,但是测试过的几个测试用例都是通过的 + +var findLadders = function(beginWord, endWord, wordList) { + var dict = new Set(wordList); + var len = ladderLength(beginWord, endWord, wordList); + var result = []; + find([beginWord]); + + function find(arr){ + var last = arr[arr.length - 1]; + + if (arr.length >= len) { + last === endWord && result.push(arr); + return false; + } + + var list = gen(last); + + for (var i = 0; i < list.length; ++i) { + if (~arr.indexOf(list[i])) { + continue; + } + + arr.push(list[i]); + find(arr.slice(0)); + arr.pop(); + } + } + + return result; + + function ladderLength(beginWord, endWord, wordList) { + if (!dict.has(endWord)) { + return 0; + } + + var queueA = [beginWord]; + var queueB = [ endWord]; + var levelA = {}; + var levelB = {}; + levelA[beginWord] = 1; + levelB[ endWord] = 1; + + var level; + + while (queueA.length && queueB.length) { + level = check(queueA, levelA, queueB, levelB); + + if (level) { + return level; + } + + level = check(queueB, levelB, queueA, levelA); + + if (level) { + return level; + } + } + + return 0; + + function check(thisQ, thisL, thatQ, thatL){ + var word = thisQ.shift(); + var plus = thisL[word] + 1; + var list = gen(word); + + for (var i = 0; i < list.length; ++i) { + if (thisL[list[i]]) { + continue; + } + + if (thatL[list[i]]) { + return thisL[word] + thatL[list[i]]; + } + + thisQ.push(list[i]); + thisL[list[i]] = plus; + } + + return 0; + } + } + + function gen(s){ + var result = []; + + for (var i = 0; i < s.length; ++i) { + for (var j = 0; j < 26; ++j) { + var word = s.substring(0, i) + String.fromCharCode(j + 97) + s.substring(i + 1); + dict.has(word) && word !== s && result.push(word); + } + } + + return result; + } +}; diff --git a/Week 03/id_008/LeetCode_127_008.js b/Week 03/id_008/LeetCode_127_008.js new file mode 100644 index 000000000..6326f0ad6 --- /dev/null +++ b/Week 03/id_008/LeetCode_127_008.js @@ -0,0 +1,361 @@ +/** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ + +// 单向 BFS + +var ladderLength = function(beginWord, endWord, wordList) { + if (!~wordList.indexOf(endWord)) { + return 0; + } + + var visited = {}; + visited[beginWord] = 1; + + var queue = [beginWord]; + var level = {}; + + level[beginWord] = 1; + + while (str = queue.shift()) { + var search = gen(str); + + for (var i = 0; i < search.length; ++i) { + if (search[i] === endWord) { + return level[str] + 1; + } + + queue.push(search[i]); + visited[search[i]] = 1; + level[search[i]] = level[str] + 1; + } + } + + return 0; + + function gen(s){ + var result = []; + + for (var i = 0; i < wordList.length; ++i) { + if (compare(s, wordList[i]) === 1 && !visited[wordList[i]]) { + result.push(wordList[i]); + } + } + + return result; + } + + function compare(a, b){ + var total = 0; + + for (var i = 0; i < 8; ++i) { + a.charAt(i) === b.charAt(i) || ++total; + + if (total > 1) { + return 2; + } + } + + return total; + } +}; + +/** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ + +// 双向 BFS + +var ladderLength = function(beginWord, endWord, wordList) { + if (!~wordList.indexOf(endWord)) { + return 0; + } + + var bank = []; + + for (var i = 0; i < wordList.length; ++i) { + ~bank.indexOf(wordList[i]) || bank.push(wordList[i]); + } + + var compareA = [beginWord]; + var compareB = [ endWord]; + var levelA = 1; + var levelB = 1; + + while (1) { + for (var i = 0; i < compareA.length; ++i) { + for (var j = 0; j < compareB.length; ++j) { + var cmp = compare(compareA[i], compareB[j]); + + if (cmp < 2) { + return levelA + levelB; + } + } + } + + if (levelA <= levelB) { + var nextA = genAll(compareA); + + if (nextA.length) { + ++levelA; + compareA = nextA; + continue; + } + + var nextB = genAll(compareB); + + if (nextB.length) { + ++levelB; + compareB = nextB; + continue; + } + + break; + } + else { + var nextB = genAll(compareB); + + if (nextB.length) { + ++levelB; + compareB = nextB; + continue; + } + + var nextA = genAll(compareA); + + if (nextA.length) { + ++levelA; + compareA = nextA; + continue; + } + + break; + } + } + + return 0; + + function genAll(arr){ + var result = []; + + for (var i = 0; i < arr.length; ++i) { + result = result.concat(gen(arr[i])); + } + + return result; + } + + function gen(s){ + var result = []; + + for (var i = 0; i < bank.length; ++i) { + if (compare(s, bank[i]) === 1) { + result.push(bank[i]); + bank[i] = bank[bank.length - 1]; + --bank.length; + --i; + } + } + + return result; + } + + function compare(a, b){ + var total = 0; + + for (var i = 0; i < a.length; ++i) { + a.charCodeAt(i) === b.charCodeAt(i) || ++total; + + if (total > 1) { + return 2; + } + } + + return total; + } +}; + +/** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ + +// 双向 BFS 优化 + +var ladderLength = function(beginWord, endWord, wordList) { + var bankA = new Set(wordList); + var bankB = new Set(wordList); + + if (!bankA.has(endWord)) { + return 0; + } + + var visitedA = new Map(); + var visitedB = new Map(); + visitedA.set(beginWord, 0); + visitedB.set( endWord, 0); + + var queue = [{ level: 0, type: "A", word: beginWord }, { level: 0, type: "B", word: endWord }]; + var check; + + while (check = queue.shift()) { + if (check.type === "A") { + if (visitedB.has(check.word)) { + return check.level + visitedB.get(check.word) + 1; + } + + genA(check.word).forEach(function(t){ + queue.push({ level: check.level + 1, type: "A", word: t }); + visitedA.set(t, check.level + 1); + }); + } + else { + if (visitedA.has(check.word)) { + return check.level + visitedA.get(check.word) + 1; + } + + genB(check.word).forEach(function(t){ + queue.push({ level: check.level + 1, type: "B", word: t }); + visitedB.set(t, check.level + 1); + }); + } + } + + return 0; + + function genA(s){ + var result = []; + + gen(s).forEach(function(t){ + if (bankA.has(t)) { + result.push(t); + bankA.delete(t); + } + }); + + return result; + } + + function genB(s){ + var result = []; + + gen(s).forEach(function(t){ + if (bankB.has(t)) { + result.push(t); + bankB.delete(t); + } + }); + + return result; + } + + function gen(s){ + var result = []; + + for (let i = 0; i < s.length; ++i) { + for (let j = 0; j < 26; ++j) { + s.charCodeAt(i) === j + 97 || result.push(s.substring(0, i) + String.fromCharCode(j + 97) + s.substring(i + 1)); + } + } + + return result; + } + + function compare(a, b){ + var total = 0; + + for (var i = 0; i < a.length; ++i) { + a.charCodeAt(i) === b.charCodeAt(i) || ++total; + + if (total > 1) { + return 2; + } + } + + return total; + } +}; + +/** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ + +// 双向 BFS 优化 + +var ladderLength = function(beginWord, endWord, wordList) { + var dict = new Set(wordList); + + if (!dict.has(endWord)) { + return 0; + } + + var queueA = [beginWord]; + var queueB = [ endWord]; + var levelA = {}; + var levelB = {}; + levelA[beginWord] = 1; + levelB[ endWord] = 1; + + var level; + + while (queueA.length && queueB.length) { + level = check(queueA, levelA, queueB, levelB); + + if (level) { + return level; + } + + level = check(queueB, levelB, queueA, levelA); + + if (level) { + return level; + } + } + + return 0; + + function check(thisQ, thisL, thatQ, thatL){ + var word = thisQ.shift(); + var plus = thisL[word] + 1; + var list = gen(word); + + for (var i = 0; i < list.length; ++i) { + if (thisL[list[i]]) { + continue; + } + + if (thatL[list[i]]) { + return thisL[word] + thatL[list[i]]; + } + + thisQ.push(list[i]); + thisL[list[i]] = plus; + } + + return 0; + } + + function gen(s){ + var result = []; + + for (var i = 0; i < s.length; ++i) { + for (var j = 0; j < 26; ++j) { + var word = s.substring(0, i) + String.fromCharCode(j + 97) + s.substring(i + 1); + dict.has(word) && word !== s && result.push(word); + } + } + + return result; + } +}; diff --git a/Week 03/id_008/LeetCode_153_008.js b/Week 03/id_008/LeetCode_153_008.js new file mode 100644 index 000000000..bdbefefc8 --- /dev/null +++ b/Week 03/id_008/LeetCode_153_008.js @@ -0,0 +1,42 @@ +/** + * @param {number[]} nums + * @return {number} + */ +var findMin = function(nums) { + if (nums.length === 1 || nums[0] < nums[nums.length - 1]) { + return nums[0]; + } + + if (nums.length === 2) { + return nums[0] < nums[1] ? nums[0] : nums[1]; + } + + var L = 0; + var R = nums.length - 2; + var harf = Math.floor((L + R) / 2); + + while (L <= R) { + if (nums[harf] > nums[harf + 1]) { + return nums[harf + 1]; + } + + if (L === R) { + if (nums[L] > nums[L + 1]) { + return nums[L + 1]; + } + + return 0; + } + + if (nums[harf] > nums[R + 1]) { + L = harf + 1; + } + else if (nums[harf + 1] < nums[L]) { + R = harf - 1; + } + + harf = Math.floor((L + R) / 2); + } + + return 0; +}; diff --git a/Week 03/id_008/LeetCode_200_008.js b/Week 03/id_008/LeetCode_200_008.js new file mode 100644 index 000000000..2d0cebd59 --- /dev/null +++ b/Week 03/id_008/LeetCode_200_008.js @@ -0,0 +1,44 @@ +/** + * @param {character[][]} grid + * @return {number} + */ +var numIslands = function(grid) { + var result = 0; + + while (1) { + var island = hasIsland(); + + if (!~island[0]) { + break; + } + + dropIsland(island[0], island[1]); + ++result; + } + + return result; + + function hasIsland(){ + for (var i = 0; i < grid.length; ++i) { + for (var j = 0; j < grid[i].length; ++j) { + if (grid[i][j] === "1") { + return [i, j]; + } + } + } + + return [-1, -1]; + } + + function dropIsland(i, j){ + if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] === "0") { + return false; + } + + grid[i][j] = "0"; + dropIsland(i + 1, j); + dropIsland(i - 1, j); + dropIsland(i, j + 1); + dropIsland(i, j - 1); + } +}; diff --git a/Week 03/id_008/LeetCode_22_008.js b/Week 03/id_008/LeetCode_22_008.js new file mode 100644 index 000000000..0a06d7129 --- /dev/null +++ b/Week 03/id_008/LeetCode_22_008.js @@ -0,0 +1,75 @@ +/** + * @param {number} n + * @return {string[]} + */ +var generateParenthesis = function(n) { + var cache = [[]]; + + gen(n); + return cache[n]; + + function gen(k){ + if (cache[k]) { + return cache[k]; + } + + if (k <= 0) { + return []; + } + + var next = gen(k - 1); + var _r = []; + + _r.push("()" + (next[0] || "")); + + for (var i = 0; i < next.length; ++i) { + var arr = next[i].split(""); + + for (var j = 0; j < arr.length; ++j) { + var temp = arr.slice(0); + temp[j] += "()"; + _r.push(temp.join("")); + } + } + + _r = distinct(_r); + cache[k] = _r; + return _r; + } + + function distinct(arr){ + var result = []; + + for (var i = 0; i < arr.length; ++i) { + !~result.indexOf(arr[i]) && result.push(arr[i]); + } + + return result; + } +}; + +/** + * @param {number} n + * @return {string[]} + */ +var generateParenthesis = function(n) { + var result = []; + putChar("", 0, 0); + + function putChar(s, L, R){ + if (L === n && R === n) { + result.push(s); + return null; + } + + if (L < n) { + putChar(s + "(", L + 1, R); + } + + if (R < L) { + putChar(s + ")", L, R + 1); + } + } + + return result; +}; diff --git a/Week 03/id_008/LeetCode_33_008.js b/Week 03/id_008/LeetCode_33_008.js new file mode 100644 index 000000000..dc7f2064d --- /dev/null +++ b/Week 03/id_008/LeetCode_33_008.js @@ -0,0 +1,73 @@ +/** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ +var search = function(nums, target) { + var L = 0; + var R = nums.length - 1; + var harf = Math.floor((L + R) / 2); + + if (nums.length === 0) { + return -1; + } + + if (nums.length === 1) { + return nums[0] === target ? 0 : -1; + } + + while (L <= R) { + if (L === R) { + if (nums[L] === target) { + return L; + } + + return -1; + } + + if (nums[L] < nums[R]) { + if (nums[harf] > target) { + R = harf - 1; + } + else if (nums[harf] < target) { + L = harf + 1; + } + else { + return harf; + } + + harf = Math.floor((L + R) / 2); + } + else { + if (nums[harf] > target) { + if (nums[harf] > nums[R] && target < nums[L]) { + L = harf + 1; + } + else if (nums[harf] > nums[R] && target > nums[R]) { + R = harf - 1; + } + else if (nums[harf] < nums[L] && target < nums[L]) { + R = harf - 1; + } + } + else if (nums[harf] < target) { + if (nums[harf] < nums[L] && target > nums[R]) { + R = harf - 1; + } + else if (nums[harf] < nums[L] && target < nums[L]) { + L = harf + 1; + } + else if (nums[harf] > nums[R] && target > nums[R]) { + L = harf + 1; + } + } + else { + return harf; + } + + harf = Math.floor((L + R) / 2); + } + } + + return -1; +}; diff --git a/Week 03/id_008/LeetCode_367_008.js b/Week 03/id_008/LeetCode_367_008.js new file mode 100644 index 000000000..a3b812107 --- /dev/null +++ b/Week 03/id_008/LeetCode_367_008.js @@ -0,0 +1,26 @@ +/** + * @param {number} num + * @return {boolean} + */ +var isPerfectSquare = function(num) { + if (num === 0 || num === 1) { + return true; + } + + var L = 0; + var R = num; + var m = Math.floor(R / 2); + + while (m * m > num || (m + 1) * (m + 1) <= num) { + if (m * m > num) { + R = m; + m = Math.floor((L + R) / 2); + } + else if ((m + 1) * (m + 1) <= num) { + L = m; + m = Math.floor((L + R) / 2) + 1; + } + } + + return m * m === num; +}; diff --git a/Week 03/id_008/LeetCode_433_008.js b/Week 03/id_008/LeetCode_433_008.js new file mode 100644 index 000000000..05355a38a --- /dev/null +++ b/Week 03/id_008/LeetCode_433_008.js @@ -0,0 +1,61 @@ +/** + * @param {string} start + * @param {string} end + * @param {string[]} bank + * @return {number} + */ +var minMutation = function(start, end, bank) { + if (!~bank.indexOf(end)) { + return -1; + } + + var visited = [start]; + var queue = [start]; + var level = {}; + var node; + var str; + + level[start] = 0; + + while (str = queue.shift()) { + var search = gen(str); + + for (var i = 0; i < search.length; ++i) { + if (search[i] === end) { + return level[str] + 1; + } + + queue.push(search[i]); + visited.push(search[i]); + level[search[i]] = level[str] + 1; + } + } + + return -1; + + function gen(s){ + var result = []; + + for (var i = 0; i < bank.length; ++i) { + if (compare(s, bank[i]) === 1 && !~visited.indexOf(bank[i])) { + result.push(bank[i]); + } + } + + return result; + } + + function compare(a, b){ + var total = 0; + + for (var i = 0; i < 8; ++i) { + a.charAt(i) === b.charAt(i) || ++total; + + if (total > 1) { + return 2; + } + } + + return total; + } +}; diff --git a/Week 03/id_008/LeetCode_455_008.js b/Week 03/id_008/LeetCode_455_008.js new file mode 100644 index 000000000..54fcd7875 --- /dev/null +++ b/Week 03/id_008/LeetCode_455_008.js @@ -0,0 +1,23 @@ +/** + * @param {number[]} g + * @param {number[]} s + * @return {number} + */ +var findContentChildren = function(g, s) { + g.sort(function(a, b){ return a - b; }); + s.sort(function(a, b){ return b - a; }); + + var total = 0; + var cookie = 0; + + for (var i = 0; i < g.length; ++i) { + while (cookie = s.pop()) { + if (cookie >= g[i]) { + ++total; + break; + } + } + } + + return total; +}; diff --git a/Week 03/id_008/LeetCode_45_008.js b/Week 03/id_008/LeetCode_45_008.js new file mode 100644 index 000000000..b97bc84ea --- /dev/null +++ b/Week 03/id_008/LeetCode_45_008.js @@ -0,0 +1,50 @@ +/** + * @param {number[]} nums + * @return {number} + */ +var jump = function(nums) { + if (nums.length <= 1) { + return 0; + } + + function findMax(k){ + if (k + nums[k] + 1 >= nums.length) { + return true; + } + + var cover = []; + + for (var i = k + 1; i <= k + nums[k]; ++i) { + cover.push(i + nums[i]); + } + + var max = 0, maxI = 0; + + for (var i = 0; i < cover.length; ++i) { + if (cover[i] > cover[maxI]) { + maxI = i; + max = cover[i]; + } + } + + if (maxI + k + 1 >= nums.length - 1) { + return true; + } + + return maxI + k + 1; + } + + var step = 0; + var currentPoint = 0; + + while (true) { + currentPoint = findMax(currentPoint); + ++step; + + if (currentPoint === true) { + break; + } + } + + return step; +}; diff --git a/Week 03/id_008/LeetCode_515_008.js b/Week 03/id_008/LeetCode_515_008.js new file mode 100644 index 000000000..6a40bc132 --- /dev/null +++ b/Week 03/id_008/LeetCode_515_008.js @@ -0,0 +1,45 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {number[]} + */ +var largestValues = function(root) { + var result = []; + search(root, 0); + + for (var i = 0; i < result.length; ++i) { + result[i] = max(result[i]); + } + + return result; + + function search(node, level){ + if (!node) { + return false; + } + + if (typeof result[level] === "undefined") { + result[level] = []; + } + + result[level].push(node.val); + search(node.left , level + 1); + search(node.right, level + 1); + } + + function max(arr){ + var max = arr[0]; + + for (var i = 1; i < arr.length; ++i) { + arr[i] > max && (max = arr[i]); + } + + return max; + } +}; diff --git a/Week 03/id_008/LeetCode_529_008.js b/Week 03/id_008/LeetCode_529_008.js new file mode 100644 index 000000000..a2e1c015d --- /dev/null +++ b/Week 03/id_008/LeetCode_529_008.js @@ -0,0 +1,52 @@ +/** + * @param {character[][]} board + * @param {number[]} click + * @return {character[][]} + */ +var updateBoard = function(board, click) { + open(click[0], click[1]); + return board; + + function open(x, y){ + if (x < 0 || x >= board.length || y < 0 || y >= board[0].length) { + return false; + } + + if (board[x][y] === "M") { + board[x][y] = "X"; + } + else if (board[x][y] === "E") { + var total = count(x, y); + + if (total) { + board[x][y] = "" + total; + } + else { + board[x][y] = "B"; + open(x - 1, y - 1); + open(x , y - 1); + open(x + 1, y - 1); + open(x - 1, y ); + open(x + 1, y ); + open(x - 1, y + 1); + open(x , y + 1); + open(x + 1, y + 1); + } + } + + return false; + } + + function count(x, y){ + var total = 0; + try { if (board[x - 1][y - 1] === "M") ++total; } catch (e){} + try { if (board[x ][y - 1] === "M") ++total; } catch (e){} + try { if (board[x + 1][y - 1] === "M") ++total; } catch (e){} + try { if (board[x - 1][y ] === "M") ++total; } catch (e){} + try { if (board[x + 1][y ] === "M") ++total; } catch (e){} + try { if (board[x - 1][y + 1] === "M") ++total; } catch (e){} + try { if (board[x ][y + 1] === "M") ++total; } catch (e){} + try { if (board[x + 1][y + 1] === "M") ++total; } catch (e){} + return total; + } +}; diff --git a/Week 03/id_008/LeetCode_55_008.js b/Week 03/id_008/LeetCode_55_008.js new file mode 100644 index 000000000..a4d9d801e --- /dev/null +++ b/Week 03/id_008/LeetCode_55_008.js @@ -0,0 +1,30 @@ +/** + * @param {number[]} nums + * @return {boolean} + */ +var canJump = function(nums) { + var canJump0List = []; + + for (var i = 0; i < nums.length - 1; ++i) { + if (nums[i] === 0) { + var canJump0 = false; + + for (var j = i - 1; j >= 0; --j) { + if (nums[j] > i - j) { + canJump0 = true; + break; + } + } + + canJump0List.push(canJump0); + } + } + + for (var i = 0; i < canJump0List.length; ++i) { + if (!canJump0List[i]) { + return false; + } + } + + return true; +}; diff --git a/Week 03/id_008/LeetCode_69_008.js b/Week 03/id_008/LeetCode_69_008.js new file mode 100644 index 000000000..09129a9f0 --- /dev/null +++ b/Week 03/id_008/LeetCode_69_008.js @@ -0,0 +1,26 @@ +/** + * @param {number} x + * @return {number} + */ +var mySqrt = function(x) { + if (x === 0 || x === 1) { + return x; + } + + var L = 0; + var R = x; + var m = R / 2; + + while (Math.abs(x - m * m) > 0.001) { + if (m * m > x) { + R = m; + m = (L + R) / 2; + } + else { + L = m; + m = (L + R) / 2; + } + } + + return Math.floor(m); +}; diff --git a/Week 03/id_008/LeetCode_74_008.js b/Week 03/id_008/LeetCode_74_008.js new file mode 100644 index 000000000..5922ebc98 --- /dev/null +++ b/Week 03/id_008/LeetCode_74_008.js @@ -0,0 +1,53 @@ +/** + * @param {number[][]} matrix + * @param {number} target + * @return {boolean} + */ +var searchMatrix = function(matrix, target) { + if (matrix.length === 0 || matrix[0].length === 0) { + return false; + } + + var len = matrix.length * matrix[0].length; + var L = 0; + var R = len - 1; + var harf = Math.floor((L + R) / 2); + + while (L <= R) { + if (L === R) { + if (N(L) === target) { + return true; + } + + return false; + } + + var n = N(harf); + + if (n > target) { + R = harf - 1; + } + else if (n < target) { + L = harf + 1; + } + else { + return true; + } + + harf = Math.floor((L + R) / 2); + } + + return false; + + function X(k){ + return (k - Y(k)) / matrix[0].length; + } + + function Y(k){ + return k % matrix[0].length; + } + + function N(k){ + return matrix[X(k)][Y(k)]; + } +}; diff --git a/Week 03/id_008/LeetCode_860_008.js b/Week 03/id_008/LeetCode_860_008.js new file mode 100644 index 000000000..58e1769a1 --- /dev/null +++ b/Week 03/id_008/LeetCode_860_008.js @@ -0,0 +1,42 @@ +/** + * @param {number[]} bills + * @return {boolean} + */ +var lemonadeChange = function(bills) { + var box = []; + box[5] = 0; + box[10] = 0; + box[20] = 0; + + for (var i = 0; i < bills.length; ++i) { + if (bills[i] === 5) { + ++box[5]; + } + else if (bills[i] === 10) { + if (box[5] > 0) { + --box[5]; + } + else { + return false; + } + + ++box[10]; + } + else if (bills[i] === 20) { + if (box[10] > 0 && box[5] > 0) { + --box[10]; + --box[5]; + } + else if (box[5] > 2) { + box[5] -= 3; + } + else { + return false; + } + + ++box[20]; + } + } + + return true; +}; diff --git a/Week 03/id_008/LeetCode_874_008.js b/Week 03/id_008/LeetCode_874_008.js new file mode 100644 index 000000000..6d32dc839 --- /dev/null +++ b/Week 03/id_008/LeetCode_874_008.js @@ -0,0 +1,77 @@ +/** + * @param {number[]} commands + * @param {number[][]} obstacles + * @return {number} + */ +var robotSim = function(commands, obstacles) { + var direction = "N"; + var obs = []; + var x = 0; + var y = 0; + var max = 0; + var distance = 0; + + for (var i = 0; i < obstacles.length; ++i) { + if (typeof obs[obstacles[i][0]] === "undefined") { + obs[obstacles[i][0]] = []; + } + + obs[obstacles[i][0]][obstacles[i][1]] = 1; + } + + for (var i = 0; i < commands.length; ++i) { + if (commands[i] === -1) { + switch (direction) { + case "N": direction = "E"; break; + case "E": direction = "S"; break; + case "S": direction = "W"; break; + case "W": direction = "N"; break; + } + } + else if (commands[i] === -2) { + switch (direction) { + case "N": direction = "W"; break; + case "W": direction = "S"; break; + case "S": direction = "E"; break; + case "E": direction = "N"; break; + } + } + else { + for (var j = 0; j < commands[i]; ++j) { + if (direction === "N") { + if (isBlank(x, y + 1)) { + y += 1; + } + } + else if (direction === "S") { + if (isBlank(x, y - 1)) { + y -= 1; + } + } + else if (direction === "W") { + if (isBlank(x - 1, y)) { + x -= 1; + } + } + else if (direction === "E") { + if (isBlank(x + 1, y)) { + x += 1; + } + } + } + + distance = x * x + y * y; + distance > max && (max = distance); + } + } + + return max; + + function isBlank(x, y){ + if (obs[x] && obs[x][y]) { + return false; + } + + return true; + } +}; diff --git a/Week 03/id_013/LeetCode_01_013.py b/Week 03/id_013/LeetCode_01_013.py new file mode 100644 index 000000000..6b3b09675 --- /dev/null +++ b/Week 03/id_013/LeetCode_01_013.py @@ -0,0 +1,34 @@ +""" +第一题:[126. 单词接龙 II](https://leetcode-cn.com/problems/word-ladder-ii/) +""" + + +class Solution: + def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: + from collections import defaultdict + if endWord not in wordList or not endWord or not beginWord or not wordList: + return 0 + L = len(beginWord) + + all_combo_dict = defaultdict(list) + for word in wordList: + for i in range(L): + all_combo_dict[word[:i] + '*' + word[i + 1:]].append(word) + + queue = [(beginWord, 1)] + visited = {beginWord: True} + while queue: + current_word, level = queue.pop(0) + for i in range(L): + intermediate_word = current_word[:i] + '*' + current_word[i + 1:] + + for word in all_combo_dict[intermediate_word]: + if word == endWord: + return level + 1 + + if word not in visited: + visited[word] = True + queue.append((word, level + 1)) + + all_combo_dict[intermediate_word] = [] + return 0 \ No newline at end of file diff --git a/Week 03/id_013/LeetCode_03_013.py b/Week 03/id_013/LeetCode_03_013.py new file mode 100644 index 000000000..c8b3e9201 --- /dev/null +++ b/Week 03/id_013/LeetCode_03_013.py @@ -0,0 +1,20 @@ +""" +第三题:[126. 单词接龙 II](https://leetcode-cn.com/problems/word-ladder-ii/) +""" + +class Solution: + def numIslands(self, grid: [[str]]) -> int: + def dfs(grid, i, j): + if not 0 <= i < len(grid) or not 0 <= j < len(grid[0]) or grid[i][j] == '0': return + grid[i][j] = '0' + dfs(grid, i + 1, j) + dfs(grid, i, j + 1) + dfs(grid, i - 1, j) + dfs(grid, i, j - 1) + count = 0 + for i in range(len(grid)): + for j in range(len(grid[0])): + if grid[i][j] == '1': + dfs(grid, i, j) + count += 1 + return count \ No newline at end of file diff --git a/Week 03/id_013/LeetCode_05_013.py b/Week 03/id_013/LeetCode_05_013.py new file mode 100644 index 000000000..fd1737a51 --- /dev/null +++ b/Week 03/id_013/LeetCode_05_013.py @@ -0,0 +1,23 @@ +""" +第五题:[860. 柠檬水找零](https://leetcode-cn.com/problems/lemonade-change/) +""" + +class Solution: + def lemonadeChange(self, bills: List[int]) -> bool: + five, ten = 0, 0 + for b in bills: + if b == 5: + five += 1 + elif b == 10: + if not five: return False + five -= 1 + ten += 1 + else: + if ten and five: + ten -= 1 + five -= 1 + elif five > 2: + five -= 3 + else: + return False + return True \ No newline at end of file diff --git a/Week 03/id_013/LeetCode_06_013.py b/Week 03/id_013/LeetCode_06_013.py new file mode 100644 index 000000000..47b4dce50 --- /dev/null +++ b/Week 03/id_013/LeetCode_06_013.py @@ -0,0 +1,12 @@ +""" +第六题:122. 买卖股票的最佳时机 II +""" + +class Solution(object): + def maxProfit(self, prices): + profit = 0 + for day in range(len(prices)-1): + differ = prices[day+1] - prices[day] + if differ > 0: + profit += differ + return profit \ No newline at end of file diff --git a/Week 03/id_013/LeetCode_10_013.py b/Week 03/id_013/LeetCode_10_013.py new file mode 100644 index 000000000..5d40015ad --- /dev/null +++ b/Week 03/id_013/LeetCode_10_013.py @@ -0,0 +1,27 @@ +""" +第十题:33. 搜索旋转排序数组 +""" + + +class Solution: + def search(self, nums: List[int], target: int) -> int: + size = len(nums) + if size == 0: + return -1 + + left = 0 + right = size - 1 + while left < right: + mid = left + (right - left + 1) // 2 + if nums[mid] < nums[right]: + if nums[mid] <= target <= nums[right]: + left = mid + else: + right = mid - 1 + else: + if nums[left] <= target <= nums[mid - 1]: + right = mid - 1 + else: + left = mid + + return left if nums[left] == target else -1 \ No newline at end of file diff --git a/Week 03/id_013/LeetCode_11_013.py b/Week 03/id_013/LeetCode_11_013.py new file mode 100644 index 000000000..68c61f3f0 --- /dev/null +++ b/Week 03/id_013/LeetCode_11_013.py @@ -0,0 +1,25 @@ +""" +第十一题:74. 搜索二维矩阵 +""" + + +class Solution: + def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: + m = len(matrix) + if m == 0: + return False + n = len(matrix[0]) + + left = 0 + right = m * n - 1 + while left <= right: + pivot_idx = (left + right) // 2 + pivot_element = matrix[pivot_idx // n][pivot_idx % n] + if target == pivot_element: + return True + else: + if target < pivot_element: + right = pivot_idx - 1 + else: + left = pivot_idx + 1 + return False \ No newline at end of file diff --git a/Week 03/id_013/NOTE.md b/Week 03/id_013/NOTE.md index a6321d6e2..9ecf983dc 100644 --- a/Week 03/id_013/NOTE.md +++ b/Week 03/id_013/NOTE.md @@ -1,4 +1,310 @@ -# NOTE - - - +> 笔记内容为整理其他技术博客资料 + +二分查找法作为一种常见的查找方法,将原本是线性时间提升到了对数时间范围,大大缩短了搜索时间,但它有一个前提,就是必须在有序数据中进行查找。 + +二分查找很好写,却很难写对,据统计只有10%的程序员可以写出没有bug的的二分查找代码。出错原因主要集中在判定条件和边界值的选择上,很容易就会导致越界或者死循环的情况。 + +下面对二分查找及其变形进行总结: + +## **1. 最基本的二分查找** + +```java +public int binarySearch(int[] A, int target, int n){ + int low = 0, high = n, mid; + while(low <= high){ + mid = low + (high - low) / 2; + if(A[mid] == target){ + return mid; + }else if(A[mid] > target){ + high = mid - 1; + }else{ + low = mid + 1; + } + } + return -1; +} +``` + +其中,有几个要注意的点: +\1. 循环的判定条件是:`low <= high` +\2. 为了防止数值溢出,`mid = low + (high - low)/2` +\3. 当 `A[mid]`不等于`target`时,`high = mid - 1`或`low = mid + 1` + +> leetcode参考:[Search Insert Position](https://link.zhihu.com/?target=https%3A//leetcode.com/problems/search-insert-position/description/) + +## **2. 查找目标值区域的左边界/查找与目标值相等的第一个位置/查找第一个不小于目标值数的位置** + +> A = [1,3,3,5, **7** ,7,7,7,8,14,14] +> target = 7 +> return 4 + +```java +public int binarySearchLowerBound(int[] A, int target, int n){ + int low = 0, high = n, mid; + while(low <= high){ + mid = low + (high - low) / 2; + if(target <= A[mid]){ + high = mid - 1; + }else{ + low = mid + 1; + } + } + if(low < A.length && A[low] == target) + return low; + else + return -1; +} +``` + +## **3. 查找目标值区域的右边界/查找与目标值相等的最后一个位置/查找最后一个不大于目标值数的位置** + +> A = [1,3,3,5,7,7,7, **7** ,8,14,14] +> target = 7 +> return 7 + +```java +public int binarySearchUpperBound(int[] A, int target, int n){ + int low = 0, high = n, mid; + while(low <= high){ + mid = low + (high - low) / 2; + if(target >= A[mid]){ + low = mid + 1; + }else{ + high = mid - 1; + } + } + if(high >= 0 && A[high] == target) + return high; + else + return -1; +} +``` + +此题以可变形为`查找第一个大于目标值的数/查找比目标值大但是最接近目标值的数`,我们已经找到了最后一个不大于目标值的数,那么再往后进一位,返回`high + 1`,就是第一个大于目标值的数。 + +> 剑指offer:[数字在排序数组中出现的次数](https://link.zhihu.com/?target=https%3A//weiweiblog.cn/getnumberofk/) + +## **4. 查找最后一个小于目标值的数/查找比目标值小但是最接近目标值的数** + +此题以可由第 2 题变形而来,我们已经找到了目标值区域的下(左)边界,那么再往左退一位,即`low - 1`,就是最后一个小于目标值的数。其实`low - 1`也是退出循环后`high`的值,因为此时 `high`刚好等于`low - 1`,它小于`low`,所以 while 循环结束。我们只要判断`high`是否超出边界即可。 + +> A = [1,3,3, **5** ,7,7,7,7,8,14,14] +> target = 7 +> return 3 + +```java +int low = 0, high = n, mid; +while(low <= high){ + mid = low + (high - low) / 2; + if(target <= A[mid]){ + high = mid - 1; + }else{ + low = mid + 1; + } +} +return high < 0 ? -1 : high; +``` + +## **5. 查找第一个大于目标值的数/查找比目标值大但是最接近目标值的数** + +此题以可由第 3 题变形而来,我们已经找到了目标值区域的上(右)边界,那么再往右进一位,即`high + 1`,就是第一个大于目标值的数。其实`high + 1`也是退出循环后`low`的值,因为此时 `low`刚好等于`high + 1`,它大于`high`,所以 while 循环结束。我们只要判断`low`是否超出边界即可。 + +> A = [1,3,3,5,7,7,7,7, **8** ,14,14] +> target = 7 +> return 8 + +```java +int low = 0, high = n, mid; +while(low <= high){ + mid = low + (high - low) / 2; + if(target >= A[mid]){ + low = mid + 1; + }else{ + high = mid - 1; + } +} +return low > n ? -1 : low; +``` + +## **6. 旋转数组返回最小元素** + +## **6.1 查找旋转数组的最小元素(假设不存在重复数字)** + +> LeetCode: [Find Minimum in Rotated Sorted Array](https://link.zhihu.com/?target=https%3A//leetcode.com/problems/find-minimum-in-rotated-sorted-array/) +> Input: [3,4,5,**1**,2] +> Output: 1 + +```java +public int findMin(int[] nums) { + int len = nums.length; + if(len == 0) + return -1; + int left = 0, right = len - 1, mid; + + while(left < right){ + mid = left + (right - left) / 2; + if(nums[mid] > nums[right]) + left = mid + 1; + else{ + right = mid; + } + } + return nums[left]; +} +``` + +注意这里和之前的二分查找的几点区别: +\1. 循环判定条件为`left < right`,没有等于号 +\2. 循环中,通过比较nums[left]与num[mid]的值来判断mid所在的位置: + +- 如果`nums[mid] > nums[right]`,说明前半部分是有序的,最小值在后半部分,令`left = mid + 1`; +- 如果`nums[mid] <= num[right]`,说明最小值在前半部分,令`right = mid`。 + +最后,left会指向最小值元素所在的位置。 + +## **6.2 查找旋转数组的最小元素(存在重复项)** + +> LeetCode: [Find Minimum in Rotated Sorted Array II](https://link.zhihu.com/?target=https%3A//leetcode.com/problems/find-minimum-in-rotated-sorted-array-ii) +> 剑指offer:[旋转数组的最小数字](https://link.zhihu.com/?target=https%3A//weiweiblog.cn/minnumberinrotatearray/) +> Input: [2,2,2,**0**,1] +> Output: 0 + +```java +public int findMin(int[] nums) { + int len = nums.length; + if(len == 0) + return -1; + int left = 0, right = len - 1, mid; + while(left < right){ + mid = left + (right - left) / 2; + if(nums[mid] > nums[right]) + left = mid + 1; + else if(nums[mid] < nums[right]) + right = mid; + else + right--; + } + return nums[left]; +} +``` + +和之前不存在重复项的差别是:当`nums[mid] == nums[right]`时,我们不能确定最小值在 `mid`的左边还是右边,所以我们就让右边界减一。 + +## **7. 在旋转排序数组中搜索** + +## **7.1 不考虑重复项** + +> LeetCode: [Search in Rotated Sorted Array](https://link.zhihu.com/?target=https%3A//leetcode.com/problems/search-in-rotated-sorted-array) + +法一: + +- 先利用方法 6.1 查找数组中的最小元素,即确定分界点的位置 +- 把旋转的数组当成偏移,用`(offset + mid) % len`来求真实的 mid 的位置。 +- 然后用二分查找来定位目标值 + +```java +public int search(int[] nums, int target) { + int len = nums.length; + if(len == 0) + return -1; + int left = 0, right = len - 1, mid; + while(left < right){ + mid = left + (right - left) / 2; + if(nums[mid] > nums[right]) + left = mid + 1; + else + right = mid; + } + int offset = left; + left = 0; + right = len - 1; + while(left <= right){ + mid = left + (right - left) / 2; + int realmid = (mid + offset) % len; + if(nums[realmid] == target) + return realmid; + else if(nums[realmid] < target) + left = mid + 1; + else + right = mid - 1; + } + return -1; +} +``` + +法二:其实没有必要找到旋转数组的分界点,对于搜索左侧还是右侧我们是可以根据mid跟high的元素大小来判定出来的,直接根据target的值做二分搜索就可以了。 + +```java +public int search(int[] nums, int target) { + int len = nums.length; + if(len == 0) + return -1; + int left = 0, right = len - 1, mid; + while(left <= right){ + mid = left + (right - left) / 2; + if(nums[mid] == target) + return mid; + else if(nums[left] <= nums[mid]){ + if(target < nums[mid] && target >= nums[left]) + right = mid - 1; + else + left = mid + 1; + }else if(nums[mid] <= nums[right]){ + if(target > nums[mid] && target <= nums[right]) + left = mid + 1; + else + right = mid - 1; + } + } + return -1; +} +``` + +## **7.2 存在重复项** + +> LeetCode: [Search in Rotated Sorted Array II](https://link.zhihu.com/?target=https%3A//leetcode.com/problems/search-in-rotated-sorted-array-ii/) + +```java +public boolean search(int[] nums, int target) { + int len = nums.length; + if(len == 0) + return false; + int left = 0, right = len - 1, mid; + while(left <= right){ + mid = left + (right - left) / 2; + if(nums[mid] == target) + return true; + else if(nums[mid] > nums[right]){ + if(target < nums[mid] && target >= nums[left]) + right = mid; + else + left = mid + 1; + }else if(nums[mid] < nums[right]){ + if(target > nums[mid] && target <= nums[right]) + left = mid + 1; + else + right = mid; + }else{ + right --; + } + } + return false; +} +``` + +## **8. 二维数组中的查找** + +> 剑指offer:[二维数组中的查找](https://link.zhihu.com/?target=https%3A//weiweiblog.cn/find2array/) + +二维数组是有序的,从右上角来看,向左数字递减,向下数字递增。因此可以利用二分查找的思想,从右上角出发: + +- 当要查找数字比右上角数字大时,下移; +- 当要查找数字比右上角数字小时,左移; + + + +### 思考题 + +思路: + +还是二分查找,比较中间值与左右两边届值的大小,确定有序部分在哪一块,递归这个过程。 \ No newline at end of file diff --git a/Week 03/id_023/LeetCode_860_023.js b/Week 03/id_023/LeetCode_860_023.js new file mode 100644 index 000000000..9d2929392 --- /dev/null +++ b/Week 03/id_023/LeetCode_860_023.js @@ -0,0 +1,30 @@ +/** + * @param {number[]} bills + * @return {boolean} + */ +var lemonadeChange = function (bills) { + var five = 0; + var ten = 0; + var len = bills.length; + for (var i = 0; i < len; i++) { + if (bills[i] == 5) { + five++; + } else if (bills[i] == 10) { + if (five == 0) { + return false + }; + five--; + ten++; + } else if (bills[i] == 20) { + if (ten > 0 && five > 0) { + ten--; + five--; + } else if (five >= 3) { + five -= 3; + } else { + return false; + } + } + } + return true; +}; \ No newline at end of file diff --git a/Week 03/id_023/leetCode_55_023.js b/Week 03/id_023/leetCode_55_023.js new file mode 100644 index 000000000..5f5a7d2c0 --- /dev/null +++ b/Week 03/id_023/leetCode_55_023.js @@ -0,0 +1,19 @@ +/** + * @param {number[]} nums + * @return {boolean} + */ +var canJump = function (nums) { + function canJumpFromWhere(position, nums) { + if (position == nums.length - 1) { + return true; + } + var furthestPosition = Math.min(position + nums[position], nums.length - 1); + for (var nextPosition = position + 1; nextPosition <= furthestPosition; nextPosition++) { + if (canJumpFromWhere(nextPosition, nums)) { + return true; + } + } + return false; + } + return canJumpFromWhere(0, nums); +}; \ No newline at end of file diff --git a/Week 03/id_038/week-03-038/.gitignore b/Week 03/id_038/week-03-038/.gitignore new file mode 100644 index 000000000..168781b21 --- /dev/null +++ b/Week 03/id_038/week-03-038/.gitignore @@ -0,0 +1,165 @@ +.vertx +*.ipr +# log files +*.log +*.log.2* +.log +out +output +dist +dist-3rd +*.txt +!agent-shell-banner.txt +!shell-banner.txt +!VERSION.txt + +# For Eclipse + +.metadata +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders +*.iml +.DS_Store + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + + +# For Java + +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# For Maven + +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# For JetBrains + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml +../../.idea/ + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +#data module ignore +node_modules + +static +public + +BETHUNE_PRO_HOST_PID +BETHUNE_PRO_AGENT_PID +*PID +APP_PID +PID_* +alert-rules-*.yml +altmng-configs.yml +alertmanager.yml +app-info-*.properties +app-info.properties +inspectResult/ + +/file-uploads/ diff --git a/Week 03/id_038/week-03-038/pom.xml b/Week 03/id_038/week-03-038/pom.xml new file mode 100644 index 000000000..a5e381387 --- /dev/null +++ b/Week 03/id_038/week-03-038/pom.xml @@ -0,0 +1,95 @@ + + 4.0.0 + com.github.kylefeng + week-03-038 + jar + 1.0-SNAPSHOT + week-03-038 + http://maven.apache.org + + UTF-8 + 1.8 + 4.12 + 5.0.0 + ${junit.version}.0 + 1.0.0 + + + + + + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + + + + maven-surefire-plugin + 2.19.1 + + + **/Test*.java + **/*Test.java + **/*Tests.java + **/*TestCase.java + + + + slow + + + + + + org.junit.platform + junit-platform-surefire-provider + ${junit.platform.version} + + + + + + + + + com.google.guava + guava + 28.1-jre + + + + org.assertj + assertj-core + 3.13.2 + test + + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test + + + + org.junit.platform + junit-platform-launcher + ${junit.platform.version} + test + + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + diff --git a/Week 03/id_038/week-03-038/src/main/java/com/github/kylefeng/LeetCode_127_038.java b/Week 03/id_038/week-03-038/src/main/java/com/github/kylefeng/LeetCode_127_038.java new file mode 100644 index 000000000..5c4d6af16 --- /dev/null +++ b/Week 03/id_038/week-03-038/src/main/java/com/github/kylefeng/LeetCode_127_038.java @@ -0,0 +1,80 @@ +package com.github.kylefeng; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * 127. 单词接龙 + * + * @author kylefeng + * @time 2019/11/1 14:33 + */ +public class LeetCode_127_038 { + + public static int ladderLength(String beginWord, String endWord, List wordList) { + if (beginWord == null || endWord == null) { + return 0; + } + + if (beginWord.isEmpty() || endWord.isEmpty()) { + return 0; + } + + if (wordList == null || wordList.isEmpty()) { + return 0; + } + + Set wordSet = new HashSet<>(wordList); + + if (!wordSet.contains(endWord)) { + return 0; + } + + + Set charSet = new HashSet<>(); + for (String word : wordList) { + char[] chars = word.toCharArray(); + for (char c : chars) { + charSet.add(c); + } + } + return check(beginWord, endWord, charSet, wordSet); + } + + private static int check(String str, String endWord, Set charSet, Set wordSet) { + int res = 1; + LinkedList queue = new LinkedList<>(); + queue.add(str); + + while (queue.size() != 0) { + res++; + + int size = queue.size(); + while (size-- > 0) { + str = queue.poll(); + char[] chars = str.toCharArray(); + for (int i = 0; i < chars.length; i++) { + char original = chars[i]; + for (Character c : charSet) { + chars[i] = c; + String toBeChecked = new String(chars); + if (!wordSet.contains(toBeChecked)) { + continue; + } + + if (toBeChecked.equals(endWord)) { + return res; + } + + queue.add(toBeChecked); + wordSet.remove(toBeChecked); + } + chars[i] = original; + } + } + } + return 0; + } +} diff --git a/Week 03/id_038/week-03-038/src/main/java/com/github/kylefeng/LeetCode_153_038.java b/Week 03/id_038/week-03-038/src/main/java/com/github/kylefeng/LeetCode_153_038.java new file mode 100644 index 000000000..577fb0195 --- /dev/null +++ b/Week 03/id_038/week-03-038/src/main/java/com/github/kylefeng/LeetCode_153_038.java @@ -0,0 +1,32 @@ +package com.github.kylefeng; + +/** + * 153. 寻找旋转排序数组中的最小值 + * + * @author kylefeng + * @time 2019/11/3 22:03 + */ +public class LeetCode_153_038 { + + public static int findMin(int[] nums) { + if (nums.length == 1) { + return nums[0]; + } + + int left = 0; + int right = nums.length - 1; + + while (left < right) { + int mid = left + (right - left) / 2; + + if (nums[mid] < nums[right]) { + right = mid; + } else { + left = mid + 1; + } + + } + return nums[left]; + } + +} diff --git a/Week 03/id_038/week-03-038/src/main/java/com/github/kylefeng/LeetCode_860_038.java b/Week 03/id_038/week-03-038/src/main/java/com/github/kylefeng/LeetCode_860_038.java new file mode 100644 index 000000000..dfb9f014c --- /dev/null +++ b/Week 03/id_038/week-03-038/src/main/java/com/github/kylefeng/LeetCode_860_038.java @@ -0,0 +1,58 @@ +package com.github.kylefeng; + +/** + * 860. 柠檬水找零 + * + * @author kylefeng + * @time 2019/11/1 17:49 + */ +public class LeetCode_860_038 { + + + public static boolean lemonadeChange(int[] bills) { + // 5 美元的数量 + int $5 = 0; + // 10 美元数量 + int $10 = 0; + + for (int i = 0; i < bills.length; i++) { + int buck = bills[i]; + + // 5 美元,不需要找零 + if (buck == 5) { + $5++; + continue; + } + + // 10 美元,必须要有至少一个 5 美元 + // 10 美元数增加,5 美元数减少 + if (buck == 10 && $5 > 0) { + $10++; + $5--; + continue; + } + + // 20 美元,要么 $5 + $10 + // 要么找 3个 $5 + if (buck == 20) { + if ($5 > 0 && $10 > 0) { + $5--; + $10--; + continue; + } + + if ($5 >= 3 && $10 == 0) { + $5--; + $5--; + $5--; + continue; + } + } + + // 找不开 + return false; + } + + return true; + } +} diff --git a/Week 03/id_038/week-03-038/src/test/java/com/github/kylefeng/LeetCode_127_038_Test.java b/Week 03/id_038/week-03-038/src/test/java/com/github/kylefeng/LeetCode_127_038_Test.java new file mode 100644 index 000000000..a07fb5462 --- /dev/null +++ b/Week 03/id_038/week-03-038/src/test/java/com/github/kylefeng/LeetCode_127_038_Test.java @@ -0,0 +1,22 @@ +package com.github.kylefeng; + +import org.junit.jupiter.api.Test; + +import static com.github.kylefeng.LeetCode_127_038.ladderLength; +import static com.google.common.collect.Lists.newArrayList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author kylefeng + * @time 2019/11/1 16:49 + */ +public class LeetCode_127_038_Test { + + + @Test + void testCases() { + assertEquals(5, ladderLength("hit", "cog", newArrayList("hot", "dot", "dog", "lot", "log", "cog"))); + assertEquals(2, ladderLength("a", "c", newArrayList("a", "b", "c"))); + } + +} diff --git a/Week 03/id_038/week-03-038/src/test/java/com/github/kylefeng/LeetCode_153_038_Test.java b/Week 03/id_038/week-03-038/src/test/java/com/github/kylefeng/LeetCode_153_038_Test.java new file mode 100644 index 000000000..320d95fe7 --- /dev/null +++ b/Week 03/id_038/week-03-038/src/test/java/com/github/kylefeng/LeetCode_153_038_Test.java @@ -0,0 +1,22 @@ +package com.github.kylefeng; + +import org.junit.jupiter.api.Test; + +import static com.github.kylefeng.LeetCode_153_038.findMin; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author kylefeng + * @time 2019/11/3 22:04 + */ +public class LeetCode_153_038_Test { + @Test + void testCase() { + int[] input1 = new int[]{3, 4, 5, 1, 2}; + int[] input2 = new int[]{4, 5, 6, 7, 0, 1, 2}; + int[] input3 = new int[]{1, 2}; + assertEquals(1, findMin(input1)); + assertEquals(0, findMin(input2)); + assertEquals(1, findMin(input3)); + } +} diff --git a/Week 03/id_038/week-03-038/src/test/java/com/github/kylefeng/LeetCode_860_038_Test.java b/Week 03/id_038/week-03-038/src/test/java/com/github/kylefeng/LeetCode_860_038_Test.java new file mode 100644 index 000000000..3c81bffcf --- /dev/null +++ b/Week 03/id_038/week-03-038/src/test/java/com/github/kylefeng/LeetCode_860_038_Test.java @@ -0,0 +1,34 @@ +package com.github.kylefeng; + +import org.junit.jupiter.api.Test; + +import static com.github.kylefeng.LeetCode_860_038.lemonadeChange; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author kylefeng + * @time 2019/11/1 17:51 + */ +public class LeetCode_860_038_Test { + + @Test + void testCases() { + int[] in1 = new int[]{5, 5, 10}; + int[] in2 = new int[]{10, 10}; + int[] in3 = new int[]{5, 5, 10, 10, 20}; + int[] in4 = new int[]{5, 5, 5, 10, 20}; + int[] in5 = new int[]{5, 5, 5, 10, 5, 20, 5, 10, 5, 20}; + int[] in6 = new int[]{5, 5, 5, 5, 10, 5, 10, 10, 10, 20}; + + assertTrue(lemonadeChange(in1)); + assertFalse(lemonadeChange(in2)); + assertFalse(lemonadeChange(in3)); + assertTrue(lemonadeChange(in4)); + assertTrue(lemonadeChange(in5)); + assertTrue(lemonadeChange(in6)); + + } + + +} diff --git a/Week 03/id_048/LeetCode_33_048.java b/Week 03/id_048/LeetCode_33_048.java new file mode 100644 index 000000000..5e18b31da --- /dev/null +++ b/Week 03/id_048/LeetCode_33_048.java @@ -0,0 +1,107 @@ +package com.leetcode.week03; + +/** + * Created by tim on 2019/11/2. + *假设按照升序排序的数组在预先未知的某个点上进行了旋转。 + + ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 + + 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。 + + 你可以假设数组中不存在重复的元素。 + + 你的算法时间复杂度必须是 O(log n) 级别。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_33_048 { + public static void main(String[] args) { + LeetCode_33_048 leetCode_33_048 = new LeetCode_33_048(); + int[] nums = {6,7,8,0,1,2,3,4,5}; + int target = 6; + int index = leetCode_33_048.search(nums,target); + System.out.println("index: " + index); + } + + int [] nums; + int target; + + /** + * 寻找最小值索引下标 + * @param left + * @param right + * @return + */ + public int find_rotate_index(int left, int right) { + if (nums[left] < nums[right]) { + return 0; + } + while (left <= right) { + int pivot = (left + right) / 2; + if (nums[pivot] > nums[pivot + 1]) //如果中间值大于右边相邻的值,就返回右边相邻的下标,这个下标就是最小值的下标。 + return pivot + 1; + else { + if (nums[pivot] < nums[left]) { // [5,6,0,1,2,3,4] 如果中间值小于左边界 + right = pivot - 1;// 右边界等于中间值左边相邻的值,就变成 [5,6,0],排查掉右边所有的。 + } else { // 否则 + left = pivot + 1; // 左边界等于中间值右边相邻的值。 + } + } + } + return 0; + } + + /** + * Binary search,单调二分查找 + * @param left + * @param right + * @return + */ + public int search(int left, int right) { + while (left <= right) { + int pivot = (left + right) / 2; //计算中间下标值 + if (nums[pivot] == target) { // 条件,如果满足条件就返回目标下标值 + return pivot; + } else { + if (target < nums[pivot]) { // 如果目标值在中间值的左边,就把右边界移到中间值下标的左边相邻位置。 + right = pivot - 1; + } else { //否则,就把左边界移到中间值下标的右边相邻位置。 + left = pivot + 1; + } + } + } + return -1; + } + + + public int search(int[] nums, int target) { + this.nums = nums; + this.target = target; + + int n = nums.length; + + if (n == 0) + return -1; + if (n == 1) + return this.nums[0] == target ? 0 : -1; + + int rotate_index = find_rotate_index(0, n - 1); + + // if target is the smallest element + if (nums[rotate_index] == target) { + return rotate_index; + } + // if array is not rotated, search in the entire array + if (rotate_index == 0) { //如果最小值的下标是0,就用search进行二分查找 + return search(0, n - 1); + } + if (target < nums[0]) { // 如果进行了旋转,则左边界为最小值的下标。用search进行查找。 + // search in the right side + return search(rotate_index, n - 1); + } + // search in the left side // 否则,则右边界为最小值的下标。用search进行查找。 + return search(0, rotate_index); + } +} diff --git a/Week 03/id_048/LeetCode_74_048.java b/Week 03/id_048/LeetCode_74_048.java new file mode 100644 index 000000000..d4a67d9bb --- /dev/null +++ b/Week 03/id_048/LeetCode_74_048.java @@ -0,0 +1,34 @@ +package com.leetcode.week03; + +/** + * Created by tim on 2019/11/2. + * 编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性: + + 每行中的整数从左到右按升序排列。 + 每行的第一个整数大于前一行的最后一个整数。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/search-a-2d-matrix + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_74_048 { + public boolean searchMatrix(int[][] matrix, int target) { + int m = matrix.length; //行数,6行 + if (m == 0) return false; + int n = matrix[0].length; //列数,4列 + + // 二分查找 + int left = 0, right = m * n - 1; + int pivotIdx, pivotElement; + while (left <= right) { + pivotIdx = (left + right) / 2; //求取中间下标值 + pivotElement = matrix[pivotIdx / n][pivotIdx % n]; //很关键的查找中间值的方法。 + if (target == pivotElement) return true; + else { + if (target < pivotElement) right = pivotIdx - 1; + else left = pivotIdx + 1; + } + } + return false; + } +} diff --git a/Week 03/id_048/SummaryWeek03.md b/Week 03/id_048/SummaryWeek03.md new file mode 100644 index 000000000..7ec3b0204 --- /dev/null +++ b/Week 03/id_048/SummaryWeek03.md @@ -0,0 +1,20 @@ +使用二分查找,寻找一个半有序数组 [4, 5, 6, 7, 0, 1, 2] 中间无序的地方。 +总体思路: +1.找出最小值,目的是找出数组的旋转点,为了后面直接寻找做准备。 +2. 二分查找方法的存在的前提的单调递增。 +3. 根据最小值和目标target值进行判断,找出左右边界,然后用二分查找方法进行查找。 +代码参考LeetCode_33_048. + +二分查找代码模板: +left, right = 0, len(array) - 1 +while left <= right: + mid = (left + right) / 2 + if array[mid] == target: + # find the target!! + break or return result + elif array[mid] < target: + left = mid + 1 + else: + right = mid - 1 + + diff --git a/Week 03/id_048/codeModel b/Week 03/id_048/codeModel new file mode 100644 index 000000000..1ef599a13 --- /dev/null +++ b/Week 03/id_048/codeModel @@ -0,0 +1,10 @@ +left, right = 0, len(array) - 1 +while left <= right: + mid = (left + right) / 2 + if array[mid] == target: + # find the target!! + break or return result + elif array[mid] < target: + left = mid + 1 + else: + right = mid - 1 \ No newline at end of file diff --git "a/Week 03/id_053/[102]\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206(BFS).java" "b/Week 03/id_053/[102]\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206(BFS).java" new file mode 100644 index 000000000..88ebd546f --- /dev/null +++ "b/Week 03/id_053/[102]\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206(BFS).java" @@ -0,0 +1,74 @@ +//给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。 +// +// 例如: +//给定二叉树: [3,9,20,null,null,15,7], +// +// 3 +// / \ +// 9 20 +// / \ +// 15 7 +// +// +// 返回其层次遍历结果: +// +// [ +// [3], +// [9,20], +// [15,7] +//] +// +// Related Topics 树 广度优先搜索 + + +//leetcode submit region begin(Prohibit modification and deletion) + + +import java.util.LinkedList; +import java.util.Queue; + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List> levelOrder(TreeNode root) { + if (root == null) { + return new ArrayList<>(); + } + List> resultList = new ArrayList<>(); + + Queue queue = new LinkedList<>(); + queue.offer(root); + //定义表示当前层数 + int level = 0; + while (!queue.isEmpty()) { + //还有更好的写法,直接添加一个匿名的容器元素,后面通过resultList.get(level)取出来使用 + List tempList = new ArrayList<>(); + resultList.add(tempList); + //process current logic + + int size = queue.size(); + for (int i = 0; i < size; i++){ + //取出当前层的全部节点 + TreeNode node = queue.poll(); + tempList.add(node.val); + //把下一级节点添加到队列中,drill down + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + level++; + } + return resultList; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week 03/id_053/[102]\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206(DFS).java" "b/Week 03/id_053/[102]\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206(DFS).java" new file mode 100644 index 000000000..1e7a64d14 --- /dev/null +++ "b/Week 03/id_053/[102]\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206(DFS).java" @@ -0,0 +1,62 @@ +//给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。 +// +// 例如: +//给定二叉树: [3,9,20,null,null,15,7], +// +// 3 +// / \ +// 9 20 +// / \ +// 15 7 +// +// +// 返回其层次遍历结果: +// +// [ +// [3], +// [9,20], +// [15,7] +//] +// +// Related Topics 树 广度优先搜索 + + +//leetcode submit region begin(Prohibit modification and deletion) + + +import java.util.LinkedList; +import java.util.Queue; + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List> levelOrder(TreeNode root) { + if (root == null) return new ArrayList<>(); + List> resultList = new ArrayList<>(); + dfs(0,root,resultList); + return resultList; + } + + private void dfs(int level,TreeNode root,List> resultList){ + //terminator + if (level == resultList.size()) { + resultList.add(new ArrayList()); + } + //process current level logic + resultList.get(level).add(root.val); + + //drill down + if (root.left != null) dfs(level +1,root.left,resultList); + if (root.right != null) dfs(level +1,root.right,resultList); + //reverse status if need + } + +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week 03/id_053/[515]\345\234\250\346\257\217\344\270\252\346\240\221\350\241\214\344\270\255\346\211\276\346\234\200\345\244\247\345\200\274(\344\274\230\345\214\226\345\211\215).java" "b/Week 03/id_053/[515]\345\234\250\346\257\217\344\270\252\346\240\221\350\241\214\344\270\255\346\211\276\346\234\200\345\244\247\345\200\274(\344\274\230\345\214\226\345\211\215).java" new file mode 100644 index 000000000..df2568db4 --- /dev/null +++ "b/Week 03/id_053/[515]\345\234\250\346\257\217\344\270\252\346\240\221\350\241\214\344\270\255\346\211\276\346\234\200\345\244\247\345\200\274(\344\274\230\345\214\226\345\211\215).java" @@ -0,0 +1,71 @@ +//您需要在二叉树的每一行中找到最大的值。 +// +// 示例: +// +// +//输入: +// +// 1 +// / \ +// 3 2 +// / \ \ +// 5 3 9 +// +//输出: [1, 3, 9] +// +// Related Topics 树 深度优先搜索 广度优先搜索 + + +//leetcode submit region begin(Prohibit modification and deletion) + +import org.w3c.dom.Element; + +import java.util.Arrays; + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List largestValues(TreeNode root) { + //BFS + if (root == null) return new ArrayList<>(); + int level = 0; + List resultList = new ArrayList<>(); + + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + int size = queue.size(); + // //每一层定义一个集合存放所有的节点值 + List eleList = new ArrayList<>(); + //将队列中的全部弹出,值添加到集合里 + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + if(eleList.isEmpty()) eleList.add(node.val); + else{ + if(eleList.get(0) < node.val) eleList.set(0,node.val); + } + if (node.left != null) queue.offer(node.left); + if (node.right != null) queue.offer(node.right); + } + //对每层的集合进行遍历找出最大值 + // int max = eleList.get(0); + // for (int i = 1; i < eleList.size(); i++) { + // if (max < eleList.get(i)) max = eleList.get(i); + // } + //将最大值加到resultList中 + resultList.addAll(eleList); + //继续下一层 + level++; + } + return resultList; + + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week 03/id_053/[515]\345\234\250\346\257\217\344\270\252\346\240\221\350\241\214\344\270\255\346\211\276\346\234\200\345\244\247\345\200\274(\344\274\230\345\214\226\345\220\216).java" "b/Week 03/id_053/[515]\345\234\250\346\257\217\344\270\252\346\240\221\350\241\214\344\270\255\346\211\276\346\234\200\345\244\247\345\200\274(\344\274\230\345\214\226\345\220\216).java" new file mode 100644 index 000000000..55fe56519 --- /dev/null +++ "b/Week 03/id_053/[515]\345\234\250\346\257\217\344\270\252\346\240\221\350\241\214\344\270\255\346\211\276\346\234\200\345\244\247\345\200\274(\344\274\230\345\214\226\345\220\216).java" @@ -0,0 +1,61 @@ +//您需要在二叉树的每一行中找到最大的值。 +// +// 示例: +// +// +//输入: +// +// 1 +// / \ +// 3 2 +// / \ \ +// 5 3 9 +// +//输出: [1, 3, 9] +// +// Related Topics 树 深度优先搜索 广度优先搜索 + + +//leetcode submit region begin(Prohibit modification and deletion) + +import org.w3c.dom.Element; + +import java.util.Arrays; + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List largestValues(TreeNode root) { + //BFS + if (root == null) return new ArrayList<>(); + int level = 0; + List resultList = new ArrayList<>(); + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + int size = queue.size(); + //每一层定义一个集合存放所有的节点值 + int max = Integer.MIN_VALUE; + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + max = Math.max(node.val,max); + if (node.left != null) queue.offer(node.left); + if (node.right != null) queue.offer(node.right); + } + //将最大值加到resultList中 + resultList.add(max); + //继续下一层 + level++; + } + return resultList; + + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week 03/id_053/[69]x \347\232\204\345\271\263\346\226\271\346\240\271.java" "b/Week 03/id_053/[69]x \347\232\204\345\271\263\346\226\271\346\240\271.java" new file mode 100644 index 000000000..5f1e7929b --- /dev/null +++ "b/Week 03/id_053/[69]x \347\232\204\345\271\263\346\226\271\346\240\271.java" @@ -0,0 +1,42 @@ +//实现 int sqrt(int x) 函数。 +// +// 计算并返回 x 的平方根,其中 x 是非负整数。 +// +// 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。 +// +// 示例 1: +// +// 输入: 4 +//输出: 2 +// +// +// 示例 2: +// +// 输入: 8 +//输出: 2 +//说明: 8 的平方根是 2.82842..., +//  由于返回类型是整数,小数部分将被舍去。 +// +// Related Topics 数学 二分查找 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int mySqrt(int x) { + if (x == 0 || x == 1) return x; + long left = 0; + long right = x; + + while (left < right) { + long middle = left + (right - left)/2; + if (middle * middle > x) { + right = middle - 1; + }else { + left = middle; + } + } + return (int)left; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week 03/id_053/\345\262\233\345\261\277\346\225\260\351\207\217200.java" "b/Week 03/id_053/\345\262\233\345\261\277\346\225\260\351\207\217200.java" new file mode 100644 index 000000000..481ee03e5 --- /dev/null +++ "b/Week 03/id_053/\345\262\233\345\261\277\346\225\260\351\207\217200.java" @@ -0,0 +1,57 @@ +/** + * @author rongsimin + * @date 2019/11/2 16:20 + */ +public class 岛屿数量200 { + private int[] dx = new int[]{-1,1,0,0}; + private int[] dy = new int[]{0,0,-1,1}; + private char[][] g; + + public int numIslands(char[][] grid) { + if (grid == null) return -1; + g = grid; + + int result = 0; + //对二维数组进行遍历,用沉到办法将1炸成0 + for (int i = 0; i < g.length; i++) { + for (int j = 0; j < g[0].length; j++) { + if (g[i][j] == '0') continue; + //否则调用函数进行炸岛 + result += floodfill(i,j); + } + } + return result; + } + + private int floodfill(int i,int j) { + //terminator + if (g[i][j] == '0') { + return 0; + } + //process current level logic + g[i][j] = '0'; + //这里写错,是在坐标轴里对X,Y轴进行平移,我写成g.length了,这个和二维数组没半毛关系 + for (int k = 0; k < dx.length; k++) { + int x = i + dx[k]; + int y = j + dy[k]; + if (x >= 0 && x < g.length && y >= 0 && y < g[0].length) { + if (g[x][y] == '0') continue; + //drill down + floodfill(x,y); + } + } + //reverse status if need + return 1; + } + + public static void main(String[] args) { + char[][] chars = new char[4][5]; + chars[0] =new char[]{'1','1','1','1','0'}; + chars[1] =new char[]{'1','1','0','1','0'}; + chars[2] =new char[]{'1','1','0','0','0'}; + chars[3] =new char[]{'0','0','0','0','0'}; + int i = new 岛屿数量200().numIslands(chars); + System.out.println(i); + } + +} diff --git a/Week 03/id_063/LeetCode_102_063.java b/Week 03/id_063/LeetCode_102_063.java new file mode 100644 index 000000000..1e3a14ce7 --- /dev/null +++ b/Week 03/id_063/LeetCode_102_063.java @@ -0,0 +1,46 @@ +/* +思路 +进行BFS,需要注意的点是每一层的节点出队列之前先保存当前队列的长度, +否则找不到当前层和下一层节点的边界在哪 + + */ + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +class TreeNode { + int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { val = x; } + } + +class Solution { + public List> levelOrder(TreeNode root) { + Queue queue = new LinkedList<>(); + List> result = new LinkedList<>(); + + if (root == null) { + return result; + } + + queue.add(root); + while (!queue.isEmpty()) { + int len = queue.size(); + List l = new ArrayList<>(len); + + while ((len--) != 0) { + TreeNode node = queue.poll(); + l.add(node.val); + if (node.left != null) queue.add(node.left); + if (node.right != null) queue.add(node.right); + } + + result.add(l); + } + + return result; + } +} \ No newline at end of file diff --git a/Week 03/id_063/LeetCode_122_063.java b/Week 03/id_063/LeetCode_122_063.java new file mode 100644 index 000000000..6b851a838 --- /dev/null +++ b/Week 03/id_063/LeetCode_122_063.java @@ -0,0 +1,38 @@ +/* +思路 + +dp[i][0] 表示第i天不持有股票的最大利润 +dp[i][1] 表示第i天持有股票的最大利润 + +dp[i][0] = max ( + dp[i-1][1] + prices[i] // 昨天为止持有股票,今天卖股票 + dp[i-1][0] // 昨天为止没股票,今天什么都不干 +) + +dp[i][1] = max ( + dp[i-1][1], // 昨天为止有股票,今天什么都不干 + dp[i-1][0] - prices[i] // 昨天为止没股票,今天买股票 +) + +最后答案是dp[n-1][0] (因为最后一天肯定手上不持有股票才可能是利润最大的) + + */ + +class Solution { + public int maxProfit(int[] prices) { + if ((prices == null) || (prices.length == 0)) { + return 0; + } + + int dp0 = 0; + int dp1 = -prices[0]; + int max0, max1; + for (int i = 1; i < prices.length; i++) { + max0 = Math.max(dp1 + prices[i], dp0); + max1 = Math.max(dp1, dp0 - prices[i]); + dp0 = max0; dp1 = max1; + } + + return dp0; + } +} \ No newline at end of file diff --git a/Week 03/id_063/LeetCode_126_063.java b/Week 03/id_063/LeetCode_126_063.java new file mode 100644 index 000000000..488e2c545 --- /dev/null +++ b/Week 03/id_063/LeetCode_126_063.java @@ -0,0 +1,176 @@ +package problem126.Solution2; + + +/* + +单纯使用BFS把所有最短路径全部找到的方法搜索空间太大了,其实可以想办法缩小搜索空间 + +假设最短距离是Len +BFS一次遍历可以找到所有距离起点距离小于等于Len的节点的从起点到该节点的最短距离 +这个在BFS搜索到终点时候,可以全部算出了,而且这个计算量是不可能省掉的 + +有了这一堆信息,再次做路径搜索的时候就不要再用BFS了,因为BFS没法利用这个信息做减枝 +但是DFS可以,DFS遍历到某一个节点时候,可以判断当前节点和终点距离,如果这个距离加上 +前面算出来的起点到这个点的最短距离后,前面BFS算出来的最短距离要大,那这个节点不可能 +再往下查找到最短距离了,也就是不可能产生最短路径,对应的分支就可马上剪掉,所以先BFS +再DFS, 这个是提升效率关键的思路 + */ + + +import com.sun.tools.javadoc.SourcePositionImpl; +import com.sun.tools.javadoc.Start; + +import java.util.*; + +class Solution { + + private Map strToMinlen; // 起点到每一个点的最短距离 + private Map> linkTable; // 邻接表 + + + private void addSingleWordToLinkTable(String s) { + for (int i = 0; i < s.length(); i++) { + String newStr = s.substring(0, i) + "?" + s.substring(i+1, s.length()); + List next = linkTable.getOrDefault(newStr, new LinkedList<>()); + + next.add(s); + linkTable.put(newStr, next); + } + } + + private void buildLinkTable(String beginWord, List wordList) { + linkTable = new HashMap<>(); + + for (String s : wordList) { + addSingleWordToLinkTable(s); + } + addSingleWordToLinkTable(beginWord); + } + + private void getMinLenBfs(String beginWord, String endWord, List wordList) { + Queue queue = new LinkedList<>(); + strToMinlen = new HashMap<>(); + + int len = 0; + queue.add(beginWord); + Set visited = new HashSet<>(); + visited.add(beginWord); + + while (!queue.isEmpty()) { + int nodeNum = queue.size(); + + while ((nodeNum--) != 0) { + String s = queue.poll(); + strToMinlen.put(s, len); // 保存最短距离 + + for (int i = 0; i < s.length(); i++) { + String newStr = s.substring(0, i) + "?" + s.substring(i + 1, s.length()); + List next = linkTable.getOrDefault(newStr, new LinkedList<>()); + + for (String nextStr : next) { + if (visited.contains(nextStr)) { + continue; + } + + if (next.equals(endWord)) { + return; + } + + visited.add(nextStr); + queue.add(nextStr); + } + } + } + + len++; + } + } + + private int getDiffNum(String a, String b) { + int cnt = 0; + + for (int i = 0; i < a.length(); i++) { + if (a.charAt(i) != b.charAt(i)) { + cnt++; + } + } + + return cnt; + } + + private void dfs(String cur, String target, List path, Set visited, List> result, int targetMinLen) { + //System.out.println(path); + + if (cur.equals(target)) { + //System.err.println(path); + + result.add(new LinkedList<>(path)); + path.remove(path.size() - 1); + visited.remove(cur); + + return; + } + + if (strToMinlen.get(cur) + getDiffNum(cur, target) > targetMinLen) { + path.remove(path.size() - 1); + visited.remove(cur); + return; + } + + // strToMinlen中找所有可能到的下一个节点,不在这个表里面的节点最后不可能通过最短路径到终点 + for (int i = 0; i < cur.length(); i++) { + String newStr = cur.substring(0, i) + "?" + cur.substring(i+1, cur.length()); + List next = linkTable.getOrDefault(newStr, new LinkedList<>()); + + for (String s : next) { + if (!strToMinlen.containsKey(s)) { + continue; + } + + if (visited.contains(s)) { + continue; + } + + // 只允许通过最短的路到下一个节点,进行减枝 + if (path.size() > strToMinlen.get(s)) { + continue; + } + + visited.add(s); + path.add(s); + dfs(s, target, path, visited, result, targetMinLen); + } + } + + path.remove(path.size() - 1); + visited.remove(cur); + } + + private void getMinPathDfs(String start, String target, List> result) { + if (!strToMinlen.containsKey(target)) { + return; + } + + List path = new LinkedList<>(); + Set visited = new HashSet(); + + path.add(start); + visited.add(start); + + dfs(start, target, path, visited, result, strToMinlen.get(target)); + } + + public List> findLadders(String beginWord, String endWord, List wordList) { + buildLinkTable(beginWord, wordList); + getMinLenBfs(beginWord, endWord, wordList); + + List> result = new LinkedList<>(); + getMinPathDfs(beginWord, endWord, result); + + return result; + } + + public static void main(String[] args) { + new Solution().findLadders("hit", "cog", Arrays.asList("hot","dot","dog","lot","log")); + } +} diff --git a/Week 03/id_063/LeetCode_127_063.java b/Week 03/id_063/LeetCode_127_063.java new file mode 100644 index 000000000..a8d2622b8 --- /dev/null +++ b/Week 03/id_063/LeetCode_127_063.java @@ -0,0 +1,129 @@ +package problem127.Solution3; + +import java.util.*; + + +/* +思路 +A* 搜索查找嘴短距离 + */ + +public class Solution { + + private class State { + String curStr; + int stepNum; + + State(String str) { + curStr = str; + stepNum = 1; + } + + State() { + curStr = null; + stepNum = 0; + } + } + + private int getDiffCharNum(String a, String b) { + int cnt = 0; + for (int i = 0; i < a.length(); i++) { + if (a.charAt(i) != b.charAt(i)) { + cnt++; + } + } + + return cnt; + } + + public int ladderLength(String beginWord, String endWord, List wordList) { + if (beginWord.length() != endWord.length()) { + return 0; + } + + HashMap> link = new HashMap<>(); + + boolean endWordExist = false; + for (int i = 0; i < wordList.size(); i++) { + if (wordList.get(i).length() != beginWord.length()) { + continue; + } + + String si = wordList.get(i); + if (si.equals(endWord)) { + endWordExist = true; + } + + for (int pos = 0; pos < si.length(); pos++) { + StringBuilder builder = new StringBuilder(si); + builder.setCharAt(pos, '?'); + List l = link.getOrDefault(builder.toString(), new LinkedList<>()); + l.add(si); + link.put(builder.toString(), l); + } + } + + if (!endWordExist) { + return 0; + } + + // 当前已经走的步数相同情况下,和目标相比变换次数少的优先出队 + PriorityQueue pq = new PriorityQueue<>(new Comparator() { + @Override + public int compare(State o1, State o2) { + if (o1.stepNum != o2.stepNum) { + return o1.stepNum - o2.stepNum; + } + + return getDiffCharNum(o1.curStr, endWord) - getDiffCharNum(o2.curStr, endWord); + } + }); + + pq.add(new State(beginWord)); + State curState = null; + Set visitedStr = new HashSet<>(); + visitedStr.add(beginWord); + + while (!pq.isEmpty()) { + curState = pq.poll(); + + for (int pos = 0; pos < curState.curStr.length(); pos++) { + StringBuilder builder = new StringBuilder(curState.curStr); + builder.setCharAt(pos, '?'); + List nextStep = link.get(builder.toString()); + if (nextStep == null) { + continue; + } + + for (String s : nextStep) { + if (visitedStr.contains(s)) { + continue; + } + + if (s.equals(endWord)) { + return curState.stepNum + 1; + } + + State newState = new State(); + newState.curStr = s; + newState.stepNum = curState.stepNum + 1; + + visitedStr.add(newState.curStr); + pq.add(newState); + } + } + } + + return 0; + } + + public static void main(String[] args) { + List list = new LinkedList<>(); + for (String s : new String[] { "hot","dot","dog","lot","log","cog"}) { + list.add(s); + } + + System.out.println(new Solution().ladderLength("hit", "cog", list)); + } +} + diff --git a/Week 03/id_063/LeetCode_153_063.java b/Week 03/id_063/LeetCode_153_063.java new file mode 100644 index 000000000..9b6d93332 --- /dev/null +++ b/Week 03/id_063/LeetCode_153_063.java @@ -0,0 +1,49 @@ +/* +思路 +广义的二分查找 + +其实就是查找发生了降序的那个特殊的点在哪 + */ + +class Solution { + public int findMin(int[] nums) { + int left = 0; + int right = nums.length - 1; + + while (left <= right) { + int mid = (left + right) >> 1; + + if (right - left < 3) { + // 缩小到一定范围就进行顺序搜索 + for (int i = left; i <= right; i++) { + if (i > 0 && nums[i] < nums[i-1]) { + return nums[i]; + } + } + + // 如果旋转点找不到,说明整个序列是单调递增的,直接返回第一个元素 + return nums[0]; + } + + // 判定是不是特殊点 + if (mid > 0 && nums[mid] < nums[mid-1]) { + return nums[mid]; + } + + if (nums[left] <= nums[mid]) { + // 左边是严格升序的,旋转点不可能在左边 + left = mid; + } else { + // 右半部分升序, 旋转点不可能在右边 + right = mid; + } + } + + return -1; + } + + public static void main(String[] args) { + int ret = new Solution().findMin(new int[] {3,4,5,1,2}); + System.out.println(ret); + } +} \ No newline at end of file diff --git a/Week 03/id_063/LeetCode_200_063.java b/Week 03/id_063/LeetCode_200_063.java new file mode 100644 index 000000000..d076dad39 --- /dev/null +++ b/Week 03/id_063/LeetCode_200_063.java @@ -0,0 +1,110 @@ + +/* +构建并查集, 遍历每一个1节点,检查每个1节点下面和右边的节点是不是1, +若是1,则将本节点和下面或者右边的节点并在一个集合中,每并一个新节点 +集合个数减去一个,每发现一个数值为1的新节点,集合数量加上一个,返回 +最后集合个数 +*/ + + +class UnionSet { + private int len; + private int[] buf; + + UnionSet(int len, char[][] grid) { + this.len = len; + buf = new int[len]; + + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[i].length; j++) { + if (grid[i][j] == '1') { + int index = i * grid[0].length + j; + buf[index] = index; + } + } + } + } + + private int findRoot(int n) { + int node; + + node = n; + while (buf[node] != node) { + node = buf[node]; + } + + int root = node; + + // 路径压缩 + node = n; + while (buf[node] != node) { + int tmp = buf[node]; + buf[node] = root; + node = tmp; + } + + return root; + } + + public boolean merge(int p, int q) { + int root_p = findRoot(p); + int root_q = findRoot(q); + + if (root_p != root_q) { + buf[root_p] = root_q; + return true; + } + + return false; + } + +} + +class Solution { + public int numIslands(char[][] grid) { + if ((grid == null) || (grid.length == 0)) { + return 0; + } + + if (grid[0].length == 0) { + return 0; + } + + UnionSet us = new UnionSet(grid.length * grid[0].length, grid); + + int unionCnt = 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[i].length; j++) { + if (grid[i][j] == '0') { + continue; + } + + unionCnt++; + + if ((i+1 < grid.length) && (grid[i+1][j] == '1')) { + if (us.merge( (i+1)*grid[0].length + j, i*grid[0].length + j )) { + unionCnt--; + } + } + + if ( (j+1 < grid[0].length) && (grid[i][j+1] == '1') ) { + if (us.merge( i*grid[0].length + j, i*grid[0].length + j+1 )) { + unionCnt--; + } + } + } + } + + return unionCnt; + } + + public static void main(String[] args) { + new Solution().numIslands(new char[][] { + {'1','1','1','1','0'}, + {'1','1','0','1','0'}, + {'1','1','0','0','0'}, + {'0','0','0','0','0'} + } + ); + } +} diff --git a/Week 03/id_063/LeetCode_22_063.java b/Week 03/id_063/LeetCode_22_063.java new file mode 100644 index 000000000..962389cca --- /dev/null +++ b/Week 03/id_063/LeetCode_22_063.java @@ -0,0 +1,30 @@ +/* +思路 +递归生成字符串,最多递归n层,每层维护左括号和右括号的数量 +往下递归的条件是左括号数量大于右括号,且左右括号数量都不超过n +*/ + +class Solution { + List dfs(List result, int leftCnt, int rightCnt, int maxCnt, char[] path, int curLevel, int maxLevel) { + if (curLevel == maxLevel) { + result.add(new String(path)); + return result; + } + + if (leftCnt < maxCnt) { + path[curLevel] = '('; + dfs(result, leftCnt + 1, rightCnt, maxCnt, path, curLevel + 1, maxLevel); + } + + if ((rightCnt < maxCnt) && (leftCnt > rightCnt)) { + path[curLevel] = ')'; + dfs(result, leftCnt, rightCnt + 1, maxCnt, path, curLevel + 1, maxLevel); + } + + return result; + } + + public List generateParenthesis(int n) { + return dfs(new ArrayList<>(n<<1), 0, 0, n, new char[n<<1], 0, n<<1); + } +} \ No newline at end of file diff --git a/Week 03/id_063/LeetCode_33_063.java b/Week 03/id_063/LeetCode_33_063.java new file mode 100644 index 000000000..4a56cd13c --- /dev/null +++ b/Week 03/id_063/LeetCode_33_063.java @@ -0,0 +1,42 @@ +/* +思路 +广义的二分查找 + */ + +class Solution { + public int search(int[] nums, int target) { + int left = 0; + int right = nums.length - 1; + + while (left <= right) { + int mid = (left + right) >> 1; + + if (nums[mid] == target) { + return mid; + } + + if (nums[left] <= nums[mid]) { + // 左半部分升序 + if (target >= nums[left] && target < nums[mid]) { + right = mid - 1; + } else { + left = mid + 1; + } + } else { + // 右半部分升序 + if (target > nums[mid] && target <= nums[right]) { + left = mid + 1; + } else { + right = mid - 1; + } + } + } + + return -1; + } + + public static void main(String[] args) { + int ret = new Solution().search(new int[] {4,5,6,7,0,1,2}, 0); + System.out.println(ret); + } +} \ No newline at end of file diff --git a/Week 03/id_063/LeetCode_367_063.java b/Week 03/id_063/LeetCode_367_063.java new file mode 100644 index 000000000..102a5c6b7 --- /dev/null +++ b/Week 03/id_063/LeetCode_367_063.java @@ -0,0 +1,33 @@ + +/* +朴素的二分查找 + */ + + +class Solution { + public boolean isPerfectSquare(int num) { + long left = 1; + long right = num; + long mid; + + while (left <= right) { + mid = (left + right) >> 1; + + if (mid * mid == num) { + return true; + } + + if (mid * mid < num) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + return false; + } + + public static void main(String[] args) { + System.out.println(new Solution().isPerfectSquare(104976)); + } +} \ No newline at end of file diff --git a/Week 03/id_063/LeetCode_433_063.java b/Week 03/id_063/LeetCode_433_063.java new file mode 100644 index 000000000..a628078d6 --- /dev/null +++ b/Week 03/id_063/LeetCode_433_063.java @@ -0,0 +1,64 @@ +/* +BFS 对最短了路径长度进行查找 + */ + + +import java.util.*; + +class Solution { + public int minMutation(String start, String end, String[] bank) { + Map> linkTable = new HashMap<>(); + + // 生成邻接表 + for (String s : bank) { + for (int i = 0; i < s.length(); i++) { + String newStr = s.substring(0, i) + "?" + s.substring(i+1, s.length()); + List next = linkTable.getOrDefault(newStr, new LinkedList<>()); + next.add(s); + linkTable.put(newStr, next); + } + } + + int length = 1; + boolean findTarget = false; + Queue queue = new LinkedList<>(); + Set visited = new HashSet<>(); + queue.add(start); + visited.add(start); + + while (!queue.isEmpty()) { + int nodeNum = queue.size(); // 当前层的节点个数 + + while ((nodeNum--) != 0) { + String curStr = queue.poll(); + + for (int i = 0; i < curStr.length(); i++) { + String newStr = curStr.substring(0, i) + "?" + curStr.substring(i + 1, curStr.length()); + List next = linkTable.getOrDefault(newStr, new LinkedList<>()); + + for (String s : next) { + if (visited.contains(s)) { + continue; + } + + if (s.equals(end)) { + findTarget = true; + return length; + } + + queue.add(s); + visited.add(s); + } + } + } + + length++; + } + + return -1; + } + + public static void main(String[] args) { + System.out.println(new Solution().minMutation("AACCGGTT", "AAACGGTA", new String[] {"AACCGGTA", "AACCGCTA", "AAACGGTA"})); + } +} \ No newline at end of file diff --git a/Week 03/id_063/LeetCode_455_063.java b/Week 03/id_063/LeetCode_455_063.java new file mode 100644 index 000000000..c0c55fd02 --- /dev/null +++ b/Week 03/id_063/LeetCode_455_063.java @@ -0,0 +1,23 @@ +/* + +贪心策略,每个饼干都发挥最大功效即可 + */ + +import java.util.Arrays; + +class Solution { + public int findContentChildren(int[] g, int[] s) { + Arrays.sort(g); Arrays.sort(s); + + int cnt = 0; + for (int i = g.length-1, j = s.length - 1; (i >= 0) && (j >= 0); ) { + if (g[i] <= s[j]) { + i--; j--; cnt++; + } else { + i--; + } + } + + return cnt; + } +} \ No newline at end of file diff --git a/Week 03/id_063/LeetCode_45_063.java b/Week 03/id_063/LeetCode_45_063.java new file mode 100644 index 000000000..626ebd411 --- /dev/null +++ b/Week 03/id_063/LeetCode_45_063.java @@ -0,0 +1,36 @@ +/* + +思路 +贪心策略 +先用dp算法计算每一个位置之前所有节点中能够到达后续节点最远的位置,得到maxLen数组 +题目说了最后一定可以到终点 +所以每跳一步,都让可达的范围尽量远,最后跳的步数就是最少的 + +*/ + +class Solution { + public int jump(int[] nums) { + int[] maxLen = new int[nums.length]; // maxLen[i]表示前i个元素能跳到最远的位置 + + maxLen[0] = nums[0]; + for (int i = 1; i < nums.length; i++) { + maxLen[i] = Math.max(maxLen[i-1], i + nums[i]); + } + + int step = 0; // 当前能跳到的最远的位置的最少步数 + int i = 0; + + while (true) { + if (i >= nums.length-1) { + return step; + } + + i = maxLen[i]; + step++; + } + } + + public static void main(String[] args) { + new Solution().jump(new int[] {1,1,1,1}); + } +} diff --git a/Week 03/id_063/LeetCode_515_063.java b/Week 03/id_063/LeetCode_515_063.java new file mode 100644 index 000000000..a1a763679 --- /dev/null +++ b/Week 03/id_063/LeetCode_515_063.java @@ -0,0 +1,52 @@ +package problem515.Solution1; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + + +/* +利用BFS做二叉树的层次遍历即可 + */ + + +class TreeNode { + int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { val = x; } + } + +class Solution { + public List largestValues(TreeNode root) { + List result = new LinkedList<>(); + Queue queue = new LinkedList<>(); + + if (root == null) { + return result; + } + + int length = 1; + queue.add(root); + + while (!queue.isEmpty()) { + int nodeNum = queue.size(); + + int max = Integer.MIN_VALUE; + while ((nodeNum--) != 0) { + TreeNode curNode = queue.poll(); + if (curNode.left != null) queue.add(curNode.left); + if (curNode.right != null) queue.add(curNode.right); + + max = Math.max(max, curNode.val); + } + + result.add(max); + length++; + } + + return result; + } +} + diff --git a/Week 03/id_063/LeetCode_529_063.java b/Week 03/id_063/LeetCode_529_063.java new file mode 100644 index 000000000..355c04204 --- /dev/null +++ b/Week 03/id_063/LeetCode_529_063.java @@ -0,0 +1,92 @@ +/* +思路 + +简单的递归搜索 + */ + + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +class Solution { + + static int[][] move = { + {1, 0}, + {0, 1}, + {-1, 0}, + {0, -1}, + {1, 1}, + {-1, 1}, + {-1, -1}, + {1, -1} + }; + + private boolean isValid(int i, int j, char[][] board) { + return (i >= 0) && (i < board.length) && (j >= 0) && (j < board[0].length); + } + + void dfs(int i, int j, char[][] board, Set visited, boolean searchOnlyEmptyBLock) { + if (searchOnlyEmptyBLock && board[i][j] != 'E') { + return; + } + + if (board[i][j] == 'M') { + board[i][j] = 'X'; + return; + } + + if (board[i][j] == 'E') { + board[i][j] = 'B'; + + int cnt = 0; + for (int ii = 0; ii < move.length; ii++) { + int pos_i = i + move[ii][0]; + int pos_j = j + move[ii][1]; + if (isValid(pos_i, pos_j, board)) { + if ((board[pos_i][pos_j] == 'M') || (board[pos_i][pos_j] == 'X')) { + cnt++; + } + } + } + + if (cnt != 0) { + board[i][j] = (char)('0' + cnt); + return; + } + + + for (int ii = 0; ii < move.length; ii++) { + if (isValid(i + move[ii][0], j + move[ii][1], board)) { + int idx = (i + move[ii][0]) * board[0].length + (j + move[ii][1]); + if (visited.contains(idx)) { + continue; + } + + visited.add(idx); + dfs(i + move[ii][0], j + move[ii][1], board, visited, true); + } + } + } + } + + public char[][] updateBoard(char[][] board, int[] click) { + char[][] result = new char[board.length][]; + for (int i = 0; i < board.length; i++) { + result[i] = Arrays.copyOf(board[i], board[i].length); + } + + dfs(click[0], click[1], result, new HashSet(), false); + + return result; + } + + public static void main(String[] args) { + new Solution().updateBoard(new char[][] { + {'E','E','E','E','E'}, + {'E','E','M','E','E'}, + {'E','E','E','E','E'}, + {'E','E','E','E','E'}, + }, new int[] {3, 0}); + } +} \ No newline at end of file diff --git a/Week 03/id_063/LeetCode_55_063.java b/Week 03/id_063/LeetCode_55_063.java new file mode 100644 index 000000000..532e0ae75 --- /dev/null +++ b/Week 03/id_063/LeetCode_55_063.java @@ -0,0 +1,26 @@ +/* +贪心策略 +从左向右迭代,更新当前能够到达的最右边的位置, +如果能够覆盖到最后的位置,就能够到达终点 + + */ + +class Solution { + public boolean canJump(int[] nums) { + int maxLen = nums[0]; + + for (int i = 1; i < nums.length; i++) { + if (maxLen >= nums.length) { + return true; + } + + if (i > maxLen) { + return false; + } + + maxLen = Math.max(i + nums[i], maxLen); + } + + return true; + } +} diff --git a/Week 03/id_063/LeetCode_69_063.java b/Week 03/id_063/LeetCode_69_063.java new file mode 100644 index 000000000..3330837c2 --- /dev/null +++ b/Week 03/id_063/LeetCode_69_063.java @@ -0,0 +1,50 @@ +/* +先用二分法缩小数值范围,然后再进行小范围搜索 + */ + +class Solution { + public int mySqrt(int x) { + if (x == 0 || x == 1) { + return x; + } + + int left = 1; + int right = x; + int mid = 1; + + while (left < right) { + mid = (left + right) >> 1; + + if (x / mid == mid) { + return mid; + } + + + if (x / mid < mid) { + right = mid - 1; + } else { + left = mid + 1; + } + } + + if (x / mid > mid) { + while (true) { + if ((x / mid >= mid) && (x / (mid+1) < (mid+1))) { + break; + } + + mid++; + } + } else { + while (true) { + if ((x / mid >= mid) && (x / (mid+1) < (mid+1))) { + break; + } + + mid--; + } + } + + return mid; + } +} \ No newline at end of file diff --git a/Week 03/id_063/LeetCode_74_063.java b/Week 03/id_063/LeetCode_74_063.java new file mode 100644 index 000000000..0ccdbd2f3 --- /dev/null +++ b/Week 03/id_063/LeetCode_74_063.java @@ -0,0 +1,51 @@ +/* +先用二分查找在哪一行,然后再在行内进行第二次二分查找 + */ + +class Solution { + public boolean searchMatrix(int[][] matrix, int target) { + + if ((matrix == null) || (matrix.length == 0) || (matrix[0].length == 0)) { + return false; + } + + int left = 0; + int right = matrix.length - 1; + + int row = -1; + while (left <= right) { + int mid = (left + right) >> 1; + + if (target >= matrix[mid][0] && target <= matrix[mid][matrix[mid].length-1]) { + row = mid; + break; + } + + else if (target < matrix[mid][0]) { + right = mid - 1; + } else { + left = mid + 1; + } + } + + if (row == -1) { + return false; + } + + left = 0; right = matrix[row].length - 1; + while (left <= right) { + int mid = (left + right) >> 1; + if (matrix[row][mid] == target) { + return true; + } + + if (target < matrix[row][mid]) { + right = mid - 1; + } else { + left = mid + 1; + } + } + + return false; + } +} \ No newline at end of file diff --git a/Week 03/id_063/LeetCode_860_063.java b/Week 03/id_063/LeetCode_860_063.java new file mode 100644 index 000000000..6aaa25676 --- /dev/null +++ b/Week 03/id_063/LeetCode_860_063.java @@ -0,0 +1,36 @@ +/* +贪心策略 +每次找钱尽量把面值大的给出去即可 + */ + +public class Solution { + public boolean lemonadeChange(int[] bills) { + int fiveCnt = 0; + int tenCnt = 0; + + for (int i = 0; i < bills.length; i++) { + if (bills[i] == 5) { + fiveCnt++; continue; + } + + if (bills[i] == 10) { + if (fiveCnt == 0) { + return false; + } + + fiveCnt--; tenCnt++; continue; + } + + if ((fiveCnt >= 1) && (tenCnt >= 1)) { + fiveCnt--; tenCnt--; continue; + } + + if (fiveCnt >= 3) { + fiveCnt -= 3; continue; + } + + return false; + } + return true; + } +} diff --git a/Week 03/id_063/LeetCode_874_063.java b/Week 03/id_063/LeetCode_874_063.java new file mode 100644 index 000000000..d9c3948b8 --- /dev/null +++ b/Week 03/id_063/LeetCode_874_063.java @@ -0,0 +1,146 @@ +/* +思路 + +简单进行模拟 + + */ + +class Solution { + private Map> obs_row; + private Map> obs_col; + + private class Pos { + int i; + int j; + Pos(int i, int j) { + this.i = i; this.j = j; + } + } + + private void buildObs(int[][] obstacles) { + obs_row = new HashMap<>(); + obs_col = new HashMap<>(); + + for (int i = 0; i < obstacles.length; i++) { + List l = obs_row.getOrDefault(obstacles[i][1], new LinkedList<>()); + l.add(obstacles[i][0]); + obs_row.put(obstacles[i][1], l); + + l = obs_col.getOrDefault(obstacles[i][0], new LinkedList<>()); + l.add(obstacles[i][1]); + obs_col.put(obstacles[i][0], l); + } + } + + private void moveUp(Pos pos, int steps) { + int start = pos.i + 1; + int end = pos.i + steps; + int target = Integer.MAX_VALUE; + boolean findObs = false; + + for (int obs_i : obs_col.getOrDefault(pos.j, new LinkedList<>())) { + if ( (obs_i >= start && obs_i <= end) ) { + findObs = true; + target = Math.min(target, obs_i); + } + } + + pos.i = findObs ? (target - 1) : end; + } + + private void moveDown(Pos pos, int steps) { + int start = pos.i - steps; + int end = pos.i-1; + int target = Integer.MIN_VALUE; + boolean findObs = false; + + for (int obs_i : obs_col.getOrDefault(pos.j, new LinkedList<>())) { + if ( (obs_i >= start && obs_i <= end) ) { + findObs = true; + target = Math.max(target, obs_i); + } + } + + pos.i = findObs ? (target + 1) : start; + } + + private void moveLeft(Pos pos, int steps) { + int start = pos.j - steps; + int end = pos.j-1; + int target = Integer.MIN_VALUE; + boolean findObs = false; + + for (int obs_j : obs_row.getOrDefault(pos.i, new LinkedList<>())) { + if ( obs_j >= start && obs_j <= end ) { + findObs = true; + target = Math.max(target, obs_j); + } + } + + pos.j = findObs ? (target + 1) : start; + } + + private void moveRight(Pos pos, int steps) { + int start = pos.j+1; + int end = pos.j + steps; + int target = Integer.MAX_VALUE; + boolean findObs = false; + + for (int obs_j : obs_row.getOrDefault(pos.i, new LinkedList<>())) { + if ( obs_j >= start && obs_j <= end ) { + findObs = true; + target = Math.min(target, obs_j); + } + } + + pos.j = findObs ? (target - 1) : end; + } + + private void moveStep(Pos pos, int direc, int steps) { + switch (direc) { + case 0: + moveUp(pos, steps); + break; + case 1: + moveRight(pos, steps); + break; + case 2: + moveDown(pos, steps); + break; + case 3: + moveLeft(pos,steps); + break; + default: + break; + } + } + + public int robotSim(int[] commands, int[][] obstacles) { + buildObs(obstacles); + + int direc = 0; + Pos pos = new Pos(0, 0); + int max = 0; + + for (int cmd : commands) { + if (cmd == -1) { + direc = (direc + 1) % 4; + } else if (cmd == -2) { + direc = (direc >= 1) ? (direc-1) : 3; + } else { + if (cmd == 0) { + continue; + } + + moveStep(pos, direc, cmd); + max = Math.max(max, pos.i * pos.i + pos.j * pos.j); + } + } + + return max; + } + + public static void main(String[] args) { + new Solution().robotSim(new int[] {4,-1, 4, -2, 4}, new int[][] {{2,4}}); + } +} \ No newline at end of file diff --git a/Week 03/id_068/jump.cpp b/Week 03/id_068/jump.cpp new file mode 100644 index 000000000..ee72de53f --- /dev/null +++ b/Week 03/id_068/jump.cpp @@ -0,0 +1,11 @@ +class Solution { +public: + bool canJump(vector& nums) { + int k = 0; + for(int i = 0; i < nums.size(); ++i) { + if(i > k) return false; + k = max(k, i + nums[i]); + } + return true; + } +}; diff --git a/Week 03/id_068/search-in-rotated-sorted-array.cpp b/Week 03/id_068/search-in-rotated-sorted-array.cpp new file mode 100644 index 000000000..8b5e79c9f --- /dev/null +++ b/Week 03/id_068/search-in-rotated-sorted-array.cpp @@ -0,0 +1,16 @@ +class Solution { +public: + int search(vector& nums, int target) { + int left = 0, right = nums.size()-1; + while(left < right) { + int mid = (left + right) / 2; + if(nums[mid] == target) return target; + else if(nums[0] <= nums[mid] && (target > nums[mid] || target < nums[0]) || + nums[0] > nums[mid] && (target > nums[mid]) && target < nums[0]) { + left = mid + 1; + } + else right = mid; + } + return left == right && nums[left] == target ? left : -1; + } +}; diff --git a/Week 03/id_078/LeetCode_33_078.java b/Week 03/id_078/LeetCode_33_078.java new file mode 100644 index 000000000..9557fd854 --- /dev/null +++ b/Week 03/id_078/LeetCode_33_078.java @@ -0,0 +1,58 @@ +//谴Ԥδ֪ijϽת +// +// ( 磬 [0,1,2,4,5,6,7] ܱΪ [4,5,6,7,0,1,2] ) +// +// һĿֵдĿֵ򷵻򷵻 -1 +// +// ԼвظԪء +// +// 㷨ʱ临Ӷȱ O(log n) +// +// ʾ 1: +// +// : nums = [4,5,6,7,0,1,2], target = 0 +//: 4 +// +// +// ʾ 2: +// +// : nums = [4,5,6,7,0,1,2], target = 3 +//: -1 +// Related Topics ֲ + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int search(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return -1; + } + int start = 0; + int end = nums.length - 1; + int mid; + while (start <= end) { + + mid = start + (end - start) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[start] <= nums[mid]) { + if (target >= nums[start] && target < nums[mid]) { + end = mid - 1; + } else { + start = mid + 1; + } + } else { + if (target <= nums[end] && target > nums[mid]) { + start = mid + 1; + } else { + end = mid - 1; + } + } + + } + return -1; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_078/LeetCode_74_078.java b/Week 03/id_078/LeetCode_74_078.java new file mode 100644 index 000000000..8ffafac9d --- /dev/null +++ b/Week 03/id_078/LeetCode_74_078.java @@ -0,0 +1,51 @@ +//дһЧ㷨ж m x n УǷһĿֵþԣ +// +// +// ÿеҰС +// ÿеĵһǰһеһ +// +// +// ʾ 1: +// +// : +//matrix = [ +// [1, 3, 5, 7], +// [10, 11, 16, 20], +// [23, 30, 34, 50] +//] +//target = 3 +//: true +// +// +// ʾ 2: +// +// : +//matrix = [ +// [1, 3, 5, 7], +// [10, 11, 16, 20], +// [23, 30, 34, 50] +//] +//target = 13 +//: false +// Related Topics ֲ + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public boolean searchMatrix(int[][] matrix, int target) { + if(matrix == null || matrix.length == 0) + return false; + int row = 0, col = matrix[0].length - 1; + while (row < matrix.length && col >= 0) { + if (target == matrix[row][col]) + return true; + else if (target > matrix[row][col]) + row++; + else + col--; + } + return false; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_083/Leetcode122.java b/Week 03/id_083/Leetcode122.java new file mode 100644 index 000000000..a9beaf6b5 --- /dev/null +++ b/Week 03/id_083/Leetcode122.java @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode id=122 lang=java + * + * [122] Best Time to Buy and Sell Stock II + */ + +// @lc code=start +class Solution { + public int maxProfit(int[] prices) { + int i = 0 ; + if(prices.length == 0) + return 0; + int valley = prices[0]; + int peak = prices[0]; + int maxProfit = 0; + while(i=prices[i+1]){ + i++; + } + valley = prices[i]; + while(i> findLadders(String beginWord, String endWord, List wordList) { + + List> ans = new ArrayList<>(); + if(!wordList.contains(endWord)){ + return ans; + } + bfs(beginWord,endWord,wordList,ans); + return ans; + } + + public void bfs(String beginWord,String endWord,ListwordList,List>ans){ + Queue> queue = new LinkedList(); + List path = new ArrayList<>(); + path.add(beginWord); + queue.offer(path); + boolean isFound = false; + Set dict = new HashSet<>(wordList); + Set visited = new HashSet<>(); + visited.add(beginWord); + + while(!queue.isEmpty()){ + int size = queue.size(); + Set subVisited = new HashSet<>(); + for(int j=0;j p = queue.poll(); + String temp = p.get(p.size() -1);//得到当前路径的末尾单词 + ArrayList neighbors = getNeighbors(temp,dict); + for(String neighbor:neighbors) { + if(!visited.contains(neighbor)){//只对没访问过的单词 + if(neighbor.equals(endWord)){//到达词尾 + isFound = true; + p.add(neighbor); + ans.add(new ArrayList(p)); + p.remove(p.size() - 1); + } + p.add(neighbor);//加入当前单词 + queue.offer(new ArrayList(p)); + p.remove(p.size()-1); + subVisited.add(neighbor); + } + } + } + visited.addAll(subVisited); + if(isFound){ + break; + } + } + } + + private ArrayList getNeighbors(String node,Setdict){ + ArrayList res = new ArrayList(); + char chs[] = node.toCharArray(); + for(char ch ='a';ch<='z';ch++ ){ + for(int i=0;i wordList) { + + int L = beginWord.length(); + HashMap> allComboDict = new HashMap>(); + + wordList.forEach( + word ->{ + for(int i=0;i transformations = allComboDict.getOrDefault(newWord,new ArrayList()); + transformations.add(word); + allComboDict.put(newWord,transformations); + } + }); + + + //Queue for BFS + Queue> Q = new LinkedList>(); + Q.add(new Pair(beginWord,1)); + + HashMapvisited = new HashMap(); + visited.put(beginWord,true); + + while(!Q.isEmpty()){ + Pair node = Q.remove(); + String word = node.getKey(); + int level = node.getValue(); + for(int i=0;i())){ + if(adjacentWord.equals(endWord)){ + return level +1; + } + if(!visited.containsKey(adjacentWord)){ + visited.put(adjacentWord,true); + Q.add(new Pair(adjacentWord,level+1)); + } + } + } + } + return 0 ; + } + +} +// @lc code=end + diff --git a/Week 03/id_083/Leetcode200.java b/Week 03/id_083/Leetcode200.java new file mode 100644 index 000000000..7e7616bac --- /dev/null +++ b/Week 03/id_083/Leetcode200.java @@ -0,0 +1,53 @@ +import sun.net.www.content.text.plain; + +/* + * @lc app=leetcode id=200 lang=java + * + * [200] Number of Islands + */ + +// @lc code=start +class Solution { + private static final int[][] directions = {{-1,0},{0,-1},{1,0},{0,1}}; + private boolean[][]marked; + private int rows; + private int cols; + private char[][]grid; + + public int numIslands(char[][] grid) { + rows = grid.length; + if(rows == 0) + return 0; + cols = grid[0].length; + this.grid = grid; + marked = new boolean[rows][cols]; + int count = 0; + for(int i=0;i=0 && x=0 && y=0;i--){ + if(i + nums[i] >= lastPos){ + lastPos = i; + } + } + return lastPos == 0; + } +} +// @lc code=end + diff --git a/Week 03/id_083/Leetcode860.java b/Week 03/id_083/Leetcode860.java new file mode 100644 index 000000000..a4ed15a1a --- /dev/null +++ b/Week 03/id_083/Leetcode860.java @@ -0,0 +1,34 @@ +/* + * @lc app=leetcode id=860 lang=java + * + * [860] Lemonade Change + */ + +// @lc code=start +class Solution { + public boolean lemonadeChange(int[] bills) { + int five = 0, ten = 0; + for(int bill:bills){ + if(bill == 5){//pay5$ + five++; + }else if(bill ==10){//pay10$ + if(five == 0) + return false; + five--; + ten++; + }else{//pay20$ + if(five>0 && ten>0){ + five --; + ten --; + }else if(five>=3){ + five -=3; + }else{ + return false; + } + } + } + return true; + } +} +// @lc code=end + diff --git a/Week 03/id_093/search_a_2d_matrix74.java b/Week 03/id_093/search_a_2d_matrix74.java new file mode 100644 index 000000000..b6e109244 --- /dev/null +++ b/Week 03/id_093/search_a_2d_matrix74.java @@ -0,0 +1,27 @@ +class search_a_2d_matrix74{ + + public static boolean search_a_2d_matrix74(int[][] matrix, int target){ + //第一步,处理空集或者null的特殊情况 + if (matrix == null || matrix.length ==0) return false; + //定义矩阵的行列和左右指针 + int m = matrix.length, n = matrix[0].length; + int left = 0, right = m*n-1; + //当首末指针不相交时,执行下列程序(整个和二分法模板很像,主要是行列,还有矩阵中mid所在点的处理) + while (left <= right){ + int mid = (left + right) / 2; + //除以列可以得到行数m1,除以列求余可以列数n1。 + int position = matrix[mid/n][mid%n]; + if (target == position) return true; + else if (position > target) right = mid - 1; + if (position < target) left = mid + 1; + } + return false; + } + + public static void main(String[] args){ + int[][] matrix1= {{1,2,3},{4,5,6},{9,10,11}}; + int target1 = 7; + System.out.println(search_a_2d_matrix74(matrix1,target1)); + } + +} diff --git a/Week 03/id_093/search_in_rotated_array33.java b/Week 03/id_093/search_in_rotated_array33.java new file mode 100644 index 000000000..d03f41e45 --- /dev/null +++ b/Week 03/id_093/search_in_rotated_array33.java @@ -0,0 +1,43 @@ +class search_in_rotated_array33{ + + public static int search_in_rotated_array33(int[] nums, int target){ + //第一步,处理空集或者null的特殊情况 + if (nums == null || nums.length ==0){ + return -1; + } + //定义首末指针 + int left = 0; + int right = nums.length - 1; + //当首末指针不相交时, + while (left <= right){ + //中位数为首末指针的中间,如果中位数等于目标值,直接就输出结果的下标/指针mid; + int mid = (right + left) / 2; + if (nums[mid] == target) return mid; + //如果首指针的值不大于中位数的值(该区间内无旋转) + if (nums[left] <= nums[mid]) { + //如果target值在左侧区间 + if(target >= nums[left] && target < nums[mid]) + //那么把末指针移到中间指针的左方一格 + right = mid - 1; + //如果target值在右侧区间,那把左侧指针移到中间指针右侧一格 + else left = mid + 1; + } + else { + //如果target在中右侧区间 + if (target <= nums[right] && target > nums[mid]) + //把首指针移到中间指针右侧 + left = mid + 1; + //如果target在左侧区间,把末指针移动到中间偏左一格 + else right = mid - 1; + } + } + return -1; + } + + public static void main(String[] args){ + int[] nums1 = {5,6,9,1,2,3}; + int target1 = 8; + System.out.println(search_in_rotated_array33(nums1,target1)); + } + +} diff --git a/Week 03/id_098/LeetCode_127_098.java b/Week 03/id_098/LeetCode_127_098.java new file mode 100644 index 000000000..5cac154f0 --- /dev/null +++ b/Week 03/id_098/LeetCode_127_098.java @@ -0,0 +1,137 @@ +// 最快解法解析 利用双端 BFS 双向交替遍历可能性 +class Solution { + public List> findLadders(String beginWord, String endWord, List wordList) { + //结果集 + List> result =new ArrayList<>(); + //将字典转成hashset便于用于contains判断是否存在 + Set words =new HashSet<>(wordList); + //当结尾词不在字典集中,返回空 + if (!words.contains(endWord)) + return result; + //自顶向下查找集(前向集) + Set start =new HashSet<>(); + //将开始词放入前向集 + start.add(beginWord); + //自底向上查找集(后向集) + Set end =new HashSet<>(); + //将结束词放入后向集 + end.add(endWord); + //利用双端BFS建树 + //严格来说不是颗完整树,是各种可能的一个路径片段,最后用深度优先遍历将路径片段串联起来完成输出 + Map> map_tree=new HashMap<>(); + //如果双端建树,失败则直接返回 + if (!DoubleBuild_bfs(start,end,map_tree,true,words)) + return result; + //利用深度优先遍历建里的树,返回结果 + DFS0(map_tree,result,beginWord,new LinkedList(),endWord); + return result; + + } + + + /** + *todo 核心 : bfs 双向搜索建树; + * boolean b 标志是正向搜索还是反向搜索的标志位 + */ + private boolean DoubleBuild_bfs(Set start, Set end, Map> map_tree, boolean b,Set words) { + + boolean success=false; + //将遍历到的词从字典集中删除 + words.removeAll(start); + //需要拓展可能性的集为空则返回 + if (start.size()==0) + return false; + //下一层的搜索集合 + Set nextCeng =new HashSet<>(); + //向下一层搜索 + for (String str: start) { + char[] c=str.toCharArray(); + //单词从左到右循环替换每一个字母 + for (int j = 0; j ()); + map_tree.get(key).add(value); + + // 如果该词在对向的搜索集中出现,则证明找到解答 + if (end.contains(s)) + success=true; + } + } + //将原来字符替换回去 + c[j]=c0; + } + } + if (success) + //如果找到解,结束递归返回上一层 + return true; + else{ + //当搜索层单词个数大于对向层时,用对向层的搜索集进行拓展搜索,否则还按当前方向拓展搜索 + if (nextCeng.size()>end.size()) + return DoubleBuild_bfs(end,nextCeng,map_tree,!b,words); + else + return DoubleBuild_bfs(nextCeng,end,map_tree,b,words); + } + + } + + //深度优先遍历树 + private void DFS0(Map> map_tree, List> result,String begin,LinkedList list,String end) { + //将遍历词加入临时栈 + list.addLast(begin); + //当到达终了词时,将遍历路径加入结果集 + if (begin.equals(end)){ + result.add(new ArrayList<>(list)); + } + //当遍历词不在树中,则回退到上一个节点 + if (!map_tree.containsKey(begin)){ + list.removeLast(); + return; + } + //深度优先遍历 + List next=map_tree.get(begin); + //找到该节点的下面所有节点一直遍历至终点词位置 + for (String str : next) { + DFS0(map_tree,result,str,list,end); + } + //回退到上一个节点 + list.removeLast(); + } + +} + +/* 曾经尝试了一下对该算法进行改进 + 在 ["hot","dot","dog","lot","log","cog"] 查找出现的所有字母 以减少 for (char i = 'a'; i <='z'; i++) + 这里的循环次数 + Set charset = new HashSet(); + for (int i=0; i < wordList.size(); i++) { + String tmpWord = wordList.get(i); + char[] tmpChars = tmpWord.toCharArray(); + for (int j = 0; j < tmpChars.length; j++) { + if (!charset.contains(tmpChars[j])) + charset.add(tmpChars[j]); + } + } + Character[] chars = new Character[charset.size()]; + charset.toArray(chars); + + 但是实际提交后发现,速度反而慢了很多,究其原因在于当词汇很多的时候找出上段代码本身就会耗时很高 + */ \ No newline at end of file diff --git a/Week 03/id_098/LeetCode_153_098.java b/Week 03/id_098/LeetCode_153_098.java new file mode 100644 index 000000000..0b67d276b --- /dev/null +++ b/Week 03/id_098/LeetCode_153_098.java @@ -0,0 +1,15 @@ +//强上二分查找法 +class Solution { + public int findMin(int[] nums) { + int left = 0; + int right = nums.length - 1; + + while(left < right){ + int mid = (left + right) / 2; + if(nums[mid] > nums[right]) left = mid + 1; + else right = mid; + } + return nums[left]; + + } +} \ No newline at end of file diff --git a/Week 03/id_098/LeetCode_45_098.java b/Week 03/id_098/LeetCode_45_098.java new file mode 100644 index 000000000..fece493d0 --- /dev/null +++ b/Week 03/id_098/LeetCode_45_098.java @@ -0,0 +1,30 @@ +//仿照老师55题贪心算法,最优解解析 +class Solution { + public int jump(int[] nums) { + + int n = nums.length; + + if(n < 2) return 0; + //上一步最远长度下标 + int pre_max_idx = nums[0]; + //当前最远长度下标 + int cur_max_idx = nums[0]; + //默认至少跳一步 + int jump_min = 1; + + for(int i = 0;i < n;i++){ + //当前下标 > 当前最远长度下标则需要跳跃, + //同时将当前最远距离更新,因为终点肯定可到达 + //当在最远长度范围内可以不跳 + if(i > cur_max_idx){ + jump_min++; + cur_max_idx = pre_max_idx; + } + //当前可跳下标超过前最远长度时,更新前最远距离下标 + if(pre_max_idx < nums[i] + i) + pre_max_idx = nums[i] + i; + } + return jump_min; + + } +} \ No newline at end of file diff --git a/Week 03/id_103/LeeCode_122_103.java b/Week 03/id_103/LeeCode_122_103.java new file mode 100644 index 000000000..1d6feef18 --- /dev/null +++ b/Week 03/id_103/LeeCode_122_103.java @@ -0,0 +1,26 @@ +package com.homework.week3; + +public class LeeCode_122_103 { + + public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) { + return 0; + } + int i = 0; + int maxProfit = 0; + int less; + int more; + while (i < prices.length - 1) { + while (i < prices.length - 1 && prices[i] >= prices[i + 1]) { + i++; + } + less = prices[i]; + while (i < prices.length - 1 && prices[i] <= prices[i + 1]) { + i++; + } + more = prices[i]; + maxProfit += more - less; + } + return maxProfit; + } +} diff --git a/Week 03/id_103/LeeCode_126_103.java b/Week 03/id_103/LeeCode_126_103.java new file mode 100644 index 000000000..39cdcdff9 --- /dev/null +++ b/Week 03/id_103/LeeCode_126_103.java @@ -0,0 +1,80 @@ +package com.homework.week3; + +import java.util.*; + +public class LeeCode_126_103 { + + public List> findLadders(String beginWord, String endWord, List wordList) { + + Queue queue = new java.util.concurrent.LinkedBlockingQueue<>(); + Map dis = new HashMap<>(); + Map> relation = new HashMap<>(); + + dis.put(beginWord, 0); + queue.add(beginWord); + while (!queue.isEmpty()) { + // 获取未访问节点中,路径最短的节点,所有边的权重为1 + String str = queue.poll(); + int minPath = dis.get(str); + + // 更新与未访问节点中路径最短节点相邻节点的最短路径,同时记录节点的最短路径前一节点 + for (String key : wordList) { + // 判断两个节点是否相邻 + int k = 0; + for (int i = 0; i < key.length(); i++) { + if (str.charAt(i) != key.charAt(i)) { + k++; + } + if (1 < k) { + break; + } + } + // 相邻节点,更新最短路径,同时记录节点的最短路径前一节点 + if (1 == k) { + if (null == relation.get(key) || null == dis.get(key) || minPath + 1 < dis.get(key)) { + List re = new ArrayList<>(); + re.add(str); + relation.put(key, re); + } else { + if (minPath + 1 == dis.get(key)) { + relation.get(key).add(str); + } + } + if (!dis.containsKey(key)) { + queue.add(key); + } + dis.put(key, Math.min(minPath + 1, null == dis.get(key) ? Integer.MAX_VALUE : dis.get(key))); + } + } + + // 剔除已访问过节点 + dis.remove(str); + wordList.remove(str); + } + + List> res = new LinkedList<>(); + LinkedList tmp = new LinkedList<>(); + tmp.addFirst(endWord); + dfs(endWord, tmp, relation, res, beginWord); + + return res; + } + + private void dfs(String cur, LinkedList tmp, Map> relation, List> res, String beginWord) { + if (cur.equals(beginWord)) { + res.add(new LinkedList<>(tmp)); + return; + } + if (!relation.containsKey(cur)) { + return; + } + + List pre = relation.get(cur); + for (int i = 0; i < pre.size(); i++) { + cur = pre.get(i); + tmp.addFirst(cur); + dfs(cur, tmp, relation, res, beginWord); + tmp.removeFirst(); + } + } +} diff --git a/Week 03/id_103/LeeCode_200_103.java b/Week 03/id_103/LeeCode_200_103.java new file mode 100644 index 000000000..c3decee3d --- /dev/null +++ b/Week 03/id_103/LeeCode_200_103.java @@ -0,0 +1,40 @@ +package com.homework.week3; + +public class LeeCode_200_103 { + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0 || grid[0].length == 0) { + return 0; + } + int row = grid.length;//行数 + int column = grid[0].length;//列数 + int count = 0; + for (int i = 0; i < row; i++) { + for (int j = 0; j < column; j++) { + if (grid[i][j] == '1') { + count++; + combine(grid, i, j); + } + } + } + return count; + } + + public static void combine(char[][] grid, int x, int y) { + grid[x][y] = '2'; + if (x > grid.length - 1 && y > grid[0].length - 1) { + return; + } + if (x < grid.length - 1 && grid[x + 1][y] == '1') {//向下 + combine(grid, x + 1, y); + } + if (y < grid[0].length - 1 && grid[x][y + 1] == '1') {//向右 + combine(grid, x, y + 1); + } + if (x > 0 && grid[x - 1][y] == '1') {//向上 + combine(grid, x - 1, y); + } + if (y > 0 && grid[x][y - 1] == '1') {//向左 + combine(grid, x, y - 1); + } + } +} diff --git a/Week 03/id_103/LeeCode_33_103.java b/Week 03/id_103/LeeCode_33_103.java new file mode 100644 index 000000000..14947be36 --- /dev/null +++ b/Week 03/id_103/LeeCode_33_103.java @@ -0,0 +1,31 @@ +package com.homework.week3; + +public class LeeCode_33_103 { + + public int search(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return -1; + } + int low = 0, high = nums.length - 1; + while (low <= high) { + int mid = low + (high - low) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[mid] < nums[high]) { + if (nums[mid] < target && nums[high] >= target) { + low = mid + 1; + } else { + high = mid - 1; + } + } else { + if (nums[mid] > target && nums[low] <= target) { + high = mid - 1; + } else { + low = mid + 1; + } + } + } + return -1; + } +} diff --git a/Week 03/id_103/LeeCode_455_103.java b/Week 03/id_103/LeeCode_455_103.java new file mode 100644 index 000000000..e9db57b2b --- /dev/null +++ b/Week 03/id_103/LeeCode_455_103.java @@ -0,0 +1,24 @@ +package com.homework.week3; + +import java.util.Arrays; + +public class LeeCode_455_103 { + + public int findContentChildren(int[] g, int[] s) { + //首先对两个数组进行排序 + Arrays.sort(g); + Arrays.sort(s); + //孩子数组下标与饼干数组下标 + int gIndex = 0; + int sIndex = 0; + while (gIndex < g.length && sIndex < s.length) { + //说明第sIndex块饼干能满足第gIndex个孩子的胃口 + if (g[gIndex] <= s[sIndex]) { + gIndex++; + } + //寻求第sIndex+1块饼干的满足情况 + sIndex++; + } + return gIndex; + } +} diff --git a/Week 03/id_103/LeeCode_45_103.java b/Week 03/id_103/LeeCode_45_103.java new file mode 100644 index 000000000..e8f4e2c45 --- /dev/null +++ b/Week 03/id_103/LeeCode_45_103.java @@ -0,0 +1,19 @@ +package com.homework.week3; + +//参考官方题解 +public class LeeCode_45_103 { + + public int jump(int[] nums) { + int end = 0; + int maxPosition = 0; + int steps = 0; + for (int i = 0; i < nums.length - 1; i++) { + maxPosition = Math.max(maxPosition, nums[i] + i); + if ( i == end) { + end = maxPosition; + steps++; + } + } + return steps; + } +} diff --git a/Week 03/id_103/LeeCode_860_103.java b/Week 03/id_103/LeeCode_860_103.java new file mode 100644 index 000000000..cb00fa813 --- /dev/null +++ b/Week 03/id_103/LeeCode_860_103.java @@ -0,0 +1,28 @@ +package com.homework.week3; + +public class LeeCode_860_103 { + + public boolean lemonadeChange(int[] bills) { + int count_money[] = {0, 0, 0}; //其中,从前往后分别表示5,10,20的数量。 + for (int bill : bills) { + if (bill == 5) { + count_money[0] += 1; + } else if (bill == 10 && count_money[0] != 0) { + count_money[0] -= 1; + count_money[1] += 1; + } else if (bill == 10 && count_money[0] == 0) { + return false; + } else if (bill == 20 && count_money[1] != 0 && count_money[0] != 0) { + count_money[0] -= 1; + count_money[1] -= 1; + count_money[2] += 1; + } else if (bill == 20 && count_money[0] >= 3) { + count_money[0] -= 3; + count_money[2] += 1; + } else { + return false; + } + } + return true; + } +} diff --git a/Week 03/id_108/LeeCode_033_108.java b/Week 03/id_108/LeeCode_033_108.java new file mode 100644 index 000000000..9ee59b4da --- /dev/null +++ b/Week 03/id_108/LeeCode_033_108.java @@ -0,0 +1,41 @@ +package study; + +public class LeeCode_033_108 { + + public static int SearchNode(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return -1; + } + int left = 0; + int right = nums.length - 1; + int mid; + while (left <= right) { + mid = left + (right - left) / 2; + if (nums[mid] == target) { + return mid; + } + // 前半部分有序,注意此处用小于等于 + if (nums[left] <= nums[mid]) { + // target在前半部分 + if (target >= nums[left] && target < nums[mid]) { + right = mid - 1; + } else { + left = mid + 1; + } + } else { + if (target <= nums[right] && target > nums[mid]) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + } + return -1; + + } + public static void main(String[] args) { + int ret = new LeeCode_033_108().SearchNode(new int[] {4, 5, 6, 7, 0, 1, 2},0); + System.out.println(ret); + } +} diff --git a/Week 03/id_108/LeeCode_860_108.java b/Week 03/id_108/LeeCode_860_108.java new file mode 100644 index 000000000..3955f61b6 --- /dev/null +++ b/Week 03/id_108/LeeCode_860_108.java @@ -0,0 +1,51 @@ +package study; + +/** + * + * @Leecode 860. 柠檬水找零 思路: + * @link https://www.jianshu.com/p/1e1a9a81bbc0 + * 如果收到5块的,就保存起来 + 如果收到10块的,就检查是否有五块的去找零,然后把十块的保存起来 + 关键是收到20的,这里就用到贪心算法。 + 因为10是5的倍数,一张10块的能做的两张5块的也能做,反过来则不行,所以10的优先级要低于5块的。 + 换句话说就是:两张5块的要优于一张10块的。所以我们就要先去检查是否有10块的,然后再去检查5块的 + + 如果是这里的钱数不是5,10,20这种有倍数关系的,而是2,5,9等等这种没什么联系的钱币,就不能用到贪心算法了。 + */ +public class LeeCode_860_108 { + + public static boolean lemonadeChange(int[] bills) { + int five = 0, ten = 0; + for (int bill : bills) { + if (bill == 5) { + five++; + } else if (bill == 10) { + ten++; + five--; + if (five < 0) + return false; + } else { + if (ten > 0) { + ten--; + five--; + if (five < 0) + return false; + } else if (five > 0) { + five = five - 3; ///? + if (five < 0) + return false; + } else { + return false; + } + } + } + return true; + } + + public static void main(String[] args) { + @SuppressWarnings("static-access") + boolean ret = new LeeCode_860_108().lemonadeChange(new int[] {10,10}); + System.out.println(ret); + } + +} diff --git a/Week 03/id_113/LeetCode122.py b/Week 03/id_113/LeetCode122.py new file mode 100644 index 000000000..817f6d8df --- /dev/null +++ b/Week 03/id_113/LeetCode122.py @@ -0,0 +1,8 @@ +class Solution: + def maxProfit(self, prices: List[int]) -> int: + profit = 0 + for i in range(1, len(prices)): + tmp = prices[i] - prices[i - 1] + if tmp > 0: + profit += tmp + return profit diff --git a/Week 03/id_113/LeetCode153.py b/Week 03/id_113/LeetCode153.py new file mode 100644 index 000000000..c03852c4f --- /dev/null +++ b/Week 03/id_113/LeetCode153.py @@ -0,0 +1,30 @@ +class Solution: + # 暴力搜索 + def findMin(self, nums: List[int]) -> int: + min_value = 99999999 + + for value in nums: + if value < min_value: + min_value = value + + return min_value + + # 二分查找 + def findMin(self, nums: List[int]) -> int: + left, right = 0, len(nums) - 1 + + while left < right: + mid = (left + right) // 2 + + if nums[left] >= nums[mid] >= nums[right]: + return nums[right] + elif nums[mid] > nums[right]: + left = mid + 1 + elif nums[left] > nums[mid]: + right = mid + else: + return nums[left] + return nums[left] + + + diff --git a/Week 03/id_113/LeetCode367.py b/Week 03/id_113/LeetCode367.py new file mode 100644 index 000000000..4ac35f203 --- /dev/null +++ b/Week 03/id_113/LeetCode367.py @@ -0,0 +1,19 @@ +class Solution: + def isPerfectSquare(self, num: int) -> bool: + if num == 0: + return False + + left = 1 + right = num + + while left <= right: + mid = left + (right - left) // 2 + + if mid ** 2 == num: + return True + elif mid ** 2 < num: + left = mid + 1 + else: + right = mid - 1 + + return False diff --git a/Week 03/id_113/LeetCode455.py b/Week 03/id_113/LeetCode455.py new file mode 100644 index 000000000..63166c7ae --- /dev/null +++ b/Week 03/id_113/LeetCode455.py @@ -0,0 +1,19 @@ +class Solution: + def findContentChildren(self, g: List[int], s: List[int]) -> int: + res = 0 + + g.sort() + s.sort() + + g_length, s_length = len(g), len(s) + i = j = 0 + + while i < g_length and j < s_length: + if g[i] <= s[j]: + res += 1 + i += 1 + j += 1 + else: + j += 1 + + return res diff --git a/Week 03/id_113/LeetCode69.py b/Week 03/id_113/LeetCode69.py new file mode 100644 index 000000000..2b592d00c --- /dev/null +++ b/Week 03/id_113/LeetCode69.py @@ -0,0 +1,25 @@ +class Solution: + # 二分查找 + def mySqrt(self, x: int) -> int: + if x == 0 or x == 1: + return x + + left = 1 + right = x + + while left <= right: + mid = left + (right - left) // 2 + + if mid * mid < x: + left = mid + 1 + elif mid * mid > x: + right = mid - 1 + else: + return mid + + return right + + def mySqrt(self, x: int) -> int: + pass + + diff --git a/Week 03/id_113/LeetCode74.py b/Week 03/id_113/LeetCode74.py new file mode 100644 index 000000000..52c2efc70 --- /dev/null +++ b/Week 03/id_113/LeetCode74.py @@ -0,0 +1,53 @@ +class Solution: + # 暴力搜索 + def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: + for row in matrix: + for i in row: + if target == i: + return True + + return False + + # 降维检索 + def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: + temp = [] + for l in matrix: + temp.extend(l) + + left = 0 + right = len(temp) - 1 + + while left <= right: + mid = left + (right - left) // 2 + if temp[mid] == target: + return True + elif temp[mid] < target: + left = mid + 1 + else: + right = mid -1 + + return False + + # 不降维检索: + # 1.从左下角开始,判断target可能在哪一行 + # 2.在该行中进行二分查找 + def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: + if not matrix or not matrix[0]: + return False + + m, n = len(matrix), len((matrix[0])) + + while matrix[m - 1][0] > target and m > 1: + m -= 1 + + left, right = 0, n - 1 + while left <= right: + mid = left + (right - left) // 2 + tmp = matrix[m - 1][mid] + if tmp == target: + return True + elif tmp > target: + right = mid - 1 + else: + left = mid + 1 + return False diff --git a/Week 03/id_113/LeetCode860.py b/Week 03/id_113/LeetCode860.py new file mode 100644 index 000000000..775feda88 --- /dev/null +++ b/Week 03/id_113/LeetCode860.py @@ -0,0 +1,22 @@ +class Solution: + def lemonadeChange(self, bills: List[int]) -> bool: + five = ten = 0 + + for bill in bills: + if bill == 5: + five += 1 + elif bill == 10: + if not five: + return False + five -= 1 + ten += 1 + else: + if ten and five: + ten -= 1 + five -= 1 + elif five >= 3: + five -= 3 + else: + return False + + return True diff --git a/Week 03/id_113/LeetCode874.py b/Week 03/id_113/LeetCode874.py new file mode 100644 index 000000000..cda30557f --- /dev/null +++ b/Week 03/id_113/LeetCode874.py @@ -0,0 +1,21 @@ +class Solution(object): + def robotSim(self, commands, obstacles): + dx = [0, 1, 0, -1] + dy = [1, 0, -1, 0] + x = y = di = 0 + obstacle_set = set(map(tuple, obstacles)) + ans = 0 + + for cmd in commands: + if cmd == -2: #left + di = (di - 1) % 4 + elif cmd == -1: #right + di = (di + 1) % 4 + else: + for k in range(cmd): + if (x+dx[di], y+dy[di]) not in obstacle_set: + x += dx[di] + y += dy[di] + ans = max(ans, x*x + y*y) + + return ans \ No newline at end of file diff --git a/Week 03/id_118/LeetCode_126_118.py b/Week 03/id_118/LeetCode_126_118.py new file mode 100644 index 000000000..014210ccb --- /dev/null +++ b/Week 03/id_118/LeetCode_126_118.py @@ -0,0 +1,43 @@ +from collections import defaultdict + + +class Solution(object): + """ + This solution uses collections' defaultdict + """ + def findLadders(self, beginWord, endWord, wordList): + """ + :type beginWord: str + :type endWord: str + :type wordList: List[str] + :rtype: List[List[str]] + """ + wordSet = set([]) + for word in wordList: + wordSet.add(word) + + letters = 'abcdefghijklmnopqrstuvwxyz' + level = set([beginWord]) + + parents = defaultdict(set) + + while level and endWord not in parents: + next_level = defaultdict(set) + for word in level: + for i in range(len(beginWord)): + p1 = word[:i] + p2 = word[i + 1:] + for j in letters: + # accelerate + if word[i] != j: + childWord = p1 + j + p2 + if childWord in wordSet and childWord not in parents: + next_level[childWord].add(word) + level = next_level + parents.update(next_level) + + res = [[endWord]] + while res and res[0][0] != beginWord: + res = [[p] + r for r in res for p in parents[r[0]]] + + return res \ No newline at end of file diff --git a/Week 03/id_118/LeetCode_127_118.py b/Week 03/id_118/LeetCode_127_118.py new file mode 100644 index 000000000..5898c6254 --- /dev/null +++ b/Week 03/id_118/LeetCode_127_118.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +# Created by Isaac Zhou at 10/31/2019 +from collections import deque +from typing import List + + +class Solution1: + """ + Brute Force: Will TLE + """ + + def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: + queue = [(beginWord, 1)] + visited = set() + letters = "abcdefghijklmnopqrstuvwxyz" + + while queue: + word, dist = queue.pop(0) + + if word == endWord: + return dist + + for i in range(len(word)): + for j in letters: + tmp = word[:i] + j + word[i + 1:] + if tmp not in visited and tmp in wordList: + visited.add(tmp) + queue.append([tmp, dist + 1]) + return 0 + + +class Solution2: + """ + This solution uses a hashmap to store all the state spaces + Use more space for less time + """ + + def ladderLength(self, beginWord, endWord, wordList): + """ + main method body + :param beginWord: word to start + :param endWord: target end word + :param wordList: word lists + :return: steps + """ + if (not beginWord) or (not endWord) or (not wordList): + return 0 + state_d = self._construct_dict(wordList) + return self.bfs(beginWord, endWord, state_d) + + def _construct_dict(self, wordList): + """ + convert wordList into a neighbor dictionary of list + :param wordList: + :return: + """ + res = {} + for w in wordList: + for i in range(len(w)): + tmp_word = w[:i] + "_" + w[i + 1:] + res[tmp_word] = res.get(tmp_word, []) + [w] + return res + + def bfs(self, beginWord, endWord, state_d): + """ + bfs + :param beginWord: + :param endWord: + :param state_d: + :return: + """ + queue, visited = deque([(beginWord, 1)]), set() + while queue: + word, steps = queue.popleft() + if word == endWord: + return steps + if word not in visited: + visited.add(word) + for i in range(len(word)): + tmp_word = word[:i] + "_" + word[i + 1:] + neighbor_words = state_d.get(tmp_word, []) + for nw in neighbor_words: + if nw not in visited: + queue.append((nw, steps+1)) + return 0 diff --git a/Week 03/id_118/LeetCode_200_118.py b/Week 03/id_118/LeetCode_200_118.py new file mode 100644 index 000000000..21c64471f --- /dev/null +++ b/Week 03/id_118/LeetCode_200_118.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Created by Isaac Zhou at 11/3/2019 +from collections import deque +from typing import List + + +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + if not grid: + return 0 + count = 0 + # create a logic grid, initially all False + check = [[False for _ in range(len(grid[0]))] for _ in range(len(grid))] + + for i in range(len(grid)): + for j in range(len(grid[0])): + if grid[i][j] == "1" and not check[i][j]: + count += 1 + self.helper(grid, check, i, j) + return count + + def helper(self, grid, check, i, j): + queue = deque([(i, j)]) + while queue: + i, j = queue.popleft() + if 0 <= i < len(grid) and 0 <= j < len(grid[0]) and grid[i][ + j] == "1" and not check[i][j]: + check[i][j] = True + queue.extend([(i - 1, j), (i + 1, j), (i, j - 1), (i, j + 1)]) diff --git a/Week 03/id_118/LeetCode_33_118.py b/Week 03/id_118/LeetCode_33_118.py new file mode 100644 index 000000000..01d8ef05b --- /dev/null +++ b/Week 03/id_118/LeetCode_33_118.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Created by Isaac Zhou at 11/3/2019 +from typing import List + + +class Solution: + def search(self, nums: List[int], target: int) -> int: + if not nums: + return -1 + + l, r = 0, len(nums) - 1 + while l <= r: + mid = l + (r - l) // 2 + if target == nums[mid]: + return mid + + if nums[l] <= nums[mid]: + if nums[l] <= target <= nums[mid]: + r = mid - 1 + else: + l = mid + 1 + else: + if nums[mid] <= target <= nums[r]: + l = mid + 1 + else: + r = mid - 1 + + return -1 diff --git a/Week 03/id_118/LeetCode_455_118.py b/Week 03/id_118/LeetCode_455_118.py new file mode 100644 index 000000000..503789a6b --- /dev/null +++ b/Week 03/id_118/LeetCode_455_118.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Created by Isaac Zhou at 10/31/2019 +from typing import List + + +class Solution: + def findContentChildren(self, g: List[int], s: List[int]) -> int: + g, s = sorted(g, reverse=True), sorted(s, reverse=True) + + res, g_i, s_i = 0, 0, 0 + + while g_i < len(g) and s_i < len(s): + if g[g_i] <= s[s_i]: + res += 1 + g_i += 1 + s_i += 1 + else: + g_i += 1 + + return res diff --git a/Week 03/id_118/LeetCode_69_118.py b/Week 03/id_118/LeetCode_69_118.py new file mode 100644 index 000000000..084c98bf7 --- /dev/null +++ b/Week 03/id_118/LeetCode_69_118.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Created by Isaac Zhou at 10/31/2019 + +class Solution: + """ + This is the binary search solution + Time: O(logn) + Space: O(1) + """ + + def mySqrt(self, x: int) -> int: + # edge case + if x <= 1: + return x + l, r = 0, x + while l < r: + mid = l + (r - l) // 2 + if mid ** 2 <= x < (mid + 1) ** 2: + return mid + elif x < mid ** 2: + r = mid + else: + l = mid + 1 + + +class Solution2: + """ + This is Integer Newton solution + will have TLE issue + """ + + def mySqrt(self, x: int) -> int: + r = x + while x < r ** 2: + r = (r + x / r) / 2 + return int(r) diff --git a/Week 03/id_123/LeetCode_153_123.c b/Week 03/id_123/LeetCode_153_123.c new file mode 100644 index 000000000..d841e5430 --- /dev/null +++ b/Week 03/id_123/LeetCode_153_123.c @@ -0,0 +1,28 @@ +// int findMin(int* nums, int numsSize){ +// int left = 0; +// int right = numsSize -1; +// while(leftnums[i+1]) + return nums[i+1]; + } + return -1; + +} \ No newline at end of file diff --git a/Week 03/id_123/LeetCode_33_123.c b/Week 03/id_123/LeetCode_33_123.c new file mode 100644 index 000000000..04cf2917c --- /dev/null +++ b/Week 03/id_123/LeetCode_33_123.c @@ -0,0 +1,33 @@ +int search(int* nums, int numsSize, int target){ + if(numsSize==0) + return -1; + int left = 0; + int right = numsSize -1; + while(left<=right){ + int mid = left+(right-left)/2; + printf("%d,%d,\n",left,mid); + if(nums[mid]==target) + return mid; + if(nums[left]==target) + return left; + if(nums[right]==target) + return right; + #[left,mid] + if(nums[left]target||nums[mid]target) + right=mid-1; + else + left=mid+1; + } + } + + return -1; + +} \ No newline at end of file diff --git a/Week 03/id_123/LeetCode_74_123.c b/Week 03/id_123/LeetCode_74_123.c new file mode 100644 index 000000000..822451b90 --- /dev/null +++ b/Week 03/id_123/LeetCode_74_123.c @@ -0,0 +1,12 @@ +bool searchMatrix(int** matrix, int matrixSize, int* matrixColSize, int target){ + printf("%d",matrixSize); + for(int i=0; i> LevelOrder(TreeNode root) + { + var result = new List>(); + + var queue = new List>(); + queue.Add(new Tuple(root, 0)); + while (queue.Count() > 0) + { + var node = queue[0].Item1; + var level = queue[0].Item2; + queue.RemoveAt(0); + if (node is null) + { + continue; + } + if (result.Count() == level) + { + result.Add(new List()); + } + result[level++].Add(node.val); + if (node.left != null) + queue.Add(new Tuple(node.left, level)); + if (node.right != null) + queue.Add(new Tuple(node.right, level)); + } + return result; + } +} + + + +//BFS without passing level +public class Solution +{ + public IList> LevelOrder(TreeNode root) + { + var result = new List>(); + if (root is null) + { + return result; + } + var queue = new List(); + queue.Add(root); + + while (queue.Count() > 0) + { + var levelResult = new List(); + var levelQueue = new List(); + while (queue.Count() > 0) + { + var node = queue[0]; + queue.RemoveAt(0); + + levelResult.Add(node.val); + if (node.left != null) levelQueue.Add(node.left); + if (node.right != null) levelQueue.Add(node.right); + } + queue.AddRange(levelQueue); + result.Add(levelResult); + } + return result; + } +} + +//DFS recursion +public class Solution +{ + public IList> LevelOrder(TreeNode root) + { + var result = new List>(); + _DFS(root, 0, result); + return result; + } + + + private void _DFS(TreeNode node, int level, IList> result) + { + if (node is null) + { + return; + } + + if (result.Count() == level) + { + result.Add(new List()); + } + + result[level++].Add(node.val); + + _DFS(node.left, level, result); + _DFS(node.right, level, result); + } +} + diff --git a/Week 03/id_128/LeetCode_122_128.cs b/Week 03/id_128/LeetCode_122_128.cs new file mode 100644 index 000000000..ce3ad7c60 --- /dev/null +++ b/Week 03/id_128/LeetCode_122_128.cs @@ -0,0 +1,44 @@ +//buy and sale on each day when price is higher than prior day +public class Solution +{ + public int MaxProfit(int[] prices) + { + var profit = 0; + for (int i = 1; i < prices.Length; i++) + { + if (prices[i - 1] < prices[i]) + { + profit += prices[i] - prices[i - 1]; + } + } + return profit; + } +} + + +//peak - valley +public class Solution +{ + public int MaxProfit(int[] prices) + { + if (prices.Length <= 1) return 0; + var profit = 0; + var valley = prices[0]; + var peak = prices[0]; + for (int i = 1; i < prices.Length; i++) + { + if (prices[i] > prices[i - 1]) + { + peak = prices[i]; + } + else + { + profit += peak - valley; + valley = prices[i]; + peak = prices[i]; + } + } + profit += peak - valley; + return profit; + } +} \ No newline at end of file diff --git a/Week 03/id_128/LeetCode_153_128.cs b/Week 03/id_128/LeetCode_153_128.cs new file mode 100644 index 000000000..fb6ef6726 --- /dev/null +++ b/Week 03/id_128/LeetCode_153_128.cs @@ -0,0 +1,42 @@ +/* + * @lc app=leetcode id=153 lang=csharp + * + * [153] Find Minimum in Rotated Sorted Array + */ + +// @lc code=start +public class Solution +{ + public int FindMin(int[] nums) + { + if (nums.Length <= 0) return -1; + if (nums.Length == 1) return nums[0]; + return nums[FindPivotIndex(nums)]; + } + + private int FindPivotIndex(int[] nums) + { + int start = 0; + int end = nums.Length - 1; + if (nums[start] < nums[end]) return 0; + while (start <= end) + { + int mid = (start + end) / 2; + if (nums[mid + 1] <= nums[mid]) + { + return mid + 1; + } + else if (nums[mid] < nums[start]) + { + end = mid - 1; + } + else + { + start = mid + 1; + } + } + return 0; + } +} +// @lc code=end + diff --git a/Week 03/id_128/LeetCode_200_128.cs b/Week 03/id_128/LeetCode_200_128.cs new file mode 100644 index 000000000..3f4ed49cf --- /dev/null +++ b/Week 03/id_128/LeetCode_200_128.cs @@ -0,0 +1,42 @@ +public class Solution +{ + public int NumIslands(char[][] grid) + { + + var num = 0; + for (int i = 0; i < grid.Length; i++) + { + for (int j = 0; j < grid[i].Length; j++) + { + if (grid[i][j] == '1') + { + SinkIsland(i, j, grid); + num++; + } + } + } + return num; + } + + private void SinkIsland(int a, int b, char[][] grid) + { + var queue = new Queue>(); + queue.Enqueue(new Tuple(a, b)); + + while (queue.Count() > 0) + { + var spot = queue.Dequeue(); + var i = spot.Item1; + var j = spot.Item2; + if (i >= 0 && j >= 0 && i < grid.Length && j < grid[i].Length && grid[i][j] == '1') + { + grid[i][j] = '0'; + queue.Enqueue(new Tuple(i + 1, j)); + queue.Enqueue(new Tuple(i - 1, j)); + queue.Enqueue(new Tuple(i, j + 1)); + queue.Enqueue(new Tuple(i, j - 1)); + + } + } + } +} \ No newline at end of file diff --git a/Week 03/id_128/LeetCode_22_128.cs b/Week 03/id_128/LeetCode_22_128.cs new file mode 100644 index 000000000..b5856f275 --- /dev/null +++ b/Week 03/id_128/LeetCode_22_128.cs @@ -0,0 +1,55 @@ +//dfs +public class Solution +{ + public IList GenerateParenthesis(int n) + { + var result = new List(); + _GenerateParentheis(0, 0, n, "", result); + return result; + } + + + private void _GenerateParentheis(int i, int j, int n, string tempStr, IList result) + { + if (j >= n) + { + result.Add(tempStr); + return; + } + + if (i < n) + { + _GenerateParentheis(i + 1, j, n, tempStr + '(', result); + } + if (j < i) + { + _GenerateParentheis(i, j + 1, n, tempStr + ')', result); + } + } +} + +//bfs +public class Solution +{ + public IList GenerateParenthesis(int n) + { + var result = new List(); + + var queue = new List>(); + queue.Add(new Tuple("(", 1, 0)); + + while (queue.Count() > 0) + { + var cur = queue[0]; + queue.RemoveAt(0); + if (cur.Item3 >= n) + { + result.Add(cur.Item1); + } + if (cur.Item2 < n) queue.Add(new Tuple(cur.Item1 + "(", cur.Item2 + 1, cur.Item3)); + if (cur.Item3 < cur.Item2) queue.Add(new Tuple(cur.Item1 + ")", cur.Item2, cur.Item3 + 1)); + } + + return result; + } +} diff --git a/Week 03/id_128/LeetCode_33_128.cs b/Week 03/id_128/LeetCode_33_128.cs new file mode 100644 index 000000000..544a139ad --- /dev/null +++ b/Week 03/id_128/LeetCode_33_128.cs @@ -0,0 +1,143 @@ +//一遍二分查找 +public class Solution +{ + public int Search(int[] nums, int target) + { + if (nums.Length == 0) + return -1; + if (nums[0] <= nums[nums.Length - 1]) + return BinarySearch(nums, target, 0, nums.Length - 1); + else + return BinarySearch1(nums, target, 0, nums.Length - 1); + + + } + + public int BinarySearch(int[] nums, int target, int start, int end) + { + while (start <= end) + { + int mid = (start + end) / 2; + if (nums[mid] > target) + { + end = mid - 1; + } + else if (nums[mid] < target) + { + start = mid + 1; + } + else + { + return mid; + } + } + return -1; + } + + public int BinarySearch1(int[] nums, int target, int start, int end) + { + if (nums[start] < nums[end]) + return 0; + int lastValue = nums[end]; + while (start <= end) + { + int mid = (start + end) / 2; + if (nums[mid] == target) + return mid; + if (nums[mid] > lastValue && target > lastValue) + { + if (target < nums[mid]) + end = mid - 1; + else + start = mid + 1; + } + else if (nums[mid] > lastValue && target <= lastValue) + { + start = mid + 1; + } + else if (nums[mid] < lastValue && target > lastValue) + { + end = mid - 1; + } + else if (nums[mid] <= lastValue && target <= lastValue) + { + if (target < nums[mid]) + end = mid - 1; + else + start = mid + 1; + } + else + { + return -1; + } + } + return -1; + } +} + + + + + +//先找pivot point 即2遍二分查找 +public class Solution +{ + public int Search(int[] nums, int target) + { + if (nums.Length == 0) return -1; + if (nums.Length == 1) return nums[0] == target ? 0 : -1; + int pivotIndex = FindPivotIndex(nums); + if (nums[pivotIndex] == target) + return pivotIndex; + if (pivotIndex == 0) + return BinarySearch(nums, target, 0, nums.Length - 1); + if (target >= nums[0]) + return BinarySearch(nums, target, 0, pivotIndex - 1); + return BinarySearch(nums, target, pivotIndex, nums.Length - 1); + } + + private int FindPivotIndex(int[] nums) + { + int start = 0; + int end = nums.Length - 1; + if (nums[start] < nums[end]) return 0; + while (start <= end) + { + int mid = (start + end) / 2; + if (nums[mid + 1] <= nums[mid]) + { + return mid + 1; + } + else if (nums[mid] < nums[start]) + { + end = mid - 1; + } + else + { + start = mid + 1; + } + } + return 0; + } + + public int BinarySearch(int[] nums, int target, int start, int end) + { + while (start <= end) + { + int mid = (start + end) / 2; + if (nums[mid] > target) + { + end = mid - 1; + } + else if (nums[mid] < target) + { + start = mid + 1; + } + else + { + return mid; + } + } + return -1; + } +} \ No newline at end of file diff --git a/Week 03/id_128/LeetCode_433_128.cs b/Week 03/id_128/LeetCode_433_128.cs new file mode 100644 index 000000000..19ec0d4fc --- /dev/null +++ b/Week 03/id_128/LeetCode_433_128.cs @@ -0,0 +1,49 @@ + +public class Solution +{ + public int MinMutation(string start, string end, string[] bank) + { + if (start.Equals(end)) return 0; + + var geneBank = new HashSet(); + foreach (var gene in bank) + { + geneBank.Add(gene); + } + + char[] nucleotides = new char[] { 'A', 'C', 'G', 'T' }; + int level = 0; + + var queue = new Queue(); + queue.Enqueue(start); + + while (queue.Count() > 0) + { + int size = queue.Count(); + while (size-- > 0) + { + string currGene = queue.Dequeue(); + if (currGene.Equals(end)) return level; + var charArray = currGene.ToCharArray(); + for (int i = 0; i < charArray.Length; i++) + { + char temp = charArray[i]; + foreach (char nucleotide in nucleotides) + { + charArray[i] = nucleotide; + var mutation = new String(charArray); + if (geneBank.Contains(mutation)) + { + geneBank.Remove(mutation); + queue.Enqueue(mutation); + } + } + charArray[i] = temp; + } + } + level++; + } + return -1; + } +} + diff --git a/Week 03/id_128/LeetCode_515_128.cs b/Week 03/id_128/LeetCode_515_128.cs new file mode 100644 index 000000000..fdea83e81 --- /dev/null +++ b/Week 03/id_128/LeetCode_515_128.cs @@ -0,0 +1,58 @@ +//bfs +public class Solution +{ + public IList LargestValues(TreeNode root) + { + if (root is null) return new List(); + return _BFS(root); + } + + private IList _BFS(TreeNode root) + { + var result = new List(); + var queue = new Queue(); + queue.Enqueue(root); + var level = 0; + + while (queue.Count() > 0) + { + var size = queue.Count(); + while (size-- > 0) + { + var node = queue.Dequeue(); + if (level == result.Count()) result.Add(Int32.MinValue); + if (node.val > result[level]) result[level] = node.val; + if (node.left != null) queue.Enqueue(node.left); + if (node.right != null) queue.Enqueue(node.right); + } + level += 1; + } + return result; + } +} + + +//dfs +public class Solution +{ + public IList LargestValues(TreeNode root) + { + var result = new List(); + if (root is null) return new List(); + _DFS(root, 0, result); + return result; + } + + private IList _DFS(TreeNode node, int level, IList result) + { + if (node is null) + return; + if (result.Count() == level) + result.Add(int32.MinValue); + if (node.val > result[level]) + result[level++] = node.val; + _DFS(node.left, level, result); + _DFS(node.right, level, result); + + } +} \ No newline at end of file diff --git a/Week 03/id_128/LeetCode_529_128.cs b/Week 03/id_128/LeetCode_529_128.cs new file mode 100644 index 000000000..b389ac207 --- /dev/null +++ b/Week 03/id_128/LeetCode_529_128.cs @@ -0,0 +1,52 @@ +//dfs +public class Solution +{ + private int GetMineNumber(int row, int col, int m, int n, char[][] board) + { + int number = 0; + for (int i = -1; i < 2; i++) + { + for (int j = -1; j < 2; j++) + { + if (i == 0 && j == 0) continue; + int r = row + i, c = col + j; + if (r < 0 || r >= m || c < 0 || c < 0 || c >= n) continue; + if (board[r][c] == 'M' || board[r][c] == 'X') number++; + } + } + return number; + } + + public char[][] UpdateBoard(char[][] board, int[] click) + { + int m = board.Length, n = board[0].Length; + int row = click[0], col = click[1]; + + if (board[row][col] == 'M') + { // Mine + board[row][col] = 'X'; + return board; + } + int count = GetMineNumber(row, col, m, n, board); + // If it is not a 'B', stop further DFS. + if (count > 0) + { + board[row][col] = (char)(count + '0'); + return board; + } + // Continue DFS to adjacent cells. + board[row][col] = 'B'; + for (int i = -1; i < 2; i++) + { + for (int j = -1; j < 2; j++) + { + if (i == 0 && j == 0) continue; + int r = row + i, c = col + j; + if (r < 0 || r >= m || c < 0 || c < 0 || c >= n) continue; + if (board[r][c] == 'E') UpdateBoard(board, new int[] { r, c }); + } + } + return board; + } +} + diff --git a/Week 03/id_128/LeetCode_74_128.cs b/Week 03/id_128/LeetCode_74_128.cs new file mode 100644 index 000000000..81b496552 --- /dev/null +++ b/Week 03/id_128/LeetCode_74_128.cs @@ -0,0 +1,24 @@ +public class Solution +{ + public bool SearchMatrix(int[][] matrix, int target) + { + int m = matrix.Length; + if (m == 0) return false; + int n = matrix[0].Length; + int left = 0; + int right = m * n - 1; + + while (left <= right) + { + int midIdx = (left + right) / 2; + int midValue = matrix[midIdx / n][midIdx % n]; + if (midValue == target) + return true; + else if (midValue < target) + left = midIdx + 1; + else + right = midIdx - 1; + } + return false; + } +} \ No newline at end of file diff --git a/Week 03/id_128/NOTE.md b/Week 03/id_128/NOTE.md index a6321d6e2..162625b7c 100644 --- a/Week 03/id_128/NOTE.md +++ b/Week 03/id_128/NOTE.md @@ -1,4 +1,107 @@ # NOTE - +重新学习了斐波那契数列 (爬楼梯) 问题 + +top down 递归with memo 最易想到 +bottom up 的 brute force 和 递归 +然后引申出 迭代形式的 O(1)空间的 斐波那契通常算法 - 由此又学习了尾递归 (线性递归时可以优化为尾递归) +继续深入 即 矩阵幂运算 +f(n) +f(n-1) +| +f(n-1), f(n-2) +f(n-1) +| +1 * f(n-1), 1 * f(n-2) +1* f(n-1), 0 * f(n-2) +| +1, 1 * f(n-1) +1, 0 f(n-2) +| +1, 1 ^(n-1) * f(2) +1, 0 f(1) +| +1, 1 ^(n-1) * f(1) +1, 0 f(0) + +即转化为求 1,1 的 n-1次矩阵幂 然后取左上第一项的值 + 1,0 + +其中 求矩阵幂 又用到了 pow(n,x) (leetcode_50)的快速幂算法 + +快速幂算法 亦可 递归 或 迭代 + + + + + +本周的课程是对上周递归课程在几种细分问题的总结 + +DFS 一般递归 及通过循环与栈配合 模拟递归 +BFS 循环与队列配合的变相递归 + +对图操作时 注意对访问过的节点的判断 + + +贪心算法 + 回束 = 动态规划 + +局部最优解 -> 全局最优解 = 贪心, 局部次优解(通过回束) -> 全局最优解 = 动态规划 + +贪心是思路 - 要证明问题可以通过局部结论得出全局结论 +贪心的技巧 - 如 反向贪心, 需要多做题 增长经验 + +可以应用 二分搜索 的三要素 +1 有序 +2 有上下界 +3 有索引 + + + +question: 使用二分查找,寻找一个半有序数组 [4, 5, 6, 7, 0, 1, 2] 中间无序的地方 + +即寻找 数组中最小值的下标- nums[i] > nums[i+1] + + + +//先找pivot point 即2遍二分查找 +public class Solution +{ + public int SearchPivotPoint(int[] nums) + { + if (nums.Length <= 1) return -1; + return FindPivotIndex(nums); + } + + private int FindPivotIndex(int[] nums) + { + int start = 0; + int end = nums.Length - 1; + if (nums[start] < nums[end]) return 0; + while (start <= end) + { + int mid = (start + end) / 2; + if (nums[mid + 1] <= nums[mid]) + { + return mid + 1; + } + else if (nums[mid] < nums[start]) + { + end = mid - 1; + } + else + { + start = mid + 1; + } + } + return 0; + } +} + + + +遗留内容 + +牛顿迭代法 +并查集 +更多的贪心习题 diff --git a/Week 03/id_133/leetcode_127_133.java b/Week 03/id_133/leetcode_127_133.java new file mode 100644 index 000000000..be77a5170 --- /dev/null +++ b/Week 03/id_133/leetcode_127_133.java @@ -0,0 +1,76 @@ + +//leetcode 题号127 单词接龙 + +import java.io.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; +import java.util.List; +import java.util.ArrayList; + +public class Solution { + + public int ladderLength(String beginWord, String endWord, List wordList) { + + Set beginSet = new HashSet(), endSet = new HashSet(); + + int len = 1; + // int strLen = beginWord.length(); + HashSet visited = new HashSet(); + + beginSet.add(beginWord); + endSet.add(endWord); + while (!beginSet.isEmpty() && !endSet.isEmpty()) { + if (beginSet.size() > endSet.size()) { + Set set = beginSet; + beginSet = endSet; + endSet = set; + } + + Set temp = new HashSet(); + for (String word : beginSet) { + char[] chs = word.toCharArray(); + + for (int i = 0; i < chs.length; i++) { + for (char c = 'a'; c <= 'z'; c++) { + char old = chs[i]; + chs[i] = c; + String target = String.valueOf(chs); + + if (endSet.contains(target)) { + return len + 1; + } + + if (!visited.contains(target) && wordList.contains(target)) { + temp.add(target); + visited.add(target); + } + chs[i] = old; + } + } + } + + beginSet = temp; + len++; + } + + return 0; + } + + public static void main(String[] args) { + + String beginWord = "hit"; + String endWord = "cog"; + List wordList = new ArrayList(); + wordList.add("hot"); + wordList.add("dot"); + wordList.add("dog"); + wordList.add("lot"); + wordList.add("log"); + wordList.add("cog"); + + Solution solution = new Solution(); + int result = solution.ladderLength(beginWord, endWord, wordList); + System.out.println(result); + } +} \ No newline at end of file diff --git a/Week 03/id_133/leetcode_860_133.java b/Week 03/id_133/leetcode_860_133.java new file mode 100644 index 000000000..7711dd79e --- /dev/null +++ b/Week 03/id_133/leetcode_860_133.java @@ -0,0 +1,43 @@ + +//leetcode 题号860 柠檬水找零 + +//国际站看到的这个方法,思路确实很清晰,如下 +/** + * 当用户收到20美元,我们有两种方式找零 + * 1.找三张5美元 + * 2.找一张10美元和一张5美元 + * 这两种第二种是更优的,为什么呢?因为一个原则:手里要尽可能多的留5美元的零钱 + * + * 因此我们有以下判断: + * 1.统计手里的5美元和10美元数量 + * 2.如果顾客给了5美元,则5美元加1 + * 3.如果顾客给了10美元,则10美元加1、5美元减1 + * 4.如果顾客给了20美元,则10美元减1、5美元减1 或 5美元减3 + * 5.同时判断下5美元的数量是否是正数,不是直接返回false + */ + +public class Solution { + + public boolean lemonadeChange(int[] bills) { + int five = 0, ten = 0; + for (int i : bills) { + if (i == 5) five++; + else if (i == 10) {five--; ten++;} + else if (ten > 0) {ten--; five--;} + else five -= 3; + if (five < 0) return false; + } + return true; + } + + public static void main(String[] args) { + + int[] buyMonery = {5,5,10,10,5,20}; + + Solution solution = new Solution(); + + boolean result = solution.lemonadeChange(buyMonery); + + System.out.println(result); + } +} \ No newline at end of file diff --git a/Week 03/id_138/LeetCode_102_138.java b/Week 03/id_138/LeetCode_102_138.java new file mode 100644 index 000000000..16bd6a5bc --- /dev/null +++ b/Week 03/id_138/LeetCode_102_138.java @@ -0,0 +1,41 @@ +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * 二叉树的层次遍历 + * @author L + * + */ +public class LeetCode_102_138 { + + public List> levelOrder(TreeNode root) { + //最终结果输出 + List> res = new ArrayList>(); + if(root == null) + return res; + //历史访问记录 + Queue queue = new LinkedList(); + queue.add(root); + int level = 0; + while(!queue.isEmpty()) { + //每一层初始化一个结果集合保存访问的数据 + res.add(new ArrayList()); + int size = queue.size(); + for(int i=0;iprices.length) + return 0; + int maxSum = 0; + for(int start = day;startprices[start]) { + int profit = maxProfitHelper(prices,i+1)+prices[i]-prices[start]; + if(profit > maxProfit) { + maxProfit = profit; + } + } + } + if(maxProfit > maxSum) { + maxSum = maxProfit; + } + } + + return maxSum; + } + + /** + * 计算峰谷差值 + * @param prices + * @return + */ + public int maxProfit(int[] prices) { + if(prices==null || prices.length==0){ + return 0; + } + int start = 0; + int valley = prices[0];//低谷 + int peak = prices[0];//高峰 + + int max = 0; + while(start< (prices.length-1)) { + while(start=prices[start+1]) {//股票跌值 + start++; + } + valley = prices[start]; + while(startprices[i]) { + sum += prices[i+1]-prices[i]; + } + } + return sum; + } +} diff --git a/Week 03/id_138/LeetCode_127_138.java b/Week 03/id_138/LeetCode_127_138.java new file mode 100644 index 000000000..0d348baef --- /dev/null +++ b/Week 03/id_138/LeetCode_127_138.java @@ -0,0 +1,93 @@ +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; + +/** + * 单词接龙 + * 给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则: + 每次转换只能改变一个字母。 + 转换过程中的中间单词必须是字典中的单词。 + * @author L + * + */ +public class LeetCode_127_138 { + public class Pair{ + + /** + * Key of this Pair. + */ + private K key; + + /** + * Gets the key for this pair. + * @return key for this pair + */ + public K getKey() { return key; } + + /** + * Value of this this Pair. + */ + private V value; + + /** + * Gets the value for this pair. + * @return value for this pair + */ + public V getValue() { return value; } + + /** + * Creates a new pair + * @param key The key for this pair + * @param value The value to use for this pair + */ + public Pair(K key,V value) { + this.key = key; + this.value = value; + } + } + public int ladderLength(String beginWord, String endWord, List wordList) { + int length = beginWord.length(); + Map> dict = new HashMap(); + + wordList.forEach(word->{ + for(int i=0;i values = dict.getOrDefault(key, new ArrayList()); + values.add(word); + dict.put(key, values); + } + }); + + Queue> queue = new LinkedList>(); + queue.add(new Pair(beginWord, 1)); + + Map visited = new HashMap(); + visited.put(beginWord, true); + + while(!queue.isEmpty()) { + Pair pair = queue.remove(); + String word = pair.getKey(); + int level = pair.getValue(); + for(int i=0;i())){ + if(value.equals(endWord)) { + return level+1; + } + + if(!visited.containsKey(value)) { + visited.put(value, true); + queue.add(new Pair(value, level+1)); + } + } + } + } + return 0; + + } +} diff --git a/Week 03/id_138/LeetCode_153_138.java b/Week 03/id_138/LeetCode_153_138.java new file mode 100644 index 000000000..dbfc36112 --- /dev/null +++ b/Week 03/id_138/LeetCode_153_138.java @@ -0,0 +1,35 @@ +/** + * 寻找旋转排序数组中的最小值 + * 假设按照升序排序的数组在预先未知的某个点上进行了旋转。 + ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 + 请找出其中最小的元素。 + * @author L + * + */ +public class LeetCode_153_138 { + public int findMin(int[] nums) { + if(nums.length == 1) { + return nums[0]; + } + int left =0,right = nums.length-1; + if(nums[right] > nums[0]) { + return nums[0]; + } + while(right>=left) { + int mid = left +(right-left)/2; + if(nums[mid]>nums[mid+1]) { + return nums[mid+1]; + } + if(nums[mid-1]>nums[mid]) { + return nums[mid]; + } + if(nums[mid]>nums[0]) { + left = mid+1; + }else { + right = mid-1; + } + } + + return -1; + } +} diff --git a/Week 03/id_138/LeetCode_200_138.java b/Week 03/id_138/LeetCode_200_138.java new file mode 100644 index 000000000..2d6d6124d --- /dev/null +++ b/Week 03/id_138/LeetCode_200_138.java @@ -0,0 +1,38 @@ +/** + * 岛屿数量 + * 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。 + * 你可以假设网格的四个边均被水包围。 + * @author L + * + */ +public class LeetCode_200_138 { + public int numIslands(char[][]grid) { + if(grid == null || grid.length == 0) { + return 0; + } + + int nr = grid.length;//行的长度 + int nc = grid[0].length;//列的长度 + int count = 0; + for(int r=0;r= nr || c >= nc || grid[r][c] == '0') { + return; + } + grid[r][c] = '0'; + dfs(grid,r-1,c,nr,nc);//向上搜索 + dfs(grid,r+1,c,nr,nc);//向下搜索 + dfs(grid,r,c-1,nr,nc);//向左 + dfs(grid,r,c+1,nr,nc);//向右 + } +} diff --git a/Week 03/id_138/LeetCode_322_138.java b/Week 03/id_138/LeetCode_322_138.java new file mode 100644 index 000000000..d183db8b6 --- /dev/null +++ b/Week 03/id_138/LeetCode_322_138.java @@ -0,0 +1,75 @@ +import java.util.Arrays; + +/** + * 零钱兑换 + * 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。 + * @author L + * + */ +public class LeetCode_322_138 { + /** + * 递归-动态规划-自上往下 + * @param coins + * @param amount + * @return + */ + public int coinChange_solution1(int[] coins, int amount) { + if(amount <=0) { + return 0; + } + + return coinChangeHelper(coins, amount, new int[amount]); + } + + private int coinChangeHelper(int[] coins, int amount,int[] count) { + if (amount < 0) + return -1; + if(amount == 0) { + return 0; + } + //最终结果 + if(count[amount-1] != 0) + return count[amount-1]; + + int min = Integer.MAX_VALUE; + for(int coin : coins) { + int rs = coinChangeHelper(coins, amount-coin, count); + if(rs >= 0 && rs < min) { + min = rs+1; + } + } + count[amount-1] = (min == Integer.MAX_VALUE?-1:min); + return count[amount-1]; + } + + /** + * 动态规划-从下到上 + * @param coins + * @param amount + * @return + */ + public int coinChange_solution2(int[] coins, int amount) { + if(amount <=0) { + return 0; + } + int[] dp = new int[amount+1]; + dp[0] = 0; + final int max = Integer.MAX_VALUE; + for(int i=1;i <= amount;i++) { + int cost = max; + + for(int j=0;j>>1; + if(nums[mid] == target) { + return mid; + } + if(nums[mid] <= nums[right-1]) {//可能在mid左边也可能在右边需要继续判断 + //右边是有序的 + if(nums[mid]= 0; i--) {//从右侧开始标记 + int jump = Math.min(i + nums[i], nums.length - 1);//判断(索引i的位置+该位置最大可跳跃值)是否超过数组的长度-1 + for (int j = i + 1; j <= jump; j++) { + if (flag[j] == 1) {//该位置可以跳跃到最后 + flag[i] = 1; + break; + } + } + } + return flag[0] == 1;//第一个位置是否可以呢。 + + } + + /** + * 贪心算法 + * 从右向左循环,对于每个节点检查是否存在一种方式可以到达GOOD的位置(currentPosition+nums[currentPosition]>=leftmostGoodIndex)。 + * 如果可行,则当前索引标记为GOOD,同时这个位置也成为最左侧的GOOD点。一直重复到数组的开头。如果坐标0的元素也可以,则成功。 + */ + public boolean canJump2(int[] nums) { + int last = nums.length-1; + for(int i = last;i>=0;i--) { + if(i+nums[i] >= last) { + last = i; + } + } + return last == 0; + } +} diff --git a/Week 03/id_138/LeetCode_69_138.java b/Week 03/id_138/LeetCode_69_138.java new file mode 100644 index 000000000..f34104b51 --- /dev/null +++ b/Week 03/id_138/LeetCode_69_138.java @@ -0,0 +1,41 @@ +/** + * x的平方根 + * @author L + * + */ +public class LeetCode_69_138 { + /** + * 牛顿迭代法: r = (r+x/r)/2 + * @param x + * @return + */ + public int mySqrt(int x) { + if(x==0) + return 0; + long a = x; + while(a*a>x) { + a = (a+x/a)/2; + } + return (int)a; + } + + /** + * 二分法 + * @param x + * @return + */ + public int mySqut2(int x) { + long left = 0; + long right = Integer.MAX_VALUE; + while(left>>1;//无符号右移1即/2 取中间值 + long square = mid*mid; + if(square > x) { + right = mid-1;//向左收敛 + }else { + left = mid;//向右收敛 + } + } + return (int)left; + } +} diff --git a/Week 03/id_138/LeetCode_860_138.java b/Week 03/id_138/LeetCode_860_138.java new file mode 100644 index 000000000..3e3c9d226 --- /dev/null +++ b/Week 03/id_138/LeetCode_860_138.java @@ -0,0 +1,37 @@ +/** + * 柠檬水找零 + * 在柠檬水摊上,每一杯柠檬水的售价为 5 美元。 + 顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。 + 每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。 + 注意,一开始你手头没有任何零钱。 + 如果你能给每位顾客正确找零,返回 true ,否则返回 false 。 + * @author L + * + */ +public class LeetCode_860_138 { + public boolean lemonadeChange(int[] bills) { + int five = 0; + int ten = 0; + for(int bill : bills) { + if(bill == 5) { + five++; + }else if(bill == 10) { + if(five == 0) {//5美元钞票的个数为0,无法找零 ,返回false + return false; + } + five--; + ten++; + }else if(bill == 20) { + if(five>0 && ten>0) {//收入20bill可以找零 + five--; + ten--; + }else if(five >= 3) {//可以找零 + five = five-3; + }else {//上述两种可以找零的情况不存在 + return false; + } + } + } + return true; + } +} diff --git a/Week 03/id_138/NOTE.md b/Week 03/id_138/NOTE.md index a6321d6e2..2e161c95f 100644 --- a/Week 03/id_138/NOTE.md +++ b/Week 03/id_138/NOTE.md @@ -1,4 +1,101 @@ # NOTE +Week3学习总结 - - +深度优先遍历(DFS)与广度优先遍历(BFS) +1.算法应用的相关数据结构:树 或者 图(图的数据结构本章课程并未介绍,个人了解一下。因为前面说过,树是一种特殊的图。有助于理解DFS与BFS算法的应用场景。) + 图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成。图的遍历即从图中某一顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次。 +2.深度优先搜索(Depth First Search) + 思路:类似树的先序遍历,是树的先序遍历的推广。 + 1)从树的根节点出发,访问树。 + 2)找到刚访问过得节点的第一个未被访问的子节点,访问该节点。以该节点为新节点,重复此步骤,直至刚访问的节点没有未被访问的子节点为止。 + 3)返回前一个访问过得且仍有未被访问的节点的父节点,找到该节点的下一个未被访问的子节点,访问该节点。 + 4)重复步骤2,3,直至所有节点都被访问过。 + DFS代码模板: + + 1)递归方式 + visited = set() #记录已经访问过的节点 + def dfs(node,visited): #递归方法 + if node in visited: #terminater 递归终止条件 + #already visited + return + + visited.add(node)#加入已访问节点 + + #process current node here. + ... + /*通用处理,如果是二叉树,只需要处理左孩子和右孩子*/ + for next_node in node.children(): + if not next_node in visited: + dfs(next_node,visited) + 2)非递归方式 + def dfs(self,root): + if root is None: + return []; + visited, stack=[],[root] + while stack: #循环截止条件:栈为空 + node = stack.pop() #从栈里弹出节点 + visited.add(node)#加入到已访问记录 + + proecess(node)#处理当前节点 + nodes = generate_related_nodes(node)#获取当前节点的子节点 + stack.push(nodes)#压入栈中 + #other processing work + ... + 3)应用场景:如果一个问题,能通过归纳画图分解成像树一样的分叉处理,然后重复分叉直到叶子节点,那么都可以归结为深度优先(DFS)处理。 +3.广度优先搜索(Breadth First Search) + 思路:类似树的按层级遍历: + 广度优先搜索使用队列(queue)来实现,整个过程也可以看做一个倒立的树形: + 1)把根节点放到队列的末尾。 + 2)每次从队列的头部取出一个元素,查看这个元素所有的下一级元素,把它们放到队列的末尾。并把这个元素记为它下一级元素的前驱。 + 3)找到所要找的元素时结束程序。 + 4)如果遍历整个树还没有找到,结束程序。 + BFS代码模板: + + def bfs(graph,start,end) + queue = [] + visited = [] + queue.append(start) + visited.add(start) + + while queue: + node = queue.pop() + visited.add(node) + + + process(node) + nodes = genereate_related_nodes(node) + queue.push(nodes) + + #other processing work + ... +4.贪心算法 + 贪心算法是指在对某个问题分步骤求解时,总是在当前步骤做出当前的最好选择,而不是考虑所有步骤的情况,即局部最优解。基于此,贪心算法与动态规划算法存在本质的区别。 + 贪心算法对于在每个步骤的解决方案都会做出选择,不能回退。动态规划则会保存每步的解决方案,并根据当前结果进行选择,可以回退。 + 贪心算法没有固定的算法框架,算法设计的关键是贪心策略的选择。必须注意的是,贪心算法不是对所有问题都能得到整体最优解,选择的贪心策略必须具备无后效性,即某个状态以后的过程不会影响以前的状态,只与当前状态有关。 + 所以对所采用的贪心策略一定要仔细分析其是否满足无后效性。 + 由上可知,贪心算法解决问题存在一定的局限性,适应场景必须满足存在最优子结构,并且子问题最优解能递推到最终问题的最优解。例如背包问题,哈夫曼编码问题,图的最小生成树问题。 + 贪心算法模板: + + 从问题的某一初始解出发; + while (能朝给定总目标前进一步) + { + 利用可行的决策,求出可行解的一个解元素 + } + 由所有解元素组合成问题的一个可行解 + +5.二分查找 + 二分查找的前提条件 + 1)目标函数单调性,即目标序列是递增或者递减的 + 2)存在上下限,不能是无穷的元素(bounded) + 3)能够通过索引访问(index accessible) + 二分查找模板(左右边界向中间收敛) + + left,right = 0,len(array)-1; + while(left<=right){ + mid = (left+right)/2; + if array[mid] == target: //find target + break or return result; + else if array[mid] preNum){ + result += (prices[i] - preNum); + } + preNum = prices[i]; + + } + return result; + } +} +// @lc code=end \ No newline at end of file diff --git a/Week 03/id_143/LeetCode_127_143.java b/Week 03/id_143/LeetCode_127_143.java new file mode 100644 index 000000000..7fb1ea8d2 --- /dev/null +++ b/Week 03/id_143/LeetCode_127_143.java @@ -0,0 +1,63 @@ +import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +/* + * @lc app=leetcode.cn id=127 lang=java + * + * [127] 单词接龙 + * + * 在bfs模板后,调试了各种办法,不是超时,就是边界条件有问题。一直没忍着看题解,总是觉得快Get the point,结果花费许久时间。 + * 排名很是落后。。 + * + */ + +// @lc code=start +class Solution { + public int ladderLength(String beginWord, String endWord, List wordList) { + if(!wordList.contains(endWord)){ + return 0; + } + Map visiedLevel = new HashMap(); + LinkedList deque = new LinkedList(); + deque.offerLast(beginWord); + visiedLevel.put(beginWord,1); + //用deque实现按层遍历BFS + while(!deque.isEmpty()) { + String cs = deque.pollFirst(); + int csl_min = visiedLevel.get(cs); + if (cs.equals(endWord)) { + return visiedLevel.get(cs); + } + + for (int i = 0; i < wordList.size(); i++) { + if (near(cs, wordList.get(i))) { + if (!visiedLevel.containsKey(wordList.get(i))) { + deque.offerLast(wordList.get(i)); + visiedLevel.put(wordList.get(i), csl_min + 1); + } else { + if (visiedLevel.get(wordList.get(i)) > (csl_min + 1)) { + visiedLevel.put(wordList.get(i), csl_min + 1); + } + } + } + } + } + return 0; + } +//判断两个单词是否相近,即相差为1 + public boolean near(String a,String b){ + int c = 0; + for(int i=0;i nums[left + 1]){ + return nums[left +1]; + }else if(nums[left] < nums[midIndex] ){ //前段有序,去后半段找 + left = midIndex ; + }else if(nums[right] > nums[midIndex] && nums[midIndex] <= nums[left]){ + right = midIndex ; + } + + } + return nums[0]; + + } +} +// @lc code=end + diff --git a/Week 03/id_143/LeetCode_33_143.java b/Week 03/id_143/LeetCode_33_143.java new file mode 100644 index 000000000..6090cb6fe --- /dev/null +++ b/Week 03/id_143/LeetCode_33_143.java @@ -0,0 +1,31 @@ +/* + * @lc app=leetcode.cn id=33 lang=java + * + * [33] 搜索旋转排序数组 + */ + +// @lc code=start +class Solution { + public int search(int[] nums, int target) { + int l = 0; + int h = nums.length - 1; + while(l < h){ + int mid = l + (h - l)/2 ; + + // 当后旋转发生在后段,且目标不可能在前段时。 + if (nums[0] <= nums[mid] && (target > nums[mid] || target < nums[0])) { + l = mid + 1; + // 当前段发生旋转时,且目标不可能在前段时。 + } else if (target > nums[mid] && target < nums[0]) { + l = mid + 1; + } else {//目标在前段 + h = mid; + } + + + } + return l == h && nums[l] == target ? l : -1; + } +} +// @lc code=end + diff --git a/Week 03/id_143/LeetCode_69_143.java b/Week 03/id_143/LeetCode_69_143.java new file mode 100644 index 000000000..eb8bbd24d --- /dev/null +++ b/Week 03/id_143/LeetCode_69_143.java @@ -0,0 +1,20 @@ +/* + * @lc app=leetcode.cn id=69 lang=java + * + * [69] x 的平方根 + */ + +// @lc code=start +class Solution { + public int mySqrt(int x) { + long x0 = x; + + while (x0*x0 > x) { + x0 = (x0 + x / x0) / 2; + } + return (int)x0; + + } +} +// @lc code=end + diff --git a/Week 03/id_143/LeetCode_74_143.java b/Week 03/id_143/LeetCode_74_143.java new file mode 100644 index 000000000..d772774aa --- /dev/null +++ b/Week 03/id_143/LeetCode_74_143.java @@ -0,0 +1,39 @@ +/* + * @lc app=leetcode.cn id=74 lang=java + * + * [74] 搜索二维矩阵 + */ + +// @lc code=start +class Solution { + public boolean searchMatrix(int[][] matrix, int target) { + + int left = 0; + int rows = matrix.length; + if(rows == 0 ){ + return false; + } + int cols = matrix[0].length; + int right = rows * cols - 1; + int midIndex,midValue; + while(left <= right){ + midIndex = left + (right - left) / 2; + midValue = matrix[midIndex / cols][midIndex % cols]; + + if(target == midValue){ + return true; + }else if(target > midValue){ + left = midIndex + 1; + }else { + right = midIndex - 1 ; + } + } + + + + return false; + + } +} +// @lc code=end + diff --git a/Week 03/id_143/LeetCode_860_143.java b/Week 03/id_143/LeetCode_860_143.java new file mode 100644 index 000000000..1035b1903 --- /dev/null +++ b/Week 03/id_143/LeetCode_860_143.java @@ -0,0 +1,43 @@ +/* + * @lc app=leetcode.cn id=860 lang=java + * + * [860] 柠檬水找零 + * 我感觉像暴力多过于贫心。。。但居然2ms,打败了100% + */ + +// @lc code=start +class Solution { + public boolean lemonadeChange(int[] bills) { + int c5 = 0; + int c10 = 0; + for(int b : bills){ + if(b == 5){ //5 + c5 += 1; + }else if( b == 10){ //10 + if(c5 > 0){ + c5 -= 1; + c10 += 1; + }else{ + return false; + } + }else { //20 + if(c5 > 0){ + if(c10 > 0){ + c5 -= 1; + c10 -= 1; + }else if( c5 > 2){ + c5 -= 3 ; + }else{ + return false; + } + }else{ + return false; + } + } + } + return true; + } + +} +// @lc code=end + diff --git a/Week 03/id_143/NOTE.md b/Week 03/id_143/NOTE.md index a6321d6e2..818bb4804 100644 --- a/Week 03/id_143/NOTE.md +++ b/Week 03/id_143/NOTE.md @@ -1,4 +1,126 @@ -# NOTE +# 算法训练营学习 +# 第三周 +## 第九课 +### 深度优先DFS +1. 主要针对树型数据结构,时间复杂度O(n),每个节点最多一次访问。 +2. 模板: +``` +递归算法: +visited = set() +def dfs(node, visited): + # terminator 和递归形式一致 + if node in visited: + #already visited + return + visited.add(node) + # process current node here + … + for next_node in node.children(): + if not next_node in visited: + dfs(next_node, visited) +非弟归算法(主要基于栈的方式实现): + def DFS(self, tree): - + if tree.root is None: + return [] + visited, stack = [], [tree.root] + + while stack: + node = stack.pop() + visited.add(node) + process(node) + #获取Node的子节点 + nodes = generate_related_nodes(node) + stack.push(nodes) + + # other processing work + ... + +``` +### 广度优先BFS +1. 主要针对于树型结构,时间复杂度O(n),按层遍历,用于最短路径搜索。 +2. 代码模板: +``` +主要使用双端队列,或双端链表来处理。 +def BFS(graph, start, end): + queue = [] + queue.append([start]) + visited.add(start) + + while queue: + node = queue.pop() + visited.add(node) + + process(node) + # 获取node的所有子节点 + nodes = generate_related_nodes(node) + queue.push(nodes) + # other processing work + ... +``` +## 第十课 +### 贪心算法Greedy +> 贪心算法是一种在每一步选择中都采取在当前状态下最好或者最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法。 + 贪心算法与动态规划的不通在于它对每个子问题的解决方案都作出选择,不能回退。动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能 +1. 贪心、动态规划区别 + - 贪心:当下每一个子问题都可以做局部最优判断,不能回退。只有在特殊情况下可以达到全局最优。 + - 动态归划: 在贪心的基础上记录了以前的运算结果,并根据以前结果对当前情况进行选择,有回退功能。 +2. 贪心、动态规划使用的场景和特点 + - 贪心:求图中最小生成树、哈夫曼编码等。一般不适用处理工程和生活中的遇到的问题,但一旦能用贪心法求解,一般来讲均为最佳办法。 + - 贪心: 在解题时,需先证明 它是最优子结构。即问题能分解为子问题来处理,且子问题的最优解能推导最终问题最优解 + - 动态规划:后面课程再讲 +## 第十一课 +### 二分查找 +1. 二分查找的前提 + - 目标函数存在单调性(这里可包含有规律的单调性) + - 存在上下界 + - 能够通过索引|下标访问 +2. 代码模板 +``` +left, right = 0, len(array) - 1 +while left <= right: + mid = (left + right) / 2 + if array[mid] == target: + # find the target! + break or return result + elif array[mid] < target: + left = mid + 1 + else: + right = mid - 1 +``` +### 旋转排序数组学习 +1. 前置条件 有序处均为升序。 +2. 边界条件 注意阴险的数组长度为0或1。 +3. 通用角法思路 + ``` + 1、套上模板。 + 2、判断需要调整的是左边界还是右边界。 + 3、如何判断是调左边界还是右边界,引出下列几种思路 + + ``` + - 穷举,算是暴力不? + ``` + 将左中右三个点,当作一条曲线,除退出条件外只会有四种可能。 + 1.升、升,(根据目标与nums[mid]大小比较,调整左右界,超界-1) + 2.升、降,(根据目标落在前半段,right = mid;落在后半段的判断需要结合) + 3.降、升,(此情况,此二个元素即可获得结果) + 4.降、降,(此情况,已经达到边界收敛了,不应出现。) + ``` + - 判断调整边界,先判断哪端有序 + ``` + 先处理向前收缩 + 1. 前半有序,且目标在前端。(left right 表明前段有反转。(target < mid,或 target > left ) + 此时判断条件里得加上升序这个隐藏条件,如果在上第一段上升,target>left>mid,如果在第二段上升,target { + let low = 0; + let hight = nums.length - 1; + while (low < hight) { + let mid = parseInt((low + hight) / 2, 10); + if ((nums[0] > target) ^ (nums[0] > nums[mid]) ^ (target > nums[mid])) { + low = mid + 1; + } else { + hight = mid; + } + } + return low === hight && nums[low] === target ? low : -1; +}; diff --git a/Week 03/id_153/LeetCode_74_153.js b/Week 03/id_153/LeetCode_74_153.js new file mode 100644 index 000000000..d83babd8e --- /dev/null +++ b/Week 03/id_153/LeetCode_74_153.js @@ -0,0 +1,30 @@ +/** + * @param {number[][]} matrix + * @param {number} target + * @return {boolean} + */ +const searchMatrix = (matrix, target) => { + let m = matrix.length; + if (m === 0) { + return false; + } + let n = matrix[0].length; + + let left = 0; + let right = m * n - 1; + let pivotIdx, pivotElement; + while (left <= right) { + pivotIdx = parseInt((left + right) / 2, 10); + pivotElement = matrix[parseInt(pivotIdx / n, 10)][pivotIdx % n]; + if (target === pivotElement) { + return true; + } else { + if (target < pivotElement) { + right = pivotIdx - 1; + } else { + left = pivotIdx + 1; + } + } + } + return false; +}; diff --git a/Week 03/id_153/NOTE.md b/Week 03/id_153/NOTE.md index a6321d6e2..e6fc771b9 100644 --- a/Week 03/id_153/NOTE.md +++ b/Week 03/id_153/NOTE.md @@ -1,4 +1,89 @@ -# NOTE +# 总结 +1. DFS (深度优先搜索) + 常用栈 + * 递归写法 + ```javascript + const visited = new Set(); - + const dfs(node, visited) => { + // 递归终止条件 + if (visited.includes(node)) { + return; + } + visited.push(node); + for (let next_node in node.children()) { + if(!visited.includes(next_node)) { + dfs(next_node, visited); + } + } + } + ``` + * 非递归写法 + ```javascript + const dfs(tree) => { + if (!tree.root) { + return []; + } + let visited = new Set(); + let stack = [tree.root]; + while(stack) { + let node = stack.pop(); + visited.push(node); + + process(node) + nodes = generate_related_nodes(node); + stack.push(nodes); + } + // other processing work + // ... + } + ``` +2. BFS (广度优先搜索) + 常用队列 + ```javascript + const dfs = (graph, start, end) => { + let queue = []; + queue.push(start); + let visited = new Set(); + visited.push(start) + + while(queue) { + let node = queue.pop(); + visited.push(node); + + process(node); + nodes = generate_related_nodes(node); + queue.push(nodes); + } + // other processing work + // ... + } + ``` +3. 贪心算法 + * 在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法。 + + * 与动态规划的不同之处在于它对每个子问题的解决方案都作出选择,不能回退,动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。 +4. Binary Search (二分查找) + 使用二分查找是有条件的 + * 数组是有序的,单调递增或单调递减 + * 有边界(上下界) + 时间复杂度 O(logn) + ```javascript + const binarySearch = (array, target) => { + let left = 0; + let right = array.length - 1; + while(left <= right) { + let mid = parseInt((left + right) / 2, 10); + if (array[mid] === target) { + // find the target + // break or return result + return target; + } else if (array[mid] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + } + ``` \ No newline at end of file diff --git "a/Week 03/id_158/102.\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206.cs" "b/Week 03/id_158/102.\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206.cs" new file mode 100644 index 000000000..a3ffaa5d1 --- /dev/null +++ "b/Week 03/id_158/102.\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206.cs" @@ -0,0 +1,44 @@ +/* + * @lc app=leetcode.cn id=102 lang=csharp + * + * [102] 二叉树的层次遍历 + */ + +// @lc code=start + +using System.Collections.Generic; +using System.Linq; +/** +* Definition for a binary tree node. +* public class TreeNode { +* public int val; +* public TreeNode left; +* public TreeNode right; +* public TreeNode(int x) { val = x; } +* } +*/ +public class Solution { + IList> levels = new List>(); + + public void Dfs(TreeNode node, int level) { + if (levels.Count == level) + levels.Add(new List()); + + levels.ElementAt(level).Add(node.val); + + if (node.left != null) + Dfs(node.left, level + 1); + if (node.right != null) + Dfs(node.right, level + 1); + } + public IList> LevelOrder(TreeNode root) { + if(root==null){ + return levels; + } + Dfs(root,0); + return levels; + } + +} +// @lc code=end + diff --git "a/Week 03/id_158/122.\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272-ii.cs" "b/Week 03/id_158/122.\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272-ii.cs" new file mode 100644 index 000000000..faaaf5fd7 --- /dev/null +++ "b/Week 03/id_158/122.\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272-ii.cs" @@ -0,0 +1,22 @@ +/* + * @lc app=leetcode.cn id=122 lang=csharp + * + * [122] 买卖股票的最佳时机 II + */ + +// @lc code=start +public class Solution { + public int MaxProfit(int[] prices) { + int MaxProfit=0; + for (int i = 1; i < prices.Length; i++) + { + if (prices[i]>prices[i-1]) + { + MaxProfit += prices[i]-prices[i-1]; + } + } + return MaxProfit; + } +} +// @lc code=end + diff --git "a/Week 03/id_158/367.\346\234\211\346\225\210\347\232\204\345\256\214\345\205\250\345\271\263\346\226\271\346\225\260.cs" "b/Week 03/id_158/367.\346\234\211\346\225\210\347\232\204\345\256\214\345\205\250\345\271\263\346\226\271\346\225\260.cs" new file mode 100644 index 000000000..57ad46b5a --- /dev/null +++ "b/Week 03/id_158/367.\346\234\211\346\225\210\347\232\204\345\256\214\345\205\250\345\271\263\346\226\271\346\225\260.cs" @@ -0,0 +1,28 @@ +/* + * @lc app=leetcode.cn id=367 lang=csharp + * + * [367] 有效的完全平方数 + */ + +// @lc code=start +public class Solution { + public bool IsPerfectSquare(int num) { + if(num==0){return false;} + long left =0,right=num; + while (leftnum){ + right = mid-1; + } else if(square=0; i--) + { + if(nums[i]+i>=canReachable){ + canReachable = i; + } + } + return canReachable==0; + } +} +// @lc code=end + diff --git "a/Week 03/id_158/69.x-\347\232\204\345\271\263\346\226\271\346\240\271.cs" "b/Week 03/id_158/69.x-\347\232\204\345\271\263\346\226\271\346\240\271.cs" new file mode 100644 index 000000000..cda6effb6 --- /dev/null +++ "b/Week 03/id_158/69.x-\347\232\204\345\271\263\346\226\271\346\240\271.cs" @@ -0,0 +1,41 @@ +/* + * @lc app=leetcode.cn id=69 lang=csharp + * + * [69] x 的平方根 + */ + +// @lc code=start +public class Solution { + public int MySqrt(int x) { + + long left = 0; + long right = x; + long mid = 0; + while (left < right) + { + mid = (right + left) / 2; + long square = mid * mid; + if (square > x) + { + right = mid - 1; + } + else + { + left = mid+1; + } + } + + if (right * right == x) + { + return (int)right; + } + if (left * left > x) + { + return (int)left - 1; + } + + return (int)left ; + } +} +// @lc code=end + diff --git "a/Week 03/id_158/860.\346\237\240\346\252\254\346\260\264\346\211\276\351\233\266.cs" "b/Week 03/id_158/860.\346\237\240\346\252\254\346\260\264\346\211\276\351\233\266.cs" new file mode 100644 index 000000000..ce04ab3fd --- /dev/null +++ "b/Week 03/id_158/860.\346\237\240\346\252\254\346\260\264\346\211\276\351\233\266.cs" @@ -0,0 +1,43 @@ +/* + * @lc app=leetcode.cn id=860 lang=csharp + * + * [860] 柠檬水找零 + */ + +// @lc code=start +using System; + +public class Solution { + public bool LemonadeChange(int[] bills) { + if(bills==null || bills.Length==0 || bills[0]!=5){ + return false; + } + int five=0; + int ten=0; + for (int i = 0; i < bills.Length; i++) + { + if (bills[i]==5) + { + five++; + } + else if(bills[i]==10){ + if(five==0) return false; + five--; + ten ++; + } + else { + if(five>0 && ten>0){ + ten--; + five--; + } else if(five>3){ + five-=3; + }else { + return false; + } + } + } + return true; + } +} +// @lc code=end + diff --git a/Week 03/id_158/NOTE.md b/Week 03/id_158/NOTE.md index a6321d6e2..84c6c8e81 100644 --- a/Week 03/id_158/NOTE.md +++ b/Week 03/id_158/NOTE.md @@ -1,4 +1,82 @@ # NOTE - +DFS +递归写法 +``` +visited = set() + +def dfs(node, visited): +if node in visited: # terminator + # already visited + return + + visited.add(node) + + # process current node here. + ... + for next_node in node.children(): + if not next_node in visited: + dfs(next_node, visited) +``` +非递归写法 +``` +def DFS(self, tree): + + if tree.root is None: + return [] + + visited, stack = [], [tree.root] + + while stack: + node = stack.pop() + visited.add(node) + + process (node) + nodes = generate_related_nodes(node) + stack.push(nodes) + + # other processing work + ... +``` +BFS + +``` +def BFS(graph, start, end): + + queue = [] + queue.append([start]) + visited.add(start) + + while queue: + node = queue.pop() + visited.add(node) + + process(node) + nodes = generate_related_nodes(node) + queue.push(nodes) + + # other processing work + ... +``` +贪心算法(Greedy) +问题可以分解成子问题来解决,子问题的最优解能递推到最终问题的最优解,这种子问题最优解称为最优子结构 + +二分查找(Binary Search) +前提: + + 目标函数单调性(单调递增或递减) + + 存在上下界 + + 能够通过索引访问 +模板 +``` +left, right = 0, len(array) - 1 +while left <= right: + mid = (left + right) / 2 + if array[mid] == target: + # find the target!! + break or return result + elif array[mid] < target: + left = mid + 1 + else: + right = mid - 1 +``` diff --git a/Week 03/id_173/LeetCode_122_173.cpp b/Week 03/id_173/LeetCode_122_173.cpp new file mode 100644 index 000000000..a6c8e8106 --- /dev/null +++ b/Week 03/id_173/LeetCode_122_173.cpp @@ -0,0 +1,17 @@ +/* + * 122. 买卖股票的最佳时机 II + */ + +class Solution { +public: + int maxProfit(vector& prices) { + int maxprofit = 0; + + for(int i=1; i prices[i-1]) + maxprofit += prices[i] - prices[i-1]; + } + + return maxprofit; + } +}; \ No newline at end of file diff --git a/Week 03/id_173/LeetCode_126_173.cpp b/Week 03/id_173/LeetCode_126_173.cpp new file mode 100644 index 000000000..d3b67884c --- /dev/null +++ b/Week 03/id_173/LeetCode_126_173.cpp @@ -0,0 +1,61 @@ +/* + * 126. 单词接龙 II + */ + +class Solution { +public: + vector> findLadders(string beginWord, string endWord, vector& wordList) { + vector> res; + unordered_set wordDict(wordList.begin(), wordList.end()); + + if(wordDict.empty() || !wordDict.count(endWord)) + return res; + + queue> paths; //存取所有可能的转换序列 + paths.push({beginWord}); + unordered_set visited; + int length = 1, minLength = INT_MAX; + + while(!paths.empty()) { + if(length > minLength) //超出最短转换序列的长度,则跳出循环 + break; + + for(string s : visited) //将字典中上层已访问过的单词剔除,防止再次访问 + wordDict.erase(s); + + visited.clear(); + + int size = paths.size(); + + for(int i=0; i path = paths.front(); + paths.pop(); + + string str = path.back(); //获取当前序列的最后一个单词 + for(int j=0; j newPath = path; + newPath.push_back(str); + if(str == endWord) { //当前 str 是目标单词 + minLength = length; //更新最短转换序列长度 + res.push_back(newPath); //将符合要求的转换序列进行存放 + } + else { //当前 str 不是目标单词 + visited.insert(str); //将 str 加入到已访问集合中 + paths.push(newPath); //将新的转换序列加入到paths中 + } + } + } + str[j] = c; + } + } + + length++; //转换序列长度加一 + } + + return res; + } +}; \ No newline at end of file diff --git a/Week 03/id_173/LeetCode_127_173.cpp b/Week 03/id_173/LeetCode_127_173.cpp new file mode 100644 index 000000000..764b20fa5 --- /dev/null +++ b/Week 03/id_173/LeetCode_127_173.cpp @@ -0,0 +1,43 @@ +/* + * 127. 单词接龙 + */ + +class Solution { +public: + int ladderLength(string beginWord, string endWord, vector& wordList) { + unordered_set wordDict(wordList.begin(), wordList.end()); + if(!wordDict.count(endWord)) + return 0; + + unordered_set curEnd({beginWord}); + unordered_set otherEnd({endWord}); + int length = 1; + + while(!curEnd.empty() && !otherEnd.empty()) { + if(otherEnd.size() < curEnd.size()) + swap(curEnd, otherEnd); + + unordered_set tempSet; + for(string str : curEnd) { + for(int i=0; i& nums) { + int left=0, right=nums.size()-1; + + while(left < right) { + if(nums[left] < nums[right]) + return nums[left]; + + int mid = left+(right-left)/2; + + if(nums[mid] >= nums[left]) + left = mid+1; + else + right = mid; + } + + return nums[left]; + } +}; \ No newline at end of file diff --git a/Week 03/id_173/LeetCode_200_173.cpp b/Week 03/id_173/LeetCode_200_173.cpp new file mode 100644 index 000000000..8f354e6ac --- /dev/null +++ b/Week 03/id_173/LeetCode_200_173.cpp @@ -0,0 +1,34 @@ +/* + * 200. 岛屿数量 + */ + +class Solution { +public: + int numIslands(vector>& grid) { + vector> map = grid; + int islands = 0; + + for(int i=0; i>& map, int x, int y) { + if(x<0 || x==map.size() || y<0 || y==map[0].size() || map[x][y] == '0') + return; + + map[x][y] = '0'; + + sinkIslands(map, x-1, y); + sinkIslands(map, x+1, y); + sinkIslands(map, x, y-1); + sinkIslands(map, x, y+1); + } +}; \ No newline at end of file diff --git a/Week 03/id_173/LeetCode_33_173.cpp b/Week 03/id_173/LeetCode_33_173.cpp new file mode 100644 index 000000000..c488a3b7a --- /dev/null +++ b/Week 03/id_173/LeetCode_33_173.cpp @@ -0,0 +1,21 @@ +/* + * 33. 搜索旋转排序数组 + */ + +class Solution { +public: + int search(vector& nums, int target) { + int left=0, right=nums.size()-1; + + while(left < right) { + int mid = left + (right - left) / 2; + + if(!((nums[0]<=nums[mid]) ^ (targetnums[mid]))) + left = mid + 1; + else + right = mid; + } + + return (left==right && target==nums[left]) ? left : -1; + } +}; \ No newline at end of file diff --git a/Week 03/id_173/LeetCode_455_173.cpp b/Week 03/id_173/LeetCode_455_173.cpp new file mode 100644 index 000000000..96f3da14b --- /dev/null +++ b/Week 03/id_173/LeetCode_455_173.cpp @@ -0,0 +1,20 @@ +/* + * 455. 分发饼干 + */ + +class Solution { +public: + int findContentChildren(vector& g, vector& s) { + sort(g.begin(), g.end()); + sort(s.begin(), s.end()); + + int i=0, j=0; + while(i= g[i]) + i++; + j++; + } + + return i; + } +}; \ No newline at end of file diff --git a/Week 03/id_173/LeetCode_529_173.cpp b/Week 03/id_173/LeetCode_529_173.cpp new file mode 100644 index 000000000..b0c7259d5 --- /dev/null +++ b/Week 03/id_173/LeetCode_529_173.cpp @@ -0,0 +1,51 @@ +/* + * 529. 扫雷游戏 + */ + +class Solution { +public: + vector> updateBoard(vector>& board, vector& click) { + if(board.empty() || click.empty()) + return board; + + if(board[click[0]][click[1]] == 'M') + board[click[0]][click[1]] = 'X'; + else + reveal(board, click[0], click[1]); + + return board; + } + + bool isLegal(vector>& board, int x, int y) { + return (x>=0 && x=0 && y>& board, int x, int y) { + if(!isLegal(board, x, y) || board[x][y]!='E') + return; + + int mines = 0; + if(isLegal(board, x-1, y-1) && board[x-1][y-1]=='M') mines++; + if(isLegal(board, x-1, y ) && board[x-1][ y ]=='M') mines++; + if(isLegal(board, x-1, y+1) && board[x-1][y+1]=='M') mines++; + if(isLegal(board, x , y-1) && board[ x ][y-1]=='M') mines++; + if(isLegal(board, x , y+1) && board[ x ][y+1]=='M') mines++; + if(isLegal(board, x+1, y-1) && board[x+1][y-1]=='M') mines++; + if(isLegal(board, x+1, y ) && board[x+1][ y ]=='M') mines++; + if(isLegal(board, x+1, y+1) && board[x+1][y+1]=='M') mines++; + + if(mines != 0) + board[x][y] = (char)(mines+'0'); + else { + board[x][y] = 'B'; + reveal(board, x-1, y-1); + reveal(board, x-1, y ); + reveal(board, x-1, y+1); + reveal(board, x , y-1); + reveal(board, x , y+1); + reveal(board, x+1, y-1); + reveal(board, x+1, y ); + reveal(board, x+1, y+1); + } + } +}; \ No newline at end of file diff --git a/Week 03/id_173/LeetCode_55_173.cpp b/Week 03/id_173/LeetCode_55_173.cpp new file mode 100644 index 000000000..14a168e1d --- /dev/null +++ b/Week 03/id_173/LeetCode_55_173.cpp @@ -0,0 +1,20 @@ +/* + * 55. 跳跃游戏 + */ + +class Solution { +public: + bool canJump(vector& nums) { + if(nums.empty()) + return true; + + int endPos = nums.size()-1; + + for(int i=endPos; i>=0; --i) { + if(i+nums[i] >= endPos) + endPos = i; + } + + return endPos == 0; + } +}; \ No newline at end of file diff --git a/Week 03/id_173/LeetCode_74_173.cpp b/Week 03/id_173/LeetCode_74_173.cpp new file mode 100644 index 000000000..aa07906bc --- /dev/null +++ b/Week 03/id_173/LeetCode_74_173.cpp @@ -0,0 +1,27 @@ +/* + * 74. 搜索二维矩阵 + */ + +class Solution { +public: + bool searchMatrix(vector>& matrix, int target) { + if(matrix.empty()) + return false; + + int rows = matrix.size(), cols = matrix[0].size(); + int left = 0, right = rows*cols-1; + + while(left <= right) { + int mid = left+(right-left)/2; + int num = matrix[mid/cols][mid%cols]; + if(num == target) + return true; + else if(num < target) + left = mid + 1; + else + right = mid - 1; + } + + return false; + } +}; \ No newline at end of file diff --git a/Week 03/id_173/LeetCode_860_173.cpp b/Week 03/id_173/LeetCode_860_173.cpp new file mode 100644 index 000000000..0c74bf588 --- /dev/null +++ b/Week 03/id_173/LeetCode_860_173.cpp @@ -0,0 +1,26 @@ +/* + * 860. 柠檬水找零 + */ + +class Solution { +public: + bool lemonadeChange(vector& bills) { + int five = 0, ten = 0; + + for(int i : bills) { + if(i == 5) + five++; + else if(i == 10) + five--,ten++; + else if(ten > 0) + five--,ten--; + else + five -= 3; + + if(five < 0) + return false; + } + + return true; + } +}; \ No newline at end of file diff --git a/Week 03/id_173/LeetCode_874_173.cpp b/Week 03/id_173/LeetCode_874_173.cpp new file mode 100644 index 000000000..f621f4995 --- /dev/null +++ b/Week 03/id_173/LeetCode_874_173.cpp @@ -0,0 +1,39 @@ +/* + * 874. 模拟行走机器人 + */ + +class Solution { +public: + int robotSim(vector& commands, vector>& obstacles) { + int dx[] = {0, 1, 0, -1}; + int dy[] = {1, 0, -1, 0}; + int dir = 0; + int x = 0, y = 0; + + set> obstacleSet; + for(vector obstacle : obstacles) + obstacleSet.insert(make_pair(obstacle[0], obstacle[1])); + + int res = 0; + for(int cmd : commands) { + if(cmd == -1) + dir = (dir+1)%4; + else if(cmd == -2) + dir = (dir+3)%4; + else { + for(int i=0; i +  **贪心算法与动态规划的不同在于它对每个子问题的解决方案都做出选择,不能回退。而动态规划会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。**
+  贪心算法可以解决一些最优化问题,如:求图中的最小生成树、求哈夫曼编码等,然而对于工程和生活中的问题,贪心法一般不能得到我们所要求的答案。
+  一旦一个问题可以通过贪心法来解决,那么贪心法一般是解决这个问题的最好办法。由于贪心法的高效性以及其所求得的答案比较接近最优结果,贪心法也可以用作辅助算法或者直接解决一些要求结果不特别精确的问题。
+ +### 2. 适应场所 +  所要求解的问题能够分解成子问题来解决,并且子问题的最优解能递推到最终问题的最优解(这一点有时候需要证明)。
+ +# 四、二分查找(Binary Search) +### 1. 前提条件 +- 目标函数具有单调性 +- 存在上下界 +- 能够通过索引访问 + +### 2. 代码模板 +```python +left, right = 0, len(array) - 1 + +while left <= right: + mid = (left + right) / 2 + + if array[mid] == target: + break or return result + elif array[mid] < target: + left = mid + 1 + else: + right = mid - 1 +``` + +# 五、使用二分查找,寻找一个半有序数组 [4, 5, 6, 7, 0, 1, 2] 中间无序的地方。 +```C++ +class Solution { +public: + int findDisorderedPos(vector& nums) { + int left=0, right=nums.size()-1; + + //情况一:无旋转,即全有序数组,不存在无序的地方,返回-1 + if(nums[0] < nums[right]) + return -1; + + //情况二:有旋转,即半有序数组,存在无序的地方,返回无序地方的后一个位置 + while(left <= right) { + int mid = left + (right - left) / 2; + + //(1) 找到了无序的地方 + if(nums[mid] > nums[mid+1]) //如果mid为无序地方的前一个位置,那么返回mid+1(如:3 4 [5] 1 2) + return mid+1; + if(nums[mid] < nums[mid-1]) //如果mid为无序地方的后一个位置,那么返回mid(如:4 5 [1] 2 3) + return mid; + + //(2) 未找到无序的地方 + if(nums[mid] > nums[0]) //如果 nums[mid] > nums[0] ,则无序的地方在后半段 + left = mid + 1; + else //否则,无序的地方在前半段 + right = mid - 1; + } + + return -1; + } +}; +``` diff --git a/Week 03/id_183/Leetcode_122_183.cpp b/Week 03/id_183/Leetcode_122_183.cpp new file mode 100644 index 000000000..c12b83dfc --- /dev/null +++ b/Week 03/id_183/Leetcode_122_183.cpp @@ -0,0 +1,23 @@ +/* + * @lc app=leetcode id=122 lang=cpp + * + * [122] Best Time to Buy and Sell Stock II + */ + +// @lc code=start +class Solution { +public: + int maxProfit(vector& prices) { + //每天都做买卖 + //assunmpt for the best option (always) + int profit = 0; + for(int i =1;i 0) profit += tmp; + } + return profit; + } +}; +// @lc code=end + diff --git a/Week 03/id_183/Leetcode_153_183.cpp b/Week 03/id_183/Leetcode_153_183.cpp new file mode 100644 index 000000000..dcc274a6a --- /dev/null +++ b/Week 03/id_183/Leetcode_153_183.cpp @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode id=153 lang=cpp + * + * [153] Find Minimum in Rotated Sorted Array + */ + +// @lc code=start +class Solution { +public: + int findMin(vector& nums) { + //对于二分查找 + //1. 定义左边界和youbianjie + int left = 0; + int right = nums.size() - 1; + while(left < right) + { + //逼近left + int mid = left + (right - left)/2; + if(nums[mid]>nums[right]) left =mid+1; + else right = mid; + } + //因为升序排列。所以 + return nums[left]; + } +}; +// @lc code=end + diff --git a/Week 03/id_183/Leetcode_22_183.cpp b/Week 03/id_183/Leetcode_22_183.cpp new file mode 100644 index 000000000..da19dd33b --- /dev/null +++ b/Week 03/id_183/Leetcode_22_183.cpp @@ -0,0 +1,31 @@ +/* + * @lc app=leetcode id=22 lang=cpp + * + * [22] Generate Parentheses + */ + +// @lc code=start +class Solution { +public: + void helper(int left, int right, string tmp, vector &result) { + if (left == 0 && right == 0) { + result.push_back(tmp); + return; + } + + if (left>right || left<0 || right<0) //左括号用的比右括号少 + return ; + + helper(left-1, right, tmp+'(', result); + helper(left, right-1, tmp+')', result); + + } + vector generateParenthesis(int n) { + vectorresult; + helper(n,n, "", result); + return result; + } +}; + +// @lc code=end + diff --git a/Week 03/id_183/Leetcode_455_183.cpp b/Week 03/id_183/Leetcode_455_183.cpp new file mode 100644 index 000000000..ef9fc8bc9 --- /dev/null +++ b/Week 03/id_183/Leetcode_455_183.cpp @@ -0,0 +1,45 @@ +/* + * @lc app=leetcode id=455 lang=cpp + * + * [455] Assign Cookies + */ + +// @lc code=start +class Solution { +public: + static bool cmp(const int &a,const int &b) + { + return a& g, vector& s) { + if(g.size()<1) return 0; + sort(s.begin(),s.end(),cmp); + sort(g.begin(),g.end(),cmp); + int num=0; + //对于每个小朋友 + for(int i=0;i=g[i]) + { + num++; + //这块饼干退出 + s.erase(s.begin()+j); + break; + } + } + //小朋友也退出 + g.erase(g.begin()+i); + //由于size-, + //i--对应队列的第一个小朋友 + i--; + } + return num; + } +}; + +// @lc code=end + diff --git a/Week 03/id_183/Leetcode_55_183.cpp b/Week 03/id_183/Leetcode_55_183.cpp new file mode 100644 index 000000000..7fa15e05d --- /dev/null +++ b/Week 03/id_183/Leetcode_55_183.cpp @@ -0,0 +1,23 @@ +/* + * @lc app=leetcode id=55 lang=cpp + * + * [55] Jump Game + */ + +// @lc code=start +class Solution { +public: + bool canJump(vector& nums) + { + int k = 0; + for (int i = 0; i < nums.size(); i++) + { + if (i > k) return false; + k = max(k, i + nums[i]); + } + return true; + } +}; + +// @lc code=end + diff --git a/Week 03/id_183/Leetcode_860_183.cpp b/Week 03/id_183/Leetcode_860_183.cpp new file mode 100644 index 000000000..6d272ab49 --- /dev/null +++ b/Week 03/id_183/Leetcode_860_183.cpp @@ -0,0 +1,54 @@ +/* + * @lc app=leetcode id=860 lang=cpp + * + * [860] Lemonade Change + */ + +// @lc code=start +class Solution { +public: + bool lemonadeChange(vector& bills) { + int five =0; + int ten = 0; + for(int i =0;i=3) + { + five -=3; + } + else if(ten >0&& five > 0) + { + five --; + ten --; + } + else + { + return false; + } + + } + if(bills[i] == 10) + { + if(five>0) + { + five --; + ten++; + } + else{ + return false; + } + + } + if(bills[i]==5) + { + five++; + } + + } + return true; + } +}; +// @lc code=end + diff --git a/Week 03/id_183/Leetcode_874_183.cpp b/Week 03/id_183/Leetcode_874_183.cpp new file mode 100644 index 000000000..ae6618069 --- /dev/null +++ b/Week 03/id_183/Leetcode_874_183.cpp @@ -0,0 +1,136 @@ +/* + * @lc app=leetcode id=874 lang=cpp + * + * [874] Walking Robot Simulation + */ + +// @lc code=start +class Solution { +public: + int robotSim(vector& commands, vector>& obstacles) { + int a = 0;//方向 + int x = 0;//机器人的x坐标 + int y = 0;//机器人的y坐标 + int maxn = 0;//最大欧式距离 + int mindes;//最近的障碍物某一坐标 + bool flag=false;//标志是否被障碍物挡住 + for (int i = 0; i < commands.size(); i++) + { + if(flag && commands[i]!=-1 && commands[i]!=-2)//被障碍物挡住,停止前进 + continue; + if (commands[i] == -1) + { + a = (a + 1) % 4; + flag=false; + continue; + } + if (commands[i] == -2) + { + a = (a + 3) % 4; + flag=false; + continue; + } + int k = 0; + if (a == 0)//北方向 + { + mindes=INT_MAX; + for (; k < obstacles.size(); k++) + { + if (obstacles[k][0] == x && obstacles[k][1]>y) + { + mindes=min(mindes,obstacles[k][1]); + } + } + if (mindes!=INT_MAX) + { + if (mindes > (y + commands[i])) + y += commands[i]; + else + { + y = mindes - 1; + flag=true; + } + } + else y += commands[i]; + maxn = max(maxn, x*x + y * y); + continue; + } + if (a == 1)//东方向 + { + mindes=INT_MAX; + for (; k < obstacles.size(); k++) + { + if (obstacles[k][1] == y && obstacles[k][0]>x) + { + mindes=min(mindes,obstacles[k][0]); + } + } + if (mindes!=INT_MAX) + { + if (mindes > (x + commands[i])) + x += commands[i]; + else + { + x = mindes - 1; + flag=true; + } + } + else x += commands[i]; + maxn = max(maxn, x*x + y * y); + continue; + } + if (a == 2)//南方向 + { + mindes=INT_MIN; + for (; k < obstacles.size(); k++) + { + if (obstacles[k][0] == x && obstacles[k][1] 0 { + // length, element := queue.Len(), []int{} + // for i := 0; i < length; i++ { + // node := queue.Remove(queue.Front()).(*TreeNode) + // element = append(element, node.Val) + // if node.Left != nil { + // queue.PushBack(interface{}(node.Left)) + // } + // if node.Right != nil { + // queue.PushBack(interface{}(node.Right)) + // } + // } + // ret = append(ret, element) + // level++ + // } + // } + // bfsFunc(root) + + return ret +} + +// @lc code=end diff --git a/Week 03/id_188/leetCode-121-188.go b/Week 03/id_188/leetCode-121-188.go new file mode 100644 index 000000000..776dff6a6 --- /dev/null +++ b/Week 03/id_188/leetCode-121-188.go @@ -0,0 +1,52 @@ +/* + * @lc app=leetcode.cn id=121 lang=golang + * + * [121] 买卖股票的最佳时机 + * + * https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/description/ + * + * algorithms + * Easy (51.13%) + * Likes: 596 + * Dislikes: 0 + * Total Accepted: 91.4K + * Total Submissions: 178.4K + * Testcase Example: '[7,1,5,3,6,4]' + * + * 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 + * + * 如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。 + * + * 注意你不能在买入股票前卖出股票。 + * + * 示例 1: + * + * 输入: [7,1,5,3,6,4] + * 输出: 5 + * 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 + * ⁠ 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。 + * + * + * 示例 2: + * + * 输入: [7,6,4,3,1] + * 输出: 0 + * 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 + * + * + */ + +// @lc code=start +package leetCode + +func maxProfit(prices []int) int { + ret := 0 + for index := 1; index < len(prices); index++ { + if prices[index] > prices[index-1] { + ret += prices[index] - prices[index-1] + } + } + return ret +} + +// @lc code=end diff --git a/Week 03/id_188/leetCode-127-188.go b/Week 03/id_188/leetCode-127-188.go new file mode 100644 index 000000000..22abc2476 --- /dev/null +++ b/Week 03/id_188/leetCode-127-188.go @@ -0,0 +1,114 @@ +/* + * @lc app=leetcode.cn id=127 lang=golang + * + * [127] 单词接龙 + * + * https://leetcode-cn.com/problems/word-ladder/description/ + * + * algorithms + * Medium (37.09%) + * Likes: 152 + * Dislikes: 0 + * Total Accepted: 13.4K + * Total Submissions: 35.6K + * Testcase Example: '"hit"\n"cog"\n["hot","dot","dog","lot","log","cog"]' + * + * 给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord + * 的最短转换序列的长度。转换需遵循如下规则: + * + * + * 每次转换只能改变一个字母。 + * 转换过程中的中间单词必须是字典中的单词。 + * + * + * 说明: + * + * + * 如果不存在这样的转换序列,返回 0。 + * 所有单词具有相同的长度。 + * 所有单词只由小写字母组成。 + * 字典中不存在重复的单词。 + * 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。 + * + * + * 示例 1: + * + * 输入: + * beginWord = "hit", + * endWord = "cog", + * wordList = ["hot","dot","dog","lot","log","cog"] + * + * 输出: 5 + * + * 解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", + * ⁠ 返回它的长度 5。 + * + * + * 示例 2: + * + * 输入: + * beginWord = "hit" + * endWord = "cog" + * wordList = ["hot","dot","dog","lot","log"] + * + * 输出: 0 + * + * 解释: endWord "cog" 不在字典中,所以无法进行转换。 + * + */ + +// @lc code=start +package leetCode + +import "container/list" + +func ladderLength(beginWord string, endWord string, wordList []string) int { + wordLen, comoboDict := len(beginWord), make(map[string][]string, len(wordList)*3) + + for _, word := range wordList { + index := 0 + for index < wordLen { + key := word[0:index] + "*" + word[index+1:] + comoboDict[key] = append(comoboDict[key], word) + index++ + } + } + + level := 0 + queue := list.New() + queue.PushBack(interface{}(beginWord)) + visited := make(map[string]bool, len(wordList)) + + for queue.Len() > 0 { + level++ + queueIndex, queueCount := 0, queue.Len() + for queueIndex < queueCount { + queueIndex++ + word := queue.Remove(queue.Front()).(string) + if visited[word] { + continue + } + if word == endWord { + return level + } + visited[word] = true + wordIndex := 0 + for wordIndex < wordLen { + key := word[0:wordIndex] + "*" + word[wordIndex+1:] + wordIndex++ + words, exist := comoboDict[key] + if !exist { + continue + } + for _, value := range words { + if value != word && !visited[value] { + queue.PushBack(interface{}(value)) + } + } + } + } + } + return 0 +} + +// @lc code=end diff --git a/Week 03/id_188/leetCode-200-188.go b/Week 03/id_188/leetCode-200-188.go new file mode 100644 index 000000000..419e660da --- /dev/null +++ b/Week 03/id_188/leetCode-200-188.go @@ -0,0 +1,85 @@ +/* + * @lc app=leetcode.cn id=200 lang=golang + * + * [200] 岛屿数量 + * + * https://leetcode-cn.com/problems/number-of-islands/description/ + * + * algorithms + * Medium (45.69%) + * Likes: 265 + * Dislikes: 0 + * Total Accepted: 35.9K + * Total Submissions: 78.6K + * Testcase Example: '[["1","1","1","1","0"],["1","1","0","1","0"],["1","1","0","0","0"],["0","0","0","0","0"]]' + * + * 给定一个由 '1'(陆地)和 + * '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。 + * + * 示例 1: + * + * 输入: + * 11110 + * 11010 + * 11000 + * 00000 + * + * 输出: 1 + * + * + * 示例 2: + * + * 输入: + * 11000 + * 11000 + * 00100 + * 00011 + * + * 输出: 3 + * + * + */ + +// @lc code=start +package leetCode + +func numIslands(grid [][]byte) int { + if len(grid) == 0 { + return 0 + } + ret := 0 + for x, _ := range grid { + if len(grid[x]) == 0 { + continue + } + for y, _ := range grid[x] { + if grid[x][y] == '1' { + ret++ + dfs(grid, x, y) + } + } + } + return ret +} + +func dfs(grid [][]byte, x, y int) { + if grid[x][y] == '0' { + return + } + grid[x][y] = '0' + if x > 0 { + dfs(grid, x-1, y) + } + if x < len(grid)-1 { + dfs(grid, x+1, y) + } + if y > 0 { + dfs(grid, x, y-1) + } + if y < len(grid[x])-1 { + dfs(grid, x, y+1) + } + +} + +// @lc code=end diff --git a/Week 03/id_188/leetCode-455-188.go b/Week 03/id_188/leetCode-455-188.go new file mode 100644 index 000000000..ca0e9dee4 --- /dev/null +++ b/Week 03/id_188/leetCode-455-188.go @@ -0,0 +1,83 @@ +/* + * @lc app=leetcode.cn id=455 lang=golang + * + * [455] 分发饼干 + * + * https://leetcode-cn.com/problems/assign-cookies/description/ + * + * algorithms + * Easy (51.93%) + * Likes: 113 + * Dislikes: 0 + * Total Accepted: 17.9K + * Total Submissions: 34.3K + * Testcase Example: '[1,2,3]\n[1,1]' + * + * 假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi + * ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i + * ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。 + * + * 注意: + * + * 你可以假设胃口值为正。 + * 一个小朋友最多只能拥有一块饼干。 + * + * 示例 1: + * + * + * 输入: [1,2,3], [1,1] + * + * 输出: 1 + * + * 解释: + * 你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。 + * 虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。 + * 所以你应该输出1。 + * + * + * 示例 2: + * + * + * 输入: [1,2], [1,2,3] + * + * 输出: 2 + * + * 解释: + * 你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。 + * 你拥有的饼干数量和尺寸都足以让所有孩子满足。 + * 所以你应该输出2. + * + * + */ + +// @lc code=start +package leetCode + +import ( + "sort" +) + +func findContentChildren(g []int, s []int) int { + if len(g) == 0 || len(s) == 0 { + return 0 + } + sort.Ints(g) + sort.Ints(s) + sIndex, ret := 0, 0 + for _, gValue := range g { + for sIndex < len(s) { + sValue := s[sIndex] + sIndex++ + if sValue >= gValue { + ret++ + break + } + } + if sIndex == len(s) { + break + } + } + return ret +} + +// @lc code=end diff --git a/Week 03/id_188/leetCode-69-188.go b/Week 03/id_188/leetCode-69-188.go new file mode 100644 index 000000000..09e31a06e --- /dev/null +++ b/Week 03/id_188/leetCode-69-188.go @@ -0,0 +1,64 @@ +/* + * @lc app=leetcode.cn id=69 lang=golang + * + * [69] x 的平方根 + * + * https://leetcode-cn.com/problems/sqrtx/description/ + * + * algorithms + * Easy (37.10%) + * Likes: 247 + * Dislikes: 0 + * Total Accepted: 68.9K + * Total Submissions: 185.1K + * Testcase Example: '4' + * + * 实现 int sqrt(int x) 函数。 + * + * 计算并返回 x 的平方根,其中 x 是非负整数。 + * + * 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。 + * + * 示例 1: + * + * 输入: 4 + * 输出: 2 + * + * + * 示例 2: + * + * 输入: 8 + * 输出: 2 + * 说明: 8 的平方根是 2.82842..., + * 由于返回类型是整数,小数部分将被舍去。 + * + * + */ + +// @lc code=start +package leetCode + +func mySqrt(x int) int { + if x == 0 { + return 0 + } + + if x == 1 { + return 1 + } + + left, right := 1, x/2+1 + + for left < right { + mind := left + (right-left+1)/2 + if mind*mind > x { + right = mind - 1 + } else { + left = mind + } + } + + return left +} + +// @lc code=end diff --git a/Week 03/id_188/leetCode_55_188.go b/Week 03/id_188/leetCode_55_188.go new file mode 100644 index 000000000..777555702 --- /dev/null +++ b/Week 03/id_188/leetCode_55_188.go @@ -0,0 +1,54 @@ +/* + * @lc app=leetcode.cn id=55 lang=golang + * + * [55] 跳跃游戏 + * + * https://leetcode-cn.com/problems/jump-game/description/ + * + * algorithms + * Medium (36.49%) + * Likes: 353 + * Dislikes: 0 + * Total Accepted: 40.1K + * Total Submissions: 109.5K + * Testcase Example: '[2,3,1,1,4]' + * + * 给定一个非负整数数组,你最初位于数组的第一个位置。 + * + * 数组中的每个元素代表你在该位置可以跳跃的最大长度。 + * + * 判断你是否能够到达最后一个位置。 + * + * 示例 1: + * + * 输入: [2,3,1,1,4] + * 输出: true + * 解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。 + * + * + * 示例 2: + * + * 输入: [3,2,1,0,4] + * 输出: false + * 解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。 + * + * + */ + +// @lc code=start +package leetCode + +func canJump(nums []int) bool { + if len(nums) == 0 { + return false + } + endIndex := len(nums) - 1 + for index := endIndex; index >= 0; index-- { + if nums[index]+index >= endIndex { + endIndex = index + } + } + return endIndex == 0 +} + +// @lc code=end diff --git a/Week 03/id_198/LeetCode_0_198.go b/Week 03/id_198/LeetCode_0_198.go new file mode 100644 index 000000000..ee6816d36 --- /dev/null +++ b/Week 03/id_198/LeetCode_0_198.go @@ -0,0 +1,36 @@ +package leetcode + +func searchRotatedIndex(nums []int) int { + + nl := len(nums) + if nl < 2 { + + return -1 + } + + l := 0 + r := nl - 1 + m := r / 2 + for l < r && l >= 0 { + + if m > 0 && nums[m] < nums[m-1] { + + return m + } + if nums[m] > nums[m+1] { + + return m + 1 + } + if nums[l] < nums[m] { + + l = m + 1 + + } else { + + r = m - 1 + } + m = (l + r) / 2 + } + + return -1 +} diff --git a/Week 03/id_198/LeetCode_0_198_test.go b/Week 03/id_198/LeetCode_0_198_test.go new file mode 100644 index 000000000..2f056c996 --- /dev/null +++ b/Week 03/id_198/LeetCode_0_198_test.go @@ -0,0 +1,28 @@ +package leetcode + +import "testing" + +func Test_search(t *testing.T) { + type args struct { + nums []int + } + tests := []struct { + name string + args args + want int + }{ + {name: "test1", args: args{nums: []int{4, 5, 6, 7, 0, 1, 2}}, want: 4}, + {name: "test2", args: args{nums: []int{7, 8, 1, 2, 3, 4, 5, 6}}, want: 2}, + {name: "test3", args: args{nums: []int{1, 2, 3, 4, 5, 6}}, want: -1}, + {name: "test4", args: args{nums: []int{5, 1, 2, 3, 4}}, want: 1}, + {name: "test5", args: args{nums: []int{1, 3}}, want: -1}, + {name: "test6", args: args{nums: []int{5, 1, 3}}, want: 1}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := searchRotatedIndex(tt.args.nums); got != tt.want { + t.Errorf("search() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/Week 03/id_198/LeetCode_102_198.go b/Week 03/id_198/LeetCode_102_198.go new file mode 100644 index 000000000..8d104c884 --- /dev/null +++ b/Week 03/id_198/LeetCode_102_198.go @@ -0,0 +1,50 @@ +package leetcode + +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +// 本部分基于BFS(一种遍历或搜索树或图数据结构的算法。它从树根(或图的某个任意节点)开始,在进入下一个深度级别之前,探测当前深度的所有邻居节点 +// 可能和老师的模板有一定出入, 本实现在完成当前节点遍历当前深度(所有)节点时将下一级节点缓存,以便下次访问。 +// 每个节点在该过程中至少访问两次,因此时间复杂度为O(2b^d)=>O(b^d) +// 中间存在暂存行为,因此空间复杂度为O(b^d) +func levelOrder(root *TreeNode) [][]int { + + rt := make([][]int, 0) + if root == nil { + return rt + } + nodes := []*TreeNode{} + nodes = append(nodes, root) + level := 0 + for len(nodes) > 0 { + + rt = append(rt, make([]int, 0)) + next := []*TreeNode(nil) + for i := 0; i < len(nodes); i++ { + + rt[level] = append(rt[level], nodes[i].Val) + if nodes[i].Left != nil { + + next = append(next, nodes[i].Left) + } + if nodes[i].Right != nil { + + next = append(next, nodes[i].Right) + } + } + nodes = next + level++ + } + return rt +} diff --git a/Week 03/id_198/LeetCode_102_198_test.go b/Week 03/id_198/LeetCode_102_198_test.go new file mode 100644 index 000000000..f622d236f --- /dev/null +++ b/Week 03/id_198/LeetCode_102_198_test.go @@ -0,0 +1,26 @@ +package leetcode + +import ( + "reflect" + "testing" +) + +func Test_levelOrder(t *testing.T) { + type args struct { + root *TreeNode + } + tests := []struct { + name string + args args + want [][]int + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := levelOrder(tt.args.root); !reflect.DeepEqual(got, tt.want) { + t.Errorf("levelOrder() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/Week 03/id_198/LeetCode_33_198.go b/Week 03/id_198/LeetCode_33_198.go new file mode 100644 index 000000000..8531b5fa7 --- /dev/null +++ b/Week 03/id_198/LeetCode_33_198.go @@ -0,0 +1,68 @@ +package leetcode + +// 因为给定的目标数组在某个点有序的,因此使用二分法查找。 +// 相较于nums完全有序的情况下,经过旋转的数组在使用二分法时应关注如何避免旋转点对于查找过程的影响. +// len(nums) == 1的判定是避免进入二分法查找过程,若nums的长度恰好为1,若存在目标值,则直接返回0;否则返回-1,避免进入不必要的循环(个人习惯,起始完全可以避免) +func search(nums []int, target int) int { + + nl := len(nums) + if nl <= 0 { + + return -1 + } + + if nl == 1 { + + if nums[0] == target { + + return 0 + } + + return -1 + } + + l := 0 + r := nl - 1 + m := r / 2 + for l < r && l >= 0 { + + if nums[l] == target { + + return l + } + + if nums[m] == target { + + return m + } + if nums[r] == target { + + return r + } + + if nums[l] < nums[m] { + + if target > nums[l] && target < nums[m] { + + r = m - 1 + } else { + + l = m + 1 + } + + } else { + + if target > nums[m] && target < nums[r] { + + l = m + 1 + } else { + + r = m - 1 + } + + } + m = (l + r) / 2 + } + + return -1 +} diff --git a/Week 03/id_198/LeetCode_33_198_test.go b/Week 03/id_198/LeetCode_33_198_test.go new file mode 100644 index 000000000..f88262a40 --- /dev/null +++ b/Week 03/id_198/LeetCode_33_198_test.go @@ -0,0 +1,31 @@ +package leetcode + +import "testing" + +func Test_search(t *testing.T) { + type args struct { + nums []int + target int + } + tests := []struct { + name string + args args + want int + }{ + {name: "test1", args: args{nums: []int{4, 5, 6, 7, 0, 1, 2}, target: 3}, want: -1}, + {name: "test2", args: args{nums: []int{4, 5, 6, 7, 0, 1, 2}, target: 0}, want: 4}, + {name: "test3", args: args{nums: []int{4, 5, 6, 7, 0, 1, 2}, target: 1}, want: 5}, + {name: "test4", args: args{nums: []int{7, 8, 1, 2, 3, 4, 5, 6}, target: 2}, want: 3}, + {name: "test5", args: args{nums: []int{1, 2, 3, 4, 5, 6}, target: 4}, want: 3}, + {name: "test6", args: args{nums: []int{5, 1, 2, 3, 4}, target: 1}, want: 1}, + {name: "test7", args: args{nums: []int{1, 3}, target: 2}, want: -1}, + {name: "test7", args: args{nums: []int{5, 1, 3}, target: 2}, want: -1}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := search(tt.args.nums, tt.args.target); got != tt.want { + t.Errorf("search() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/Week 03/id_198/LeetCode_81_198.go b/Week 03/id_198/LeetCode_81_198.go new file mode 100644 index 000000000..ed6be74ca --- /dev/null +++ b/Week 03/id_198/LeetCode_81_198.go @@ -0,0 +1,62 @@ +package leetcode + +func search(nums []int, target int) bool { + + nl := len(nums) + if nl <= 0 { + + return false + } + + l := 0 + r := nl - 1 + m := r / 2 + for l < r && l >= 0 { + + if nums[l] == target || nums[m] == target || nums[r] == target { + + return true + } + + reduce := false + for i := l + 1; i < len(nums) && nums[l] == nums[i]; i++ { + + l = i + reduce = true + } + + for i := r - 1; r > 0 && nums[r] == nums[i]; i-- { + r = i + reduce = true + } + + if !reduce { + + if nums[l] < nums[m] { + + if target > nums[l] && target < nums[m] { + + r = m - 1 + } else { + + l = m + 1 + } + + } else { + + if target > nums[m] && target < nums[r] { + + l = m + 1 + } else { + + r = m - 1 + } + + } + } + + m = (l + r) / 2 + } + + return false +} diff --git a/Week 03/id_198/LeetCode_81_198_test.go b/Week 03/id_198/LeetCode_81_198_test.go new file mode 100644 index 000000000..30948b5d9 --- /dev/null +++ b/Week 03/id_198/LeetCode_81_198_test.go @@ -0,0 +1,26 @@ +package leetcode + +import "testing" + +func Test_search(t *testing.T) { + type args struct { + nums []int + target int + } + tests := []struct { + name string + args args + want bool + }{ + {name: "test1", args: args{nums: []int{1, 3, 1, 1, 1}, target: 3}, want: true}, + {name: "test2", args: args{nums: []int{1, 1, 3, 1}, target: 3}, want: true}, + {name: "test3", args: args{nums: []int{1, 1}, target: 0}, want: false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := search(tt.args.nums, tt.args.target); got != tt.want { + t.Errorf("search() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/Week 03/id_198/NOTE.md b/Week 03/id_198/NOTE.md index a6321d6e2..6b3441adc 100644 --- a/Week 03/id_198/NOTE.md +++ b/Week 03/id_198/NOTE.md @@ -1,4 +1,100 @@ # NOTE - +## 学习总结 +### 如何查找解 + +1. 分析问题适合的数据结构 +2. 选定适合的1中数据结构的算法 +3. 求解 + +## 使用二分查找,寻找一个半有序数组 [4, 5, 6, 7, 0, 1, 2] 中间无序的地方 + +该问题和[33. 搜索旋转排序数组](https://leetcode-cn.com/problems/search-in-rotated-sorted-array/)的解法类似。 +搜索旋转排序数组中,我的解法是优先确定有序部分(经过旋转的数组在使用二分法后,无论是左半部分还有右半部分,至少有一部分是有序的),判断是否目标值是否在有序部分,存在则继续在有序分查找,否则进入另一部分查找. +该题的同样,确定有序部分,若左半部分有序,则进入右半部分进入查找旋转点。 +比较mid值和mid-1的值, +* 若nums[mid-1] > nums[mid]则mid为无序点 +* 若nums[mid] > nums[mid+1]则mid+1为无序点 + +代码参考: + +## 本周课程涉及编程模板 + +### 二分查找模板 + +```python +left, right = 0, len(array) - 1 +while left <= right: + mid = (left + right) / 2 + if array[mid] == target: + # find the target!! + break or return result + elif array[mid] < target: + left = mid + 1 + else: + right = mid - 1 +``` + +### DFS - Recursion +```python +visited = set() + +def dfs(node, visited): +if node in visited: # terminator + # already visited + return + + visited.add(node) + + # process current node here. + ... + for next_node in node.children(): + if not next_node in visited: + dfs(next_node, visited) +``` +### DFS - loop + +```python +def DFS(self, tree): + + if tree.root is None: + return [] + + visited, stack = [], [tree.root] + + while stack: + node = stack.pop() + visited.add(node) + + process (node) + nodes = generate_related_nodes(node) + stack.push(nodes) + # other processing work + ... +``` + +### BFS + +```python +def BFS(graph, start, end): + + queue = [] + queue.append([start]) + visited.add(start) + + while queue: + node = queue.pop() + visited.add(node) + + process(node) + nodes = generate_related_nodes(node) + queue.push(nodes) + + # other processing work + ... +``` + +## 阅读文章 + +- [Origin of Quake3's Fast InvSqrt() - Page 1](https://www.beyond3d.com/content/articles/8/) \ No newline at end of file diff --git a/Week 03/id_198/sth_test.go b/Week 03/id_198/sth_test.go new file mode 100644 index 000000000..115c305e7 --- /dev/null +++ b/Week 03/id_198/sth_test.go @@ -0,0 +1,35 @@ +package leetcode + +import "testing" + +func TestSlice(t *testing.T) { + + t.Logf("%v\n", []int(nil)) + t.Logf("%v", []int{}) + t.Logf("%v", make([]int, 0)) + t.Logf("%v", make([]int, 2)) + t.Logf("val=%v,len=%d,cap=%d", *new([]int), len(*new([]int)), cap(*new([]int))) + t.Logf("val=%v,len=%d,cap=%d", *new([2]int), len(*new([2]int)), cap(*new([2]int))) + t.Logf("[2]int{} => val=%v,len=%d,cap=%d", [2]int{}, len([2]int{}), cap([2]int{})) + + a := make([]int, 2) + t.Logf("%#v => val=%v,len=%d,cap=%d", a, a, len(a), cap(a)) + a = append(a, 1) + t.Logf("%#v => val=%v,len=%d,cap=%d", a, a, len(a), cap(a)) + a = append(a, 1) + t.Logf("%#v => val=%v,len=%d,cap=%d", a, a, len(a), cap(a)) + a = append(a, 1) + t.Logf("%#v => val=%v,len=%d,cap=%d", a, a, len(a), cap(a)) + + a = nil + t.Logf("%#v => val=%v,len=%d,cap=%d", a, a, len(a), cap(a)) + + a = append(a, 1) + t.Logf("%#v => val=%v,len=%d,cap=%d", a, a, len(a), cap(a)) + + a = append(a, 1) + t.Logf("%#v => val=%v,len=%d,cap=%d", a, a, len(a), cap(a)) + + a = a[:0] + t.Logf("%#v => val=%v,len=%d,cap=%d", a, a, len(a), cap(a)) +} diff --git a/Week 03/id_203/LeetCode_200_203.go b/Week 03/id_203/LeetCode_200_203.go new file mode 100644 index 000000000..1d898499e --- /dev/null +++ b/Week 03/id_203/LeetCode_200_203.go @@ -0,0 +1,59 @@ +package week03 + +/** +给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。 + +示例 1: + +输入: +11110 +11010 +11000 +00000 + +输出: 1 +示例 2: + +输入: +11000 +11000 +00100 +00011 + +输出: 3 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/number-of-islands +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +var g [][]byte + +func numIslands(grid [][]byte) int { + isLands := 0 + + g = grid + + for i := 0; i < len(g); i++ { + for j := 0; j < len(g[i]); j++ { + isLands += sink(i, j) + } + } + + return isLands +} + +func sink(i, j int) int { + if i < 0 || i == len(g) || j < 0 || j == len(g[i]) || g[i][j] == '0' { + return 0 + } + + g[i][j] = '0' + + sink(i + 1, j) + sink(i - 1, j) + sink(i, j + 1) + sink(i, j - 1) + + return 1 +} \ No newline at end of file diff --git a/Week 03/id_203/LeetCode_33_203.go b/Week 03/id_203/LeetCode_33_203.go new file mode 100644 index 000000000..c8a232e78 --- /dev/null +++ b/Week 03/id_203/LeetCode_33_203.go @@ -0,0 +1,48 @@ +package week03 + +/** +假设按照升序排序的数组在预先未知的某个点上进行了旋转。 + +( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 + +搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。 + +你可以假设数组中不存在重复的元素。 + +你的算法时间复杂度必须是 O(log n) 级别。 + +示例 1: + +输入: nums = [4,5,6,7,0,1,2], target = 0 +输出: 4 +示例 2: + +输入: nums = [4,5,6,7,0,1,2], target = 3 +输出: -1 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +func search(nums []int, target int) int { + lo , hi := 0, len(nums) - 1 + + for lo <= hi { + mid := (lo + hi) / 2 + + if nums[mid] == target { + return mid + } + + if (nums[0] <= target && nums[mid] > target) || + (target < nums[mid] && nums[0] > nums[mid]) || + (nums[mid] < nums[0] && target >= nums[0]) { + hi = mid - 1 + } else { + lo = mid + 1 + } + } + + return -1 +} diff --git a/Week 03/id_213/FindContentChildren.java b/Week 03/id_213/FindContentChildren.java new file mode 100644 index 000000000..6ee4432e0 --- /dev/null +++ b/Week 03/id_213/FindContentChildren.java @@ -0,0 +1,18 @@ +//思路: 将两个小组排序:优先满足胃口小的小朋友,如果一个饼干不能不满足最小朋友胃口的需求, +// 将饼干尺寸数组的下标右移,直到能够满足小朋友的需求。计算到的小朋友胃口的人数即为所求。 +class Solution { + public int findContentChildren(int[] g, int[] s) { + + if (g == null || s == null) return 0; + Arrays.sort(g); + Arrays.sort(s); + int gi = 0, si = 0; + while (gi < g.length && si < s.length) { + if (g[gi] <= s[si]) { + gi++; + } + si++; + } + return gi; + } +} \ No newline at end of file diff --git a/Week 03/id_213/GenerateParenthesis.java b/Week 03/id_213/GenerateParenthesis.java new file mode 100644 index 000000000..5c0f6c9de --- /dev/null +++ b/Week 03/id_213/GenerateParenthesis.java @@ -0,0 +1,21 @@ +//思路: 产生括号对,首先要有左括弧,然后在有右括弧。左括弧大于等于右括弧,并且左括弧的数量必须小于等于n。 + +class Solution { + public List generateParenthesis(int n) { + List ans = new ArrayList(); + backtrack(ans, "", 0, 0, n); + return ans; + } + + public void backtrack(List ans, String cur, int open, int close, int max){ + if (cur.length() == max * 2) { + ans.add(cur); + return; + } + + if (open < max) + backtrack(ans, cur+"(", open+1, close, max); + if (close < open) + backtrack(ans, cur+")", open, close+1, max); + } +} \ No newline at end of file diff --git a/Week 03/id_213/LemonadeChange.java b/Week 03/id_213/LemonadeChange.java new file mode 100644 index 000000000..097f3330f --- /dev/null +++ b/Week 03/id_213/LemonadeChange.java @@ -0,0 +1,30 @@ +//思路:(1)当出现十元面值时,只能用五元,如果没有,返回false。 +// (2)当出现20元时,优先使用十元加上一张五元的组合,如果没有十元,用三张五元替换, +// 如果没有返回false; +// (3)当遍历完数组,则返回true; +class Solution { + public boolean lemonadeChange(int[] bills) { + int five = 0, ten = 0; + for (int bill: bills) { + if (bill == 5) + five++; + else if (bill == 10) { + if (five == 0) return false; + five--; + ten++; + } else { + if (five > 0 && ten > 0) { + five--; + ten--; + } else if (five >= 3) { + five -= 3; + } else { + return false; + } + } + } + + return true; + } +} + diff --git a/Week 03/id_213/LevelOrder.java b/Week 03/id_213/LevelOrder.java new file mode 100644 index 000000000..496e93b0d --- /dev/null +++ b/Week 03/id_213/LevelOrder.java @@ -0,0 +1,19 @@ +//思路:这个题目主要利用了ArrayList可以根据下标获取存储在数组中的元素, +//在每一层建立一个数组是这个题目的关键步骤。 +class LevelOrder { + List> levels = new ArrayList(); + public List> levelOrder(TreeNode root) { + if (root == null) return levels; + helper(root, 0); + return levels; + } + public void helper(TreeNode root,int level) { + if (levels.size() == level) + levels.add(new ArrayList()); + levels.get(level).add(root.val); + if (root.left != null) + helper(root.left, level + 1); + if (root.right != null) + helper(root.right, level + 1); + } +} \ No newline at end of file diff --git a/Week 03/id_213/MaxProfit.java b/Week 03/id_213/MaxProfit.java new file mode 100644 index 000000000..5465d8329 --- /dev/null +++ b/Week 03/id_213/MaxProfit.java @@ -0,0 +1,22 @@ +//思路:题目前提是我们可以获取股票在整个阶段的价格,做出左右选择。 +// (1)当股票价格一直上涨我们可以不用考虑,直到价格最高,获取最高收益。 +// (2)我们需要选择在股票下降的前一天抛出,然后在第二天以较低价格买入, +// (3)考虑到一个周期中有多次涨跌,我们用数组记录这些极点,用极大值的和减去极小值的和即为所求值/ +class Solution { + public int maxProfit(int[] prices) { + int i = 0; + int valley = prices[0]; + int peak = prices[0]; + int maxProfit = 0; + while (i < prices.length - 1) { + while (i < prices.length - 1 && prices[i] >= prices[i + 1]) + i++; + valley = prices[i]; + while (i < prices.length - 1 && prices[i] <= prices[i + 1]) + i++; + peak = prices[i]; + maxProfit += peak - valley; + } + return maxProfit; + } +} \ No newline at end of file diff --git a/Week 03/id_218/LeetCode_102_218.java b/Week 03/id_218/LeetCode_102_218.java new file mode 100644 index 000000000..953b5284c --- /dev/null +++ b/Week 03/id_218/LeetCode_102_218.java @@ -0,0 +1,46 @@ +package leetcode.week3; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.List; + +/** + * https://leetcode-cn.com/problems/binary-tree-level-order-traversal/#/description + * + * @author eason.feng at 2019/10/30/0030 13:22 + **/ +public class LeetCode_102_218 { + + List> res = new ArrayList>(); + + public List> levelOrder(TreeNode root) { + if (root == null) { + return res; + } + helper(root, 0); + return res; + } + + private void helper(TreeNode root, int level) { + if (res.size() == level) { + res.add(new ArrayList()); + } + res.get(level).add(root.val); + if (root.left != null) { + helper(root.left, level + 1); + } + if (root.right != null) { + helper(root.right, level + 1); + } + } + +} + +class TreeNode { + int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { + val = x; + } +} diff --git a/Week 03/id_218/LeetCode_153_218.java b/Week 03/id_218/LeetCode_153_218.java new file mode 100644 index 000000000..61203412c --- /dev/null +++ b/Week 03/id_218/LeetCode_153_218.java @@ -0,0 +1,34 @@ +package leetcode.week3; + +/** + * https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/submissions/ + * + * @author eason.feng at 2019/11/3/0003 18:51 + **/ +public class LeetCode_153_218 { + + public int findMin(int[] nums) { + if (nums == null || nums.length == 0) { + return -1; + } + if (nums.length == 1) { + return nums[0]; + } + return getMin(0, nums.length - 1, nums); + } + + private int getMin(int low, int high, int[] nums) { + int mid = (low + high) / 2; + if (nums[mid] < nums[high]) { + high = mid; + return getMin(low, high, nums); + } else { + if (low == high) { + return nums[low]; + } + low = mid + 1; + return getMin(low, high, nums); + } + } + +} diff --git a/Week 03/id_218/LeetCode_200_218.java b/Week 03/id_218/LeetCode_200_218.java new file mode 100644 index 000000000..1808d2b59 --- /dev/null +++ b/Week 03/id_218/LeetCode_200_218.java @@ -0,0 +1,44 @@ +package leetcode.week3; + +/** + * https://leetcode-cn.com/problems/number-of-islands/ + * + * @author eason.feng at 2019/10/30/0030 16:00 + **/ +public class LeetCode_200_218 { + + int dx[] = {-1, 1, 0, 0}; + int dy[] = {0, 0, -1, 1}; + char[][] g; + + public int numIslands(char[][] grid) { + int islands = 0; + g = grid; + for (int i = 0; i < g.length; i++) { + for (int j = 0; j < g[i].length; j++) { + if (g[i][j] == 0) { + continue; + } + islands += sink(i, j); + } + } + return islands; + } + + private int sink(int i, int j) { + if (g[i][j] == 0) { + return 0; + } + g[i][j] = 0; + for (int k = 0; k < dx.length; k++) { + int x = i + dx[k], y = j + dy[k]; + if (x > 0 && x < g.length && y < g[i].length) { + if (g[x][y] == 0) { + continue; + } + sink(x, y); + } + } + return 1; + } +} diff --git a/Week 03/id_218/LeetCode_200_218_Repeat2.java b/Week 03/id_218/LeetCode_200_218_Repeat2.java new file mode 100644 index 000000000..af9fbf46d --- /dev/null +++ b/Week 03/id_218/LeetCode_200_218_Repeat2.java @@ -0,0 +1,43 @@ +package leetcode.week3; + +/** + * @author eason.feng at 2019/10/31/0031 16:01 + **/ +public class LeetCode_200_218_Repeat2 { + + int dx[] = {-1, 1, 0, 0}; + int dy[] = {0, 0, -1, 1}; + char[][] g; + + public int numIslands(char[][] grid) { + g = grid; + int lands = 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0 ; j < grid[i].length; j++) { + if (grid[i][j] == 0) { + continue; + } + lands += sink(i, j); + } + } + return lands; + } + + private int sink(int i, int j) { + if (g[i][j] == 0) { + return 0; + } + g[i][j] = 0; + for (int k = 0; k < dx.length; k++) { + int x = i + dx[k], y = j + dy[k]; + if (x > 0 && x < g.length && y < g[i].length) { + if (g[x][y] == 0) { + continue; + } + sink(x, y); + } + } + + return 1; + } +} diff --git a/Week 03/id_218/LeetCode_33_218.java b/Week 03/id_218/LeetCode_33_218.java new file mode 100644 index 000000000..e24827a32 --- /dev/null +++ b/Week 03/id_218/LeetCode_33_218.java @@ -0,0 +1,86 @@ +package leetcode.week3; + +/** + * https://leetcode-cn.com/problems/search-in-rotated-sorted-array/submissions/ + * + * @author eason.feng at 2019/11/3/0003 19:17 + **/ +public class LeetCode_33_218 { + + public static void main(String[] args) { + LeetCode_33_218 leetCode_33_218 = new LeetCode_33_218(); + int arr[] = {4,5,6,7,0,1,2}; + int res = leetCode_33_218.search(arr, 5); + System.out.println(res); + } + + public int search(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return -1; + } + if (nums.length == 1) { + if (nums[0] == target) { + return 0; + } + else { + return -1; + } + } + return matchTarget(0, nums.length - 1, target, nums); + } + + private int matchTarget(int low, int high, int target, int[] nums) { + if(nums[low] == target) { + return low; + } + if (nums[high] == target) { + return high; + } + if (low == high) { + return -1; + } + int mid = (low + high) / 2; + if (nums[mid] == target) { + return mid; + } + else if (nums[mid] > target){ + if (nums[low] > target) { + if (nums[low] < nums[mid]) { + return this.matchTarget(mid + 1, high, target, nums); + } + else { + if (mid - 1 < 0 || mid - 1 < low) { + return -1; + } + return this.matchTarget(low, mid - 1, target, nums); + } + } + else { + if (mid - 1 < 0 || mid - 1 < low) { + return -1; + } + return this.matchTarget(low, mid - 1, target, nums); + } + } + else { + if (nums[mid] > nums[high]) { + return this.matchTarget(mid + 1, high, target, nums); + } + else { + if (nums[high] > target) { + if (mid + 1 >= nums.length || mid + 1 > high) { + return -1; + } + return this.matchTarget(mid + 1, high, target, nums); + } + else { + if (mid - 1 < 0 || mid - 1 < low) { + return -1; + } + return this.matchTarget(low, mid - 1, target, nums); + } + } + } + } + +} diff --git a/Week 03/id_218/LeetCode_74_218.java b/Week 03/id_218/LeetCode_74_218.java new file mode 100644 index 000000000..8a7368d04 --- /dev/null +++ b/Week 03/id_218/LeetCode_74_218.java @@ -0,0 +1,44 @@ +package leetcode.week3; + +/** + * @author eason.feng at 2019/11/3/0003 16:55 + **/ +public class LeetCode_74_218 { + + public boolean searchMatrix(int[][] matrix, int target) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + return false; + } + int col = matrix[0].length - 1; + int row = 0; + while (row < matrix.length) { + if (matrix[row][col] < target) { + row++; + } else if (matrix[row][col] > target) { + return binarySearch(matrix[row], target); + } else { + return true; + } + } + return false; + } + + private boolean binarySearch(int[] matrix, int target) { + int low = 0; + int high = matrix.length - 1; + while (low < high) { + int mid = (low + high) / 2; + if (matrix[mid] == target) { + return true; + } + if (matrix[mid] > target) { + high = mid - 1; + } + if (matrix[mid] < target) { + low = mid + 1; + } + } + return false; + } + +} diff --git a/Week 03/id_218/LeetCode_74v2_218.java b/Week 03/id_218/LeetCode_74v2_218.java new file mode 100644 index 000000000..29a193091 --- /dev/null +++ b/Week 03/id_218/LeetCode_74v2_218.java @@ -0,0 +1,25 @@ +package leetcode.week3; + +/** + * @author eason.feng at 2019/11/3/0003 18:35 + **/ +public class LeetCode_74v2_218 { + + public boolean searchMatrix(int[][] matrix, int target) { + if (matrix == null || matrix.length == 0) { + return false; + } + int col = matrix[0].length - 1; + int row = 0; + while (row < matrix.length && col >= 0) { + if (matrix[row][col] < target) { + row++; + } else if(matrix[row][col] > target) { + col--; + } else { + return true; + } + } + return false; + } +} diff --git a/Week 03/id_218/NOTE.md b/Week 03/id_218/NOTE.md index a6321d6e2..64d38860a 100644 --- a/Week 03/id_218/NOTE.md +++ b/Week 03/id_218/NOTE.md @@ -1,4 +1,62 @@ -# NOTE +### 知识点 +#### 深度优先搜索、广度优先搜索的实现和特性 - +#### 深度优先搜索 +* 搜索-遍历 + * 每个节点仅仅要访问一次 + * 对于节点的访问顺序不限 + * 深度优先:DFS (1:递归实现,需要注意节点有没有被访问;2:使用栈来实现) + * 广度优先:BFS(使用队列的方式+循环来实现) + * 优先级优先(该类算法称为启发式搜索, 深度学习 ) + +Todo:深度优先 广度优先 java版本模板 + +* floodfill思维方法。来源于算法题(题号200),找到一个岛,把自己及附近都夷为平地。(DFS) + + +#### 贪心算法(Greedy) + +贪心算法与动态规划的不同在于它对每个子问题的解决方案都做出选择,不能回退。动态规划是会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。 + +贪心算法一遍是解决一些最优化问题。如:求图中的最小生成树,求哈夫曼编码等。但是,贪心算法一般不能得到我们所要求的答案。 + +* 贪心:当下做局部最优判断; +* 回溯:能够回退; +* 动态规划:最优判断 + 回退。 + + +322 https://leetcode-cn.com/problems/coin-change/ + + + +#### 二分查找 + + +* 前提 + * 目标函数单调性(单调递增或者递减) + * 存在上下界(bounded) + * 能给通过索引访问(index accessable) + +* 代码模板 +``` +left, right = 0, len(array)-1 +while left <= right + mid = (left + right) / 2 + if array[mid] == target: + #find the target!! + elif array[mid] < target: + left = mid + 1 + else: + right = mid - 1 +``` + +https://shimo.im/docs/hjQqRQkGgwd9g36J/read +![2b5cbfaa196f83c88185d95d4e16703f.png](en-resource://database/2483:0) + +#### 总结 + +* 不断的复习和巩固了切题的四个步骤。其次,记住了很多类型的算法的对应模板。这块需要我日后反复记忆。才能帮助自己快速的解决问题。 +* 使用二分查找,寻找一个半有序数组 [4, 5, 6, 7, 0, 1, 2] 中间无序的地方 + * 1:按照二分查找,如果,array[mid + 1] > array[mid] 则 left = mid+1 + * 如果array[mid + 1] < array[mid] 则 mid +1 即为无序的地方。 diff --git a/Week 03/id_223/LeetCode_127_223.java b/Week 03/id_223/LeetCode_127_223.java new file mode 100644 index 000000000..185289b84 --- /dev/null +++ b/Week 03/id_223/LeetCode_127_223.java @@ -0,0 +1,48 @@ +public class Solution { + + public int ladderLength(String beginWord, String endWord, Set wordList) { + Set beginSet = new HashSet(), endSet = new HashSet(); + + int len = 1; + int strLen = beginWord.length(); + HashSet visited = new HashSet(); + + beginSet.add(beginWord); + endSet.add(endWord); + while (!beginSet.isEmpty() && !endSet.isEmpty()) { + if (beginSet.size() > endSet.size()) { + Set set = beginSet; + beginSet = endSet; + endSet = set; + } + + Set temp = new HashSet(); + for (String word : beginSet) { + char[] chs = word.toCharArray(); + + for (int i = 0; i < chs.length; i++) { + for (char c = 'a'; c <= 'z'; c++) { + char old = chs[i]; + chs[i] = c; + String target = String.valueOf(chs); + + if (endSet.contains(target)) { + return len + 1; + } + + if (!visited.contains(target) && wordList.contains(target)) { + temp.add(target); + visited.add(target); + } + chs[i] = old; + } + } + } + + beginSet = temp; + len++; + } + + return 0; + } + } \ No newline at end of file diff --git a/Week 03/id_223/LeetCode_33_223.java b/Week 03/id_223/LeetCode_33_223.java new file mode 100644 index 000000000..2a8e35208 --- /dev/null +++ b/Week 03/id_223/LeetCode_33_223.java @@ -0,0 +1,15 @@ +class Solution { + public int search(int[] nums, int target) { + int left = 0; + int right = nums.length-1; + while (left < right) { + int mid = (left + right) / 2; + if (nums[0] <= nums[mid] && (target > nums[mid] || target < nums[0])) { + left = mid + 1; + }else if(target < nums[0] && target > nums[mid]) { + left = mid + 1; + }else right = mid; + } + return left == right && target == nums[left] ? left : -1; + } +} \ No newline at end of file diff --git a/Week 03/id_223/NOTE.md b/Week 03/id_223/NOTE.md index a6321d6e2..ef28ae4e6 100644 --- a/Week 03/id_223/NOTE.md +++ b/Week 03/id_223/NOTE.md @@ -1,4 +1,106 @@ # NOTE +## Week 3 +### 深度优先搜索和广度优先搜索 +#### Part 1 +- 每个节点都要访问一次 +- 每个节点仅仅要访问一次 +``` +DFS 代码 - 递归写法 +visited = set() +def dfs(node, visited): + if node in visited: # terminator + # already visited +return +visited.add(node) +# process current node here. +... +for next_node in node.children(): if not next_node in visited: + dfs(next node, visited) +``` +``` +非递归写法 +def DFS(self, tree): + + if tree.root is None: + return [] + + visited, stack = [], [tree.root] + + while stack: + node = stack.pop() + visited.add(node) + + process (node) + nodes = generate_related_nodes(node) + stack.push(nodes) + + # other processing work + ... +``` + +``` +BFS 代码模板 - 广度优先 +def BFS(graph, start, end): + + queue = [] + queue.append([start]) + visited.add(start) + + while queue: + node = queue.pop() + visited.add(node) + + process(node) + nodes = generate_related_nodes(node) + queue.push(nodes) + + # other processing work + ... +``` +#### Part2 +- 深度优先搜索(DFS) + +在这个策略中,我们采用深度作为优先级,以便从跟开始一直到达某个确定的叶子,然后再返回根到达另一个分支。 + +深度优先搜索策略又可以根据根节点、左孩子和右孩子的相对顺序被细分为先序遍历,中序遍历和后序遍历。 + +- 广度优先搜索(BFS) + +我们按照高度顺序一层一层的访问整棵树,高层次的节点将会比低层次的节点先被访问到。 + + +### 贪心算法 +#### Part 1 +> 贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有 利)的选择,从而希望导致结果是全局最好或最优的算法。 +> 贪心算法与动态规划的不同在于它对每个子问题的解决方案都做出选择,不 能回退。动态规划则会保存以前的运算结果,并根据以前的结果对当前进行 选择,有回退功能。 +- 贪心:当下做局部最优判断 +- 回溯:能够回退 +- 动态规划:最优判断 + 回退 + + +### 二分查找 +#### Part1 +##### 二分查找的前提 +1. 目标函数单调性(单调递增或者递减) y=x^2; +1. 存在上下界(bounded) +1. 能够通过索引访问(index accessible) + + +``` +代码模版 +left, right = 0, len(array) - 1 +while left <= right: + mid = (left + right) / 2 + if array[mid] == target: + # find the target!! + break or return result + elif array[mid] < target: + left = mid + 1 + else: +right = mid - 1 +``` +#### Part2 +##### 半有序数组查找无序的地方 diff --git a/Week 03/id_233/LeetCode_35_233.java b/Week 03/id_233/LeetCode_35_233.java new file mode 100644 index 000000000..6c7765c16 --- /dev/null +++ b/Week 03/id_233/LeetCode_35_233.java @@ -0,0 +1,25 @@ +package com.luxuedong.BinarySearchPractice; + +/** + * describe: + * creator: luxuedong + * date: 2019/11/3 14:10 + */ +public class LeetCode_35_searchInsert { + public int searchInsert(int[] nums, int target) { + int max, min, mid; + min = 0; + max = nums.length - 1; + + while (min <= max) { + mid = (max + min) >> 1; + if (target > nums[mid]) + min = mid + 1; + else if (target < nums[mid]) + max = mid - 1; + else + return mid; + } + return min; + } +} diff --git a/Week 03/id_233/LeetCode_69_233.java b/Week 03/id_233/LeetCode_69_233.java new file mode 100644 index 000000000..664e9638e --- /dev/null +++ b/Week 03/id_233/LeetCode_69_233.java @@ -0,0 +1,64 @@ +package com.luxuedong.BinarySearchPractice; + +/** + * describe: + * creator: luxuedong + * date: 2019/10/31 17:01 + */ +public class LeetCode_69_mySqrt { + + public int mySqrt1(int x) { + if (x <= 1) return x; + long min = 1; + long max = x; + while (min < max) { + long mid = min + (max - min + 1) / 2;//避免 max+min 溢出 + if (mid * mid > x) + max = mid - 1; + else + min = mid; + } + return (int) min; + } + + /** + * 二分查找 + * + * @param x + * @return + */ + public int mySqrt2(int x) { + long min = 0; + long max = x / 2 + 1; + while (min < max) { + long mid = (min + max + 1) >>> 1; + long square = mid * mid; + if (square > x) { + max = mid - 1; + } else { + min = mid; + } + } + return (int) min; + } + + /** + * 牛顿迭代法 + * + * @param x + * @return + */ + public int mySqrt3(int x) { + if (x <= 1) return x; + long r = x; + while (r * r > x) + r = (r + x / r) >> 1; + return (int) r; + } + + public static void main(String[] args) { + LeetCode_69_mySqrt mySqrt = new LeetCode_69_mySqrt(); + int i = mySqrt.mySqrt1(2147395600); + System.out.println(i); + } +} diff --git a/Week 03/id_233/LeetCode_704_233.java b/Week 03/id_233/LeetCode_704_233.java new file mode 100644 index 000000000..70df1bcd5 --- /dev/null +++ b/Week 03/id_233/LeetCode_704_233.java @@ -0,0 +1,25 @@ +package com.luxuedong.BinarySearchPractice; + +/** + * describe: + * creator: luxuedong + * date: 2019/11/3 14:12 + */ +public class LeetCode_704_search { + public int search(int[] nums, int target) { + int max, min, mid; + min = 0; + max = nums.length - 1; + + while (min <= max) { + mid = (max + min) >> 1; + if (target > nums[mid]) + min = mid + 1; + else if (target < nums[mid]) + max = mid - 1; + else + return mid; + } + return -min - 1; + } +} diff --git a/Week 03/id_233/NOTE.md b/Week 03/id_233/NOTE.md index a6321d6e2..8599ce893 100644 --- a/Week 03/id_233/NOTE.md +++ b/Week 03/id_233/NOTE.md @@ -1,4 +1,78 @@ -# NOTE +# 233-Week 03 学习总结 +## 知识点 +### 1 搜索 +- 搜索就是在树(图/状态集)中寻找特定节点 +- 搜索的本质就是每个节点都要访问一次,且只访问一次 +- 根据节点访问顺序不同可分为: + - 深度优先 DFS + - 广度优先 BFS +``` +//DFS 代码模版 递归实现 +visited = set() +def dfs(node, visited): +if node in visited: # terminator + # already visited + return + visited.add(node) + # process current node here. + ... + for next_node in node.children(): + if not next_node in visited: + dfs(next_node, visited) +//DFS 代码模版 循环+栈实现 +def dfs(self, tree): + if tree.root is None: + return [] + visited, stack = [], [tree.root] + while stack: + node = stack.pop() + visited.add(node) + process (node) + nodes = generate_related_nodes(node) + stack.push(nodes) - + # other processing work + ... +//BFS 代码模版 循环+队列 实现 +def BFS(graph, start, end): + queue = [] + queue.append([start]) + visited.add(start) + while queue: + node = queue.pop() + visited.add(node) + process(node) + nodes = generate_related_nodes(node) + queue.push(nodes) + # other processing work + ... +``` +### 2 贪心算法 Greedy +- 贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(最有利)的选择,从而希望导致结果是全局最好或最优的算法 +- 对每个子问题的解决方案都做出最优选择,==***且不能回退!!!***== +- 贪心、回溯、动态比较 + - 贪心:当下局部最优判断 + - 回溯:能够回退 + - 动态规划:动态规划会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能 +### 3 二分查找 BinarySearch +- 二分查找法又称折半查找法 +- 二分查找前提 + - 目标函数单调性(单调递增或单调递减)***==有序(sorted)==*** + - 存在上下界 ***==(bounded)==*** + - 能够通过索引访问 ***==(index accessidle)==*** +- [二分查找代码模版解读](https://leetcode-cn.com/problems/search-insert-position/solution/te-bie-hao-yong-de-er-fen-cha-fa-fa-mo-ban-python-/) +``` +//代码模版 +left, right = 0, len(array) - 1 +while left <= right: + mid = (left + right) / 2 + if array[mid] == target: + # find the target!! + break or return result + elif array[mid] < target: + left = mid + 1 + else: + right = mid - 1 +return -left-1//left>right的情况 +``` \ No newline at end of file diff --git a/Week 03/id_243/LeetCode_220_243.java b/Week 03/id_243/LeetCode_220_243.java new file mode 100644 index 000000000..c172eb8e0 --- /dev/null +++ b/Week 03/id_243/LeetCode_220_243.java @@ -0,0 +1,77 @@ +/** + * @author eazonshaw + * @date 2019/11/3 16:39 + * + * 题目:220.岛屿数量 + * + * 描述:给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。 + * + * 例子: + * 输入: + * 11000 + * 11000 + * 00100 + * 00011 + * 输出: 3 + * + * 链接:https://leetcode-cn.com/problems/number-of-islands/solution/dao-yu-shu-liang-by-leetcode/ + * + */ +public class LeetCode_220_243 { + + public int numIslands(char[][] grid) { + + //临界值判断 + if(grid == null || grid.length == 0){ + return 0; + } + + //x轴 + int x = grid[0].length; + //y轴 + int y = grid.length; + //返回值 + int nums = 0; + + + //双循环 + for(int y_now = 0;y_now < y;y_now ++){ + for(int x_now = 0;x_now < x;x_now ++){ + //如果遇到岛屿,则记录 + if(grid[y_now][x_now] == '1'){ + nums++; + //下沉周围的岛屿 + dfs(grid,x_now,y_now); + } + } + } + return nums; + } + + private void dfs(char[][] grid, int x_now, int y_now) { + //终止条件 + //判断是否越界 + int x = grid[0].length; + int y = grid.length; + if(x_now>=x || y_now>=y || x_now<0 || y_now<0){ + return; + } + + //判断当前坐标是否为岛屿 + if(grid[y_now][x_now] == '0'){ + return; + } + + //执行当前层逻辑,下沉岛屿 + grid[y_now][x_now] = '0'; + + //下坠,继续判断当前位置的上下左右坐标 + dfs(grid,x_now,y_now-1); + dfs(grid,x_now,y_now+1); + dfs(grid,x_now-1,y_now); + dfs(grid,x_now+1,y_now); + + } + + +} diff --git a/Week 03/id_243/LeetCode_33_243.java b/Week 03/id_243/LeetCode_33_243.java new file mode 100644 index 000000000..f14fff1be --- /dev/null +++ b/Week 03/id_243/LeetCode_33_243.java @@ -0,0 +1,44 @@ +/** + * @author eazonshaw + * @date 2019/11/3 22:52 + * 题目:33. 搜索旋转排序数组 + * + * 描述:假设按照升序排序的数组在预先未知的某个点上进行了旋转。 + * ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 + * 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。 + * 你可以假设数组中不存在重复的元素。 + * 你的算法时间复杂度必须是 O(log n) 级别。 + * + * 例子: + * 输入: nums = [4,5,6,7,0,1,2], target = 0 + * 输出: 4 + * + * 链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array/ + */ +public class LeetCode_33_243 { + + //思路:硬上二分查找的模板 + public int search(int[] nums, int target) { + //左边界 + int left = 0; + //右边界 + int right = nums.length-1; + + while (left> 1); + //如果[0,mid]为有序,向右规约 + if(nums[0]<=nums[mid] && (target>nums[mid] || targetnums[mid] && target=0;i--){ + //实时更新从后往前遍历过程中能达到的最远坐标位置 + if(nums[i] + i >= canReachable){ + canReachable = i; + } + } + //最远坐标位置是起点 + return canReachable == 0; + } + + +} diff --git a/Week 03/id_243/NOTE.md b/Week 03/id_243/NOTE.md index a6321d6e2..141c7a0d4 100644 --- a/Week 03/id_243/NOTE.md +++ b/Week 03/id_243/NOTE.md @@ -1,4 +1,165 @@ -# NOTE +# Week 3 学习总结 +## DFS & BFS +> 根据老师的python模板,手写了一个java的模板,不过还是unchecked的,待修改。感觉这部分最重要的就是对两种搜索算法的模板代码的理解和记忆,然后就是灵活运用。 + +链接: +* [DFS模板-python](https://shimo.im/docs/ddgwCccJQKxkrcTq/read) +* [BFS模板-python](https://shimo.im/docs/P8TqKHGKt3ytkYYd/read) + +1. DFS (Depth First Search) 深度优先搜索 + +> 模板(递归) +``` +//定义一个泛型 +private List visited = new ArrayList<>(); + +//递归函数 +private void dfs(Node node){ + + //终止条件 + if(visited.contains(node)){ + return; + } + + //当前层逻辑 + visited.add(node); + + //遍历当前节点的子节点 + for(Node childNode:node.children){ + if(!visited.contains(childNode)){ + dfs(childNode); + } + } + +} +``` +> 模板(栈) +``` +// 手动维护一个栈 + // 手动维护一个栈 + void dfs(Node node){ + //如果根节点为空,则返回 + if(node == null){ + return; + } + + //定义输出泛型 + List visited = new ArrayList<>(); + + //手动维护一个栈,并初始化 + Stack stack = new Stack<>(); + stack.push(node); + + while (!stack.isEmpty()){ + //当前节点出栈 + Node currentNode = stack.pop(); + //记录访问 + visited.add(currentNode); + //执行逻辑 + process(currentNode); + // + for(Node childNode:currentNode.children){ + stack.push(childNode); + } + } + + } +``` +2. BFS(Breadth First Search) 广度优先搜索 +> 模板(队列) +``` +private void bfs(Node root){ + //初始化队列 + Queue queue = new LinkedList<>(); + //当前数组 + List visited = new ArrayList<>(); + //初始化队列 + queue.add(root); + //队列不为空 + while (!queue.isEmpty()){ + //取出队头 + Node currentNode = queue.poll(); + if(currentNode==null || visited.contains(currentNode)) continue; + //记录访问 + visited.add(currentNode); + //执行逻辑 + pocess(currentNode); + //将当前的节点的子节点入队 + queue.addAll(currentNode.children); + + } +} +``` +## 贪心算法(greedy algorithm) +### 定义 +一种在每一步选择中都采取在当前状态下最好或最优(最有利)的选择,从而希望导致的结果时全局最好或最优的算法。 +### 比较 +1. 贪心算法对每个子问题的解决方案都做出选择,不能回退。动态规划则会保存以前的运算结果,并根据以前结果对当前进行选择,具有回退功能。 +2. 对比: +* 贪心:当下做局部最优判断。 +* 回溯:能够回退。 +* 动态规划:最优判断+回退。 +### 重点难点 +> 动态规划的场景一般会有**期望值**,以及满足期望值所限制的**限制值**。 + +重点是证明场景可以用贪心算法: +* 贪心算法能够得到最优解; +* 可以从前往后、从后往前或局部切入进行贪心。 +### 应用场景 +* Huffman coding 霍夫曼编码 +> 霍夫曼编码是一种十分有效的编码方法,广泛用于数据压缩中,其压缩率通常在 20%~90% 之间。 +思想就是,将出现频率高的字符尽可能用比较短的编码转换,同时避免冲突。 +* Prim和kruskua最小生成树 +* Dijkstra单源最短路径算法 +## 二分查找(Binary Search) +二分查找的前提: +1. 目标函数的单调性 +2. 存在上下界 +3. 能够通过索引访问 +> 核心:分治思想。时间复杂度:O(logN) + +使用场景: +1. 顺序表结构,如数组:如果使用链表的话,在查找时的时间复杂度高,当然,如果链表使用跳表等情况的话例外。 +2. 针对的是**有序**的数据。 +3. 数据量太小不适用:因为这个时候往往直接遍历即可。不过有个情况例外,当比较环节较麻烦,如两个长字符串进行比较时,为了减少比较环节的耗时,用二分查找更有优势。 +4. 数据量太大不适用:因为底层是用数组,需要在内存开一个很长的连续空间。 +5. 适用于静态数据,没有频繁的数据插入和删除操作。 + +### 二分查找模板 +``` +public int bsearch(int[] a, int n, int value) { + int low = 0; + int high = n - 1; + + while (low <= high) { + int mid = (low + high) / 2; + if (a[mid] == value) { + return mid; + } else if (a[mid] < value) { + low = mid + 1; + } else { + high = mid - 1; + } + } + + return -1; +} +``` +> 代码中,为了避免在数据较大时,low+high大小会溢出,可以换种写法:`low + ((high - low) >> 1);`。 + +### 思考题: +> 使用二分查找,寻找一个半有序数组 [4, 5, 6, 7, 0, 1, 2] 中间无序的地方。时间复杂度为O(logN)。 + +思路:取中间点mid,与左边界left进行比较, +若[left,mid]是有序的,即`nums[mid]>nums[left]`,则向右规约; +若[left,mid]不是有序的,即`nums[mid]> levelOrder(TreeNode root) { + List> ans = new LinkedList<>(); + if(root == null) return ans; + Queue q = new LinkedList(); + q.add(root); + int level = 0; + while(q.isEmpty() == false){ + ans.add(new ArrayList()); + int size = q.size(); + for(int i=0;i> ans = new LinkedList<>(); +// public void dfs(TreeNode root,int depth){ +// List temp = new LinkedList<>(); +// if(ans.size() == depth){ +// ans.add(new LinkedList<>()); +// } +// ans.get(depth).add(root.val); +// if(root.left != null) dfs(root.left,depth+1); +// if(root.right != null) dfs(root.right,depth+1); +// } +// public List> levelOrder(TreeNode root) { +// if(root == null) return ans; +// dfs(root,0); +// return ans; +// } +// } diff --git a/Week 03/id_253/LeetCode_122_253.java b/Week 03/id_253/LeetCode_122_253.java new file mode 100644 index 000000000..72e8b813f --- /dev/null +++ b/Week 03/id_253/LeetCode_122_253.java @@ -0,0 +1,12 @@ +class Solution { + public int maxProfit(int[] prices) { + if(prices.length == 0) return 0; + int sum=0; + for(int i=0;i wordList) { + if(wordList.contains(endWord) == false) return 0; + Queue q = new LinkedList<>(); + HashSet dic = new HashSet<>(wordList); + HashSet visited = new HashSet<>(); + visited.add(beginWord); + q.add(beginWord); + int count =1; + while(q.isEmpty() == false){ + int size = q.size(); + for(int i=0;i=nr || j>=nc || grid[i][j] == '0') return; + grid[i][j] = '0'; + sink(i-1,j,grid); + sink(i+1,j,grid); + sink(i,j-1,grid); + sink(i,j+1,grid); + } + public int numIslands(char[][] grid) { + if(grid == null || grid.length == 0) return 0; + int nr=grid.length;int nc = grid[0].length; + int islands=0; + for(int i=0;i= nums[0] && (target>nums[mid] || targetnums[mid]){ + left=mid+1; + }else{ + right=mid; + } + } + return (left==right&&nums[left]==target)?left:-1; + } +} diff --git a/Week 03/id_253/LeetCode_367_253.java b/Week 03/id_253/LeetCode_367_253.java new file mode 100644 index 000000000..b2d4113dc --- /dev/null +++ b/Week 03/id_253/LeetCode_367_253.java @@ -0,0 +1,17 @@ +class Solution { + public boolean isPerfectSquare(int num) { + long left=0;long right = num; + long mid; + while(left < right){ + mid = left+(right-left+1)/2; + if(mid * mid == num) return true; + if(mid * mid > num){ + right=mid-1; + } + if(mid * mid < num){ + left=mid; + } + } + return false; + } +} diff --git a/Week 03/id_253/LeetCode_455_253.java b/Week 03/id_253/LeetCode_455_253.java new file mode 100644 index 000000000..389946c2d --- /dev/null +++ b/Week 03/id_253/LeetCode_455_253.java @@ -0,0 +1,18 @@ +class Solution { + public int findContentChildren(int[] g, int[] s) { + int i=0;int j=0; + int ans=0; + Arrays.sort(g); + Arrays.sort(s); + while(j=g[i]){ + ans++; + j++; + i++; + }else{ + j++; + } + } + return ans; + } +} diff --git a/Week 03/id_253/LeetCode_45_253.java b/Week 03/id_253/LeetCode_45_253.java new file mode 100644 index 000000000..9fa517dbf --- /dev/null +++ b/Week 03/id_253/LeetCode_45_253.java @@ -0,0 +1,13 @@ +class Solution { + public int jump(int[] nums) { + int jumps=0,end=0,current=0; + for(int i=0;i=m || y<0 || y>=n || board[x][y] != 'E') return; + int mine = adjMine(board,x,y,m,n); + if(mine > 0) board[x][y]=(char)(mine+'0'); + else{ + board[x][y] ='B'; + for(int[] dir : dirc){ + dfs(board,x+dir[0],y+dir[1],m,n,dirc); + } + } + } + public int adjMine(char[][] board ,int x,int y ,int m ,int n){ + int cnt=0; + for(int i=x-1;i<=x+1;i++){ + for(int j=y-1;j<=y+1;j++){ + if(i>=0 && j>=0 && i=0;i--){ + if(nums[i]+i>=reach) reach=i; + } + return reach==0; + } +} diff --git a/Week 03/id_253/LeetCode_69_253.java b/Week 03/id_253/LeetCode_69_253.java new file mode 100644 index 000000000..d0f82296b --- /dev/null +++ b/Week 03/id_253/LeetCode_69_253.java @@ -0,0 +1,9 @@ +class Solution { + public int mySqrt(int x) { + long a=x; + while(a*a>x){ + a=(a+x/a)/2; + } + return (int) a; + } +} diff --git a/Week 03/id_253/LeetCode_74_253.java b/Week 03/id_253/LeetCode_74_253.java new file mode 100644 index 000000000..bf7e85595 --- /dev/null +++ b/Week 03/id_253/LeetCode_74_253.java @@ -0,0 +1,20 @@ +class Solution { + public boolean searchMatrix(int[][] matrix, int target) { + int m=matrix.length; + if(m==0) return false; + int n=matrix[0].length; + int a; + int left=0;int right = n*m-1;int mid; + while(left<=right){ + mid=(left+right)/2; + a=matrix[mid/n][mid%n]; + if(target == a) return true; + else{if(target>=a){ + left=mid+1; + }else{ + right=mid-1; + }} + } + return false; + } +} diff --git a/Week 03/id_253/LeetCode_860_253.java b/Week 03/id_253/LeetCode_860_253.java new file mode 100644 index 000000000..cc72a1838 --- /dev/null +++ b/Week 03/id_253/LeetCode_860_253.java @@ -0,0 +1,27 @@ +class Solution { + public boolean lemonadeChange(int[] bills) { + if(bills.length == 0) return false; + int five=0;int ten=0; + if(bills[0] != 5) return false; + for(int i=0;i0){ + five--; + }else{ + return false; + } + } + if(bills[i] == 20) { + if(ten>0 && five>0){ + ten--; + five--; + }else if(ten ==0 && five >2){ + five-=3; + }else return false; + } + } + return true; + } +} diff --git a/Week 03/id_253/LeetCode_874_253.java b/Week 03/id_253/LeetCode_874_253.java new file mode 100644 index 000000000..e91a9fecb --- /dev/null +++ b/Week 03/id_253/LeetCode_874_253.java @@ -0,0 +1,26 @@ +class Solution { + public int robotSim(int[] commands, int[][] obstacles) { + Set set = new HashSet<>(); + for(int[] ob : obstacles){ + set.add(ob[0]+" "+ob[1]); + } + int d=0;int x=0;int y=0;int result=0; + int[][] dir={{0,1},{1,0},{0,-1},{-1,0}}; + for(int c : commands){ + if(c == -1){ + d++; + if(d==4) d=0; + } + else if(c == -2){ + d--; + if(d== -1) d=3; + }else{ + while(c-->0 && set.contains((x+dir[d][0])+" "+(y+dir[d][1]))==false){ + x+=dir[d][0];y+=dir[d][1]; + } + } + result=Math.max(result,x*x+y*y); + } + return result; + } +} diff --git a/Week 03/id_258/LeetCode_102_258.js b/Week 03/id_258/LeetCode_102_258.js new file mode 100644 index 000000000..c9ead3041 --- /dev/null +++ b/Week 03/id_258/LeetCode_102_258.js @@ -0,0 +1,40 @@ +/* + * @lc app=leetcode.cn id=102 lang=javascript + * + * [102] 二叉树的层次遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {number[][]} + */ +var levelOrder = function (root) { + let result = []; + let level = 0; + + var dfs = function (root, level) { + if (!root) { + return; + } + + if (level >= result.length) { + result[level] = []; + } + result[level].push(root.val); + level++ + dfs(root.left, level) + dfs(root.right, level) + } + + dfs(root, level); + return result; +}; +// @lc code=end \ No newline at end of file diff --git a/Week 03/id_258/LeetCode_200_258.js b/Week 03/id_258/LeetCode_200_258.js new file mode 100644 index 000000000..e1219340d --- /dev/null +++ b/Week 03/id_258/LeetCode_200_258.js @@ -0,0 +1,50 @@ +/* + * @lc app=leetcode.cn id=200 lang=javascript + * + * [200] 岛屿数量 + */ + +// @lc code=start +/** + * @param {character[][]} grid + * @return {number} + */ +var numIslands = function (grid) { + let count = 0; + + function depthSearch(x, y) { + if (grid[x][y] === '1') { + grid[x][y] = '0'; + } else { + return + } + + if (x < grid.length - 1) { + depthSearch(x + 1, y); + } + + if (y < grid[x].length - 1) { + depthSearch(x, y + 1); + } + + if (x > 0 && x < grid.length) { + depthSearch(x - 1, y); + } + + if (y > 0 && y < grid[x].length) { + depthSearch(x, y - 1); + } + } + + for (let i = 0; i < grid.length; i++) { + for (let j = 0; j < grid[i].length; j++) { + if (grid[i][j] === '1') { + count++; + depthSearch(i, j); + } + } + } + + return count; +}; +// @lc code=end \ No newline at end of file diff --git a/Week 03/id_258/LeetCode_33_258.js b/Week 03/id_258/LeetCode_33_258.js new file mode 100644 index 000000000..4e65763fc --- /dev/null +++ b/Week 03/id_258/LeetCode_33_258.js @@ -0,0 +1,42 @@ +/* + * @lc app=leetcode.cn id=33 lang=javascript + * + * [33] 搜索旋转排序数组 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ +var search = function (nums, target) { + let left = 0; + + let right = nums.length - 1; + + while (left <= right) { + let mid = Math.floor((left + right) / 2); + + if (nums[mid] === target) { + return mid; + } + + if (nums[left] <= nums[mid]) { + if (nums[left] <= target && target <= nums[mid]) { + right = mid - 1; + } else { + left = mid + 1; + } + } else { + if (nums[right] >= target && target >= nums[mid]) { + left = mid + 1; + } else { + right = mid - 1; + } + } + } + + return -1; +}; +// @lc code=end \ No newline at end of file diff --git a/Week 03/id_273/LeetCode_102_273.java b/Week 03/id_273/LeetCode_102_273.java new file mode 100644 index 000000000..f28638a28 --- /dev/null +++ b/Week 03/id_273/LeetCode_102_273.java @@ -0,0 +1,24 @@ +//102. 二叉树层序遍历 + +//解法1:广度优先算法 执行用时击败99% +//思路:通过维护一个队列遍历每一层获取到的节点 +//时间复杂度O(N) +//空间复杂度O(N) +public List> levelOrder(TreeNode root) { + List> result = new ArrayList<>(); + if (root == null) return result; + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + int size = queue.size(); + List temp = new ArrayList<>(); + for (int i = 0; i < size; i++) { + TreeNode curr = queue.poll(); + temp.add(curr.val); + if (curr.left != null) queue.offer(curr.left); + if (curr.right != null) queue.offer(curr.right); + } + result.add(temp); + } + return result; +} diff --git a/Week 03/id_273/LeetCode_122_273.java b/Week 03/id_273/LeetCode_122_273.java new file mode 100644 index 000000000..302e748fe --- /dev/null +++ b/Week 03/id_273/LeetCode_122_273.java @@ -0,0 +1,84 @@ +//122. 买卖股票的最佳时机II + + +//股票问题统一分析: +//1. 穷举所有的"状态" +// 每天都可以有三种选择分别是:买入buy, 卖出sell, 无操作rest +// 买入必须在卖出之后, 因为题目限制只能完成一次交易后才能开始第二次交易 +// 卖出必须在买入之后, 因为卖出的前提是要持有股票 +// 无操作可以在买入后继续保持持有股票, 也可以在卖出后继续保持不持有股票 + +// 那么可以通过一个三维数组存放这几种状态的全部组合:DP[i][k][0 or 1] +// 语义为:当前为第i天,还能再进行k次交易,当前未持有/持有股票 +// 最后要求的结果则是:DP[n - 1][0][0] +// 即:最后一天, 不能再进行交易, 且手上没持有股票 + +//2. 状态转移方程: +// 那么统一的状态转移方程就可以确定如下: +// 1. DP[i][k][0] = max(DP[i - 1][k][0], DP[i - 1][k][1] + prices[i]) +// 即:第i天未持有股票, 那么可以从中择优:i - 1天也未持有股票, 第i天继续保持未持有; i - 1天持有股票, 第i天抛售股票完成一次交易 +// 2. DP[i][k][1] = max(DP[i - 1][k][1], DP[i - 1][k - 1][0] - prices[i]) +// 即:第i天持有股票, 那么可以从中择优:i - 1天持有股票, 第i天继续保持持有; i - 1天未持有股票, 进行一次交易k - 1, 买入一支股票 +// BaseCase: +// DP[i][0][0] = 0 :因为可交易次数为0, 不允许进行买入操作, 因此利润一定是0 + +//3. 当前问题分析 +// 对于当前题目的要求, K = +infinite, 也就是这几天内可以进行多笔交易, 则状态方程如下: +// 1. DP[i][k][0] = max(DP[i - 1][k][0], DP[i - 1][k][1] + prices[i]) +// 2. DP[i][k][1] = max(DP[i - 1][k][1], DP[i - 1][k - 1][0] - prices[i]) +// 可以发现第2个转移方程中出现的"DP[i - 1][k - 1][0]", 由于k为正无穷, 因此k - 1 = k +// 而所有k对状态转移不产生影响, 因此状态K可以省略不写 +// 到最后, 这个问题的状态转移方程如下: +// 1. DP[i][0] = max(DP[i - 1][0], DP[i - 1][1] + prices[i]) +// 2. DP[i][1] = max(DP[i - 1][1], DP[i - 1][0] - prices[i]) + + + + +//解法1:贪心思想 执行用时:1ms +//思路:若当天价格大于前一天的价格, 就进行买卖操作, 也就是获取差值总额 +//时间复杂度O(n) +//空间复杂度O(1) +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int sum = 0; + for (int i = 0; i < prices.length - 1; i++) { + if (prices[i] < prices[i + 1]) sum += prices[i + 1] - prices[i]; + } + return sum; +} + +//解法2:动态规划 执行用时:3ms +//思路:和121题基本一致, 通过一个二维数组存储每一天持有股票和未持有股票的利润最大值 +// 不同点在于:现在我们可以进行多笔交易。因此, 若当天选择持有股票, 情况就稍有变化: +// 当天可以选择继续持有前一天的股票以及买入股票。若选择买入股票, 那么就需要加上当天抛售股票获得的利润额 +// dp[i][1] = max(dp[i - 1][1], dp[i][0] - prices[i]) +//时间复杂度:O(n) +//空间复杂度:O(n) +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int[][] dp = new int[prices.length][2]; + dp[0][1] = -prices[0]; + dp[0][0] = 0; + for (int i = 1; i < prices.length; 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][0] - prices[i]); + } + return dp[dp.length - 1][0]; +} + +//解法2.1:动态规划-空间压缩 执行用时:2ms +//思路:参考121动态规划的空间压缩 +//时间复杂度:O(n) +//空间复杂度:O(1) +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int[] dp = new int[2]; + dp[0] = 0; + dp[1] = -prices[0]; + for (int i = 1; i < prices.length; i++) { + dp[0] = Math.max(dp[0], dp[1] + prices[i]); + dp[1] = Math.max(dp[1], dp[0] - prices[i]); + } + return dp[0]; +} \ No newline at end of file diff --git a/Week 03/id_273/LeetCode_127_273.java b/Week 03/id_273/LeetCode_127_273.java new file mode 100644 index 000000000..b9a9b9620 --- /dev/null +++ b/Week 03/id_273/LeetCode_127_273.java @@ -0,0 +1,83 @@ +//127. 单词接龙 + +//解法1:BFS 执行用时:90ms +//思路:和433. 基因序列的思路一样, 区别仅在于转换单词时需要遍历26个字符 +// 实例: hit(begin) +// ↓ result + 1 +// hot +// ↓ result + 1 +// dot lot +// ↓ result + 1 +// log +// ↓ result + 1 +// cog(end) +//时间复杂度:O(wordList.length * 26 * eachWordLen) +//空间复杂度:O(wordList.len * 3) +public int ladderLength(String beginWord, String endWord, List wordList) { + if (beginWord.equals(endWord)) return 1; + Set visited = new HashSet<>(), wordSet = new HashSet<>(wordList); + visited.add(beginWord); + Queue queue = new LinkedList<>(); + queue.offer(beginWord); + int result = 1; + while (!queue.isEmpty()) { + int size = queue.size(); + while (size-- > 0) { + String str = queue.poll(); + if (str.equals(endWord)) return result; + char[] temp = str.toCharArray(); + for (int i = 0; i < temp.length; i++) { + char old = temp[i]; + for (char j = 'a'; j <= 'z'; j++) { + temp[i] = j; + String next = String.valueOf(temp); + if (visited.add(next) && wordSet.contains(next)) queue.offer(next); + } + temp[i] = old; + } + } + result++; + } + return 0; +} + +//解法2:双向BFS 执行用时:18ms +//思路:start和end两端同时进行BFS遍历, 在中间相遇时return result +// 至于为什么双向BFS的效率要比单端BFS效率更高, 是因为BFS遍历层数越深, 探索的成本就成倍增加 +// 因此, 从两个单端浅遍历的BFS遍历要比一个单端深遍历的BFS成本小很多 +//时间复杂度:O(wordList.length * eachWordLen * 26) +//空间复杂度:O(wordList.len * 3) +public int ladderLength(String beginWord, String endWord, List wordList) { + if (beginWord.equals(endWord)) return 1; + Set visited = new HashSet<>(), start = new HashSet<>(), end = new HashSet<>(), wordListSet = new HashSet<>(wordList); + if (!wordListSet.contains(endWord)) return 0; + start.add(beginWord); end.add(endWord); + visited.add(beginWord); visited.add(endWord); + int len = 1; + while (!start.isEmpty() && !end.isEmpty()) { + if (start.size() > end.size()) { + Set set = start; + start = end; + end = set; + } + Set temp = new HashSet<>(); + for (String s : start) { + char[] charArr = s.toCharArray(); + for (int i = 0; i < charArr.length; i++) { + char old = charArr[i]; + for (char c = 'a'; c <= 'z'; c++) { + charArr[i] = c; + String next = String.valueOf(charArr); + if (end.contains(next)) return len + 1; + if (wordListSet.contains(next) && visited.add(next)) { + temp.add(next); + } + } + charArr[i] = old; + } + } + start = temp; + len++; + } + return 0; +} \ No newline at end of file diff --git a/Week 03/id_273/LeetCode_153_273.java b/Week 03/id_273/LeetCode_153_273.java new file mode 100644 index 000000000..b9567ea14 --- /dev/null +++ b/Week 03/id_273/LeetCode_153_273.java @@ -0,0 +1,33 @@ +//153. 寻找旋转排序数组中的最小值 + +//解法1:二分查找 执行用时击败100% +//思路:根据二分查找的有序性, 寻找数组中的偏移点 +// 如果当前mid > nums[right], 说明偏移点存在于[mid + 1, right] +// else 如果当前mid <= nums[right], 说明(mid, right]这部分是有序的, 偏移点存在于[left, mid] +//时间复杂度:O(logN) +//空间复杂度:O(1) +public int findMin(int[] nums) { + int left = 0; + int right = nums.length - 1; + while (left < right) { + int mid = (left + right) >> 1; + if (nums[mid] > nums[right]) { + left = mid + 1; + } else { + right = mid; + } + } + return nums[left]; +} + +//解法2:作弊 执行用时击败100% +//思路:因为是有序数组旋转, 直接寻找数组中最小的元素, 它就是偏移点 +//时间复杂度:O(n) +//空间复杂度:O(1) +public int findMin(int[] nums) { + int min = Integer.MAX_VALUE; + for (int i = 0; i < nums.length; i++) { + if (min > nums[i]) min = nums[i]; + } + return min; +} \ No newline at end of file diff --git a/Week 03/id_273/LeetCode_200_273.java b/Week 03/id_273/LeetCode_200_273.java new file mode 100644 index 000000000..1304a534f --- /dev/null +++ b/Week 03/id_273/LeetCode_200_273.java @@ -0,0 +1,93 @@ +//200. 岛屿数量 + +//解法1:深度优先 执行用时:2ms +//思路:遍历二维数组, 每遍历到一个为‘1’的坐标, 深度优先递归将它自身与周围相邻的‘1’全部转换为‘0’, 这样第二次遍历时就可以排除掉第一次记录的岛屿 +// 相当于每递归一次就是将一个岛屿炸沉, 递归的次数就是岛屿的数量 +int[][] moves = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; +int count = 0; +public int numIslands(char[][] grid) { + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == '1') { + dfs(grid, i, j); + count++; + } + } + } + return count; +} + +private void dfs(char[][] grid, int i, int j) { + if (grid[i][j] == '#' || c != '1') return; + grid[i][j] = '#'; + for (int[] move : moves) { + int x = move[0] + i; + int y = move[1] + j; + if (x < 0 || y < 0 || x >= grid.length || y >= grid[0].length) continue; + dfs(grid, x, y); + } +} + + +//解法2:并查集 执行用时:5ms +//思路:我们把二维矩阵中的每个岛屿都想象成一组联通图, 只需要计算联通图的个数就可以得知二维坐标中有几个岛屿 +// 为此, 可以通过并查集进行实现: +// 1. 将二维矩阵中所有方块作为独立集合存入并查集 +// 2. 依次判断每个方块是否是陆地, 若是陆地, 那么就将该方块与4联通的周围4个方块进行并查集的合并操作 +// 3. 最后计算并查集中不相交的联通图数即可 +public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0 || grid[0].length == 0) return 0; + int[][] dis = new int[][]{{1, 0}, {0, -1}}; + UnionFind uf = new UnionFind(grid); + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == '1') { + for (int[] d : dis) { + int x = i + d[0]; + int y = j + d[1]; + if (x >= 0 && x < grid.length && y >= 0 && y < grid[0].length && grid[x][y] == '1') { + int id1 = i * grid[0].length + j; + int id2 = x * grid[0].length + y; + uf.union(id1, id2); + } + } + } + } + } + return uf.count; +} + +class UnionFind { + int[] parent; + int count; + + public UnionFind(char[][] grid) { + this.parent = new int[grid.length * grid[0].length]; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == '1') { + int id = i * grid[0].length + j; + parent[id] = id; + count++; + } + } + } + } + + public void union(int node1, int node2) { + int x = find(node1); + int y = find(node2); + if (x != y) {//merge x and y + parent[x] = y; + count--; + } + } + + private int find(int node) { + while (node != parent[node]) { + parent[node] = parent[parent[node]]; + node = parent[node]; + } + return node; + } +} \ No newline at end of file diff --git a/Week 03/id_273/LeetCode_229_273.java b/Week 03/id_273/LeetCode_229_273.java new file mode 100644 index 000000000..d9dff51ea --- /dev/null +++ b/Week 03/id_273/LeetCode_229_273.java @@ -0,0 +1,45 @@ +//229. 扫雷游戏 + +//解法1:DFS +//思路 +class Solution { + public char[][] updateBoard(char[][] board, int[] click) { + int m = board.length;//长 + int n = board[0].length;//宽 + int row = click[0]; + int col = click[1]; + + if (board[row][col] == 'M') { + board[row][col] = 'X'; + } + else { + int count = 0; + for (int i = -1; i<2; i++) { + for (int j = -1; j<2; j++) { + if (i == 0 && j == 0) continue; + int r = row + i; + int c = col + j; + if (r < 0 || c < 0 || r >= m || c >= n) continue; + if (board[r][c] == 'M' || board[r][c] == 'X') count++; + } + } + if (count > 0) { + board[row][col] = (char)(count + '0'); + } else { + board[row][col] = 'B'; + for (int i = -1; i<2; i++) { + for (int j = -1; j<2; j++) { + if (i == 0 && j == 0) continue; + int r = row + i; + int c = col + j; + if (r < 0 || c < 0 || r >= m || c >= n) continue; + if (board[r][c] == 'E') updateBoard(board, new int[]{r,c}); + } + } + } + + } + + return board; + } +} \ No newline at end of file diff --git a/Week 03/id_273/LeetCode_22_273.java b/Week 03/id_273/LeetCode_22_273.java new file mode 100644 index 000000000..2379896fe --- /dev/null +++ b/Week 03/id_273/LeetCode_22_273.java @@ -0,0 +1,71 @@ +//22. 括号生成 + +//解法1:回溯法 +//思路:首先获取到所有可能的括号组合,然后添加约束条件,筛选出所有符合要求的str: +// 左括号可以出现在任意位置,而右括号只能出现在左括号的后面 +class Solution { + public List generateParenthesis(int n) { + List res = new ArrayList<>(); + backStrack(res, "", 0, 0, n); + return res; + } + + private void backStrack(List res, String str, int open, int close, int max) { + if (str.length() == max*2) { + res.add(str); + return; + } + if (open < max) { + backStrack(res, str+"(", open + 1, close, max); + } + if (close < open) { + backStrack(res, str+")", open, close + 1, max); + } + } +} + +//解法2:bfs解法 +//思路:创建一个队列用于维护每次添加的括号组合, 当poll的str长度为n*2时将其添加到result +class Solution { + public List generateParenthesis(int n) { + List result = new ArrayList<>(); + Queue queue = new LinkedList<>(); + queue.offer("("); + + while (!queue.isEmpty()) { + String str = queue.poll(); + if (str.length() == n*2) result.add(str); + + int left = countMehodLeft(str);//获取str中的左括号数 + int right = countMethodRight(str);//获取str中的右括号数 + + if (left < n) { + queue.offer(str + "("); + } + if (right < left) { + queue.offer(str + ")"); + } + } + return result; + } + + private int countMehodLeft(String str) { + int count = 0; + for (int i = 0; i= c && dp[sum - c] != -1) { + int subProblem = dp[sum - c] + 1; + min = min < 0 ? subProblem : Math.min(subProblem, min); + } + } + dp[sum] = min; + } + return dp[amount]; +} diff --git a/Week 03/id_273/LeetCode_33_273.java b/Week 03/id_273/LeetCode_33_273.java new file mode 100644 index 000000000..734ee1cb3 --- /dev/null +++ b/Week 03/id_273/LeetCode_33_273.java @@ -0,0 +1,77 @@ +//33. 搜索旋转排序数组 + +//解法1:两个二分法 执行用时击败89% +//思路:先通过logN复杂度的二分查找获取到最小值index, 也就是旋转后的偏移点 +// 然后判断target位于旋转后的高位还是低位, 并在高位或低位区间通过二分查找查询target +//时间复杂度:O(logn) +//空间复杂度:O(1) +public int search(int[] nums, int target) { + if (nums.length == 0) return -1; + int index = findMinIndex(nums); + int left = 0; + int right = nums.length - 1; + //若target小于等于数组中末尾元素, 说明target位于[index, nums.right - 1]区间 + if (target <= nums[nums.length - 1]) left = index; + //else 位于[0, index - 1]区间 + else right = index - 1; + while (left <= right) { + int mid = (left + right) >> 1; + if (target == nums[mid]) return mid; + else if (target > nums[mid]) left = mid + 1; + else right = mid - 1; + } + return -1; +} + +private int findMinIndex(int[] nums) { + int left = 0; + int right = nums.length - 1; + while (left < right) { + int mid = (left + right) >> 1; + if (nums[mid] > nums[right]) left = mid + 1; + else right = mid; + } + return left; +} + + +//解法2:一个二分搞定 +//思路:先判断mid ~ right部分是有序的还是存在偏移点 +// mid ~ right 有序:说明偏移点一定存在于mid之前 +// 若target位于有序区间[mid, right], 且target大于mid的话, 只需要在[mid+1,right]部分寻找target即可 +// else 去左半部分[0,mid]区间寻找, right = mid; +// mid ~ right 无序:说明mid ~ right部分一定存在偏移点 +// 若target位于存在偏移点的区间[mid, right], 且target大于mid的话, 只需要在[mid+1, 偏移点Index - 1]的区间寻找target即可 +// 同样的,若target位于存在偏移点的区间[mid, right], 且target小于等于right的话, 只需要在[mid + 1, right]区间寻找targe即可 +// (上述两种情况, 都需要移动left = mid + 1) +// else 去左半部分[0,mid]区间寻找, right = mid; +//时间复杂度O(logN) +//空间复杂度O(1) +//总结:相比起解法1采用的两个二分查找, 该方法的逻辑判断要复杂很多, 要考虑的边界条件也蛮多的, 通过测试后发现两种方法的性能也相差无几, +// 个人的话会更偏向于第一种解法。 +class Solution { + public int search(int[] nums, int target) { + if (nums.length == 0) return -1; + int len = nums.length; + int left = 0; + int right = len - 1; + while (left < right) { + int mid = (left + right)/2; + if (nums[mid] <= nums[right]) { // mid ~ right 部分有序 + if (target > nums[mid] && target <= nums[right]) { + left = mid + 1; + } else { + right = mid; + } + } else {//mid ~ right 部分可能存在偏移点 + if (target > nums[mid] || target <= nums[right]) {//说明target位于mid右侧 且一定不在mid位置 + left = mid + 1; + } else { + right = mid; + } + } + } + return (left == right && target != nums[left]) ? -1 : left; + } +} +//else 当0 ~ mid 存在偏移, 也就意味着mid ~ right是旋转后的低位部分, 若left要向后规约, target必须大于mid \ No newline at end of file diff --git a/Week 03/id_273/LeetCode_367_273.java b/Week 03/id_273/LeetCode_367_273.java new file mode 100644 index 000000000..4bdf99774 --- /dev/null +++ b/Week 03/id_273/LeetCode_367_273.java @@ -0,0 +1,24 @@ +//367. 有效的完全平方数 + +//解法1:二分法 +//思路: +//时间复杂度:O(logN) +//空间复杂度:O(1) +class Solution { + public boolean isPerfectSquare(int num) { + long left = 1; + long right = num; + + while (left <= right) { + long mid = (left + right)/2; + if (mid * mid == num) { + return true; + } else if (mid * mid < num) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return false; + } +} \ No newline at end of file diff --git a/Week 03/id_273/LeetCode_433_273.java b/Week 03/id_273/LeetCode_433_273.java new file mode 100644 index 000000000..51fd8d901 --- /dev/null +++ b/Week 03/id_273/LeetCode_433_273.java @@ -0,0 +1,79 @@ +//433. 最小基因变化 + +//解法1:广度优先遍历 执行用时:1ms +//思路:根据基因库遍历转换start序列的每一个基因字符, 观察每一次基因变化的结果是否存在于基因库bank中: +// 若存在于基因库中, 但并不是目标基因end, 那么就以这个基因为start继续进行基因转换(注意不要重复转换之前转换过的基因--Set) +// 若存在于基因库中, 且是目标基因end, return转换的次数即可 +// 题目的核心就是图的广度优先遍历 +// 例如: AACCGGTT(start) +// ↓ result+1 +// AACCGCTA +// ↓ result+1 +// AACCGCTA AAACGCTA(target) + +public int minMutation(String start, String end, String[] bank) { + Set visited = new HashSet<>(), bankSet = new HashSet<>(Arrays.asList(bank)); + visited.add(start); + if (!bankSet.contains(end)) return -1; + Queue queue = new LinkedList<>(); + queue.offer(start); + char[] genertic = new char[]{'A', 'C', 'G', 'T'}; + int result = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + while (size-- > 0) { + String str = queue.poll(); + if (str.equals(end)) return result; + char[] temp = str.toCharArray(); + for (int i = 0; i < temp.length; i++) { + char old = temp[i]; + for (char c : genertic) { + temp[i] = c; + String next = String.valueOf(temp); + if (visited.add(next) && bankSet.contains(next)) queue.offer(next); + } + temp[i] = old; + } + } + result++; + } + return -1; +} + +//解法2:双向BFS 执行用时:1ms +//思路:同127. 解法2 +public int minMutation(String start, String end, String[] bank) { + if (start.equals(end)) return 0; + List bankList = new ArrayList<>(Arrays.asList(bank)); + Set visited = new HashSet<>(), startSet = new HashSet<>(), + endSet = new HashSet<>(), bankSet = new HashSet<>(bankList); + if (!bankSet.contains(end)) return -1; + startSet.add(start); + endSet.add(end); + char[] genertc = new char[]{'A', 'C', 'G', 'T'}; + int len = 1; + while (!startSet.isEmpty() && !endSet.isEmpty()) { + if(startSet.size() > endSet.size()) { + Set set = startSet; + startSet = endSet; + endSet = set; + } + Set temp = new HashSet<>(); + for (String s : startSet) { + char[] charArr = s.toCharArray(); + for (int i = 0; i < charArr.length; i++) { + char c = charArr[i]; + for (int j = 0; j < genertc.length; j++) { + charArr[i] = genertc[j]; + String next = String.valueOf(charArr); + if (endSet.contains(next)) return len; + if (bankSet.contains(next) && visited.add(next)) temp.add(next); + } + charArr[i] = c; + } + } + startSet = temp; + len++; + } + return -1; +} \ No newline at end of file diff --git a/Week 03/id_273/LeetCode_455_273.java b/Week 03/id_273/LeetCode_455_273.java new file mode 100644 index 000000000..9f0f39261 --- /dev/null +++ b/Week 03/id_273/LeetCode_455_273.java @@ -0,0 +1,20 @@ +//455. 分发饼干 + +//解法1:双指针 +public int findContentChildren(int[] g, int[] s) { + Arrays.sort(g); + Arrays.sort(s); + int sum = 0; + int i = 0; + int j = 0; + while (i < s.length && j < g.length) { + if (s[i] >= g[j]) { + i++; + j++; + sum++; + } else { + i++; + } + } + return sum; +} \ No newline at end of file diff --git a/Week 03/id_273/LeetCode_45_273.java b/Week 03/id_273/LeetCode_45_273.java new file mode 100644 index 000000000..2f7ec4ad9 --- /dev/null +++ b/Week 03/id_273/LeetCode_45_273.java @@ -0,0 +1,37 @@ +//45. 跳跃游戏 + +//解法1:从后往前贪心 执行用时击败约14% +//思路:遍历数组, 寻找第一个能够直接跳跃到达终点position的index +// 每当成功寻找到上述index, 更新position=index, count++, 继续重复上述步骤寻找, 直到position = 0 +//时间复杂度O(n^2) +//空间复杂度O(1) +public int jump(int[] nums) { + int count = 0; + int position = nums.length - 1; + while (position != 0) { + for (int i = 0; i < position; i++) { + if (nums[i] + i >= position) { + count++; + position = i; + break; + } + } + } + return count; +} + +//解法2:贪心算法 执行用时击败约97% +//思路:遍历数组, 每一次遍历都寻找当前位置所能到达的范围内跳的最远的position, 每成功找到一次count++ +public int jump(int[] nums) { + int maxLen = 0; + int end = 0; + int count = 0; + for (int i = 0; i < nums.length - 1; i++) { + maxLen = Math.max(maxLen, nums[i] + i); + if (i == end) { + end = maxLen; + count++; + } + } + return count; +} \ No newline at end of file diff --git a/Week 03/id_273/LeetCode_515_273.java b/Week 03/id_273/LeetCode_515_273.java new file mode 100644 index 000000000..04d802eca --- /dev/null +++ b/Week 03/id_273/LeetCode_515_273.java @@ -0,0 +1,53 @@ +//515. 在每个树行中找到最大值 + +//解法1:BFS 执行用时击败约40% +//思路:广度优先遍历, 然后获取每一层ArrayList中的最大值返回即可 +class Solution { + public List largestValues(TreeNode root) { + Queue queue = new LinkedList<>(); + List result = new ArrayList<>(); + if (root == null) return result; + queue.offer(root); + while (!queue.isEmpty()) { + //获取当前层的node数 + int size = queue.size(); + List temp = new ArrayList<>(); + for (int i = 0; i < size;i++) { + TreeNode curr = queue.poll(); + if (curr.left != null) queue.offer(curr.left); + if (curr.right != null) queue.offer(curr.right); + temp.add(curr.val); + } + result.add(getMax(temp)); + } + return result; + } + + private int getMax(List list) { + int max = Integer.MIN_VALUE; + for (int i : list) { + if (max < i) { + max = i; + } + } + return max; + } +} + +//解法2:DFS 执行用时击败约100% +//思路:深度优先遍历, 用一个level记录当前层数, 遍历所有节点, 在放入result数组之前与result数组中"对应当前递归level的索引下的元素"比较大小 +class Solution { + public List largestValues(TreeNode root) { + List result = new ArrayList<>(); + dfs(root, 0, result); + return result; + } + + private void dfs(TreeNode node, int level, List list) { + if (node == null) return; + if (level == list.size()) list.add(node.val); + else list.set(level, Math.max(list.get(level), node.val)); + dfs(node.left, level + 1, list); + dfs(node.right, level + 1, list); + } +} \ No newline at end of file diff --git a/Week 03/id_273/LeetCode_529_273.java b/Week 03/id_273/LeetCode_529_273.java new file mode 100644 index 000000000..51961aa6e --- /dev/null +++ b/Week 03/id_273/LeetCode_529_273.java @@ -0,0 +1,46 @@ +//扫雷游戏 + +//解法1:深度优先递归DFS 执行用时击败100% +//思路:根据题目要求, 首先判断click坐标下的方块是否存在地雷, 若存在地雷则修改方块为‘X’ +// 若该坐标下不存在地雷, 就需要对周围的方块进行判断是否存在地雷, 并记录周围地雷数 +// 若周围存在地雷, 将该坐标方块修改为地雷数count +// 若周围不存在地雷, 将该坐标方块标记为已挖掘‘B’ +// 对该坐标周围未挖掘的方块‘E’递归执行上述步骤, 直到无更多方块可被揭露 +public char[][] updateBoard(char[][] board, int[] click) { + int m = board.length;//长 + int n = board[0].length;//宽 + int row = click[0]; + int col = click[1]; + //如果地雷被挖出, 游戏结束, 该坐标修改为‘X’ + if (board[row][col] == 'M') board[row][col] = 'X'; + else { + int count = 0;//记录周围的地雷数目 + //通过两次遍历获取周围所有格子的坐标 观察周围是否有格子存在地雷 + for (int i = -1; i < 2; i++) { + for (int j = -1; j < 2; j++) { + if (i == 0 && j == 0) continue; + int r = row + i, c = col + j; + if (r < 0 || c < 0 || r >= m || c >= n) continue;//越界判断 + if (board[r][c] == 'M' || board[r][c] == 'X') count++; + } + } + //若方块周围存在地雷, 那么该方块‘E’设置为地雷数count + if (count > 0) { + board[row][col] = (char)(count + '0'); + } else { + //若不存在地雷, 那么该方块‘E’设置为已挖过区域‘B’ + board[row][col] = 'B'; + //通过两次遍历获取周围所有格子的坐标, 若存在未挖过格子‘E’, 对其递归上述步骤 + for (int i = -1; i < 2; i++) { + for (int j = -1; j < 2; j++) { + if (i == 0 && j == 0) continue; + int r = row + i; + int c = col + j; + if (r < 0 || c < 0 || r >= m || c >= n) continue; + if (board[r][c] == 'E') updateBoard(board, new int[]{r, c}); + } + } + } + } + return board; +} \ No newline at end of file diff --git a/Week 03/id_273/LeetCode_55_273.java b/Week 03/id_273/LeetCode_55_273.java new file mode 100644 index 000000000..23e46bfda --- /dev/null +++ b/Week 03/id_273/LeetCode_55_273.java @@ -0,0 +1,27 @@ +//55. 跳跃游戏 + +//解法1:贪心算法 +//思路:从后往前贪心, 从最后一个位置Last开始, 向前寻找第一个跳跃后能够到Last的索引index, 找到后Last = index, 最后若Last指向0, 说明能够跳跃到达末尾 +public boolean canJump(int[] nums) { + int last = nums.length - 1; + for (int i = nums.length - 1; i >= 0; i--) { + if (nums[i] + i >= last) last = i; + } + return last == 0; +} + +//解法2:回溯法 提交超时未通过 +//思路:递归观察当前position能跳跃到达的所有位置, 若当前position到达末尾, 说明可达return true +public boolean canJump(int[] nums) { + return canJumpRecur(nums, 0); +} + +private boolean canJumpRecur(int[] nums, int position) { + if (position == nums.length - 1) return true; + //当前index能够到的最远位置 + int furtherJump = Math.min(nums.length - 1, position + nums[position]); + for (int nextPos = position + 1; nextPos <= furtherJump; nextPos++) { + if (canJumpRecur(nums, nextPos)) return true; + } + return false; +} \ No newline at end of file diff --git a/Week 03/id_273/LeetCode_69_273.java b/Week 03/id_273/LeetCode_69_273.java new file mode 100644 index 000000000..c557269b0 --- /dev/null +++ b/Week 03/id_273/LeetCode_69_273.java @@ -0,0 +1,39 @@ +//69. x的平方根 + +//解法1:二分法 +//思路:根据"一个数的平方根不大于其本身的一半"可以得知 x 的平方根位于0 ~ x/2 + 1之间, 因此只需要对该区间进行二分查找 +// 若mid*mid > x, 则认为x的平方根位于mid右半部分, +// 若mid*mid <= x, 则认为x的平方根位于mid本身及其左半部分 +// 当left >= right, 循环结束, 此时的left即为 x 的平方根 +//时间复杂度O(logN) +//空间复杂度O(1) +//总结:要注意很多边界条件的判断, 该题很容易理解, 也很容易写错 +public int mySqrt(int x) { + long left = 0; + long right = x/2 + 1; + while (left < right) { + long mid = (left + right + 1)/2; + if (mid * mid > x) { + right = mid - 1; + } else { + left = mid; + } + } + return (int)left; +} + +//解法2:牛顿迭代 +//思路:不断迭代执行公式, 获取结果 +//空间复杂度O(1) +//总结:基于数学公式实现, 背下该迭代公式即可:[X0 + (a/X0)]/2 , 因为效率非常高, 现实工程场景中, 涉及到根号计算也一般都采用牛顿迭代法。 +public int mySqrt2(int x) { + long num = x; + while (num * num > x) { + num = (num + x/num)/2; + } + return (int)num; +} + + + + diff --git a/Week 03/id_273/LeetCode_74_273.java b/Week 03/id_273/LeetCode_74_273.java new file mode 100644 index 000000000..3c5b0e5ab --- /dev/null +++ b/Week 03/id_273/LeetCode_74_273.java @@ -0,0 +1,26 @@ +//74. 搜索二维矩阵 + +//解法1:二分查找 +//思路:通过观察题目发现矩阵从左到右升序, 且每行的第一位大于前一行的第二位 +// 这意味着我们可以把它当做一个有序数组来搜索 +// 因为每行最后一个元素为当前行的最大值, 如果target <= 当前行的最大值, 也就意味着target位于该行 +// 通过遍历检索到target所在的行 +// 对该行数组进行二分查找获取target +//时间复杂度:O(m*logN):二维矩阵行数为m, 列数为n +//空间复杂度:O(1) +public boolean searchMatrix(int[][] matrix, int target) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) return false; + for (int i = 0; i < matrix.length; i++) { + if (matrix[i][matrix[i].length - 1] >= target) { + int left = 0; + int right = matrix[i].length - 1; + while (left <= right) { + int mid = (left + right) >> 1; + if (matrix[i][mid] == target) return true; + else if (matrix[i][mid] < target) left = mid + 1; + else right = mid - 1; + } + } + } + return false; +} \ No newline at end of file diff --git a/Week 03/id_273/LeetCode_860_273.java b/Week 03/id_273/LeetCode_860_273.java new file mode 100644 index 000000000..d664eeda7 --- /dev/null +++ b/Week 03/id_273/LeetCode_860_273.java @@ -0,0 +1,19 @@ +//860. 柠檬水找零 + +//解法1:创建一个数组, index=0表示拥有的5块钱的张数, index=1表示拥有的10块钱的张数 +// 根据贪心算法的思想:如果收到20块钱, 首先确定是否拥有10块钱, 若有找零10+5, 若没有找零3*5 +//时间复杂度O(n) +//空间复杂度O(1) +public boolean lemonadeChange(int[] bills) { + int[] money = new int[2];// 0-5 1-10 + for (int i : bills) { + if (i == 5) money[0]++; + else if (i == 10) { money[1]++; money[0]--; } + else { + if (money[1] > 0) { money[1]--; money[0]--; } + else money[0] -= 3; + } + if (money[0] < 0) return false; + } + return true; +} \ No newline at end of file diff --git a/Week 03/id_273/NOTE.md b/Week 03/id_273/NOTE.md index a6321d6e2..50a717681 100644 --- a/Week 03/id_273/NOTE.md +++ b/Week 03/id_273/NOTE.md @@ -1,4 +1,63 @@ # NOTE - +### 第九课 深度优先搜索、广度优先搜索的实现与特性 +搜索/遍历: +1. 每个节点都访问到且仅访问一次 +2. 对于节点的访问顺序也分为:深度优先Depth First、广度优先Breadth First、优先级优先Priority First等各种顺序 + +#### 深度优先搜索 +在当前循环中不断下探到下一层递归, 直到最深。 + +```java +void dfs (TreeNode node) { + if (node == null) return; + dfs(node.left); + dfs(node.right); +} +``` + +#### 广度优先搜索 +类似水滴扩散的那种感觉 + +```java +Queue queue = new LinkedList<>(); + +void bfs (TreeNode node) { + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + if (queue.peek().left != null) queue.offer(queue.peek().left); + if (queue.peek().right != null) queue.offer(queue.peek().right); + } + } +} + +``` + +### 第十课 贪心算法的实现、特性 +1. 贪心算法是采取当前状态下的最优解, 从而导致全局结果也为最优解的算法 +2. 贪心算法与动态规划的不同在于, 它对每个子问题的解决方案都做出当前最优的选择, 不能回退。动态规划则会保存以前的运算结果, 并根据以前的结果来计算当前结果, 可以回退 +3. 贪心法可以解决一些最优化问题, 如:求图的最小生成树, 哈弗曼树编码等, 但一般情况下, 贪心法并不能获得总体上的最优解 +4. 若一个问题能够通过贪心法解决, 那么贪心法一定是解决该问题的最佳方案, 因为贪心法的高效性, 所以它常常被用作辅助算法 + + +### 第十一课 二分查找 + +#### 二分查找的前提条件 +1. 目标函数的单调性(单调递增/单调递减), 也就是要求有序 +2. 存在上下界 +3. 能够通过索引访问 + +二分查找代码模版: +```java +int left = 0; +int right = arr.length - 1; +while (left < right) { + int mid = (left + right)/2; + if (arr[mid] == target) return result; + else if (arr[mid] < target) left = mid + 1; + else right = mid - 1; +} +``` + diff --git a/Week 03/id_273/useBinarySearchFindOffSet.java b/Week 03/id_273/useBinarySearchFindOffSet.java new file mode 100644 index 000000000..9c2ee4662 --- /dev/null +++ b/Week 03/id_273/useBinarySearchFindOffSet.java @@ -0,0 +1,19 @@ +// 使用二分查找寻找半有序数组的偏移量 + +//思路:left = 0 , right = length - 1, mid = (left + right)/2, 观察arr[mid]是否大于arr[right] +// 若大于说明序列在mid之后的位置进行了旋转, mid+1位置可能是偏移点, 也可能是mid+i +// 此时left = mid + 1, 折半继续查找 +// 若小于等于说明mid之后都为有序序列, 偏移点位于mid之前 +// 此时right = mid, 折半继续查找 +// 到最后, left所指向的index则是整个数组中最小的位置, 也就是偏移点 + +public void findOffSet(int nums []) { + int left = 0; + int right = nums.length - 1; + while (left < right) { + int mid = (left + right) >> 1; + if (nums[mid] > nums[right]) left = mid + 1; + else right = mid; + } + return left; +} \ No newline at end of file diff --git a/Week 03/id_278/Leetcode_122_278.js b/Week 03/id_278/Leetcode_122_278.js new file mode 100644 index 000000000..a09a614ab --- /dev/null +++ b/Week 03/id_278/Leetcode_122_278.js @@ -0,0 +1,16 @@ +/** + * 122. Best Time to Buy and Sell Stock II + * @param {number[]} prices + * @return {number} + */ +var maxProfit = function(prices) { + let profit = 0; + + for (let i = 1; i < prices.length; ++ i) { + if (prices[i] > prices[i - 1]) { + profit += prices[i] - prices[i - 1]; + } + } + + return profit; +}; diff --git a/Week 03/id_278/Leetcode_860_278.js b/Week 03/id_278/Leetcode_860_278.js new file mode 100644 index 000000000..65335eea6 --- /dev/null +++ b/Week 03/id_278/Leetcode_860_278.js @@ -0,0 +1,39 @@ +/** + * 860. Lemonade Change + * @param {number[]} bills + * @return {boolean} + */ +var lemonadeChange = function(bills) { + const changes = { + 5: 0, + 10: 0 + }; + + for (const bill of bills) { + if (bill === 5) { + changes['5'] ++; + } else { + if (changes['5'] === 0) { + return false; + } else { + if (bill === 10) { + changes['10'] ++; + changes['5'] --; + } else { + if (changes['10'] === 0) { + changes['5'] -= 3; + } else { + changes['10'] --; + changes['5'] --; + } + } + } + } + + if (changes['5'] < 0 || changes['10'] < 0) { + return false; + } + } + + return true; +}; diff --git a/Week 03/id_283/Leetcode_127_283.java b/Week 03/id_283/Leetcode_127_283.java new file mode 100644 index 000000000..b8aa9cb70 --- /dev/null +++ b/Week 03/id_283/Leetcode_127_283.java @@ -0,0 +1,42 @@ +/* + * @lc app=leetcode id=127 lang=java + * + * [127] Word Ladder + */ +class Solution { + public int ladderLength(String beginWord, String endWord, List wordList) { + if (!wordList.contains(endWord)) return 0; + int level = 1; + Set set = new HashSet<>(); + for(String word: wordList){ + set.add(word); + } + Queue queue = new LinkedList<>(); + queue.offer(beginWord); + while(!queue.isEmpty()){ + int size = queue.size(); + for(int i = 0; i < size; i++){ + String tmpWord = queue.poll(); + char[] array = tmpWord.toCharArray(); + for(int j = 0; j < array.length; j++){ + char c = array[j]; + for(char k = 'a'; k <= 'z'; k++){ + array[j] = k; + String newWord = String.valueOf(array); + if(k != c &&set.contains(newWord)){ + queue.offer(newWord); + set.remove(newWord); + if(newWord.equals(endWord)){ + return ++level; + } + } + } + array[j] = c; + } + } + level++; + } + return 0; + } +} + diff --git a/Week 03/id_283/Leetcode_200_283.java b/Week 03/id_283/Leetcode_200_283.java new file mode 100644 index 000000000..f863c1c41 --- /dev/null +++ b/Week 03/id_283/Leetcode_200_283.java @@ -0,0 +1,31 @@ +/* + * @lc app=leetcode id=200 lang=java + * + * [200] Number of Islands + */ +class Solution { + public int numIslands(char[][] grid) { + int island = 0; + if (grid.length == 0 || grid[0].length == 0) return 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == '1') { + island++; + dfs(grid, i, j); + } + } + } + return island; + } + + public static void dfs (char[][] grid, int i, int j) { + if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == '0') + return; + grid[i][j] = '0'; + dfs(grid, i - 1, j); + dfs(grid, i, j - 1); + dfs(grid, i, j + 1); + dfs(grid, i + 1, j); + } +} + diff --git a/Week 03/id_298/number-of-islands.py b/Week 03/id_298/number-of-islands.py new file mode 100644 index 000000000..a652516bb --- /dev/null +++ b/Week 03/id_298/number-of-islands.py @@ -0,0 +1,34 @@ +class Solution: + + def numIslands(self, grid: List[List[str]]) -> int: + # 定义当前位置的搜索方向 + directions = [(-1, 0), (0, -1), (1, 0), (0, 1)] + + m = len(grid) + if m == 0: + return 0 + + n = len(grid[0]) + marked = [[False for _ in range(n)] for _ in range(m)] + + # 统计岛的个数 + count = 0 + + # 对网格进行深度优先搜索 + def _dfs(grid, i, j, m, n, marked): + marked[i][j] = True + for direction in directions: + new_i = i + direction[0] + new_j = j + direction[1] + if 0 <= new_i < m and 0 <= new_j < n and not marked[new_i][new_j] and grid[new_i][new_j] == '1': + _dfs(grid, new_i, new_j, m, n, marked) + + # 网格搜索: 从第一个格子进行深度优先搜索 + for i in range(m): + for j in range(n): + # 是没被访问过的陆地则使用深度优先搜索进行标记 + if not marked[i][j] and grid[i][j] == '1': + count += 1 + _dfs(grid, i, j, m, n, marked) + return count + diff --git a/Week 03/id_298/word-ladder.py b/Week 03/id_298/word-ladder.py new file mode 100644 index 000000000..6b271b143 --- /dev/null +++ b/Week 03/id_298/word-ladder.py @@ -0,0 +1,40 @@ +from collections import defaultdict +class Solution: + def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: + if endWord not in wordList or not endWord or not beginWord or not wordList: + return 0 + + L = len(beginWord) + + # 生成更改一次字母后的所有word字母组合 + all_combo_dict = defaultdict(list) + # 遍历词表中的每个word + for word in wordList: + for i in range(L): + all_combo_dict[word[:i] + "*" + word[i+1:]].append(word) + + # 创建广度优先搜索的队列 + queue = [(beginWord, 1)] + # 处理过的单词加入到集合中,确保不会处理相同的单词 + visited = {beginWord: True} + while queue: + current_word, level = queue.pop(0) + for i in range(L): + # Intermediate words for current word + # 当前单词的中间单词 + intermediate_word = current_word[:i] + "*" + current_word[i+1:] + + # 所有单词共享同一个中间状态 + # 使用当前单词的中间状态查询出来,对应的所有单词列表,遍历列表 + for word in all_combo_dict[intermediate_word]: + # 如果单词是结尾单词,则返回 + if word == endWord: + print(all_combo_dict) + return level + 1 + # 如果没有被访问过则加入到队列中, + if word not in visited: + visited[word] = True + queue.append((word, level + 1)) + # 处理掉的单词清空 + all_combo_dict[intermediate_word] = [] + return 0 diff --git a/Week 03/id_308/LeedCode_122.js b/Week 03/id_308/LeedCode_122.js new file mode 100644 index 000000000..bd12ed056 --- /dev/null +++ b/Week 03/id_308/LeedCode_122.js @@ -0,0 +1,32 @@ + +/** + * 题目: 买卖股票的最佳时机 II + * 语言: JavaScript + * 执行结果: 打败了70%的用户 + * 方法:贪心算法 + * */ + + +/** + * @param {number[]} prices + * @return {number} + */ +var maxProfit = function(prices) { + let buy = null; + let money = 0; + for(let i = 0; i < prices.length;i++) { + const curr = prices[i]; + const next = prices[i+1]; + + if(buy !== null){ + if(!next || next <= curr ){ + money = money + (curr - buy); + buy = null; + } + }else if(next && next > curr) { + buy = prices[i]; + } + } + + return money; +}; diff --git a/Week 03/id_308/LeedCode_200.js b/Week 03/id_308/LeedCode_200.js new file mode 100644 index 000000000..7c7f6f6c2 --- /dev/null +++ b/Week 03/id_308/LeedCode_200.js @@ -0,0 +1,48 @@ +/** + * 题目: 岛屿数量 + * 语言: JavaScript + * 执行结果: 打败了93%的用户 + * 方法:深度优先遍历 + * */ + + +/** + * @param {character[][]} grid + * @return {number} + */ +const numIslands = function(grid) { + const dy = grid.length; + if(!dy) return 0; + + const dx = grid[0].length; + let count = 0; + + for(let j = 0; j < dy;j++) { + for(let i = 0; i < dx;i++) { + if(~~grid[j][i] === 1) { + count++; + chooseLand(j,i); + } + } + } + + function chooseLand(y,x) { + const sx = [-1,1,0,0]; + const sy = [0,0,-1,1]; + + if(~~grid[y][x] === 1) { + grid[y][x] = "0"; + + for(let count = 0; count < 4; count++){ + const tempY = sy[count]+y; + const tempX = sx[count]+x; + + if(tempY >= 0 && tempY < dy && tempX >= 0 && tempX < dx && ~~grid[tempY][tempX] === 1) { + chooseLand(tempY,tempX); + }; + } + } + } + + return count; +}; diff --git a/Week 03/id_308/LeedCode_455.js b/Week 03/id_308/LeedCode_455.js new file mode 100644 index 000000000..c5b185777 --- /dev/null +++ b/Week 03/id_308/LeedCode_455.js @@ -0,0 +1,29 @@ +/** + * 题目: 分发饼干 + * 语言: JavaScript + * 执行结果: 打败了95%的用户 + * 方法:贪心算法 + * */ + +/** + * @param {number[]} g + * @param {number[]} s + * @return {number} + */ +var findContentChildren = function(g, s) { + g = g.sort((a,b)=>a-b); + s = s.sort((a,b)=>a-b); + + let count = 0; + let ds = 0; + let dg = 0; + + while (ds < s.length) { + if(s[ds++] >= g[dg]) { + count++; + dg++; + } + } + + return count; +}; diff --git a/Week 03/id_308/LeedCode_860.js b/Week 03/id_308/LeedCode_860.js new file mode 100644 index 000000000..368e4b289 --- /dev/null +++ b/Week 03/id_308/LeedCode_860.js @@ -0,0 +1,36 @@ +/** + * 题目: 柠檬水找零 + * 语言: JavaScript + * 执行结果: 打败了65%的用户 + * 方法:贪心算法 + * */ + + +/** + * @param {number[]} bills + * @return {boolean} + */ +const lemonadeChange = function (bills) { + let five = 0; + let ten = 0; + + for(let key in bills) { + const money = bills[key]; + + if(money === 5) five++; + else if(money === 10) { + if(five < 1) return false; + five--; + ten++; + }else{ + if(ten > 0 && five > 0) { + ten--; + five--; + }else if(five > 2) { + five = five-3; + }else return false; + } + } + + return true; +}; diff --git a/Week 03/id_308/NOTE.md b/Week 03/id_308/NOTE.md index a6321d6e2..f548019c1 100644 --- a/Week 03/id_308/NOTE.md +++ b/Week 03/id_308/NOTE.md @@ -1,4 +1,38 @@ -# NOTE +## 深度优先搜索、广度优先搜索的实现和特性 +#### 搜索 - 遍历 + +- 每个节点都要访问一遍 +- 每个节点仅仅要访问一遍 +- 对于节点的访问顺序不限 + * 深度优先 :depth first search + * 广度优先 :breadth first search + +#### 深度优先算法 DFS +#### 广度优先算法 BFS + +## 贪心算法的实现、特性 + +#### 贪心算法 Greedy + +- 贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法 +- 贪心算法与动态规划的不同在于它对每个子问题的解决方案都做出选择,不能回退。动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。 +- 几种算法比较 + * 贪心:当下做局部最优判断 + * 回溯:能够回退 + * 动态规划:最优判断 + 回退 +- 贪心算法可以解决一些最优问题,如:求图中最小生成树、求哈弗曼编码等。然而对于工程和生活中的问题,贪心算法一般不能得到我们所要求得答案。 +- 一旦一个问题可以通过贪心法来解决,那么贪心法一般是解决这个问题的最好办法。由于贪心法的高效性以及其所求得的答案比较接近最优结果,贪心法也可以用作辅助算法湖畔这直接解决一些要求结果不特别精确的问题。 +- 适合贪心算法的场景 + * 问题能够分解成子问题来解决,子问题的最优解能递推到最终问题的最优解。这种子问题最优解称为最优子结构 + * 贪心算法与动态规划的不同在于它对每个子问题的解决方案都作出选择,不能回退。动态规划会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。 +#### 二分查找 + +- 二分查找的前提 + * 目标函数单调性(单调递增或递减) + * 存在上下界(bounded) + * 能够通过索引访问(index accessible) + + diff --git a/Week 03/id_313/LeetCode_122_313.go b/Week 03/id_313/LeetCode_122_313.go new file mode 100644 index 000000000..2f3118e50 --- /dev/null +++ b/Week 03/id_313/LeetCode_122_313.go @@ -0,0 +1,13 @@ +package id_313 + +func maxProfit(prices []int) int { + length := len(prices) + ret := 0 + for i := 0; i < length-1; i++ { + m := prices[i+1] - prices[i] + if m > 0 { + ret += m + } + } + return ret +} diff --git a/Week 03/id_313/LeetCode_126_313.go b/Week 03/id_313/LeetCode_126_313.go new file mode 100644 index 000000000..eb3133c46 --- /dev/null +++ b/Week 03/id_313/LeetCode_126_313.go @@ -0,0 +1,85 @@ +package id_313 + +// 先bfs +// 再dfs +func findLadders(beginWord string, endWord string, words []string) [][]string { + ret := make([][]string, 0) + wordMap := make(map[string][]string, 0) + isEnd := false + cnt := 1 + // 删掉 beginword 减少循环 + for i, word := range words { + if word == beginWord { + words = append(words[:i], words[i+1:]...) + } + } + var bfs func([]string, []string) + bfs = func(words []string, nodes []string) { + cnt++ + // 存储不可转换的数据 + newWords := make([]string, 0) + // 存储可转换的 进行下一步bfs + newNodes := make([]string, 0) + for _, word := range words { + isTransed := false + for _, node := range nodes { + if isTrans(word, node) { + wordMap[node] = append(wordMap[node], word) + isTransed = true + } + } + if isTransed { + newNodes = append(newNodes, word) + if word == endWord { + isEnd = true + } + } else { + newWords = append(newWords, word) + } + } + // 直到完成转换 或者 全部遍历完事 + if isEnd || len(newNodes) == 0 || len(newWords) == 0 { + return + } + bfs(newWords, newNodes) + } + nodes := []string{beginWord} + bfs(words, nodes) + // 如果不可以转换 提前结束 + if !isEnd { + return ret + } + path := make([]string, cnt) + path[0] = beginWord + var dfs func(int) + dfs = func(index int) { + if index == cnt { + if path[index-1] == endWord { + tmp := make([]string, cnt) + copy(tmp, path) + ret = append(ret, tmp) + } + return + } + prev := path[index-1] + for _, word := range wordMap[prev] { + path[index] = word + dfs(index + 1) + } + } + dfs(1) + return ret +} + +func isTrans(word, node string) bool { + flag := false + for i := range word { + if word[i] != node[i] { + if flag { + return false + } + flag = true + } + } + return true +} diff --git a/Week 03/id_313/LeetCode_127_313.go b/Week 03/id_313/LeetCode_127_313.go new file mode 100644 index 000000000..94b3522c9 --- /dev/null +++ b/Week 03/id_313/LeetCode_127_313.go @@ -0,0 +1,42 @@ +package id_313 + +// 针对word中单个字母进行替换 +// +func ladderLength(beginWord string, endWord string, wordList []string) int { + ret := 0 + queue := make([]string, 0) + queue = append(queue, beginWord) + wordMap := make(map[string]bool) + for _, word := range wordList { + wordMap[word] = true + } + if !wordMap[endWord] { + return 0 + } + for len(queue) > 0 { + ret++ + length := len(queue) + for i := 0; i < length; i++ { + beginWord := queue[0] + queue = queue[1:] + for k := 0; k < len(beginWord); k++ { + current := []byte(beginWord) + tmp := beginWord + for j := 'a'; j <= 'z'; j++ { + current[k] = byte(j) + word := string(current) + if wordMap[string(current)] { + if endWord == word { + return ret + 1 + } + queue = append(queue, word) + delete(wordMap, word) + } + } + beginWord = tmp + } + + } + } + return 0 +} diff --git a/Week 03/id_313/LeetCode_153_313.go b/Week 03/id_313/LeetCode_153_313.go new file mode 100644 index 000000000..975122b95 --- /dev/null +++ b/Week 03/id_313/LeetCode_153_313.go @@ -0,0 +1,14 @@ +package id_313 + +func findMin(nums []int) int { + left, right := 0, len(nums)-1 + for left < right { + mid := (left + right) / 2 + if nums[right] < nums[mid] { + left = mid + 1 + } else { + right = mid + } + } + return nums[left] +} diff --git a/Week 03/id_313/LeetCode_200_313.go b/Week 03/id_313/LeetCode_200_313.go new file mode 100644 index 000000000..10ec84977 --- /dev/null +++ b/Week 03/id_313/LeetCode_200_313.go @@ -0,0 +1,56 @@ +package id_313 + +func numIslands(grid [][]byte) int { + row := len(grid) + if row == 0 { + return 0 + } + col := len(grid[0]) + + x := make([]int, 0, row*col) + y := make([]int, 0, row*col) + var push = func(i, j int) { + x = append(x, i) + y = append(y, j) + grid[i][j] = '0' + } + var pop = func() (int, int) { + i := x[0] + x = x[1:] + j := y[0] + y = y[1:] + return i, j + } + var bfs = func(i, j int) int { + if grid[i][j] == '0' { + return 0 + } + + push(i, j) + + for len(x) > 0 { + i, j = pop() + // 当前坐标 四个方向 + if 0 <= i-1 && grid[i-1][j] == '1' { + push(i-1, j) + } + if 0 <= j-1 && grid[i][j-1] == '1' { + push(i, j-1) + } + if i+1 < row && grid[i+1][j] == '1' { + push(i+1, j) + } + if j+1 < col && grid[i][j+1] == '1' { + push(i, j+1) + } + } + return 1 + } + ret := 0 + for i := 0; i < row; i++ { + for j := 0; j < col; j++ { + ret += bfs(i, j) + } + } + return ret +} diff --git a/Week 03/id_313/LeetCode_33_313.go b/Week 03/id_313/LeetCode_33_313.go new file mode 100644 index 000000000..43047eac5 --- /dev/null +++ b/Week 03/id_313/LeetCode_33_313.go @@ -0,0 +1,33 @@ +package id_313 + +func search(nums []int, target int) int { + rotated := minIndex(nums) + length := len(nums) + left, right := 0, length-1 + for left <= right { + mid := (left + right) / 2 + rotatedMid := (rotated + mid) % length + if nums[rotatedMid] < target { + left = mid + 1 + } else if nums[rotatedMid] > target { + right = mid - 1 + } else { + return rotatedMid + } + } + return -1 + +} + +func minIndex(nums []int) int { + left, right := 0, len(nums)-1 + for left < right { + mid := (left + right) / 2 + if nums[right] < nums[mid] { + left = mid + 1 + } else { + right = mid + } + } + return left +} diff --git a/Week 03/id_313/LeetCode_455_313.go b/Week 03/id_313/LeetCode_455_313.go new file mode 100644 index 000000000..4b6c7a5bd --- /dev/null +++ b/Week 03/id_313/LeetCode_455_313.go @@ -0,0 +1,16 @@ +package id_313 + +import "sort" + +func findContentChildren(g []int, s []int) int { + sort.Ints(g) + sort.Ints(s) + i, j := 0, 0 + for i < len(g) && j < len(s) { + if s[j] >= g[i] { + i++ + } + j++ + } + return i +} diff --git a/Week 03/id_313/LeetCode_45_313.go b/Week 03/id_313/LeetCode_45_313.go new file mode 100644 index 000000000..0c5cde738 --- /dev/null +++ b/Week 03/id_313/LeetCode_45_313.go @@ -0,0 +1,21 @@ +package id_313 + +func jump(nums []int) int { + length := len(nums) + maxPos, end, step := 0, 0, 0 + for i := 0; i < length-1; i++ { + maxPos = maxFunc(maxPos, i+nums[i]) + if i == end { + end = maxPos + step++ + } + } + return step +} + +func maxFunc(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/Week 03/id_313/LeetCode_55_313.go b/Week 03/id_313/LeetCode_55_313.go new file mode 100644 index 000000000..f5406ea54 --- /dev/null +++ b/Week 03/id_313/LeetCode_55_313.go @@ -0,0 +1,12 @@ +package id_313 + +func canJump(nums []int) bool { + length := len(nums) + pos := length - 1 + for i := length - 1; i >= 0; i-- { + if i+nums[i] >= pos { + pos = i + } + } + return pos == 0 +} diff --git a/Week 03/id_313/LeetCode_74_313.go b/Week 03/id_313/LeetCode_74_313.go new file mode 100644 index 000000000..885dd3424 --- /dev/null +++ b/Week 03/id_313/LeetCode_74_313.go @@ -0,0 +1,25 @@ +package id_313 + +func searchMatrix(matrix [][]int, target int) bool { + m := len(matrix) + if m == 0 { + return false + } + n := len(matrix[0]) + mid, element := 0, 0 + left, right := 0, m*n-1 + for left <= right { + mid = (left + right) / 2 + element = matrix[mid/n][mid%n] + if element == target { + return true + } else { + if target < element { + right = mid - 1 + } else { + left = mid + 1 + } + } + } + return false +} diff --git a/Week 03/id_313/LeetCode_860_313.go b/Week 03/id_313/LeetCode_860_313.go new file mode 100644 index 000000000..6e438d602 --- /dev/null +++ b/Week 03/id_313/LeetCode_860_313.go @@ -0,0 +1,27 @@ +package id_313 + +func lemonadeChange(bills []int) bool { + var five, ten int + for _, bill := range bills { + switch bill { + case 5: + five++ + case 10: + five-- + ten++ + default: + if ten > 0 { + five-- + ten-- + } else { + five -= 3 + } + + } + if ten < 0 || five < 0 { + return false + } + } + + return true +} diff --git a/Week 03/id_313/LeetCode_874_313.go b/Week 03/id_313/LeetCode_874_313.go new file mode 100644 index 000000000..52ce5ca4c --- /dev/null +++ b/Week 03/id_313/LeetCode_874_313.go @@ -0,0 +1,42 @@ +package id_313 + +import "strconv" + +func robotSim(commands []int, obstacles [][]int) int { + var ret = 0 + var dx = [][]int{{0, 1}, {1, 0}, {0, -1}, {-1, 0}} + var k = 0 + m := make(map[string]bool, len(obstacles)) + for i := 0; i < len(obstacles); i++ { + key := strconv.Itoa(obstacles[i][0]) + "," + strconv.Itoa(obstacles[i][1]) + m[key] = true + } + x, y := 0, 0 + for _, command := range commands { + if command == -1 { + k = (k + 1) % 4 + } else if command == -2 { + k = (k + 4 - 1) % 4 + } else { + cur := dx[k] + for i := 0; i < command; i++ { + key := strconv.Itoa(x+cur[0]) + "," + strconv.Itoa(y+cur[1]) + _, ok := m[key] + if ok { + break + } + x += cur[0] + y += cur[1] + } + ret = max(ret, x*x+y*y) + } + } + return ret +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/Week 03/id_318/LeetCode_122_318.py b/Week 03/id_318/LeetCode_122_318.py new file mode 100644 index 000000000..8803421f4 --- /dev/null +++ b/Week 03/id_318/LeetCode_122_318.py @@ -0,0 +1,16 @@ +# +# @lc app=leetcode id=122 lang=python3 +# +# [122] Best Time to Buy and Sell Stock II +# + +# @lc code=start +class Solution: + def maxProfit(self, prices: List[int]) -> int: + max_profit = 0 + for i in range(1, len(prices)): + if prices[i] > prices[i-1]: + max_profit += prices[i] - prices[i-1] + return max_profit +# @lc code=end + diff --git a/Week 03/id_318/LeetCode_455_318.java b/Week 03/id_318/LeetCode_455_318.java new file mode 100644 index 000000000..04fe555f4 --- /dev/null +++ b/Week 03/id_318/LeetCode_455_318.java @@ -0,0 +1,21 @@ +/* + * @lc app=leetcode id=455 lang=java + * + * [455] Assign Cookies + */ + +// @lc code=start +class Solution { + public int findContentChildren(int[] grid, int[] size) { + if(grid == null || size == null) return 0; + Arrays.sort(grid); + Arrays.sort(size); + int g = 0, s = 0; + while(g < grid.length && s < size.length) { + if(grid[g] <= size[s++]) g++; + } + return g; + } +} +// @lc code=end + diff --git a/Week 03/id_318/LeetCode_55_318.js b/Week 03/id_318/LeetCode_55_318.js new file mode 100644 index 000000000..5814065f7 --- /dev/null +++ b/Week 03/id_318/LeetCode_55_318.js @@ -0,0 +1,23 @@ +/* + * @lc app=leetcode id=55 lang=javascript + * + * [55] Jump Game + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {boolean} + */ +var canJump = function(nums) { + if (nums == null) return false; + var endReachable = nums.length - 1; + for (var i = nums.length - 1; i >= 0; i--) { + if (nums[i] + i >= endReachable) { + endReachable = i; + } + } + return endReachable == 0; +}; +// @lc code=end + diff --git a/Week 03/id_328/LemonadeChange.java b/Week 03/id_328/LemonadeChange.java new file mode 100644 index 000000000..60eb66a16 --- /dev/null +++ b/Week 03/id_328/LemonadeChange.java @@ -0,0 +1,31 @@ +public class LemonadeChange { + public boolean lemonadeChange(int[] bills) { + int fiveCount = 0 ; + int tenCount = 0; + + for(int i = 0 ; i < bills.length ; i ++){ + if(bills[i] == 5){ + fiveCount++; + }else if(bills[i] == 10){ + if (fiveCount > 0){ + tenCount++; + fiveCount--; + }else { + return false; + } + }else { + if(tenCount >0 && fiveCount >0){ + tenCount --; + fiveCount --; + }else if(fiveCount > 2){ + fiveCount -= 3; + }else{ + return false; + } + } + } + + return true; + + } +} \ No newline at end of file diff --git a/Week 03/id_328/SearchA2dMatrix.java b/Week 03/id_328/SearchA2dMatrix.java new file mode 100644 index 000000000..8ef7cea6f --- /dev/null +++ b/Week 03/id_328/SearchA2dMatrix.java @@ -0,0 +1,42 @@ +public class SearchA2dMatrix { + public boolean searchMatrix(int[][] matrix, int target) { + int rowCount = matrix.length; + if(rowCount == 0 || matrix[0].length == 0) return false; + int top = 0 ; + int bottom = rowCount - 1; + int mid = 0 ; + if(rowCount > 1){ + while( top < bottom){ + mid = (top + bottom) /2; + if(matrix[mid][0] == target){ + return true; + }else if(matrix[mid][0] < target){ + top = mid + 1; + }else { + bottom = mid - 1; + } + } + + if(matrix[top][0] > target ) top--; + + if(top < 0){ + return false; + } + + } + int left = 0 ; + int right = matrix[top].length - 1; + mid = 0; + while(left <= right){ + mid = (left + right) / 2; + if(matrix[top][mid] == target){ + return true; + }else if(matrix[top][mid] < target){ + left = mid + 1; + }else { + right = mid - 1; + } + } + return false; + } +} \ No newline at end of file diff --git a/Week 03/id_338/LeetCode_102_338.java b/Week 03/id_338/LeetCode_102_338.java new file mode 100644 index 000000000..c1523fa10 --- /dev/null +++ b/Week 03/id_338/LeetCode_102_338.java @@ -0,0 +1,33 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * Created by leesen on 2019/10/28. + */ +public class LeetCode_102_338 { + List> ans = new ArrayList(); + public List> levelOrder(TreeNode root) { + if (root == null) return new ArrayList<>(); + helper(root, 0); + return ans; + } + + private void helper(TreeNode root, int level) { + if (root == null) return; + if (ans.size() < level + 1) ans.add(level, new ArrayList()); //****如果 + ans.get(level).add(root.val); + ++level; + if (root.left != null) helper(root.left, level); + if (root.right != null) helper(root.right, level); + } + + public static void main(String[] args) { + List> ans = new ArrayList(); + ans.add(new ArrayList<>()); + ans.add(new ArrayList<>()); + ans.get(0).add(1); + ans.get(0).add(2); + ans.get(1).add(3); + System.out.println(ans); + } +} diff --git a/Week 03/id_338/LeetCode_153_338.java b/Week 03/id_338/LeetCode_153_338.java new file mode 100644 index 000000000..ce00549a1 --- /dev/null +++ b/Week 03/id_338/LeetCode_153_338.java @@ -0,0 +1,20 @@ +/** + * Created by leesen on 2019/10/31. + */ +public class LeetCode_153_338 { + //条件的梳理简化很重要,连空和单元素特殊情形都包含了 + public int findMin(int[] nums) { + int left = 0; + int right = nums.length - 1; + while (left < right) { + int mid = left + (right - left) / 2; + //[1,2,3,4,5] + //[2,3,4,5,1] + //[5,1,2,3,4] + //先判断右侧,且=mid而不是mid-1,涵盖了特殊情形,避免了边界溢出 + if (nums[mid] < nums[right]) right = mid; + else left = mid + 1; + } + return nums[left]; + } +} diff --git a/Week 03/id_338/LeetCode_33_338.java b/Week 03/id_338/LeetCode_33_338.java new file mode 100644 index 000000000..4fb25adfa --- /dev/null +++ b/Week 03/id_338/LeetCode_33_338.java @@ -0,0 +1,22 @@ +/** + * Created by leesen on 2019/10/31. + */ +public class LeetCode_33_338 { + //硬上二叉搜索, 只是夹逼判断条件变换一下, O(log + public int search(int[] nums, int target) { + int left = 0; + int right = nums.length - 1; + + while (left < right) { + int mid = left + (right - left) / 2; + if (nums[mid] == target) return mid; //****增加等值输出, 减少下面判断复杂度 + //左边单调,比最大的还大,继续往右找 + //****nums[left] <= nums[mid], 此处必须有=号 + // 否则当left和mid值相等又都不等于target的时候right = mid - 1可能正好跳过了值 + if (nums[left] <= nums[mid] && (target > nums[mid] || target < nums[left])) left = mid + 1; + else if (target < nums[left] && target > nums[mid]) left = mid + 1; //左边非单调, 但是比左边小,比mid大,继续往右找 + else right = mid - 1; + } + return left == right && nums[left] == target ? left : -1; + } +} diff --git a/Week 03/id_338/LeetCode_69_338.java b/Week 03/id_338/LeetCode_69_338.java new file mode 100644 index 000000000..8a0b56b4f --- /dev/null +++ b/Week 03/id_338/LeetCode_69_338.java @@ -0,0 +1,31 @@ +/** + * Created by leesen on 2019/10/31. + */ +public class LeetCode_69_338 { + //时间复杂度:O(logN),二分法的时间复杂度是对数级别的 + public int mySqrt(int x) { + if (x == 0 || x == 1) return x; + + long left = 1, right = x; //****left最小取1,而不是下标值0, 防止乘法溢出定义long类型 + while (left <= right) { //****因为舍弃小数,所以要<= + long mid = left + (right - left) / 2; + + if (mid * mid > x) { + right = mid - 1; + } + else { + left = mid + 1; + } + } + return (int)right; //类型转换下 + } + + //牛顿迭代法, + public int mySqrt1(int x) { + long r = x; + while (r*r > x) { + r = (r + x/r) /2; + } + return (int)r; + } +} diff --git a/Week 03/id_338/LeetCode_74_338.java b/Week 03/id_338/LeetCode_74_338.java new file mode 100644 index 000000000..c95fe76a7 --- /dev/null +++ b/Week 03/id_338/LeetCode_74_338.java @@ -0,0 +1,68 @@ +/** + * Created by leesen on 2019/10/31. + */ +public class LeetCode_74_338 { + public boolean searchMatrix(int[][] matrix, int target) { + if (matrix == null || matrix.length == 0) return false; + int rowNum = matrix.length; + int colNum = matrix[0].length; + + if (colNum == 0) return false; //****[[]] + for (int i=0; i matrix[i][colNum-1]) continue; + + //暴力法 ****j matrix[i][mid]) left = mid + 1; +// else right = mid - 1; +// } + } + + //上面两种解法的问题是,当二维数组为n*1列的时候时间复杂度退化为O(n*m) + //可以将二维数组当成一维单调直接二叉处理 + //但是rowNum * colNum可能溢出, / %这些也很耗时,反而不如上面速度快 + int begin = 0, end = rowNum * colNum - 1; + + while (begin <= end) { + int mid = begin + (end - begin) / 2; + if (matrix[mid/colNum][mid%colNum] == target) return true; //****此处求值技巧是关键,都是跟colNum做运算 + else if (matrix[mid/colNum][mid%colNum] < target) begin = mid + 1; + else end = mid - 1; + } + + //综上所述, 实际的工程中要看具体的二维数组是什么类型的 + //如果n*m都不大, 选1即可 + //如果n不大,m很大, 选2 + //如果n和m都很大, 选3 + + return false; + } + + //***终极推荐, 国际版简洁写法, while循环, 并简化指针判断 + public boolean searchMatrix1(int[][] matrix, int target) { + if (matrix == null || matrix.length == 0) return false; + int i = 0, j = matrix[0].length - 1; + while (i < matrix.length && j >= 0) { + if (matrix[i][j] == target) { + return true; + } else if (matrix[i][j] > target) { + j--; + } else { + i++; + } + } + + return false; + } +} diff --git a/Week 03/id_338/LeetCode_81_338.java b/Week 03/id_338/LeetCode_81_338.java new file mode 100644 index 000000000..083135662 --- /dev/null +++ b/Week 03/id_338/LeetCode_81_338.java @@ -0,0 +1,39 @@ +/** + * Created by leesen on 2019/10/31. + */ +public class LeetCode_81_338 { + public boolean search(int[] nums, int target) { + if (nums == null || nums.length == 0) return false; + int left = 0; + int right = nums.length - 1; + + while (left < right) { + int mid = left + (right - left) / 2; + + //相比于不重复的数据,只需要对比两端与mid的值,去重后收缩即可 + if (left != mid && nums[left] == nums[mid]) { + left++; + continue; + } + if (nums[right] == nums[mid]) { + right--; + continue; + } + + //左边单调,比最大的还大,继续往右找 + //****nums[left] <= nums[mid], 此处必须有=号 + // 否则当left和mid值相等又都不等于target的时候right = mid - 1可能正好跳过了值 + if (nums[left] <= nums[mid] && (target > nums[mid] || target < nums[left])) left = mid + 1; + + else if (target < nums[left] && target > nums[mid]) left = mid + 1; //左边非单调, 但是比左边小,比mid大,继续往右找 + else right = mid; + } + return nums[left] == target ? true : false; + } + + public static void main(String[] args) { + LeetCode_81_338 rotatedSortedArrayPro_81 = new LeetCode_81_338(); + int[] a = {2,5,6,0,0,1,2}; + System.out.println(rotatedSortedArrayPro_81.search(a, 3)); + } +} diff --git a/Week 03/id_343/LeetCode_122.go b/Week 03/id_343/LeetCode_122.go new file mode 100644 index 000000000..4be0df2fc --- /dev/null +++ b/Week 03/id_343/LeetCode_122.go @@ -0,0 +1,10 @@ +func maxProfit(prices []int) int { + var i, ret int + for i < len(prices)-1 { + if prices[i+1] > prices[i] { + ret += (prices[i+1] - prices[i]) + } + i++ + } + return ret +} diff --git a/Week 03/id_343/LeetCode_153.go b/Week 03/id_343/LeetCode_153.go new file mode 100644 index 000000000..8914e8e9e --- /dev/null +++ b/Week 03/id_343/LeetCode_153.go @@ -0,0 +1,13 @@ +func findMin(nums []int) int { + left, right := 0, len(nums)-1 + for left < right { + mid := (left + right)/2 + if nums[mid] > nums[right] { + left = mid + 1 + } else { + right = mid + } + } + + return nums[left] +} diff --git a/Week 03/id_343/LeetCode_33.go b/Week 03/id_343/LeetCode_33.go new file mode 100644 index 000000000..02fa23e75 --- /dev/null +++ b/Week 03/id_343/LeetCode_33.go @@ -0,0 +1,20 @@ +func search(nums []int, target int) int { + left, right := 0, len(nums)-1 + + for left <= right { + mid := (left + right)/2 + if nums[mid] == target { + return mid + } + + if nums[left] <= nums[mid] && (target > nums[mid] || target < nums[left]) { + left = mid + 1 + } else if (target > nums[mid] && target < nums[left]) { + left = mid + 1 + } else { + right = mid - 1 + } + } + + return -1 +} diff --git a/Week 03/id_343/LeetCode_455.go b/Week 03/id_343/LeetCode_455.go new file mode 100644 index 000000000..811f160ba --- /dev/null +++ b/Week 03/id_343/LeetCode_455.go @@ -0,0 +1,17 @@ +func findContentChildren(g []int, s []int) int { + sort.Ints(g) + sort.Ints(s) + i, j, ret := 0, 0, 0 + + for i < len(g) && j < len(s) { + if s[j] >= g[i] { + ret++ + i++ + j++ + } else { + j++ + } + } + + return ret +} diff --git a/Week 03/id_343/LeetCode_55.go b/Week 03/id_343/LeetCode_55.go new file mode 100644 index 000000000..2e357dc94 --- /dev/null +++ b/Week 03/id_343/LeetCode_55.go @@ -0,0 +1,10 @@ +func canJump(nums []int) bool { + flag := len(nums) - 1 + for i := len(nums)-1; i >= 0; i-- { + if nums[i] + i >= flag { + flag = i + } + } + + return flag == 0 +} diff --git a/Week 03/id_343/LeetCode_74.go b/Week 03/id_343/LeetCode_74.go new file mode 100644 index 000000000..7c56582f7 --- /dev/null +++ b/Week 03/id_343/LeetCode_74.go @@ -0,0 +1,34 @@ +func searchMatrix(matrix [][]int, target int) bool { + for _, v := range matrix { + if len(v) == 0 { + return false + } + if target == v[0] || target == v[len(v)-1] { + return true + } + if target < v[len(v)-1] { + return s(v, target) + } + } + + return false +} + +func s(arr []int, target int) bool { + l := len(arr) + if l == 0 { + return false + } + if l == 1 { + return arr[0] == target + } + + middle := (len(arr)-1)/2 + if arr[middle] > target { + return s(arr[:middle], target) + } else if (arr[middle] < target) { + return s(arr[middle+1:], target) + } else { + return true + } +} diff --git a/Week 03/id_353/Leetcode_200_353.cpp b/Week 03/id_353/Leetcode_200_353.cpp new file mode 100644 index 000000000..78d790857 --- /dev/null +++ b/Week 03/id_353/Leetcode_200_353.cpp @@ -0,0 +1,43 @@ +/* + * @lc app=leetcode.cn id=200 lang=cpp + * + * [200] 岛屿数量 + */ + +// @lc code=start +class Solution { +public: + int numIslands(vector>& grid) { + int nr = grid.size(); + if (!nr) return 0; + int nc = grid[0].size(); + + int num_islands = 0; + for (int r = 0; r < nr; r++) { + for (int c = 0; c < nc; c++) { + if (grid[r][c] == '1') { + num_islands++; + dfs(grid, r, c); + } + } + } + return num_islands; + } + + void dfs(vector>& grid, int r, int c) { + int nr = grid.size(); + int nc = grid[0].size(); + + grid[r][c] = '0'; + if (r - 1 >= 0 && grid[r - 1][c] == '1') + dfs(grid, r - 1, c); + if (r + 1 < nr && grid[r + 1][c] == '1') + dfs(grid, r + 1, c); + if (c - 1 >= 0 && grid[r][c - 1] == '1') + dfs(grid, r, c - 1); + if (c + 1 < nc && grid[r][c + 1] == '1') + dfs(grid, r, c + 1); + } +}; +// @lc code=end + diff --git a/Week 03/id_353/Leetcode_860_353.cpp b/Week 03/id_353/Leetcode_860_353.cpp new file mode 100644 index 000000000..2e19d1c6c --- /dev/null +++ b/Week 03/id_353/Leetcode_860_353.cpp @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode.cn id=860 lang=cpp + * + * [860] 柠檬水找零 + */ + +// @lc code=start +class Solution { +public: + bool lemonadeChange(vector& bills) { + int five = 0, ten = 0; + + for (int bill : bills) { + if (bill == 5) { + five++; + } else if (bill == 10) { + if (five == 0) return false; + five--; + ten++; + } else { + if (five > 0 && ten > 0) { + five--; + ten--; + } else if (five >= 3) { + five -= 3; + } else { + return false; + } + } + } + return true; + } +}; +// @lc code=end + diff --git a/Week 03/id_358/NOTE.md b/Week 03/id_358/NOTE.md index a6321d6e2..17b7b90f1 100644 --- a/Week 03/id_358/NOTE.md +++ b/Week 03/id_358/NOTE.md @@ -1,4 +1,189 @@ -# NOTE +# [358-Week 03] 学习总结 + +## 第三周 第九课 + +本课学习了深度优先搜索(DFS)和广度优先搜索(BFS)的原理和实战解析。 + +### DFS 模板代码: + +写法一: + +```python +visited = set() +def dfs(node, visited): + visited.add(node) + # process current node here + for next_node in node.children: + if not next_node in visited: + dfs(next_node, visited) +``` + + + +写法二: + +```python +visited = set() +def dfs(node, visited): + if node in visited: # terminator + return + visited.add(node) + # process current node here + for next_node in node.children: + if not next_node in visited: + dfs(next_node, visited) +``` + +> DFS还可以借助*栈*实现。 + +### BFS模板代码: + +借助队列 + +```python +def bfs(graph, start, end): + queue = [] + queue.add(start) + visited.add(start) + while queue: + node = queue.pop() + visited.add(node) + nodes = generate_related_nodes(node) + queue.push(nodes) + +``` + +### 实战题解析 + +#### 1. 二叉树的层次遍历 + +该问题的关键是如何在遍历的过程中按层次归集结果。 + +解法1:BFS + +解法2:DFS(每次访问时记录每个节点的层次) + +#### 2. 括号生成 + +之前出现过的题目。这里可以用DFS完成。 + +#### 3. 岛屿数量问题 + +思路:“flood fill”,“洪水算法”。遍历棋盘,遇到0跳过,遇到1使用sink方法,DFS递归处理,将其周围相邻的1都变为0.返回操作次数1;最终统计操作次数的总数 = 岛屿数量。 + +## 第三周 第十课 贪心算法 + +本课学习了贪心算法的原理和实现。 + +### 定义: + +每一步选择中都采取当前状态下最好的或最优的选择,从而希望导致全局的结果都是最优的。 + +### 与动态规划的区别: + +对每个子问题的解决方法都做出选择。 + +贪心算法不能回退。 + +动态规划会保存之前的运算结果,并根据以前的结果对当前结果进行选择,有回退功能。 + +### 用途 + +可以解决一些最优化的问题:求图的最小生成树,求哈夫曼编码等。只要能证明每一步是最优,到最后也是最优的就行。 + +> 一般不会用来解决工程上或生活中的问题。因为往往都是比较低效的算法。 +> +> 但可以作为解决问题的辅助方法。 + +### 实战题目解析 + +#### 1. 硬币组合 + +> 如果硬币可选集合固定,且数额满足整除关系,可以用贪心算法。(20,10,5,1) + +使用贪心算法。 + +> 问题可以分解成子问题。子问题的最优解递推到最终问题的最优解。 + + + +#### 2. 分饼干问题 + +思路:优先满足胃口小的小朋友。两个数组排序后,用一个while循环。 + +#### 3. 最近卖股票的时机 + +解法一:贪心算法。 + +解法二:动态规划 + +> 此题其实换个角度想,只要后一天的价格比前一天搞,就可以低买高卖,把所有利润累加就得到最大利润。 + +#### 4. 跳跃游戏 + +```js +reachable = nums.length - 1 +for(let i = nums.length -1; i >=0; i--) { + if(nums[i] + i >= reachable) { + reachable = i; + } +} +if(reachable === 0){ + return true; +} +``` + +## 第三周 第十一课 二分查找法 + +本课学习了二分查找的实现,特性。 + +### 二分查找的前提 + +1. 目标函数具有单调性 +2. 存在上下边界 +3. 能够通过索引访问 + +### 代码模板: + +```python +left, right = 0, len(array) - 1 +while left <= right: + mid = (left + right) / 2 + if arary[mid] === target: + # find the result' + return result; + elif array[mid] < target: + left = mid + 1 + else + right = mid - 1 +``` + +### 实战题 + +#### 1. 实现求平方根的函数sqrt() + +解法一: 二分 + +解法二:牛顿迭代法 + + + +### 2. 旋转数组 + +1. 暴力法: 数组还原成有序的Ologn +2. 二分查找(该问题符合使用二分查找的前提, 两个有序的数组) + + + + + + + + + + + + + - diff --git a/Week 03/id_358/leetcode-127-ladderWord-2.js b/Week 03/id_358/leetcode-127-ladderWord-2.js new file mode 100644 index 000000000..fc502deb9 --- /dev/null +++ b/Week 03/id_358/leetcode-127-ladderWord-2.js @@ -0,0 +1,72 @@ +/** 单词接龙 +先预处理 wordList,因为从beginWord到endWord,每次只变换一个字母。 +所以循环wordList,取出一个word,循环让word的每个字母替换为*,存到map里,key是替换了*后的word,值是word的数组 +这样得到的具有相邻单词的数组 +每次使用{word, level} 进行bfs遍历,level每次+1,知道遇到endWord结束; +bfs借助于队列 +使用visited数组记录访问过的单词防止重复遍历 +使用两个队列,从开头和结尾同时遍历,如果相关,则找到结果 + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ +var ladderLength = function (beginWord, endWord, wordList) { + // 1 预处理wordList + const Len = beginWord.length; // 每个单词长度一样 + const map = {} + wordList.forEach(word => { + for (let i = 0; i < Len; i++){ + const transformed = word.substr(0, i) + '*' + word.substr(i + 1) + if (map[transformed]) { + map[transformed].push(word) + } else { + map[transformed] = [word] + } + } + }) + // bfs + const begin_queue = []; + begin_queue.push({ word: beginWord, level: 1 }) + const end_queue = []; + end_queue.push({ word: endWord, level: 1 }) + const begin_visited = {} + const end_visited = {} + begin_visited[beginWord] = 1; + end_visited[endWord] = 1; + let res; + while (begin_queue.length > 0 && end_queue.length > 0) { + debugger + res = visitNode(begin_queue, begin_visited, end_queue, end_visited) + if (res) { + return res; + } + res = visitNode(end_queue, end_visited, begin_queue, begin_visited) + if (res) { + return res; + } + } + //任意一个队列遍历完毕且没有找到结果返回失败结果 + return 0; + function visitNode(begin_queue, begin_visited, end_queue, end_visited) { + const newWord = begin_queue.shift(); + let word = newWord.word, level = newWord.level; + for (let i = 0; i < Len; i++){ + const trans = word.substr(0, i) + '*' + word.substr(i + 1); + if (!map[trans]) continue; + for (let item of map[trans]) { + // 前后两个队列相交,结束 + if (end_visited[item]) { + return level + end_visited[item]; + } + // 开始队列还没遍历过,添加进去 + if (!begin_visited[item]) { + begin_visited[item] = level + 1; + begin_queue.push({word:item, level: level + 1}) + } + } + } + return 0; + } + return 0; +}; \ No newline at end of file diff --git a/Week 03/id_358/leetcode-127-ladderWord.js b/Week 03/id_358/leetcode-127-ladderWord.js new file mode 100644 index 000000000..51ecf11b8 --- /dev/null +++ b/Week 03/id_358/leetcode-127-ladderWord.js @@ -0,0 +1,60 @@ +/** 单词接龙 +先预处理 wordList,因为从beginWord到endWord,每次只变换一个字母。 +所以循环wordList,取出一个word,循环让word的每个字母替换为*,存到map里,key是替换了*后的word,值是word的数组 +这样得到的具有相邻单词的数组 +每次使用{word, level} 进行bfs遍历,level每次+1,知道遇到endWord结束; +bfs借助于队列 +使用visited数组记录访问过的单词防止重复遍历 + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ +var ladderLength = function (beginWord, endWord, wordList) { + // 1 预处理wordList + const len = beginWord.length; + const map = {} + wordList.forEach(word => { + for (let i = 0; i < word.length; i++){ + const transformed = word.substr(0, i) + '*' + word.substr(i + 1) + if (map[transformed]) { + map[transformed].push(word) + } else { + map[transformed] = [word] + } + } + }) + // bfs + const queue = []; + queue.push({ word: beginWord, level: 1 }) + const visited = {} + visited[beginWord] = true; + while (queue.length > 0) { + const node = queue.shift(); + let word = node.word, level = node.level; + for (let i = 0; i < len; i++){ + const trans = word.substr(0, i) + '*' + word.substr(i + 1); + if (!map[trans]) continue; + for (let newWord of map[trans]) { + if (newWord === endWord) { + return level + 1; + } + if (!visited[newWord]) { + visited[newWord] = true; + queue.push({word:newWord, level: level + 1}) + } + } + // 这里使用forEach是错误的,因为forEach中的return不能中止外面的遍历,抛出的异常可以中止遍历。要改成for循环 + // map[trans] && map[trans].forEach(item => { + // if (item === endWord) { + // return level + 1; + // } + // if (!visited[item]) { + // visited[item] = true; + // queue.push({word: item, level: level+1}) + // } + // }) + } + } + return 0; +}; \ No newline at end of file diff --git a/Week 03/id_358/leetcode-359-minesweeper.js b/Week 03/id_358/leetcode-359-minesweeper.js new file mode 100644 index 000000000..6ef4afdbd --- /dev/null +++ b/Week 03/id_358/leetcode-359-minesweeper.js @@ -0,0 +1,82 @@ +/** 使用BFS,在while循环里递归,可以不用借助于队列 + * @param {character[][]} board + * @param {number[]} click + * @return {character[][]} + * 1. 碰到M,改为x,输入board; + * 2. 碰到E,遍历周围M个数 count,count>0,更新该位置为count,输出board,否则更新该位置为B,并遍历相邻8个位置。 + */ + var updateBoard = function (board, click) { + const rows = board.length; + const cols = board[0].length; + const startRow = click[0]; + const startCol = click[1]; + // 记录左上,上,右上,左,右,左下,下,右下8个方向坐标,用于循环click周围8个相邻位置 + const dirs = [ + [-1, 1], [0, 1], [1, 1], + [-1, 0], [1, 0], + [-1, -1], [0, -1], [1, -1] + ]; + // 用于记录应访问过的位置防止重复添加到递归的队列里 + const visited = []; + for (let i = 0; i < rows; i++){ + visited[i] = []; + for (let j = 0; j < cols; j++){ + visited[i][j] = false; + } + } + const queue = []; + queue.push(click); + visited[startRow][startCol] = true; + bfs(startRow, startCol); + return board; + function bfs(row, col) { + // while (queue.length > 0) { + debugger + if (board[row][col] === 'M') { + board[row][col] = 'X' + return board; + } + if (board[row][col] === 'E') { + const count = countSum(row, col); + if (count > 0) { + board[row][col] = count + ''; + } else { + board[row][col] = 'B'; + visited[row][col] = true; + for (let i = 0; i < 8; i++) { + let newRow = row + dirs[i][0]; + let newCol = col + dirs[i][1]; + if (isInBoard(newRow, newCol) && !visited[newRow][newCol]) { + if (board[newRow][newCol] === 'E') { + visited[newRow][newCol] = true; + // queue.push([newRow, newCol]) + bfs(newRow, newCol); + } + + } + } + } + } + + // } + }; + + function countSum(row, col) { + let count = 0; + for (let i = 0; i < dirs.length; i++) { + let newRow = row + dirs[i][0]; + let newCol = col + dirs[i][1]; + if (isInBoard(newRow, newCol) && board[newRow][newCol] === 'M') { + count++; + } + } + return count; + } + function isInBoard(row, col) { + if (row >= 0 && row < rows && col >= 0 && col < cols) { + return true; + } else { + return false; + } + } +}; \ No newline at end of file diff --git a/Week 03/id_363/LeetCode_127_363.java b/Week 03/id_363/LeetCode_127_363.java new file mode 100644 index 000000000..5d6ef232b --- /dev/null +++ b/Week 03/id_363/LeetCode_127_363.java @@ -0,0 +1,143 @@ +package com.test.leetcode.week03; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * done 20191103 + * todo 20191104 + * todo 20191105 + * todo 20191109 + */ +public class SolutionLadderLength127 { + + + @Test + public void test1() { + System.out.println(ladderLength("hit", "cog", Arrays.asList("hot","dot","dog","lot","log","cog"))); + System.out.println(ladderLength_bfs("hot", "dog", Arrays.asList("hot","dog"))); + System.out.println(ladderLength_bfs("hit", "cog", Arrays.asList("hot","dot","dog","lot","log","cog"))); + } + + + /** + * BFS:每个单词特殊处理一下:找到词库里面所有编辑距离是1的单词放入到队列中 + * DFS:找到编辑距离是1的词继续向下dfs + * @param beginWord + * @param endWord + * @param wordList + * @return + */ + public int ladderLength_bfs(String beginWord, String endWord, List wordList) { + // 1. 词库特殊处理 + if (!wordList.contains(endWord)) { + return 0; + } + Map> workMap = new HashMap<>(); + wordList.forEach(word -> { + for (int i = 0; i < word.length(); i ++) { + String newWord = word.substring(0, i) + "*" + word.substring(i + 1, word.length()); + List candidate = workMap.getOrDefault(newWord, new LinkedList<>()); + candidate.add(word); + workMap.put(newWord, candidate); + } + }); + // 2. 使用队列 + LinkedList queue = new LinkedList<>(); + queue.add(beginWord); + Set visited = new HashSet(); + visited.add(beginWord); + int step = 1; + while (!queue.isEmpty()) { + int size = queue.size(); + while (size -- > 0) { + // 判断当前单词是否和endWord 相等1 + String cur = queue.poll(); + if (cur.equals(endWord)) { + return step; + } + // 找编辑距离是1的单词加入到队列中 + for (int i = 0; i < cur.length(); i ++) { + String newWord = cur.substring(0, i) + "*" + cur.substring(i + 1); + List probablityWords = workMap.getOrDefault(newWord, new LinkedList<>()); + for (String pword : probablityWords) { + if (visited.contains(pword)) { + continue; + } + // 已经加入到队列中的数据就不在处理了 + visited.add(pword); + queue.add(pword); + } + } + } + step ++; + } + return 0; + } + + + /** + * 1.DFS 查找当前start 和 wordList 里面编辑距离是1的词,继续dfs + * 返回的是转换序列的长度 + * @param beginWord + * @param endWord + * @param wordList + * @return + */ + public int ladderLength(String beginWord, String endWord, List wordList) { + if (!wordList.contains(endWord)) { + return 0; + } + int res = helper(beginWord, endWord, wordList, 0); + return Integer.MAX_VALUE == res ? 0 : res + 1; + } + + private int helper(String beginWord, String endWord, List wordList, int step) { + // 递归终止条件 + if (beginWord.equals(endWord)) { + return step; + } + int res = Integer.MAX_VALUE; + if (!wordList.contains(endWord)) { + return res; + } + // 处理当前层 + for (String word : wordList) { + int diff = computeEditDistance(word, beginWord); + if (diff != 1) { + continue; + } + if (endWord.equals(word)) { + return step + 1; + } + List tempWordList = new LinkedList<>(wordList); + tempWordList.remove(word); + // 下探到下一层 + res = Math.min(helper(word, endWord, tempWordList, step + 1), res); + } + // 清理当前层 + return res; + } + + private int computeEditDistance(String word, String beginWord) { + int count = 0; + for (int i = 0; i < word.length();i ++) { + if (word.charAt(i) != beginWord.charAt(i)) { + count ++; + if (count > 1) { + break; + } + } + } + return count; + } + + +} diff --git a/Week 03/id_363/LeetCode_200_363.java b/Week 03/id_363/LeetCode_200_363.java new file mode 100644 index 000000000..33d1631ee --- /dev/null +++ b/Week 03/id_363/LeetCode_200_363.java @@ -0,0 +1,145 @@ +package com.test.leetcode.week03; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.LinkedList; + +/** + * done 20191103 + * todo 20191104 + * todo 20191105 + * todo 20191109 + */ +public class ssolutionNumIsland200 { + + + @Test + public void test1() { + char[][] grid = new char[4][5]; + for (char[] c : grid) { + Arrays.fill(c, '0'); + } + grid[0][0] = '1'; + grid[0][1] = '1'; + grid[0][2] = '1'; + grid[0][3] = '1'; + grid[1][0] = '1'; + grid[1][1] = '1'; + grid[1][3] = '1'; + grid[2][0] = '1'; + grid[2][1] = '1'; + + + char[][] grid2 = new char[4][5]; + for (char[] c : grid2) { + Arrays.fill(c, '0'); + } + grid2[0][0] = '1'; + grid2[0][1] = '1'; + grid2[1][0] = '1'; + grid2[1][1] = '1'; + grid2[2][2] = '1'; + grid2[3][3] = '1'; + grid2[3][4] = '1'; + +// System.out.println(numIslands(grid)); +// System.out.println(numIslands(grid2)); + +// System.out.println(numIslands_bfs(grid)); +// System.out.println(numIslands_bfs(grid2)); + + char[][] grid3 = {{'1','1','1'},{'0','1','0'},{'1','1','1'}}; + System.out.println(numIslands_bfs(grid3)); + + } + + + /** + * 1.DFS: 遇到1,那么递归吧这个1周边的1全部置为0 + * @param grid + * @return + */ + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + int nr = grid.length; + int nc = grid[0].length; + int num = 0; + for (int r = 0; r < nr; r ++) { + for (int c = 0; c < nc; c ++) { + if (grid[r][c] == '1') { + ++ num; + helper(grid, r, c); + } + } + } + return num; + } + + private void helper(char[][] grid, int r, int c) { + // termination + int nr = grid.length; + int nc = grid[0].length; + if (r < 0 || r >= nr || c < 0 || c >= nc || grid[r][c] == '0') { + return; + } + // process + grid[r][c] = '0'; + // drill down + helper(grid, r - 1, c); + helper(grid, r + 1, c); + helper(grid, r , c - 1); + helper(grid, r , c + 1); + // reverse + } + + + /** + * bfs: 发现1,将这个1旁边所有有可能的1都加入到队列中 + * @param grid + * @return + */ + public int numIslands_bfs(char[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + int nr = grid.length; + int nc = grid[0].length; + int nums = 0; + for (int r = 0; r < nr; r ++) { + for (int c = 0; c < nc; c ++) { + if (grid[r][c] != '1') { + continue; + } + ++nums ; + LinkedList queue = new LinkedList<>(); + queue.add(r * nc + c); + while(!queue.isEmpty()) { + Integer cur = queue.poll(); + int row = cur / nc; + int col = cur % nc; + grid[row][col] = '0'; + + if (row - 1 >= 0 && grid[row - 1][col] == '1') { + queue.add((row - 1) * nc + col); + } + if (row + 1 < nr && grid[row + 1][col] == '1') { + queue.add((row + 1) * nc + col); + } + + if (col - 1 >= 0 && grid[row][col - 1] == '1') { + queue.add(row * nc + col - 1); + } + + if (col + 1 < nc && grid[row][col + 1] == '1') { + queue.add(row * nc + col + 1); + } + } + } + } + return nums; + } + +} diff --git a/Week 03/id_363/NOTE.md b/Week 03/id_363/NOTE.md index a6321d6e2..f2f516298 100644 --- a/Week 03/id_363/NOTE.md +++ b/Week 03/id_363/NOTE.md @@ -1,4 +1,35 @@ -# NOTE +# 学习总结 +#### 深度优先和广度优先 +DFS和BFS的代码模板要写熟练,DFS 可以使用栈或者递归实现,BFS可以使用队列实现 +leetcode 102题 可以尝试使用DFS求解。 +DFS/BFS代码模板(牢记) + +#### 贪心算法:可以解决一些最优问题 +贪心算法可以解决一些最优化问题,比如:求图中的最小生成树、求哈夫曼编码等。对于工程和生活中的问题,贪心算法一般不能得到我们想要的答案。 +一旦一个问题可以通过贪心算法来解决,那么贪心算法一般是解决这个问题的最好方法。由于贪心算法的高效性以及其所求得的答案比较接近最优结果,贪心算法也可以用作辅助算法或者直接解决一些要求结果不是特别精确的问题。 + +使用场景:最优子结构:通过子问题的最优解可以推到全局最优解 +贪心算法应用: +1.可以证明贪心算法可以得到全局最优解 +2.贪心算法的角度:可能是从前向后贪心,可能是从后向前贪心,也可能是从某个局部切入贪心。 + +贪心算法 vs 动态规划: +贪心算法会对每个子问题的解决方案都作出选择,且不能回退。 +动态规划会保存以前的运行结果,根据以前的运行结果来对当前的情况进行选择,可以回退。 + +贪心:当下局部最优解,不能回退 +回溯:可以回退 +动态规划:最优判断且能回退。 + +洪水算法(FloodFill):每次看到岛屿,就把这个连在一起的岛屿夷为平地。 + +#### 二分查找: +二分查找最关键的三个前提条件:[单调 上下界 索引] +1.目标函数单调性(单调递增/递减) +2.存在上下界(bounded) +3.能够通过索引访问(index access) +代码模板: + diff --git a/Week 03/id_368/LeetCode_102_368.java b/Week 03/id_368/LeetCode_102_368.java new file mode 100644 index 000000000..144c72fd3 --- /dev/null +++ b/Week 03/id_368/LeetCode_102_368.java @@ -0,0 +1,65 @@ +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public class LeetCode_102_368 { + + /*给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。 + https://leetcode-cn.com/problems/binary-tree-level-order-traversal/*/ + + public static void main(String[] args) { + + } + + // BFS + public List> levelOrderBFS(TreeNode root) { + List> res = new ArrayList<>(); + if (root == null) { + return res; + } + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + int size = queue.size(); + List list = new ArrayList<>(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + list.add(node.val); + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + res.add(list); + } + return res; + } + + public List> levelOrderDFS(TreeNode root) { + List> res = new ArrayList<>(); + if (root == null) { + return res; + } + dfs(res, 0, root); + return res; + } + + private void dfs(List> res, int level, TreeNode node) { + if (node == null) { + return; + } + if (res.size() == level) { + res.add(new ArrayList<>()); + } + res.get(level).add(node.val); + if (node.left != null) { + dfs(res, level + 1, node.left); + } + if (node.right != null) { + dfs(res, level + 1, node.right); + } + } +} diff --git a/Week 03/id_368/LeetCode_121_368.java b/Week 03/id_368/LeetCode_121_368.java new file mode 100644 index 000000000..892ac96ab --- /dev/null +++ b/Week 03/id_368/LeetCode_121_368.java @@ -0,0 +1,26 @@ +public class LeetCode_121_368 { + + /*给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 + 如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。 + 注意你不能在买入股票前卖出股票。 + 链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock*/ + + public static void main(String[] args) { + + } + + public int maxProfit(int[] prices) { + int minPrice = Integer.MAX_VALUE; + int max = 0; + for (int i = 0; i < prices.length; i++) { + if (prices[i] < minPrice) { + minPrice = prices[i]; + } else { + if (prices[i] - minPrice > max) { + max = prices[i] - minPrice; + } + } + } + return max; + } +} diff --git a/Week 03/id_368/LeetCode_122_368.java b/Week 03/id_368/LeetCode_122_368.java new file mode 100644 index 000000000..a8516a0d3 --- /dev/null +++ b/Week 03/id_368/LeetCode_122_368.java @@ -0,0 +1,21 @@ +public class LeetCode_122_368 { + + /*给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 + 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。 + 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 + 链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii*/ + + public static void main(String[] args) { + + } + + public int maxProfit(int[] prices) { + int sum = 0; + for (int i = 1; i < prices.length; i++) { + if (prices[i] > prices[i-1]) { + sum += prices[i] - prices[i-1]; + } + } + return sum; + } +} diff --git a/Week 03/id_368/LeetCode_126_368.java b/Week 03/id_368/LeetCode_126_368.java new file mode 100644 index 000000000..1817efb7c --- /dev/null +++ b/Week 03/id_368/LeetCode_126_368.java @@ -0,0 +1,132 @@ +import java.util.*; + +public class LeetCode_126_368 { + + /*给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列。 + 转换需遵循如下规则: + 每次转换只能改变一个字母。 + 转换过程中的中间单词必须是字典中的单词。 + 说明: + 如果不存在这样的转换序列,返回一个空列表。 + 所有单词具有相同的长度。 + 所有单词只由小写字母组成。 + 字典中不存在重复的单词。 + 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。 + 链接:https://leetcode-cn.com/problems/word-ladder-ii*/ + + public static void main(String[] args) { + + } + + public List> findLadders(String beginWord, String endWord, List wordList) { + List> res = new ArrayList<>(); + Set words = new HashSet<>(wordList); + if (!words.contains(endWord)) { + return res; + } + Set visited = new HashSet<>(); + Queue> queue = new LinkedList<>(); + queue.add(new ArrayList<>(Arrays.asList(beginWord))); + visited.add(beginWord); + boolean flag = false; + while (!queue.isEmpty() && !flag) { + int size = queue.size(); + Set levels = new HashSet<>(); + for (int i = 0; i < size; i++) { + List path = queue.poll(); + String word = path.get(path.size() - 1); + char[] chars = word.toCharArray(); + for (int j = 0; j < chars.length; j++) { + char temp = chars[j]; + for (char ch = 'a'; ch <= 'z'; ch++) { + chars[j] = ch; + String str = String.valueOf(chars); + if (words.contains(str) && !visited.contains(str)) { + List list = new ArrayList<>(path); + list.add(str); + queue.add(list); + levels.add(str); + if (endWord.equals(str)) { + res.add(list); + flag = true; + } + } + } + chars[j] = temp; + } + } + visited.addAll(levels); + } + return res; + } + + public List> findLadders02(String beginWord, String endWord, List wordList) { + List> res = new ArrayList<>(); + Set words = new HashSet<>(wordList); + if (!words.contains(endWord)) { + return res; + } + Map> tree = new HashMap<>(); + Set begin = new HashSet<>(), end = new HashSet<>(); + begin.add(beginWord); + end.add(endWord); + if (buildTree(words, tree, begin, end, true)) { + dfs(res, tree, beginWord, endWord, new LinkedList<>()); + } + return res; + } + + private void dfs(List> res, Map> tree, String beginWord, String endWord, LinkedList list) { + list.add(beginWord); + if (beginWord.equals(endWord)) { + res.add(new ArrayList<>(list)); + list.removeLast(); + return; + } + if (tree.containsKey(beginWord)) { + for (String word : tree.get(beginWord)) { + dfs(res, tree, word, endWord, list); + } + } + list.removeLast(); + } + + private boolean buildTree(Set words, Map> tree, Set begin, Set end, boolean isFront) { + if (begin.size() == 0) { + return false; + } + if (begin.size() > end.size()) { + return buildTree(words, tree, end, begin, !isFront); + } + words.removeAll(begin); + boolean isMeet = false; + Set levels = new HashSet<>(); + for (String word : begin) { + char[] chars = word.toCharArray(); + for (int i = 0; i < chars.length; i++) { + char temp = chars[i]; + for (char ch = 'a'; ch <= 'z'; ch++) { + chars[i] = ch; + String str = String.valueOf(chars); + if (words.contains(str)) { + levels.add(str); + String key = isFront ? word : str; + String nextWord = isFront ? str : word; + if (!tree.containsKey(key)) { + tree.put(key, new ArrayList<>()); + } + tree.get(key).add(nextWord); + if (end.contains(str)) { + isMeet = true; + } + } + } + chars[i] = temp; + } + } + if (isMeet) { + return true; + } + return buildTree(words, tree, levels, end, isFront); + } +} diff --git a/Week 03/id_368/LeetCode_127_368.java b/Week 03/id_368/LeetCode_127_368.java new file mode 100644 index 000000000..9a6e04e4d --- /dev/null +++ b/Week 03/id_368/LeetCode_127_368.java @@ -0,0 +1,141 @@ +import java.util.*; + +public class LeetCode_127_368 { + + /*给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。 + 转换需遵循如下规则: + 每次转换只能改变一个字母。 + 转换过程中的中间单词必须是字典中的单词。 + 说明: + 如果不存在这样的转换序列,返回 0。 + 所有单词具有相同的长度。 + 所有单词只由小写字母组成。 + 字典中不存在重复的单词。 + 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。 + 链接:https://leetcode-cn.com/problems/word-ladder*/ + + public static void main(String[] args) { + + } + + // BFS + public int ladderLength(String beginWord, String endWord, List wordList) { + int depth = 0; + Set distSet = new HashSet<>(wordList); + if (!wordList.contains(endWord)) { + return depth; + } + Set visited = new HashSet<>(); + Queue queue = new LinkedList<>(); + queue.add(beginWord); + visited.add(beginWord); + while (!queue.isEmpty()) { + depth++; + int size = queue.size(); + for (int i = 0; i < size; i++) { + String word = queue.poll(); + if (word.equals(endWord)) { + return depth; + } + char[] chars = word.toCharArray(); + for (int j = 0; j < chars.length; j++) { + char temp = chars[j]; + for (char ch = 'a'; ch < 'z'; ch++) { + chars[j] = ch; + String str = new String(chars); + if (!distSet.contains(str) || visited.contains(str)) { + continue; + } + queue.add(str); + visited.add(str); + } + chars[j] = temp; + } + } + } + return 0; + } + + // 双端BFS + public int ladderLengthTwoBfs(String beginWord, String endWord, List wordList) { + int depth = 0; + Set distSet = new HashSet<>(wordList); + if (!distSet.contains(endWord)) { + return depth; + } + Set beginSet = new HashSet<>(), endSet = new HashSet<>(); + beginSet.add(beginWord); + endSet.add(endWord); + while (!beginSet.isEmpty() && !endSet.isEmpty()) { + depth++; + if (beginSet.size() > endSet.size()) { + Set set = beginSet; + beginSet = endSet; + endSet = set; + } + distSet.removeAll(beginSet); + Set tempSet = new HashSet<>(); + for (String word : beginSet) { + char[] chars = word.toCharArray(); + for (int j = 0; j < chars.length; j++) { + char temp = chars[j]; + for (char ch = 'a'; ch < 'z'; ch++) { + chars[j] = ch; + String str = new String(chars); + if (!distSet.contains(str)) { + continue; + } + if (endSet.contains(str)) { + return depth+1; + } + tempSet.add(str); + } + chars[j] = temp; + } + } + beginSet = tempSet; + } + return 0; + } + + // 递归 + public int ladderLengthRecursion(String beginWord, String endWord, List wordList) { + int depth = 0; + Set distSet = new HashSet<>(wordList); + if (!distSet.contains(endWord)) { + return depth; + } + Set beginSet = new HashSet<>(), endSet = new HashSet<>(); + beginSet.add(beginWord); + endSet.add(endWord); + return search(distSet, beginSet, endSet, 0); + } + + public int search (Set distSet, Set beginSet, Set endSet, int depth) { + if (beginSet.isEmpty() || endSet.isEmpty()) { + return 0; + } + depth++; + distSet.removeAll(beginSet); + Set tempSet = new HashSet<>(); + for (String word : beginSet) { + char[] chars = word.toCharArray(); + for (int j = 0; j < chars.length; j++) { + char temp = chars[j]; + for (char ch = 'a'; ch < 'z'; ch++) { + chars[j] = ch; + String str = new String(chars); + if (!distSet.contains(str)) { + continue; + } + if (endSet.contains(str)) { + return depth+1; + } + tempSet.add(str); + } + chars[j] = temp; + } + } + return tempSet.size() > endSet.size() ? search(distSet, endSet, tempSet, depth) : search(distSet, tempSet, endSet, depth); + } +} diff --git a/Week 03/id_368/LeetCode_153_368.java b/Week 03/id_368/LeetCode_153_368.java new file mode 100644 index 000000000..46d875d8c --- /dev/null +++ b/Week 03/id_368/LeetCode_153_368.java @@ -0,0 +1,32 @@ +public class LeetCode_153_368 { + + /*假设按照升序排序的数组在预先未知的某个点上进行了旋转。 + ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 + 请找出其中最小的元素。 + 你可以假设数组中不存在重复元素。 + 链接:https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array*/ + + public static void main(String[] args) { + + } + + public int findMin(int[] nums) { + if (nums == null || nums.length == 0) { + return -1; + } + int left = 0; + int right = nums.length - 1; + if (nums[right] >= nums[left]) { + return nums[left]; + } + while (left < right) { + int mid = (left + right) / 2; + if (nums[mid] > nums[right]) { + left = mid + 1; + } else { + right = mid; + } + } + return nums[left]; + } +} diff --git a/Week 03/id_368/LeetCode_200_368.java b/Week 03/id_368/LeetCode_200_368.java new file mode 100644 index 000000000..ab6b1ff8c --- /dev/null +++ b/Week 03/id_368/LeetCode_200_368.java @@ -0,0 +1,56 @@ +import java.util.LinkedList; +import java.util.Queue; + +public class LeetCode_200_368 { + /*给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。 + 一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。 + 你可以假设网格的四个边均被水包围。 + 链接:https://leetcode-cn.com/problems/number-of-islands*/ + + public static void main(String[] args) { + + } + + public int numIslands(char[][] grid) { + int count = 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == '1') { + // dfs(grid, i, j); + bfs(grid, i, j); + count++; + } + } + } + return count; + } + + private void bfs(char[][] grid, int i, int j) { + Queue queue = new LinkedList<>(); + queue.add(new int[]{i, j}); + while (!queue.isEmpty()) { + int[] current = queue.poll(); + i = current[0]; + j = current[1]; + if (i >= 0 && i < grid.length && j >= 0 && j < grid[0].length && grid[i][j] == '1') { + grid[i][j] = '0'; + bfs(grid, i + 1, j); + bfs(grid, i - 1, j); + bfs(grid, i, j + 1); + bfs(grid, i, j - 1); + } + } + } + + + public void dfs (char[][] grid, int i, int j) { + if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == '0') { + return; + } + grid[i][j] = '0'; + dfs(grid, i + 1, j); + dfs(grid, i - 1, j); + dfs(grid, i, j + 1); + dfs(grid, i, j - 1); + } +} diff --git a/Week 03/id_368/LeetCode_33_368.java b/Week 03/id_368/LeetCode_33_368.java new file mode 100644 index 000000000..ee432a8e7 --- /dev/null +++ b/Week 03/id_368/LeetCode_33_368.java @@ -0,0 +1,41 @@ +public class LeetCode_33_368 { + + /*假设按照升序排序的数组在预先未知的某个点上进行了旋转。 + ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 + 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。 + 你可以假设数组中不存在重复的元素。 + 你的算法时间复杂度必须是 O(log n) 级别。 + 链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array*/ + + public static void main(String[] args) { + + } + + public int search(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return -1; + } + int left = 0; + int right = nums.length - 1; + while (left <= right) { + int mid = (right + left + 1) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[mid] >= nums[left]) { + if (target >= nums[left] && target < nums[mid] ) { + right = mid - 1; + } else { + left = mid + 1; + } + } else { + if (target <= nums[right] && target > nums[mid]) { + left = mid + 1; + } else { + right = mid - 1; + } + } + } + return -1; + } +} diff --git a/Week 03/id_368/LeetCode_367_368.java b/Week 03/id_368/LeetCode_367_368.java new file mode 100644 index 000000000..eb87fcb15 --- /dev/null +++ b/Week 03/id_368/LeetCode_367_368.java @@ -0,0 +1,26 @@ +public class LeetCode_367_368 { + + /*给定一个正整数 num,编写一个函数,如果 num 是一个完全平方数,则返回 True,否则返回 False。 + 说明:不要使用任何内置的库函数,如  sqrt。 + 链接:https://leetcode-cn.com/problems/valid-perfect-square*/ + + public static void main(String[] args) { + + } + + public boolean isPerfectSquare(int num) { + long left = 0; + long right = num; + while (left < right) { + long mid = left + (right - left + 1) / 2; + if (mid * mid == num) { + return true; + } else if (mid * mid > num) { + right = mid - 1; + } else { + left = mid; + } + } + return false; + } +} diff --git a/Week 03/id_368/LeetCode_433_368.java b/Week 03/id_368/LeetCode_433_368.java new file mode 100644 index 000000000..d51451c3c --- /dev/null +++ b/Week 03/id_368/LeetCode_433_368.java @@ -0,0 +1,62 @@ +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class LeetCode_433_368 { + + /*一条基因序列由一个带有8个字符的字符串表示,其中每个字符都属于 "A", "C", "G", "T"中的任意一个。 + 假设我们要调查一个基因序列的变化。一次基因变化意味着这个基因序列中的一个字符发生了变化。 + 例如,基因序列由"AACCGGTT" 变化至 "AACCGGTA" 即发生了一次基因变化。 + 与此同时,每一次基因变化的结果,都需要是一个合法的基因串,即该结果属于一个基因库。 + 现在给定3个参数 — start, end, bank,分别代表起始基因序列,目标基因序列及基因库,请找出能够使起始基因序列变化为目标基因序列所需的最少变化次数。如果无法实现目标变化,请返回 -1。 + 注意: + 起始基因序列默认是合法的,但是它并不一定会出现在基因库中。 + 所有的目标基因序列必须是合法的。 + 假定起始基因序列与目标基因序列是不一样的。 + 链接:https://leetcode-cn.com/problems/minimum-genetic-mutation*/ + + public static void main(String[] args) { + + } + + public int minMutation(String start, String end, String[] bank) { + char[] words = {'A', 'C', 'G', 'T'}; + Set dist = new HashSet<>(Arrays.asList(bank)); + if (!dist.contains(end)) { + return -1; + } + int depth = 0; + Set beginSet = new HashSet<>(), endSet = new HashSet<>(); + beginSet.add(start); + endSet.add(end); + while (!beginSet.isEmpty() && !endSet.isEmpty()) { + if (beginSet.size() > endSet.size()) { + Set set = beginSet; + beginSet = endSet; + endSet = set; + } + depth++; + dist.removeAll(beginSet); + Set levels = new HashSet<>(); + for (String word : beginSet) { + char[] chars = word.toCharArray(); + for (int i = 0; i < chars.length; i++) { + char temp = chars[i]; + for (char ch : words) { + chars[i] = ch; + String str = String.valueOf(chars); + if (dist.contains(str)) { + levels.add(str); + if (endSet.contains(str)) { + return depth; + } + } + } + chars[i] = temp; + } + } + beginSet = levels; + } + return -1; + } +} diff --git a/Week 03/id_368/LeetCode_455_368.java b/Week 03/id_368/LeetCode_455_368.java new file mode 100644 index 000000000..12e2f2492 --- /dev/null +++ b/Week 03/id_368/LeetCode_455_368.java @@ -0,0 +1,34 @@ +import java.util.Arrays; + +public class LeetCode_455_368 { + + /*假设你是一位很棒的家长,想要给你的孩子们一些小饼干。 + 但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸; + 并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi , + 我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。 + 你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。 + 注意: + 你可以假设胃口值为正。 + 一个小朋友最多只能拥有一块饼干。 + 链接:https://leetcode-cn.com/problems/assign-cookies*/ + + public static void main(String[] args) { + + } + + public int findContentChildren(int[] g, int[] s) { + Arrays.sort(g); + Arrays.sort(s); + int count = 0; + for (int i = 0, j = 0; i < g.length && j < s.length;) { + if (s[j] >= g[i]) { + i++; + j++; + count++; + } else { + j++; + } + } + return count; + } +} diff --git a/Week 03/id_368/LeetCode_45_368.java b/Week 03/id_368/LeetCode_45_368.java new file mode 100644 index 000000000..beca056df --- /dev/null +++ b/Week 03/id_368/LeetCode_45_368.java @@ -0,0 +1,23 @@ +public class LeetCode_45_368 { + + /*给定一个非负整数数组,你最初位于数组的第一个位置。 + 数组中的每个元素代表你在该位置可以跳跃的最大长度。 + 你的目标是使用最少的跳跃次数到达数组的最后一个位置。 + 链接:https://leetcode-cn.com/problems/jump-game-ii*/ + + public static void main(String[] args) { + + } + + public int jump(int[] nums) { + int end = 0, furthest = 0, step = 0; + for (int i = 0; i < nums.length - 1; i++) { + furthest = Math.max(furthest, nums[i] + i); + if (i == end) { + end = furthest; + step++; + } + } + return step; + } +} diff --git a/Week 03/id_368/LeetCode_515_368.java b/Week 03/id_368/LeetCode_515_368.java new file mode 100644 index 000000000..95a356031 --- /dev/null +++ b/Week 03/id_368/LeetCode_515_368.java @@ -0,0 +1,61 @@ +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public class LeetCode_515_368 { + /*您需要在二叉树的每一行中找到最大的值。 + https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/*/ + + public static void main(String[] args) { + + } + + public List largestValues(TreeNode root) { + List res = new ArrayList<>(); + if (root == null) { + return res; + } + Queue queue = new LinkedList<>(); + queue.add(root); + while (!queue.isEmpty()) { + int size = queue.size(); + int max = queue.peek().val; + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + if (node.val > max) { + max = node.val; + } + if (node.left != null) { + queue.add(node.left); + } + if (node.right != null) { + queue.add(node.right); + } + } + res.add(max); + } + return res; + } + + public List largestValuesRecersion(TreeNode root) { + List res = new ArrayList<>(); + bfs(0, root, res); + return res; + } + + private void bfs(int level, TreeNode node, List res) { + // termator + if (node == null) { + return; + } + if (level == res.size()) { + res.set(level, node.val); + } + if (node.val > res.get(level)) { + res.add(level, node.val); + } + bfs(level + 1, node.left, res); + bfs(level + 1, node.right, res); + } +} diff --git a/Week 03/id_368/LeetCode_529_368.java b/Week 03/id_368/LeetCode_529_368.java new file mode 100644 index 000000000..f1458be8b --- /dev/null +++ b/Week 03/id_368/LeetCode_529_368.java @@ -0,0 +1,105 @@ +import java.util.LinkedList; +import java.util.Queue; + +public class LeetCode_529_368 { + + /*让我们一起来玩扫雷游戏! + 给定一个代表游戏板的二维字符矩阵。 'M' 代表一个未挖出的地雷,'E' 代表一个未挖出的空方块,'B' 代表没有相邻(上,下,左,右,和所有4个对角线)地雷的已挖出的空白方块,数字('1' 到 '8')表示有多少地雷与这块已挖出的方块相邻,'X' 则表示一个已挖出的地雷。 + 现在给出在所有未挖出的方块中('M'或者'E')的下一个点击位置(行和列索引),根据以下规则,返回相应位置被点击后对应的面板: + 如果一个地雷('M')被挖出,游戏就结束了- 把它改为 'X'。 + 如果一个没有相邻地雷的空方块('E')被挖出,修改它为('B'),并且所有和其相邻的方块都应该被递归地揭露。 + 如果一个至少与一个地雷相邻的空方块('E')被挖出,修改它为数字('1'到'8'),表示相邻地雷的数量。 + 如果在此次点击中,若无更多方块可被揭露,则返回面板。 + 链接:https://leetcode-cn.com/problems/minesweeper*/ + + public static void main(String[] args) { + + } + + public char[][] updateBoard(char[][] board, int[] click) { + int m = board.length, n = board[0].length; + Queue queue = new LinkedList<>(); + queue.add(click); + while (!queue.isEmpty()) { + int[] cell = queue.poll(); + int row = cell[0], col = cell[1]; + if (board[row][col] == 'M') { + board[row][col] = 'X'; + } else { + int count = 0; + for (int i = -1; i < 2; i++) { + for (int j = -1; j < 2; j++) { + if (i == 0 && j == 0) { + continue; + } + int r = row + i, c = col + j; + if (r < 0 || r >= m || c < 0 || c >= n) { + continue; + } + if (board[r][c] == 'M' || board[r][c] == 'X') { + count++; + } + } + } + if (count > 0) { + board[row][col] = (char)(count + '0'); + } else { + board[row][col] = 'B'; + for (int i = -1; i < 2; i++) { + for (int j = -1; j < 2; j++) { + if (i == 0 && j == 0) { + continue; + } + int r = row + i, c = col + j; + if (r < 0 || r >= m || c < 0 || c >= n) { + continue; + } + if (board[r][c] == 'E') { + queue.add(new int[]{r, c}); + board[r][c] = 'B'; + } + } + } + } + } + } + return board; + } + + private int[] rowTo = {-1, -1, -1, 0, 1, 1, 1, 0}; + private int[] colTo = {-1, 0, 1, 1, 1, 0, -1, -1}; + + public char[][] updateBoardRecursion(char[][] board, int[] click) { + int row = click[0]; + int col = click[1]; + dfs(board, row, col); + return board; + } + + private void dfs(char[][] board, int row, int col){ + if (board[row][col] == 'M') { + board[row][col] = 'X'; + } else { + int count = 0; + for (int i = 0; i < rowTo.length; i++) { + int r = row + rowTo[i]; + int c = col + colTo[i]; + if (r >= 0 && r < board.length && c >= 0 && c < board[0].length && board[r][c] == 'M') { + count++; + } + } + if (count > 0) { + board[row][col] = (char)(count + '0'); + } else { + board[row][col] = 'B'; + for (int i = 0; i < rowTo.length; i++) { + int r = row + rowTo[i]; + int c = col + colTo[i]; + if (r >= 0 && r < board.length && c >= 0 && c < board[0].length && board[r][c] == 'E') { + dfs(board, r, c); + } + } + } + } + } +} diff --git a/Week 03/id_368/LeetCode_55_368.java b/Week 03/id_368/LeetCode_55_368.java new file mode 100644 index 000000000..8e8ba4911 --- /dev/null +++ b/Week 03/id_368/LeetCode_55_368.java @@ -0,0 +1,44 @@ +public class LeetCode_55_368 { + + /*给定一个非负整数数组,你最初位于数组的第一个位置。 + 数组中的每个元素代表你在该位置可以跳跃的最大长度。 + 判断你是否能够到达最后一个位置。*/ + + public static void main(String[] args) { + + } + + public boolean canJump(int[] nums) { + int[] memo = new int[nums.length]; + memo[memo.length - 1] = 1; + return canJumpPosition(0, nums, memo); + } + + public boolean canJumpPosition (int position, int[] nums, int[] memo) { + if (memo[position] != 0) { + if (memo[position] == 1) { + return true; + } + return false; + } + int furthestJump = Math.min(position + nums[position], nums.length - 1); + for (int nextPosition = position + 1; nextPosition <= furthestJump; nextPosition++) { + if (canJumpPosition(nextPosition, nums, memo)) { + memo[position] = 1; + return true; + } + } + memo[position] = 2; + return false; + } + + public boolean canJump02(int[] nums) { + int lastPosition = nums.length - 1; + for (int i = nums.length - 1; i >= 0; i--) { + if (nums[i] + i >= lastPosition) { + lastPosition = i; + } + } + return lastPosition == 0; + } +} diff --git a/Week 03/id_368/LeetCode_69_368.java b/Week 03/id_368/LeetCode_69_368.java new file mode 100644 index 000000000..b6ef83340 --- /dev/null +++ b/Week 03/id_368/LeetCode_69_368.java @@ -0,0 +1,36 @@ +public class LeetCode_69_368 { + + /*实现 int sqrt(int x) 函数。 + 计算并返回 x 的平方根,其中 x 是非负整数。 + 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。 + 链接:https://leetcode-cn.com/problems/sqrtx*/ + + public static void main(String[] args) { + + } + + public int mySqrt(int x) { + if (x == 0) { + return x; + } + long left = 1; + long right = x; + while (left < right) { + long mid = left + (right - left + 1) / 2; + if (mid * mid > x) { + right = mid - 1; + } else { + left = mid; + } + } + return (int)left; + } + + public int mySqrt02(int x) { + long r = x; + while (r * r > x) { + r = (r + x / r) / 2; + } + return (int)r; + } +} diff --git a/Week 03/id_368/LeetCode_74_368.java b/Week 03/id_368/LeetCode_74_368.java new file mode 100644 index 000000000..b9193ad7a --- /dev/null +++ b/Week 03/id_368/LeetCode_74_368.java @@ -0,0 +1,35 @@ +public class LeetCode_74_368 { + + /*编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性: + 每行中的整数从左到右按升序排列。 + 每行的第一个整数大于前一行的最后一个整数。 + 链接:https://leetcode-cn.com/problems/search-a-2d-matrix*/ + + public static void main(String[] args) { + + } + + public boolean searchMatrix(int[][] matrix, int target) { + if (matrix == null || matrix.length == 0) { + return false; + } + int m = matrix.length; + int n = matrix[0].length; + int left = 0; + int right = m * n - 1; + int mid = 0, row = 0, col = 0; + while (left <= right) { + mid = (left + right + 1) / 2; + row = mid / n; + col = mid % n; + if (matrix[row][col] == target) { + return true; + } else if (matrix[row][col] > target) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return false; + } +} diff --git a/Week 03/id_368/LeetCode_860_368.java b/Week 03/id_368/LeetCode_860_368.java new file mode 100644 index 000000000..6039c3700 --- /dev/null +++ b/Week 03/id_368/LeetCode_860_368.java @@ -0,0 +1,39 @@ +public class LeetCode_860_368 { + + /*在柠檬水摊上,每一杯柠檬水的售价为 5 美元。 + 顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。 + 每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。 + 注意,一开始你手头没有任何零钱。 + 如果你能给每位顾客正确找零,返回 true ,否则返回 false 。 + 链接:https://leetcode-cn.com/problems/lemonade-change*/ + + public static void main(String[] args) { + + } + + public boolean lemonadeChange(int[] bills) { + int five = 0, ten = 0; + for (int bill : bills) { + if (bill == 5) { + five++; + } else if (bill == 10) { + if (five > 0) { + five--; + ten++; + } else { + return false; + } + } else { + if (ten > 0 && five > 0) { + ten--; + five--; + } else if (five >= 3) { + five -= 3; + } else { + return false; + } + } + } + return true; + } +} diff --git a/Week 03/id_368/LeetCode_874_368.java b/Week 03/id_368/LeetCode_874_368.java new file mode 100644 index 000000000..b1640d724 --- /dev/null +++ b/Week 03/id_368/LeetCode_874_368.java @@ -0,0 +1,42 @@ +import java.util.HashSet; +import java.util.Set; + +public class LeetCode_874_368 { + + /*机器人在一个无限大小的网格上行走,从点 (0, 0) 处开始出发,面向北方。该机器人可以接收以下三种类型的命令: + -2:向左转 90 度 + -1:向右转 90 度 + 1 <= x <= 9:向前移动 x 个单位长度 + 在网格上有一些格子被视为障碍物。 + 第 i 个障碍物位于网格点  (obstacles[i][0], obstacles[i][1]) + 如果机器人试图走到障碍物上方,那么它将停留在障碍物的前一个网格方块上,但仍然可以继续该路线的其余部分。 + 返回从原点到机器人的最大欧式距离的平方。 + 链接:https://leetcode-cn.com/problems/walking-robot-simulation*/ + + public static void main(String[] args) { + + } + + public int robotSim(int[] commands, int[][] obstacles) { + Set set = new HashSet<>(); + for (int[] obstacle : obstacles) { + set.add(obstacle[0] + "-" + obstacle[1]); + } + int[][] dirs = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; + int x = 0, y = 0, dir = 0, max = 0; + for (int command : commands) { + if (command == -2) { + dir = dir == 0 ? 3 : dir - 1; + } else if (command == -1) { + dir = dir == 3 ? 0 : dir + 1; + } else { + while (command-- > 0 && !set.contains((x + dirs[dir][0]) + "-" + (y + dirs[dir][1]))) { + x += dirs[dir][0]; + y += dirs[dir][1]; + } + } + max = Math.max(max, x * x + y * y); + } + return max; + } +} diff --git a/Week 03/id_373/week03_373_homework/127.cpp b/Week 03/id_373/week03_373_homework/127.cpp new file mode 100644 index 000000000..507b5980e --- /dev/null +++ b/Week 03/id_373/week03_373_homework/127.cpp @@ -0,0 +1,47 @@ +public int ladderLength(String beginWord, String endWord, List wordList) { + if (!wordList.contains(endWord)) + return 0; + if (beginWord.equals(endWord)) + return 2; + + Set meets = new HashSet<>(wordList); + + Set beginSet = new HashSet<>(Collections.singleton(beginWord)); + Set endSet = new HashSet<>(Collections.singleton(endWord)); + + return this._search(1, beginSet, endSet, meets); + } + + private int _search(int level, Set beginSet, Set endSet, Set meets) + { + if (beginSet.size() == 0 || endSet.size() == 0) return 0; + + meets.removeAll(beginSet); + level++; + Set nextLevelSet = new HashSet<>(); + // iter every begin word + for (String beginWord : beginSet) { + char[] chars = beginWord.toCharArray(); + // iter for every char + for (int i = 0; i < chars.length; i++) { + char temp = chars[i]; + for (char ch = 'a'; ch < 'z'; ch++) { + chars[i] = ch; + String newWord = String.valueOf(chars); + if (!meets.contains(newWord)) continue; + if (endSet.contains(newWord)) return level; + nextLevelSet.add(newWord); + } + chars[i] = temp; + } + } + + if (nextLevelSet.size() <= endSet.size()) { + beginSet = nextLevelSet; + } else { + beginSet = endSet; + endSet = nextLevelSet; + } + + return this._search(level, beginSet, endSet, meets); + } \ No newline at end of file diff --git a/Week 03/id_373/week03_373_homework/200.cpp b/Week 03/id_373/week03_373_homework/200.cpp new file mode 100644 index 000000000..5160968ea --- /dev/null +++ b/Week 03/id_373/week03_373_homework/200.cpp @@ -0,0 +1,32 @@ +class Solution { +private: + void dfs(vector>& grid, int r, int c) { + int nr = grid.size(); + int nc = grid[0].size(); + + grid[r][c] = '0'; + if (r - 1 >= 0 && grid[r-1][c] == '1') dfs(grid, r - 1, c); + if (r + 1 < nr && grid[r+1][c] == '1') dfs(grid, r + 1, c); + if (c - 1 >= 0 && grid[r][c-1] == '1') dfs(grid, r, c - 1); + if (c + 1 < nc && grid[r][c+1] == '1') dfs(grid, r, c + 1); + } + +public: + int numIslands(vector>& grid) { + int nr = grid.size(); + if (!nr) return 0; + int nc = grid[0].size(); + + int num_islands = 0; + for (int r = 0; r < nr; ++r) { + for (int c = 0; c < nc; ++c) { + if (grid[r][c] == '1') { + ++num_islands; + dfs(grid, r, c); + } + } + } + + return num_islands; + } +}; \ No newline at end of file diff --git a/Week 03/id_373/week03_373_homework/529.cpp b/Week 03/id_373/week03_373_homework/529.cpp new file mode 100644 index 000000000..49305a291 --- /dev/null +++ b/Week 03/id_373/week03_373_homework/529.cpp @@ -0,0 +1,43 @@ +class Solution { +public: + vector> updateBoard(vector>& board, vector& click) { + update(board,click[0],click[1]); + return board; + } + + void update(vector>& board,int x,int y) + { + if(x<0||x>=board.size()||y<0||y>=board[0].size()) + return; + if(board[x][y]!='E'&&board[x][y]!='M') + return; + if(board[x][y]=='M') + { + board[x][y]='X'; + return; + } + int cnt=0; + for(int i=x-1;i<=x+1;i++) + for(int j=y-1;j<=y+1;j++) + { + if(i==x&&j==y||i<0||i>=board.size()||j<0||j>=board[0].size()) + continue; + if(board[i][j]=='M'||board[i][j]=='X') + cnt++; + } + if(cnt>0) + board[x][y]='0'+cnt; + else + { + board[x][y]='B'; + for(int i=x-1;i<=x+1;i++) + for(int j=y-1;j<=y+1;j++) + { + if(i==x&&j==y||i<0||i>=board.size()||j<0||j>=board[0].size()) + continue; + update(board,i,j); + } + } + + } +}; \ No newline at end of file diff --git a/Week 03/id_383/LeetCode_102_383.java b/Week 03/id_383/LeetCode_102_383.java new file mode 100644 index 000000000..90bde1160 --- /dev/null +++ b/Week 03/id_383/LeetCode_102_383.java @@ -0,0 +1,48 @@ +import java.util.*; + +public class LeetCode_102_383 { + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { + this.val = x; + } + } + + List> result = new ArrayList>(); + + // 方法一:递归 + public List> levelOrder1(TreeNode root) { + traversal(root, 0); + return result; + } + + public void traversal(TreeNode node, Integer level) { + if (node == null) return; + if (result.size() == level) result.add(level, new ArrayList<>()); + result.get(level).add(node.val); + if (node.left != null) traversal(node.left, level + 1); + if (node.right != null) traversal(node.right, level + 1); + } + + // 方法二: 迭代 + public List> levelOrder2(TreeNode root) { + if (root == null) return result; + Queue queue = new LinkedList<>(); + queue.add(root); + int length = 0; + while (!queue.isEmpty()) { + result.add(new ArrayList<>()); + int length2 = queue.size(); + for (int i = 0; i < length2; i++) { + TreeNode node = queue.remove(); + result.get(length).add(node.val); + if (node.left != null) queue.add(node.left); + if (node.right != null) queue.add(node.right); + } + length++; + } + return result; + } +} diff --git a/Week 03/id_383/LeetCode_33_383.java b/Week 03/id_383/LeetCode_33_383.java new file mode 100644 index 000000000..f032f6bc2 --- /dev/null +++ b/Week 03/id_383/LeetCode_33_383.java @@ -0,0 +1,31 @@ +public class LeetCode_33_383 { + + public int search(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return -1; + } + int left = 0; + int right = nums.length - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + if (nums[mid] == target) { + return mid; + } + + if (nums[left] <= nums[mid]) { + if (nums[left] <= target && target <= nums[mid]) { + right = mid - 1; + } else { + left = mid + 1; + } + } else { + if (nums[mid] <= target && target <= nums[right]) { + left = mid + 1; + } else { + right = mid - 1; + } + } + } + return -1; + } +} diff --git a/Week 03/id_383/LeetCode_74_383.java b/Week 03/id_383/LeetCode_74_383.java new file mode 100644 index 000000000..bc32f5cf9 --- /dev/null +++ b/Week 03/id_383/LeetCode_74_383.java @@ -0,0 +1,24 @@ +public class LeetCode_74_383 { + + public boolean searchMatrix(int[][] matrix, int target) { + int m = matrix.length; + if (m == 0) return false; + int n = matrix[0].length; + if (n == 0) return false; + + int left = 0; + int right = m * n - 1; + while (left <= right) { + int mid = (left + right) / 2; + int value = matrix[mid / n][mid % n]; + if (value == target) return true; + if (target < value) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return false; + } + +} diff --git a/Week 03/id_388/LeeCode_107_388.java b/Week 03/id_388/LeeCode_107_388.java new file mode 100644 index 000000000..22a81903b --- /dev/null +++ b/Week 03/id_388/LeeCode_107_388.java @@ -0,0 +1,75 @@ +package com.company.leetcode.editor.cn; +//给定一个二叉树,返回其节点底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历) +// +// 例如: +//给定二叉树 [3,9,20,null,null,15,7], +// +// 3 +// / \ +// 9 20 +// / \ +// 15 7 +// +// +// 返回其自底向上的层次遍历为: +// +// [ +// [15,7], +// [9,20], +// [3] +//] +// +// Related Topics 树 广度优先搜索 + + + +//leetcode submit region begin(Prohibit modification and deletion) + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class TreeNode_107 { + int val; + TreeNode_107 left; + TreeNode_107 right; + TreeNode_107(int x) { val = x; } +} +class Solution_107 { + public List> levelOrderBottom(TreeNode_107 root) { + LinkedList> res = new LinkedList<>(); + if (root == null) { + return new ArrayList<>(res); + } + List queue = new ArrayList<>(); + queue.add(root); + while (!queue.isEmpty()) { + List nextQueue = new ArrayList<>(); + + List level = new ArrayList<>(); + for (int i = 0; i < queue.size(); i++) { + TreeNode_107 tmpNode = queue.get(i); + level.add(queue.get(i).val); + if (tmpNode.left != null) { + nextQueue.add(tmpNode.left); + } + if (tmpNode.right != null) { + nextQueue.add(tmpNode.right); + } + } + res.addFirst(level); + queue = nextQueue; + } + return new ArrayList<>(res); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_122_388.java b/Week 03/id_388/LeeCode_122_388.java new file mode 100644 index 000000000..fcc96dc26 --- /dev/null +++ b/Week 03/id_388/LeeCode_122_388.java @@ -0,0 +1,54 @@ +package com.company.leetcode.editor.cn; +//给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 +// +// 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。 +// +// 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 +// +// 示例 1: +// +// 输入: [7,1,5,3,6,4] +//输出: 7 +//解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 +//  随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。 +// +// +// 示例 2: +// +// 输入: [1,2,3,4,5] +//输出: 4 +//解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 +//  注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。 +//  因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 +// +// +// 示例 3: +// +// 输入: [7,6,4,3,1] +//输出: 0 +//解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 +// Related Topics 贪心算法 数组 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_122 { + public int maxProfit(int[] prices) { + int profit = 0; + + for (int i = 0; i < prices.length - 1; i++) { + if (prices[i] < prices[i + 1]) { + profit += prices[i + 1] - prices[i]; + } + } + + return profit; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// int res = s.maxProfit(new int[]{7,1,5,3,6,4}); +// System.out.println(res); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_126_388.java b/Week 03/id_388/LeeCode_126_388.java new file mode 100644 index 000000000..a56a2320a --- /dev/null +++ b/Week 03/id_388/LeeCode_126_388.java @@ -0,0 +1,159 @@ +package com.company.leetcode.editor.cn;//给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列。转换需遵循如下规则: +// +// +// 每次转换只能改变一个字母。 +// 转换过程中的中间单词必须是字典中的单词。 +// +// +// 说明: +// +// +// 如果不存在这样的转换序列,返回一个空列表。 +// 所有单词具有相同的长度。 +// 所有单词只由小写字母组成。 +// 字典中不存在重复的单词。 +// 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。 +// +// +// 示例 1: +// +// 输入: +//beginWord = "hit", +//endWord = "cog", +//wordList = ["hot","dot","dog","lot","log","cog"] +// +//输出: +//[ +// ["hit","hot","dot","dog","cog"], +//  ["hit","hot","lot","log","cog"] +//] +// +// +// 示例 2: +// +// 输入: +//beginWord = "hit" +//endWord = "cog" +//wordList = ["hot","dot","dog","lot","log"] +// +//输出: [] +// +//解释: endWord "cog" 不在字典中,所以不存在符合要求的转换序列。 +// Related Topics 广度优先搜索 数组 字符串 回溯算法 + + +import java.util.*; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_126 { + public List> findLadders(String beginWord, String endWord, List wordList) { + List> res = new ArrayList<>(); + if (beginWord.equals(endWord)) { + return res; + } + HashSet wordDict = new HashSet<>(wordList); + if (!wordDict.contains(endWord)) { + return res; + } + Map> nodeNeighbors = new HashMap<>(); + wordDict.add(beginWord); + for (String str:wordDict) { + nodeNeighbors.put(str,new ArrayList<>()); + wordDict.add(str); + } + + Map distanceMap = new HashMap<>(); + + bfs(beginWord,endWord,wordDict,nodeNeighbors,distanceMap); + + List solution = new ArrayList<>(); + dfs(beginWord,endWord,nodeNeighbors,res,solution,distanceMap); + + return res; + + } + + private void dfs(String beginWord, String endWord, Map> nodeNeighbors, List> res, List solution,Map distanceMap) { + solution.add(beginWord); + if (beginWord.equals(endWord)) { + res.add(new ArrayList<>(solution)); + } else { + for (String next:nodeNeighbors.get(beginWord)) { + if (distanceMap.get(next) == distanceMap.get(beginWord) + 1) { + dfs(next,endWord,nodeNeighbors,res,solution,distanceMap); + } + } + } + solution.remove(solution.size() - 1); + } + + private void bfs(String beginWord, String endWord, HashSet wordDict, + Map> nodeNeighbors, Map distanceMap) { + Queue queue = new LinkedList(); + queue.add(beginWord); + distanceMap.put(beginWord,0); + int shortestDistance = Integer.MAX_VALUE; + + while (!queue.isEmpty()) { //level by level + + int size = queue.size(); + + for (int i = 0; i < size; i++) { + String curr = queue.poll(); + int currDistance = distanceMap.get(curr); + if (currDistance > shortestDistance) { + return; + } + List neighbors = getNeighbors(wordDict,curr); + for (String neighbor:neighbors) { + nodeNeighbors.get(curr).add(neighbor); + + if (distanceMap.containsKey(neighbor)) { + continue; + } + distanceMap.put(neighbor,currDistance + 1); + if (neighbor.equals(endWord)) { + shortestDistance = currDistance + 1; + } else { + queue.offer(neighbor); + } + } + } + + } + + } + + private List getNeighbors(HashSet wordDict, String curr) { + + List res = new ArrayList<>(); + char[] chs = curr.toCharArray(); + for (char c = 'a'; c <= 'z'; c++) { + for (int i = 0; i wordList = Arrays.asList(new String[]{"hot","dot","dog","lot","log","cog"}); +// +// List> res = s.findLadders(beginWord,endWord,wordList); +// System.out.println(res.toString()); +// +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_127_388.java b/Week 03/id_388/LeeCode_127_388.java new file mode 100644 index 000000000..3e0e349aa --- /dev/null +++ b/Week 03/id_388/LeeCode_127_388.java @@ -0,0 +1,120 @@ +package com.company.leetcode.editor.cn; +//给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则: +// +// +// 每次转换只能改变一个字母。 +// 转换过程中的中间单词必须是字典中的单词。 +// +// +// 说明: +// +// +// 如果不存在这样的转换序列,返回 0。 +// 所有单词具有相同的长度。 +// 所有单词只由小写字母组成。 +// 字典中不存在重复的单词。 +// 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。 +// +// +// 示例 1: +// +// 输入: +//beginWord = "hit", +//endWord = "cog", +//wordList = ["hot","dot","dog","lot","log","cog"] +// +//输出: 5 +// +//解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", +// 返回它的长度 5。 +// +// +// 示例 2: +// +// 输入: +//beginWord = "hit" +//endWord = "cog" +//wordList = ["hot","dot","dog","lot","log"] +// +//输出: 0 +// +//解释: endWord "cog" 不在字典中,所以无法进行转换。 +// Related Topics 广度优先搜索 + + +import java.util.*; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_127 { + public int ladderLength(String beginWord, String endWord, List wordList) { + + if (beginWord.equals(endWord)) { + return 0; + } + LinkedList queue = new LinkedList(); + + //计算每一层的字母 + Set charSet = new HashSet<>(); + Set wordSet = new HashSet<>(); + + for (int i = 0; i < wordList.size(); i++) { + char[] tmp = wordList.get(i).toCharArray(); + wordSet.add(wordList.get(i)); + for (int j = 0; j < tmp.length; j++) { + if (!charSet.contains(tmp[j])) { + charSet.add(tmp[j]); + } + } + } + List charList = new ArrayList(charSet); + if (!wordSet.contains(endWord)) { + return 0; + } + + queue.addFirst(beginWord); + Set visited = new HashSet<>(); + + + int level = 0; + int charIndex = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + + while (size > 0) { + String curr = queue.pollFirst(); + size--; + if (curr.equals(endWord)){ + return level + 1; + } + + char[] old = curr.toCharArray(); + + for (int i = 0; i < charList.size(); i++) { + for (int j = 0; j < old.length; j++) { + old[j] = charList.get(i); + String tmp = new String(old); + if (!visited.contains(tmp) && wordSet.contains(tmp)){ + visited.add(tmp); + queue.addLast(tmp); + } + old[j] = curr.charAt(j); + } + } + } + + level++; + } + return 0; + + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// String beginWord = "hit"; +// String endWord = "cog"; +// String[] wordList = new String[]{"hot","dot","dog","lot","log","cog"}; +// int res = s.ladderLength(beginWord,endWord,Arrays.asList(wordList)); +// System.out.println(res); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_153_388.java b/Week 03/id_388/LeeCode_153_388.java new file mode 100644 index 000000000..b2edd538b --- /dev/null +++ b/Week 03/id_388/LeeCode_153_388.java @@ -0,0 +1,85 @@ +package com.company.leetcode.editor.cn; +//假设按照升序排序的数组在预先未知的某个点上进行了旋转。 +// +// ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 +// +// 请找出其中最小的元素。 +// +// 你可以假设数组中不存在重复元素。 +// +// 示例 1: +// +// 输入: [3,4,5,1,2] +//输出: 1 +// +// 示例 2: +// +// 输入: [4,5,6,7,0,1,2] +//输出: 0 +// Related Topics 数组 二分查找 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_153 { + public int findMin(int[] nums) { + if (nums.length == 1) { + return nums[0]; + } + + //2个 + if (nums.length == 2) { + return Math.min(nums[0],nums[1]); + } + + int left = 0; + int right = nums.length - 1; + while (left < right) { + int mid = left + (right - left) / 2; + //是否是临界点 + boolean isPoint = this.checkPoint(mid,nums); + if (isPoint) { + return nums[mid]; + } + //0到mid有序 + if (nums[0] <= nums[mid] && nums[right] < nums[0]) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + return nums[left]; + + } + + private boolean checkPoint(int midIndex,int[] num) { + + int leftIndex = -1; + int rightIndex = -1; + //两边判断 + int len = num.length; + + leftIndex = midIndex - 1; + if (leftIndex < 0) { + leftIndex = len - 1; + } + rightIndex = midIndex + 1; + if (rightIndex > len - 1) { + rightIndex = 0; + } + int target = num[midIndex]; + if (target < num[leftIndex] && target < num[rightIndex]) { + return true; + } + return false; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// int[] nums = new int[]{3,1}; +// int min = s.findMin(nums); +// System.out.println(min); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_200_388.java b/Week 03/id_388/LeeCode_200_388.java new file mode 100644 index 000000000..9a768956b --- /dev/null +++ b/Week 03/id_388/LeeCode_200_388.java @@ -0,0 +1,98 @@ +package com.company.leetcode.editor.cn; +//给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。 +// +// 示例 1: +// +// 输入: +//11110 +//11010 +//11000 +//00000 +// +//输出: 1 +// +// +// 示例 2: +// +// 输入: +//11000 +//11000 +//00100 +//00011 +// +//输出: 3 +// +// Related Topics 深度优先搜索 广度优先搜索 并查集 + + +import java.util.HashSet; +import java.util.Set; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_200 { + public int numIslands(char[][] grid) { + int res = 0; + Set visited = new HashSet<>(); + for (int i = 0; i < grid.length; i++) { + char[] row = grid[i]; + for (int j = 0; j < row.length; j++) { + String tmp = i + "#" + j; + if (visited.contains(tmp)) { + continue; + } + + if (grid[i][j] == '1') { + res++; + sink(grid,i,j,visited); + } + } + } + + return res; + } + + private void sink(char[][] grid, int i, int j, Set visited) { + + if (i < 0 || i >= grid.length) { + return; + } + + if (j < 0 || j >= grid[i].length) { + return; + } + + if (visited.contains(i + "#" + j)) { + return; + } + visited.add(i + "#" + j); + if (grid[i][j] == '0') { + return; + } + + if (grid[i][j] == '1') { + grid[i][j] = '0'; + } + + //上下左右移动 + int[] x = new int[]{0,0,-1,1}; + int[] y = new int[]{-1,1,0,0}; + + for (int k = 0; k < x.length; k++) { + sink(grid,i + x[k],j + y[k],visited); + } + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// +// char[] row0 = new char[]{'1','1','1','1','0'}; +// char[] row1 = new char[]{'1','1','0','1','0'}; +// char[] row2 = new char[]{'1','1','0','0','0'}; +// char[] row3 = new char[]{'0','0','0','0','0'}; +// +// char[][] grid = new char[][]{row0,row1,row2,row3}; +// int res = s.numIslands(grid); +// System.out.println(res); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_22_388.java b/Week 03/id_388/LeeCode_22_388.java new file mode 100644 index 000000000..269395d1f --- /dev/null +++ b/Week 03/id_388/LeeCode_22_388.java @@ -0,0 +1,113 @@ +package com.company.leetcode.editor.cn; +//给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。 +// +// 例如,给出 n = 3,生成结果为: +// +// [ +// "((()))", +// "(()())", +// "(())()", +// "()(())", +// "()()()" +//] +// +// Related Topics 字符串 回溯算法 + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_22 { + + class Node { + int leftCount; + int rightCount; + String val; + + public Node(int leftCount, int rightCount, String val) { + this.leftCount = leftCount; + this.rightCount = rightCount; + this.val = val; + } + + public Node(Node node) { + this.leftCount = node.leftCount; + this.rightCount = node.rightCount; + this.val = node.val; + } + } + + //广度优先搜索 + public List generateParenthesis(int n) { + List res = new ArrayList<>(); + if (n <= 0) { + return res; + } + + Node node = new Node(1,0,"("); + LinkedList queue = new LinkedList<>(); + queue.addFirst(node); + while (!queue.isEmpty()) { + + int size = queue.size(); + while (size > 0) { + Node curr = queue.pollFirst(); + size--; + if (curr.leftCount == curr.rightCount && curr.leftCount == n) { + res.add(curr.val); + continue; + } + + if (curr.leftCount < n) { + Node newNode = new Node(curr); + newNode.val = curr.val + "("; + newNode.leftCount++; + queue.addFirst(newNode); + } + + if (curr.rightCount < curr.leftCount) { + Node newNode = new Node(curr); + newNode.val = curr.val + ")"; + newNode.rightCount++; + queue.addFirst(newNode); + } + } + + } + return res; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// List res = s.generateParenthesis(3); +// System.out.println(res.toString()); +// } + + private List res; + public List generateParenthesis2(int n) { + res = new ArrayList(); + helper(0,0,n,""); + + return res; + } + + private void helper(int left,int right,int n,String s) { + if (left == n && right == n) { + res.add(s); + return; + } + + if (left < n) { + helper(left + 1,right,n,s + "("); + } + if (right < left) { + helper(left,right + 1,n,s + ")"); + } + + } + +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_322_388.java b/Week 03/id_388/LeeCode_322_388.java new file mode 100644 index 000000000..1a2ebec81 --- /dev/null +++ b/Week 03/id_388/LeeCode_322_388.java @@ -0,0 +1,56 @@ +package com.company.leetcode.editor.cn; +//给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。 +// 如果没有任何一种硬币组合能组成总金额,返回 -1。 +// +// 示例 1: +// +// 输入: coins = [1, 2, 5], amount = 11 +//输出: 3 +//解释: 11 = 5 + 5 + 1 +// +// 示例 2: +// +// 输入: coins = [2], amount = 3 +//输出: -1 +// +// 说明: +//你可以认为每种硬币的数量是无限的。 +// Related Topics 动态规划 + + +import java.util.*; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_322 { + public int coinChange(int[] coins, int amount) { + + if (amount == 0) { + return 0; + } + + int[] dp = new int[amount + 1]; + int sum = 0; + while (++sum <= amount) { + int min = -1; + + for (int coin: coins) { + if (sum >= coin && dp[sum - coin] != -1) { + int temp = dp[sum - coin] + 1; + min = min < 0 ? temp : (temp < min ? temp : min); + } + } + + dp[sum] = min; + } + + return dp[amount]; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// //Wrong Answer: input:[186,419,83,408] 6249 Output:-1 Expected:20 +// int res = s.coinChange(new int[]{186,419,83,408},6249); +// System.out.println(res); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_33_388.java b/Week 03/id_388/LeeCode_33_388.java new file mode 100644 index 000000000..55ee42750 --- /dev/null +++ b/Week 03/id_388/LeeCode_33_388.java @@ -0,0 +1,59 @@ +package com.company.leetcode.editor.cn; +//假设按照升序排序的数组在预先未知的某个点上进行了旋转。 +// +// ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 +// +// 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。 +// +// 你可以假设数组中不存在重复的元素。 +// +// 你的算法时间复杂度必须是 O(log n) 级别。 +// +// 示例 1: +// +// 输入: nums = [4,5,6,7,0,1,2], target = 0 +//输出: 4 +// +// +// 示例 2: +// +// 输入: nums = [4,5,6,7,0,1,2], target = 3 +//输出: -1 +// Related Topics 数组 二分查找 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_33 { + public int search(int[] nums, int target) { + + if (nums.length == 0) { + return -1; + } + + int left = 0; + int right = nums.length - 1; + while (left < right) { + + int mid = left + (right - left) / 2; + //0 到mid 有序 + if (nums[0] <= nums[mid] && (target < nums[0] || target > nums[mid])) { + left = mid + 1; + } else if (target > nums[mid] && target < nums[0]) { + left = mid + 1; + } else { + right = mid; + } + + } + + return left == right && nums[left] == target? left:-1; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// int res = s.search(new int[]{1,3},1); +// System.out.println(res); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_367_388.java b/Week 03/id_388/LeeCode_367_388.java new file mode 100644 index 000000000..4b1ebaf3d --- /dev/null +++ b/Week 03/id_388/LeeCode_367_388.java @@ -0,0 +1,56 @@ +package com.company.leetcode.editor.cn; +//给定一个正整数 num,编写一个函数,如果 num 是一个完全平方数,则返回 True,否则返回 False。 +// +// 说明:不要使用任何内置的库函数,如 sqrt。 +// +// 示例 1: +// +// 输入:16 +//输出:True +// +// 示例 2: +// +// 输入:14 +//输出:False +// +// Related Topics 数学 二分查找 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_367 { + + //牛顿法 + + public boolean isPerfectSquare(int num) { + + if (num == 0 || num == 1) { + return true; + } + + int left = 1; + int right = num; + while (left <= right) { + + int mid = left + (right - left) / 2; + int res = mid * mid; + if (res == num) { + return true; + } + if (res > num) { + right = mid - 1; + continue; + } + left = mid + 1; + } + + return false; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// boolean res = s.isPerfectSquare(14); +// System.out.println(res); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_433_388.java b/Week 03/id_388/LeeCode_433_388.java new file mode 100644 index 000000000..feb4c2df6 --- /dev/null +++ b/Week 03/id_388/LeeCode_433_388.java @@ -0,0 +1,99 @@ +package com.company.leetcode.editor.cn; +//一条基因序列由一个带有8个字符的字符串表示,其中每个字符都属于 "A", "C", "G", "T"中的任意一个。 +// +// 假设我们要调查一个基因序列的变化。一次基因变化意味着这个基因序列中的一个字符发生了变化。 +// +// 例如,基因序列由"AACCGGTT" 变化至 "AACCGGTA" 即发生了一次基因变化。 +// +// 与此同时,每一次基因变化的结果,都需要是一个合法的基因串,即该结果属于一个基因库。 +// +// 现在给定3个参数 — start, end, bank,分别代表起始基因序列, +// 目标基因序列及基因库,请找出能够使起始基因序列变化为目标基因序列所需的最少变化次数。 +// 如果无法实现目标变化,请返回 -1。 +// +// 注意: +// +// +// 起始基因序列默认是合法的,但是它并不一定会出现在基因库中。 +// 所有的目标基因序列必须是合法的。 +// 假定起始基因序列与目标基因序列是不一样的。 +// +// +// 示例 1: +// +// +//start: "AACCGGTT" +//end: "AACCGGTA" +//bank: ["AACCGGTA"] +// +//返回值: 1 +// +// +// 示例 2: +// +// +//start: "AACCGGTT" +//end: "AAACGGTA" +//bank: ["AACCGGTA", "AACCGCTA", "AAACGGTA"] +// +//返回值: 2 +// +// +// 示例 3: +// +// +//start: "AAAAACCC" +//end: "AACCCCCC" +//bank: ["AAAACCCC", "AAACCCCC", "AACCCCCC"] +// +//返回值: 3 +// +// + + +import java.util.*; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_433 { + public int minMutation(String start, String end, String[] bank) { + if (start.equals(end)) return 0; + char[] charDict = new char[]{'A','C','G','T'}; + + Set bankSet = new HashSet<>(); + bankSet.addAll(Arrays.asList(bank)); + + LinkedList queue = new LinkedList<>(); + queue.add(start); + Set visited = new HashSet<>(); + visited.add(start); + int level = 0; + + while (!queue.isEmpty()) { + int size = queue.size(); + while (size > 0) { + String curr = queue.pollFirst(); + size--; + char[] currArr = curr.toCharArray(); + if (curr.equals(end) ) return level; + for (int i = 0; i < curr.length(); i++) { + for (int j = 0; j < charDict.length; j++) { + currArr[i] = charDict[j]; + String newStr = new String(currArr); + if (!visited.contains(newStr) && bankSet.contains(newStr)) { + queue.add(newStr); + visited.add(newStr); + } + } + currArr[i] = curr.charAt(i); + } + } + + + level++; + } + + return -1; + + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_455_388.java b/Week 03/id_388/LeeCode_455_388.java new file mode 100644 index 000000000..d3b9dcb51 --- /dev/null +++ b/Week 03/id_388/LeeCode_455_388.java @@ -0,0 +1,57 @@ +package com.company.leetcode.editor.cn; +//假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。 +// +// 注意: +// +// 你可以假设胃口值为正。 +//一个小朋友最多只能拥有一块饼干。 +// +// 示例 1: +// +// +//输入: [1,2,3], [1,1] +// +//输出: 1 +// +//解释: +//你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。 +//虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。 +//所以你应该输出1。 +// +// +// 示例 2: +// +// +//输入: [1,2], [1,2,3] +// +//输出: 2 +// +//解释: +//你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。 +//你拥有的饼干数量和尺寸都足以让所有孩子满足。 +//所以你应该输出2. +// +// Related Topics 贪心算法 + + +import java.util.Arrays; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_455 { + public int findContentChildren(int[] g, int[] s) { + Arrays.sort(g); + Arrays.sort(s); + + int gi = 0; + int si = 0; + + while (gi < g.length && si < s.length) { + if (g[gi] <= s[si]) { + gi++; + } + si++; + } + return gi; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_45_388.java b/Week 03/id_388/LeeCode_45_388.java new file mode 100644 index 000000000..bb155fdbd --- /dev/null +++ b/Week 03/id_388/LeeCode_45_388.java @@ -0,0 +1,58 @@ +package com.company.leetcode.editor.cn; +//给定一个非负整数数组,你最初位于数组的第一个位置。 +// +// 数组中的每个元素代表你在该位置可以跳跃的最大长度。 +// +// 你的目标是使用最少的跳跃次数到达数组的最后一个位置。 +// +// 示例: +// +// 输入: [2,3,1,1,4] +//输出: 2 +//解释: 跳到最后一个位置的最小跳跃数是 2。 +//  从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。 +// +// +// 说明: +// +// 假设你总是可以到达数组的最后一个位置。 +// Related Topics 贪心算法 数组 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_45 { + + public int jump(int[] nums) { + //贪心算法,怎么证明? + int end = 0; + int step = 0; + int maxPosition = 0; + for (int i = 0; i < nums.length; i++) { + maxPosition = Math.max(nums[i] + i,maxPosition); + if (i == end) { + step++; + end = maxPosition; + } + } + return step; + } + + public int jump2(int[] nums) { + + int position = nums.length - 1; + int steps = 0; + while (position != 0) { + for (int i = 0; i < position; i++) { + if (nums[i] + i >= position) { + position = i; + steps++; + break; + } + } + } + + return steps; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_515_388.java b/Week 03/id_388/LeeCode_515_388.java new file mode 100644 index 000000000..1da4e8ab6 --- /dev/null +++ b/Week 03/id_388/LeeCode_515_388.java @@ -0,0 +1,72 @@ +package com.company.leetcode.editor.cn; +//您需要在二叉树的每一行中找到最大的值。 +// +// 示例: +// +// +//输入: +// +// 1 +// / \ +// 3 2 +// / \ \ +// 5 3 9 +// +//输出: [1, 3, 9] +// +// Related Topics 树 深度优先搜索 广度优先搜索 + + + +//leetcode submit region begin(Prohibit modification and deletion) + +import java.util.LinkedList; +import java.util.List; + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class TreeNode_515 { + int val; + TreeNode_515 left; + TreeNode_515 right; + TreeNode_515(int x) { val = x; } +} +class Solution_515 { + public List largestValues(TreeNode_515 root) { + LinkedList res = new LinkedList<>(); + if (root == null) { + return res; + } + LinkedList queue = new LinkedList(); + queue.addFirst(root); + + while (!queue.isEmpty()) { + int size = queue.size(); + int max = Integer.MIN_VALUE; + while (size > 0) { + TreeNode_515 curr = queue.pollFirst(); + size--; + max = Math.max(curr.val,max); + + if (curr.left != null) { + queue.addLast(curr.left); + } + if (curr.right != null) { + queue.addLast(curr.right); + } + } + res.addLast(max); + } + + return res; + + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_529_388.java b/Week 03/id_388/LeeCode_529_388.java new file mode 100644 index 000000000..32db931c5 --- /dev/null +++ b/Week 03/id_388/LeeCode_529_388.java @@ -0,0 +1,187 @@ +package com.company.leetcode.editor.cn; +//让我们一起来玩扫雷游戏! +// +// 给定一个代表游戏板的二维字符矩阵。 'M' 代表一个未挖出的地雷,'E' 代表一个未挖出的空方块, +// 'B' 代表没有相邻(上,下,左,右,和所有4个对角线)地雷的已挖出的空白方块, +// 数字('1' 到 '8')表示有多少地雷与这块已挖出的方块相邻,'X' 则表示一个已挖出的地雷。 +// +// 现在给出在所有未挖出的方块中('M'或者'E')的下一个点击位置(行和列索引),根据以下规则,返回相应位置被点击后对应的面板: +// +// +// 如果一个地雷('M')被挖出,游戏就结束了- 把它改为 'X'。 +// 如果一个没有相邻地雷的空方块('E')被挖出,修改它为('B'),并且所有和其相邻的方块都应该被递归地揭露。 +// 如果一个至少与一个地雷相邻的空方块('E')被挖出,修改它为数字('1'到'8'),表示相邻地雷的数量。 +// 如果在此次点击中,若无更多方块可被揭露,则返回面板。 +// +// +// +// +// 示例 1: +// +// 输入: +// +//[['E', 'E', 'E', 'E', 'E'], +// ['E', 'E', 'M', 'E', 'E'], +// ['E', 'E', 'E', 'E', 'E'], +// ['E', 'E', 'E', 'E', 'E']] +// +//Click : [3,0] +// +//输出: +// +//[['B', '1', 'E', '1', 'B'], +// ['B', '1', 'M', '1', 'B'], +// ['B', '1', '1', '1', 'B'], +// ['B', 'B', 'B', 'B', 'B']] +// +//解释: +// +// +// +// 示例 2: +// +// 输入: +// +//[['B', '1', 'E', '1', 'B'], +// ['B', '1', 'M', '1', 'B'], +// ['B', '1', '1', '1', 'B'], +// ['B', 'B', 'B', 'B', 'B']] +// +//Click : [1,2] +// +//输出: +// +//[['B', '1', 'E', '1', 'B'], +// ['B', '1', 'X', '1', 'B'], +// ['B', '1', '1', '1', 'B'], +// ['B', 'B', 'B', 'B', 'B']] +// +//解释: +// +// +// +// +// +// 注意: +// +// +// 输入矩阵的宽和高的范围为 [1,50]。 +// 点击的位置只能是未被挖出的方块 ('M' 或者 'E'),这也意味着面板至少包含一个可点击的方块。 +// 输入面板不会是游戏结束的状态(即有地雷已被挖出)。 +// 简单起见,未提及的规则在这个问题中可被忽略。例如,当游戏结束时你不需要挖出所有地雷,考虑所有你可能赢得游戏或标记方块的情况。 +// Related Topics 深度优先搜索 广度优先搜索 + + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_529 { + public char[][] updateBoard(char[][] board, int[] click) { + Set visited = new HashSet<>(); + + if (board[click[0]][click[1]] =='M') { + board[click[0]][click[1]] = 'X'; + return board; + } + + dfs(board,visited,click[0],click[1]); + + return board; + } + + private void dfs(char[][] board, Set visited, int i, int j) { + + if (i == 5 && j == 0) { + System.out.println(1); + } + + if (i < 0 || i >= board.length) { + return; + } + if (j < 0 || j >= board[i].length) { + return; + } + + if (visited.size() == board.length * board[0].length) {//// 如果在此次点击中,若无更多方块可被揭露,则返回面板。 + return; + } + + String key = i + "#" + j; + if (visited.contains(key)) { + return; + } + + //如果是雷 + visited.add(key); + + int[] x = new int[]{0,0,-1,1,-1,1,-1,1}; + int[] y = new int[]{-1,1,0,0,-1,-1,1,1}; + boolean needDfs = true; + if (board[i][j] == 'M' || board[i][j] == 'X') { + return; + } + if (board[i][j] == 'E') { + + //计算周边地雷个数 + char mCount = '0'; + for (int k = 0; k < x.length; k++) { + int row = i + x[k]; + int col = j + y[k]; + + if (row < 0 || row >= board.length) { + continue; + } + if (col < 0 || col >= board[i].length) { + continue; + } + + if (board[row][col] == 'M' || board[row][col] == 'X') { + mCount++; + } + } + + if (mCount != '0') { + //周边有地雷 + board[i][j] = mCount; + return; + + } + //周边没有地雷 + board[i][j] = 'B'; + + //上下左右对角线 + for (int k = 0; k < x.length; k++) { + dfs(board,visited,i + x[k],j + y[k]); + } + } + + // 如果一个地雷('M')被挖出,游戏就结束了- 把它改为 'X'。 + // 如果一个没有相邻地雷的空方块('E')被挖出,修改它为('B'),并且所有和其相邻的方块都应该被递归地揭露。 + // 如果一个至少与一个地雷相邻的空方块('E')被挖出,修改它为数字('1'到'8'),表示相邻地雷的数量。 + // 如果在此次点击中,若无更多方块可被揭露,则返回面板。 + } + +// public static void main(String[] args) { +// +// char[] row0 = new char[]{'B','B','B','B','1','M','M','E'}; +// char[] row1 = new char[]{'B','B','B','B','1','4','M','E'}; +// char[] row2 = new char[]{'B','B','B','B','B','3','M','E'}; +// char[] row3 = new char[]{'B','B','B','B','B','2','M','E'}; +// char[] row4 = new char[]{'1','2','2','1','B','1','1','1'}; +// char[] row5 = new char[]{'E','M','M','1','B','B','B','B'}; +// char[] row6 = new char[]{'E','E','E','2','2','2','2','1'}; +// char[] row7 = new char[]{'E','E','E','E','M','M','E','M'}; +// +// char[][] board = new char[][]{row0,row1,row2,row3,row4,row5,row6,row7}; +// Solution s = new Solution(); +// char[][] res = s.updateBoard(board,new int[]{7,2}); +// +// for (int i = 0; i < res.length; i++) { +// System.out.println( String.valueOf(res[i])); +// } +// +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_55_388.java b/Week 03/id_388/LeeCode_55_388.java new file mode 100644 index 000000000..a78f2f16f --- /dev/null +++ b/Week 03/id_388/LeeCode_55_388.java @@ -0,0 +1,56 @@ +package com.company.leetcode.editor.cn; + +//给定一个非负整数数组,你最初位于数组的第一个位置。 +// +// 数组中的每个元素代表你在该位置可以跳跃的最大长度。 +// +// 判断你是否能够到达最后一个位置。 +// +// 示例 1: +// +// 输入: [2,3,1,1,4] +//输出: true +//解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。 +// +// +// 示例 2: +// +// 输入: [3,2,1,0,4] +//输出: false +//解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。 +// +// Related Topics 贪心算法 数组 + + +import java.util.Arrays; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_55 { + + public boolean canJump(int[] nums) { + + int max = 0; + for (int i = 0; i < nums.length; i++) { + if (i > max) return false; + max = Math.max(nums[i] + i,max); + } + return true; + } + + public boolean canJump2(int[] nums) { + //贪心算法 + if (nums.length == 0) { + return false; + } + int reachable = nums.length - 1; + for (int i = nums.length - 1; i >= 0; i--) { + if (nums[i] + i >= reachable) { + reachable = i; + } + } + return reachable == 0; + } + + +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_69_388.java b/Week 03/id_388/LeeCode_69_388.java new file mode 100644 index 000000000..37cacafb9 --- /dev/null +++ b/Week 03/id_388/LeeCode_69_388.java @@ -0,0 +1,50 @@ +package com.company.leetcode.editor.cn;//实现 int sqrt(int x) 函数。 +// +// 计算并返回 x 的平方根,其中 x 是非负整数。 +// +// 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。 +// +// 示例 1: +// +// 输入: 4 +//输出: 2 +// +// +// 示例 2: +// +// 输入: 8 +//输出: 2 +//说明: 8 的平方根是 2.82842..., +//  由于返回类型是整数,小数部分将被舍去。 +// +// Related Topics 数学 二分查找 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_69 { + public int mySqrt(int x) { + + if (x == 0 || x == 1) { + return x; + } + + int left = 1; + int right = x; + + while (left < right) { + int mid = left + (right - left) / 2; + if (mid * mid == x) { + return mid; + } + if (mid * mid > x) { + right = mid -1; + continue; + } + left = mid + 1; + } + + return right; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_74_388.java b/Week 03/id_388/LeeCode_74_388.java new file mode 100644 index 000000000..0f4702b05 --- /dev/null +++ b/Week 03/id_388/LeeCode_74_388.java @@ -0,0 +1,118 @@ +package com.company.leetcode.editor.cn; + +//编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性: +// +// +// 每行中的整数从左到右按升序排列。 +// 每行的第一个整数大于前一行的最后一个整数。 +// +// +// 示例 1: +// +// 输入: +//matrix = [ +// [1, 3, 5, 7], +// [10, 11, 16, 20], +// [23, 30, 34, 50] +//] +//target = 3 +//输出: true +// +// +// 示例 2: +// +// 输入: +//matrix = [ +// [1, 3, 5, 7], +// [10, 11, 16, 20], +// [23, 30, 34, 50] +//] +//target = 13 +//输出: false +// Related Topics 数组 二分查找 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_74 { + public boolean searchMatrix(int[][] matrix, int target) { + + if (matrix.length == 0) { + return false; + } + + for (int i = 0; i < matrix.length; i++) { + if (matrix[i].length == 0) { + return false; + } + } + + //判断在哪一行 + int leftIndex = 0; + int rightIndex = matrix.length - 1; + int len = matrix[matrix.length - 1].length; + + //边界处理 + if (target < matrix[0][0] || target > matrix[matrix.length - 1][len - 1]) { + return false; + } + + int targetRow = -1; + if (matrix[rightIndex][0] <= target) { + targetRow = matrix.length - 1; + } else { + while (leftIndex < rightIndex) { + int mid = leftIndex + (rightIndex - leftIndex) / 2; + len = matrix[mid].length; + if (matrix[mid][len - 1] == target) { + return true; + } + if (matrix[mid][len - 1] < target) { + leftIndex = mid + 1; + } else { + rightIndex = mid; + } + } + targetRow = rightIndex; + } + + //从targart row查找 + int[] row = matrix[targetRow]; + int left = 0; + int right = row.length - 1; + + while (left < right) { + int mid = left + (right - left) / 2; + + if (row[mid] == target) { + return true; + } + + if (row[mid] > target) { + right = mid - 1; + } else { + left = mid + 1; + } + } + if (left == right && row[left] == target) { + return true; + } + return false; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// +// int[] row0 = new int[]{1, 3, 5, 7}; +// int[] row1 = new int[]{10, 11, 16, 20}; +// int[] row2 = new int[]{23, 30, 34, 50}; +//// int[] row0 = new int[]{1,3}; +//// int[][] martrix = new int[][]{row0}; +// +// int[][] martrix = new int[][]{row0,row1,row2}; +// +// boolean res = s.searchMatrix(martrix,20); +// System.out.println(res); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_84_388.java b/Week 03/id_388/LeeCode_84_388.java new file mode 100644 index 000000000..9b6c25db9 --- /dev/null +++ b/Week 03/id_388/LeeCode_84_388.java @@ -0,0 +1,103 @@ +package com.company.leetcode.editor.cn;//给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 +// +// 求在该柱状图中,能够勾勒出来的矩形的最大面积。 +// +// +// +// +// +// 以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。 +// +// +// +// +// +// 图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。 +// +// +// +// 示例: +// +// 输入: [2,1,5,6,2,3] +//输出: 10 +// Related Topics 栈 数组 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_84 { + + //分治 + public int largestRectangleArea(int[] heights) { + + if (heights.length == 0) { + return 0; + } + + if (heights.length == 1) { + return heights[0]; + } + + return helper(heights,0,heights.length - 1); + } + + private int helper(int[] heights, int start, int end) { + if (start > end) { + return 0; + } + + //查找最小 + int minIndex = start; + for (int i = start; i <= end; i++) { + + if (heights[minIndex] > heights[i]) { + minIndex = i; + } + + } + int area = heights[minIndex] * (end - start + 1); + int leftArea = helper(heights,start,minIndex - 1); + int right = helper(heights,minIndex + 1,end); + + return Math.max(area,Math.max(leftArea,right)); + } + + //暴力 + public int largestRectangleArea2(int[] heights) { + + if (heights.length == 0) { + return 0; + } + + int maxArea = 0; + + for (int i = 0; i < heights.length; i++) { + + + int minHeight = heights[i]; + + for (int j = i; j < heights.length; j++) { + int nextHeight = heights[j]; + if (nextHeight < minHeight) { + minHeight = nextHeight; + } + int distance = j - i + 1; + int area = distance * minHeight; + + if (area > maxArea) { + maxArea = area; + } + } + } + + return maxArea; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// int res = s.largestRectangleArea(new int[]{9,0}); +// System.out.println(res); +// } + +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_860_388.java b/Week 03/id_388/LeeCode_860_388.java new file mode 100644 index 000000000..441320185 --- /dev/null +++ b/Week 03/id_388/LeeCode_860_388.java @@ -0,0 +1,100 @@ +package com.company.leetcode.editor.cn; +//在柠檬水摊上,每一杯柠檬水的售价为 5 美元。 +// +// 顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。 +// +// 每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。 +// +// 注意,一开始你手头没有任何零钱。 +// +// 如果你能给每位顾客正确找零,返回 true ,否则返回 false 。 +// +// 示例 1: +// +// 输入:[5,5,5,10,20] +//输出:true +//解释: +//前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。 +//第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。 +//第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。 +//由于所有客户都得到了正确的找零,所以我们输出 true。 +// +// +// 示例 2: +// +// 输入:[5,5,10] +//输出:true +// +// +// 示例 3: +// +// 输入:[10,10] +//输出:false +// +// +// 示例 4: +// +// 输入:[5,5,10,10,20] +//输出:false +//解释: +//前 2 位顾客那里,我们按顺序收取 2 张 5 美元的钞票。 +//对于接下来的 2 位顾客,我们收取一张 10 美元的钞票,然后返还 5 美元。 +//对于最后一位顾客,我们无法退回 15 美元,因为我们现在只有两张 10 美元的钞票。 +//由于不是每位顾客都得到了正确的找零,所以答案是 false。 +// +// +// +// +// 提示: +// +// +// 0 <= bills.length <= 10000 +// bills[i] 不是 5 就是 10 或是 20 +// +// Related Topics 贪心算法 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_860 { + public boolean lemonadeChange(int[] bills) { + int five = 0; + int ten = 0; + + for (int bill: + bills) { + + if (bill == 5) { + five++; + continue; + } + if (bill == 10) { + if (five > 0) { + five--; + ten++; + continue; + } + return false; + } + + if (five > 0 && ten > 0) { + five--; + ten--; + continue; + } + if (five >= 3) { + five = five - 3; + continue; + } + return false; + } + return true; + } +// +// public static void main(String[] args) { +// Solution s = new Solution(); +// boolean res = s.lemonadeChange(new int[]{5,5,5,10,20}); +// System.out.println(res); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/LeeCode_874_388.java b/Week 03/id_388/LeeCode_874_388.java new file mode 100644 index 000000000..b8359f4ae --- /dev/null +++ b/Week 03/id_388/LeeCode_874_388.java @@ -0,0 +1,104 @@ +package com.company.leetcode.editor.cn; +//机器人在一个无限大小的网格上行走,从点 (0, 0) 处开始出发,面向北方。该机器人可以接收以下三种类型的命令: +// +// +// -2:向左转 90 度 +// -1:向右转 90 度 +// 1 <= x <= 9:向前移动 x 个单位长度 +// +// +// 在网格上有一些格子被视为障碍物。 +// +// 第 i 个障碍物位于网格点 (obstacles[i][0], obstacles[i][1]) +// +// 如果机器人试图走到障碍物上方,那么它将停留在障碍物的前一个网格方块上,但仍然可以继续该路线的其余部分。 +// +// 返回从原点到机器人的最大欧式距离的平方。 +// +// +// +// 示例 1: +// +// 输入: commands = [4,-1,3], obstacles = [] +//输出: 25 +//解释: 机器人将会到达 (3, 4) +// +// +// 示例 2: +// +// 输入: commands = [4,-1,4,-2,4], obstacles = [[2,4]] +//输出: 65 +//解释: 机器人在左转走到 (1, 8) 之前将被困在 (1, 4) 处 +// +// +// +// +// 提示: +// +// +// 0 <= commands.length <= 10000 +// 0 <= obstacles.length <= 10000 +// -30000 <= obstacle[i][0] <= 30000 +// -30000 <= obstacle[i][1] <= 30000 +// 答案保证小于 2 ^ 31 +// +// Related Topics 贪心算法 + + +import java.util.HashSet; +import java.util.Set; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_874 { + public int robotSim(int[] commands, int[][] obstacles) { + + //方向处理 + int[] dx = new int[]{0,1,0,-1}; + int[] dy = new int[]{1,0,-1,0}; + int di = 0;//0-上 1-右 2-下 3-左 + Set obstacleSet = new HashSet<>(); + for (int i = 0; i < obstacles.length; i++) { + int x = obstacles[i][0]; + int y = obstacles[i][1]; + obstacleSet.add(x + "#" + y); + } + int ans = 0; + int currentX = 0; + int currentY = 0; + for (int cmd: + commands) { + if (cmd == -2) { + di = (di + 3) % 4; + continue; + } + if (cmd == -1) { + di = (di + 1) % 4; + continue; + } + + //移动 cmd 个单位 + for (int i = 0; i < cmd; i++) { + int xn = currentX + dx[di]; + int yn = currentY + dy[di]; + if (!obstacleSet.contains(xn + "#" + yn)) { + currentX = xn; + currentY = yn; + ans = Math.max(ans,xn * xn + yn * yn); + } + } + + } + + return ans; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// int[] commands = new int[]{4,-1,4,-2,4}; +// int[] o = new int[]{2,4}; +// int[][] obstacles = new int[][]{o}; +// int ans = s.robotSim(commands,obstacles); +// System.out.println(ans); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_388/NOTE.md b/Week 03/id_388/NOTE.md index a6321d6e2..ec8fb614b 100644 --- a/Week 03/id_388/NOTE.md +++ b/Week 03/id_388/NOTE.md @@ -1,4 +1,192 @@ # NOTE - +### 学习总结 + +#### 1、深度优先和广度优先 +模板 +``` +1、深度优先 +visited = set() +def dfs(node,visited): + visited.add(node) + … + for next_node in node.children(): + if not next_node in visited: + dfs(next_node,visited) + +def dfs(self,tree): + if tree.root is None: + return [] + visited,stack = [],[tree.root] + while stack: + node = stack.pop() + visited.add(node) + process(node) + nodes = generate_related_nodes(node) + stack.push(nodes) +``` + +```$xslt +2、广度优先 +def bfs(graph,start,end): + queue = [] + queue.append([start]) + visited.add(start) + + while queue: + node = queue.pop() + visited.add(node) + process(node) + nodes = generate_relateed_nodes(node) + queue.push(nodes) +``` + +单词接龙II(126),比较困的一题,学习了其他人的解法,按层遍历,当首先到满足条件的词语后记录最短路径,其他大于的一律return +``` + public List> findLadders(String beginWord, String endWord, List wordList) { + List> res = new ArrayList<>(); + if (beginWord.equals(endWord)) { + return res; + } + HashSet wordDict = new HashSet<>(wordList); + if (!wordDict.contains(endWord)) { + return res; + } + Map> nodeNeighbors = new HashMap<>(); + wordDict.add(beginWord); + for (String str:wordDict) { + nodeNeighbors.put(str,new ArrayList<>()); + wordDict.add(str); + } + + Map distanceMap = new HashMap<>(); + + bfs(beginWord,endWord,wordDict,nodeNeighbors,distanceMap); + + List solution = new ArrayList<>(); + dfs(beginWord,endWord,nodeNeighbors,res,solution,distanceMap); + + return res; + + } + + private void dfs(String beginWord, String endWord, Map> nodeNeighbors, List> res, List solution,Map distanceMap) { + solution.add(beginWord); + if (beginWord.equals(endWord)) { + res.add(new ArrayList<>(solution)); + } else { + for (String next:nodeNeighbors.get(beginWord)) { + if (distanceMap.get(next) == distanceMap.get(beginWord) + 1) { + dfs(next,endWord,nodeNeighbors,res,solution,distanceMap); + } + } + } + solution.remove(solution.size() - 1); + } + + private void bfs(String beginWord, String endWord, HashSet wordDict, + Map> nodeNeighbors, Map distanceMap) { + Queue queue = new LinkedList(); + queue.add(beginWord); + distanceMap.put(beginWord,0); + int shortestDistance = Integer.MAX_VALUE; + + while (!queue.isEmpty()) { //level by level + + int size = queue.size(); + + for (int i = 0; i < size; i++) { + String curr = queue.poll(); + int currDistance = distanceMap.get(curr); + if (currDistance > shortestDistance) { + return; + } + List neighbors = getNeighbors(wordDict,curr); + for (String neighbor:neighbors) { + nodeNeighbors.get(curr).add(neighbor); + + if (distanceMap.containsKey(neighbor)) { + continue; + } + distanceMap.put(neighbor,currDistance + 1); + if (neighbor.equals(endWord)) { + shortestDistance = currDistance + 1; + } else { + queue.offer(neighbor); + } + } + } + + } + + } + + private List getNeighbors(HashSet wordDict, String curr) { + + List res = new ArrayList<>(); + char[] chs = curr.toCharArray(); + for (char c = 'a'; c <= 'z'; c++) { + for (int i = 0; i len - 1) { + rightIndex = 0; + } + int target = num[midIndex]; + if (target < num[leftIndex] && target < num[rightIndex]) { + return true; + } + return false; + } +``` diff --git a/Week 03/id_393/LeetCode_33_393.java b/Week 03/id_393/LeetCode_33_393.java new file mode 100644 index 000000000..7904e8e83 --- /dev/null +++ b/Week 03/id_393/LeetCode_33_393.java @@ -0,0 +1,24 @@ +package no33; + +/** + * @author boyiren + * @date 2019-11-03 + */ +public class Solution { + public int search(int[] nums, int target) { + int lo = 0; + int hi = nums.length - 1; + + while (lo < hi) { + int mid = (lo + hi) / 2; + if (nums[0] <= nums[mid] && (target > nums[mid] || target < nums[0])) { + lo = mid + 1; + } else if (target > nums[mid] && target < nums[0]) { + lo = mid + 1; + } else { + hi = mid; + } + } + return lo == hi && nums[lo] == target ? lo : -1; + } +} diff --git a/Week 03/id_393/LeetCode_455_393.java b/Week 03/id_393/LeetCode_455_393.java new file mode 100644 index 000000000..d681b0815 --- /dev/null +++ b/Week 03/id_393/LeetCode_455_393.java @@ -0,0 +1,19 @@ +package no455; + +import java.util.Arrays; + +/** + * @author boyiren + * @date 2019-11-03 + */ +public class Solution { + public int findContentChildren(int[] g, int[] s) { + Arrays.sort(g); + Arrays.sort(s); + int l = 0, r = 0; + while (l < g.length && r < s.length) { + if (g[l] <= s[r++]) l++; + } + return l; + } +} diff --git a/Week 03/id_393/LeetCode_74_393.java b/Week 03/id_393/LeetCode_74_393.java new file mode 100644 index 000000000..7124a093c --- /dev/null +++ b/Week 03/id_393/LeetCode_74_393.java @@ -0,0 +1,24 @@ +package no74; + +/** + * @author boyiren + * @date 2019-11-03 + */ +public class Solution { + public boolean searchMatrix(int[][] matrix, int target) { + if (matrix == null || matrix.length == 0) { + return false; + } + int row = 0, col = matrix[0].length - 1; + while (row < matrix.length && col >= 0) { + if (target == matrix[row][col]) { + return true; + } else if (target > matrix[row][col]) { + row++; + } else { + col--; + } + } + return false; + } +} diff --git a/Week 03/id_408/LeetCode_127_408.c++ b/Week 03/id_408/LeetCode_127_408.c++ new file mode 100644 index 000000000..fd6f465b2 --- /dev/null +++ b/Week 03/id_408/LeetCode_127_408.c++ @@ -0,0 +1,30 @@ +class Solution { +public: + int ladderLength(string beginWord, string endWord, vector& wordList) { + unordered_set dict(wordList.begin(),wordList.end()); + unordered_set visited; + visited.insert(beginWord); + queue q; + q.push(beginWord); + int res=1; + while(!q.empty()){ + int size=q.size(); + while(size--){ + auto t=q.front(); + q.pop(); + for(int i=0;i>& grid, pair a) + { + if(a.first >= 0 && a.first < grid.size() && a.second >= 0 && a.second < grid[0].size()) + return true; + else + return false; + } + + int numIslands(vector>& grid) { + int ans = 0; + queue > po; + if(grid.empty()) return 0; + int m = grid.size(); + int n = grid[0].size(); + for(int y = 0; y < m; ++y) + { + for(int x = 0; x temp = po.front(); + po.pop(); + if(grid[temp.first][temp.second] == '0') + continue; + grid[temp.first][temp.second] = '0'; + pair up = {temp.first + 1, temp.second}; + pair down = {temp.first - 1, temp.second}; + pair left = {temp.first, temp.second - 1}; + pair right = {temp.first, temp.second + 1}; + if(inGrid(grid,up) && grid[temp.first + 1][temp.second] == '1') + po.push(up); + if(inGrid(grid,down) && grid[temp.first - 1][temp.second] == '1') + po.push(down); + if(inGrid(grid,left) && grid[temp.first][temp.second - 1] == '1') + po.push(left); + if(inGrid(grid,right) && grid[temp.first][temp.second + 1] == '1') + po.push(right); + } + } + } + } + return ans; + } +}; diff --git a/Week 03/id_418/LeetCode_127_418.java b/Week 03/id_418/LeetCode_127_418.java new file mode 100644 index 000000000..5deb5c5e2 --- /dev/null +++ b/Week 03/id_418/LeetCode_127_418.java @@ -0,0 +1,77 @@ +package com.ljg.leetcode.week03.a_01; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; + +/** + * WordLadder + */ +public class WordLadder { + + public static void main(String[] args) { + + } + + private class Pair { + String word; + int level; + + Pair(String word, int level) { + this.word = word; + this.level = level; + } + } + + public int ladderLength(String beginWord, String endWord, List wordList) { + int length = beginWord.length(); + + Map> dic = new HashMap>(); + wordList.stream().forEach(word -> { + for (int i = 0; i < length; i++) { + String key = word.substring(0, i) + "*" + word.substring(i + 1, length); + List list = dic.getOrDefault(key, new ArrayList()); + list.add(word); + dic.put(key, list); + } + }); + + Queue queue = new LinkedList(); + Pair beginPair = new Pair(beginWord, 1); + queue.add(beginPair); + + List visitted = new ArrayList(); + visitted.add(beginWord); + + while (!queue.isEmpty()) { + Pair pair = queue.poll(); + String word = pair.word; + int level = pair.level; + + for (int i = 0; i < length; i++) { + String key = word.substring(0, i) + "*" + word.substring(i + 1, length); + List list = dic.get(key); + + if (list != null) { + for(String w : list) { + if (w.equals(endWord)) { + return level + 1; + } else { + if(visitted.contains(w)) { + continue; + } + Pair p = new Pair(w, level+1); + visitted.add(w); + queue.add(p); + } + } + } + } + } + + return 0; + } +} \ No newline at end of file diff --git a/Week 03/id_418/LeetCode_200_418.java b/Week 03/id_418/LeetCode_200_418.java new file mode 100644 index 000000000..0df163ac0 --- /dev/null +++ b/Week 03/id_418/LeetCode_200_418.java @@ -0,0 +1,63 @@ +package com.ljg.leetcode.week03.a_02; + +/** + * NumberOfIslands + */ +public class NumberOfIslands { + + public static void main(String[] args) { + + } + + private boolean[][] visited; + + public int numIslands(char[][] grid) { + + if(grid == null || grid.length == 0) { + return 0; + } + + int num = 0; + int len1 = grid.length; + int len2 = grid[0].length; + visited = new boolean[len1][len2]; + + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[i].length; j++) { + if (grid[i][j] == '0') { + continue; + } + sink(grid, i, j); + num++; + } + } + return num; + } + + private void sink(char[][] grid, int x, int y) { + if (visited[x][y]) { + return; + } + if (grid[x][y] == '0') { + return; + } + + visited[x][y] = true; + + if (x > 0 && grid[x - 1][y] == '1') { + sink(grid, x - 1, y); + } + if (x < grid.length - 1 && grid[x + 1][y] == '1') { + sink(grid, x + 1, y); + } + + if (y > 0 && grid[x][y - 1] == '1') { + sink(grid, x, y - 1); + } + if (y < grid[x].length - 1 && grid[x][y + 1] == '1') { + sink(grid, x, y + 1); + } + + grid[x][y] = '0'; + } +} \ No newline at end of file diff --git a/Week 03/id_423/LeetCode_200_423.java b/Week 03/id_423/LeetCode_200_423.java new file mode 100644 index 000000000..af6fca0d1 --- /dev/null +++ b/Week 03/id_423/LeetCode_200_423.java @@ -0,0 +1,44 @@ +class Solution { + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + + int nr = grid.length; + int nc = grid[0].length; + int num_islands = 0; + + for (int r = 0; r < nr; ++r) { + for (int c = 0; c < nc; ++c) { + if (grid[r][c] == '1') { + ++num_islands; + grid[r][c] = '0'; + Queue neighbors = new LinkedList<>(); + neighbors.add(r * nc + c); + while (!neighbors.isEmpty()) { + int id = neighbors.remove(); + int row = id / nc; + int col = id % nc; + if (row - 1 >= 0 && grid[row-1][col] == '1') { + neighbors.add((row-1) * nc + col); + grid[row-1][col] = '0'; + } + if (row + 1 < nr && grid[row+1][col] == '1') { + neighbors.add((row+1) * nc + col); + grid[row+1][col] = '0'; + } + if (col - 1 >= 0 && grid[row][col-1] == '1') { + neighbors.add(row * nc + col-1); + grid[row][col-1] = '0'; + } + if (col + 1 < nc && grid[row][col+1] == '1') { + neighbors.add(row * nc + col+1); + grid[row][col+1] = '0'; + } + } + } + } + } + return num_islands; + } +} \ No newline at end of file diff --git a/Week 03/id_423/LeetCode_860_423.java b/Week 03/id_423/LeetCode_860_423.java new file mode 100644 index 000000000..34710f46e --- /dev/null +++ b/Week 03/id_423/LeetCode_860_423.java @@ -0,0 +1,24 @@ +class Solution { + public boolean lemonadeChange(int[] bills) { + int five = 0, ten = 0; + for (int bill : bills) { + if (bill == 5) + five++; + else if (bill == 10) { + if (five == 0) return false; + five--; + ten++; + } else { + if (five > 0 && ten > 0) { + five--; + ten--; + } else if (five >= 3) { + five -= 3; + } else { + return false; + } + } + } + return true; + } +} \ No newline at end of file diff --git a/Week 03/id_428/LeeCode_127_428.java b/Week 03/id_428/LeeCode_127_428.java new file mode 100644 index 000000000..5e5072df2 --- /dev/null +++ b/Week 03/id_428/LeeCode_127_428.java @@ -0,0 +1,45 @@ +import java.util.HashSet; +import java.util.Set; + +class Solution { + public int ladderLength(String beginWord, String endWord, List wordList) { + if (!wordList.contains(endWord)) return 0; + Set dict = new HashSet<>(wordList); + Set beginSet = new HashSet<>(); + Set endSet = new HashSet<>(); + beginSet.add(beginWord); + endSet.add(endWord); + + int step = 1; + Set visited = new HashSet<>(); + while (!beginSet.isEmpty() && !endSet.isEmpty()) { + if (beginSet.size() > endSet.size()) { + Set set = beginSet; + beginSet = endSet; + endSet = set; + } + Set temp = new HashSet<>(); + for (String word : beginSet) { + char[] chs = word.toCharArray(); + for (int i = 0; i < chs.length; i++) { + for (char c = 'a'; c <= 'z'; c++) { + char old = chs[i]; + chs[i] = c; + String target = String.valueOf(chs); + if (endSet.contains(target)) { + return step + 1; + } + if (!visited.contains(target) && dict.contains(target)) { + temp.add(target); + visited.add(target); + } + chs[i] = old; + } + } + } + beginSet = temp; + step++; + } + return 0; + } +} \ No newline at end of file diff --git a/Week 03/id_428/LeeCode_55_428.java b/Week 03/id_428/LeeCode_55_428.java new file mode 100644 index 000000000..605a100ac --- /dev/null +++ b/Week 03/id_428/LeeCode_55_428.java @@ -0,0 +1,32 @@ + +class Solution { + //No.1 Ok + public boolean canJump(int[] nums) { + int max = 0; + for(int i=0;imax) {return false;} + max = Math.max(nums[i]+i,max); + } + return true; + } + + //No2.Time Limit Exceeded +//public boolean canJumpFromPosition(int position, int[] nums) { +// if (position == nums.length - 1) { +// return true; +// } +// int furthestJump = Math.min(position + nums[position], nums.length - 1); +//// for (int nextPosition = position + 1; nextPosition <= furthestJump; nextPosition++) { +// for (int nextPosition = furthestJump; nextPosition > position; nextPosition--) { +// if (canJumpFromPosition(nextPosition, nums)) { +// +// return true; +// } +// } +// return false; +//} +// public boolean canJump(int[] nums) { +// return canJumpFromPosition(0, nums); +// } + +} \ No newline at end of file diff --git a/Week 03/id_428/LeeCode_74_428.java b/Week 03/id_428/LeeCode_74_428.java new file mode 100644 index 000000000..97d8fb37d --- /dev/null +++ b/Week 03/id_428/LeeCode_74_428.java @@ -0,0 +1,23 @@ + +class Solution { + public boolean searchMatrix(int[][] matrix, int target) { + if (matrix == null || matrix.length == 0) { + return false; + } + int start = 0, rows = matrix.length, cols = matrix[0].length; + int end = rows * cols - 1; + while (start <= end) { + int mid = (start + end) / 2; + if (matrix[mid / cols][mid % cols] == target) { + return true; + } + if (matrix[mid / cols][mid % cols] < target) { + start = mid + 1; + } else { + end = mid - 1; + } + } + return false; + } + +} \ No newline at end of file diff --git a/Week 03/id_428/NOTE.md b/Week 03/id_428/NOTE.md index a6321d6e2..d09cc067c 100644 --- a/Week 03/id_428/NOTE.md +++ b/Week 03/id_428/NOTE.md @@ -1,4 +1,126 @@ -# NOTE +# Week_03_学习总结 - +## 1、学习过程 + +​ 1)本周做题比较困难,几个题目理解上都有点难,需要反复看看。 + +​ 2)对贪心算法的理解要加深 + +​ 3)二分查找要自然的应用,需要练习 + +​ + +## 2、本周学习内容(待细化) + +​ 1)深度优先搜索和广度优先搜索 + +​ 2)贪心算法 + +​ 3)二分查找 + +## 3、贪心算法 + +### 3.1、基本概念 + + + +------ + +​ 所谓贪心算法是指,在对问题求解时,总是**做出在当前看来是最好的选择****局部最优解** + +**所以,对所采用的贪心策略一定要仔细分析其是否满足无后效性。** + +### 3.2、贪心算法的基本思路 + + + +------ + + + +- 建立数学模型来描述问题 +- 把求解的问题分成若干个子问题 +- 对每个子问题求解,得到子问题的局部最优解 +- 把子问题的解局部最优解合成原来问题的一个解 + +### 3.3、该算法存在的问题 + +- 不能保证求得的最后解是最佳的 +- 不能用来求最大值或最小值的问题 +- 只能求满足某些约束条件的可行解的范围 + +### 3.4、贪心算法适用的问题 + + + +------ + + +**贪心策略适用的前提是:局部最优策略能导致产生全局最优解。** 实际上,贪心算法适用的情况很少。一般对一个问题分析是否适用于贪心算法,可以先选择该问题下的几个实际数据进行分析,就可以做出判断。 + +## 3.5、贪心选择性质 + +所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,换句话说,当考虑做何种选择的时候,我们只考虑对当前问题最佳的选择而不考虑子问题的结果。这是贪心算法可行的第一个基本要素。贪心算法以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。**对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。** + 当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用贪心算法求解的关键特征。 + +### 3.6、贪心算法的实现框架 + + + +------ + + + 从问题的某一初始解出发: while (朝给定总目标前进一步) { 利用可行的决策,求出可行解的一个解元素。 } 由所有解元素组合成问题的一个可行解; + +### 3.7、例题分析 + + + +------ + + + 【背包问题】有一个背包,容量是M=150,有7个物品,物品可以分割成任意大小。要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。 物品:A B C D E F G 重量:35 30 60 50 40 10 25 价值:10 40 30 50 35 40 30 分析: 目标函数: ∑pi最大 约束条件是装入的物品总质量不超过背包容量:∑wi<=M( M=150) (1)根据贪心的策略,每次挑选价值最大的物品装入背包,得到的结果是否最优? (2)每次挑选所占重量最小的物品装入是否能得到最优解? (3)每次选取单位重量价值最大的物品,成为解本题的策略 + +值得注意的是,贪心算法并不是完全不可以使用,贪心策略一旦经过证明成立后,它就是一种高效的算法。比如,**求最小生成树的Prim算法和Kruskal算法都是漂亮的贪心算法**。 + 贪心算法还是很常见的算法之一,这是由于它简单易行,构造贪心策略不是很困难。 + 可惜的是,它需要证明后才能真正运用到题目的算法中。 + 一般来说,贪心算法的证明围绕着:整个问题的最优解一定由在贪心策略中存在的子问题的最优解得来的。 + 对于例题中的3种贪心策略,都是无法成立(无法被证明)的,解释如下: + 贪心策略:选取价值最大者。反例: + +W=30 + +物品:A B C + +重量:28 12 12 + +价值:30 20 20 + +根据策略,首先选取物品A,接下来就无法再选取了,可是,选取B、C则更好。 + +(2)贪心策略:选取重量最小。它的反例与第一种策略的反例差不多。 + +(3)贪心策略:选取单位重量价值最大的物品。反例: + +W=30 + +物品:A B C + +重量:28 20 10 + +价值:28 20 10 + +根据策略,三种物品单位重量价值一样,程序无法依据现有策略作出判断,如果选择A,则答案错误。但是果在条件中加一句当遇见单位价值相同的时候,优先装重量小的,这样的问题就可以解决. + +所以需要说明的是,贪心算法可以与随机化算法一起使用,具体的例子就不再多举了。(因为这一类算法普及性不高,而且技术含量是非常高的,需要通过一些反例确定随机的对象是什么,随机程度如何,但也是不能保证完全正确,只能是极大的几率正确)。 + +### 3.8、最小生成树:Prim算法 + +https://blog.csdn.net/justinzengtm/article/details/82748556 + +### 3.9、图的最小生成树(Kruskal算法) + +https://blog.csdn.net/mgsky1/article/details/77840286 + +https://blog.csdn.net/qq_41754350/article/details/81460643 diff --git a/Week 03/id_433/LeetCode_33_433.py b/Week 03/id_433/LeetCode_33_433.py new file mode 100644 index 000000000..930ab43b9 --- /dev/null +++ b/Week 03/id_433/LeetCode_33_433.py @@ -0,0 +1,19 @@ +class Solution: + def search(self, nums: List[int], target: int) -> int: + left = 0 + right = len(nums)-1 + while left <= right: + mid = (left + right)//2 + if nums[mid] == target: + return mid + elif nums[mid] >= nums[left]: + if target > nums[mid] or target < nums[left]: + left = mid + 1 + else: + right = mid - 1 + else: + if target < nums[mid] or target > nums[right]: + right = mid - 1 + else: + left = mid + 1 + return -1 \ No newline at end of file diff --git a/Week 03/id_433/LeetCode_74_433.py b/Week 03/id_433/LeetCode_74_433.py new file mode 100644 index 000000000..f62f1ca62 --- /dev/null +++ b/Week 03/id_433/LeetCode_74_433.py @@ -0,0 +1,20 @@ +class Solution: + def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: + if not matrix: + return False + + m = len(matrix) + n = len(matrix[0]) + + left = 0 + right = m*n-1 + while left <= right: + mid = (left + right)//2 + mid_ele = matrix[mid//n][mid % n] + if mid_ele == target: + return True + elif mid_ele < target: + left = mid+1 + else: + right = mid-1 + return False \ No newline at end of file diff --git a/Week 03/id_443/LeetCode_200_443.java b/Week 03/id_443/LeetCode_200_443.java new file mode 100644 index 000000000..2b94ff05e --- /dev/null +++ b/Week 03/id_443/LeetCode_200_443.java @@ -0,0 +1,65 @@ +//给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。 +// +// 示例 1: +// +// 输入: +//11110 +//11010 +//11000 +//00000 +// +//输出: 1 +// +// +// 示例 2: +// +// 输入: +//11000 +//11000 +//00100 +//00011 +// +//输出: 3 +// +// Related Topics 深度优先搜索 广度优先搜索 并查集 + +package com.modds.alltest.leetcode.editor.cn; + +public class LeetCode_200_443_NumberOfIslands { + public static void main(String[] args) { + Solution solution = new LeetCode_200_443_NumberOfIslands().new Solution(); + } + + + //leetcode submit region begin(Prohibit modification and deletion) + class Solution { + public int numIslands(char[][] grid) { + if (grid.length == 0) return 0; + + int islands = 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[i].length; j++) { + int t = grid[i][j]; + if (t == '1') { + islands++; + wrap(grid, i, j); + } + } + } + return islands; + } + + private void wrap(char[][] grid, int m, int n) { + if (m >= grid.length || m < 0) return; + if (n >= grid[0].length || n < 0) return; + if (grid[m][n] == '1') { + grid[m][n] = '0'; + wrap(grid, m - 1, n); + wrap(grid, m + 1, n); + wrap(grid, m, n - 1); + wrap(grid, m, n + 1); + } + } + } +//leetcode submit region end(Prohibit modification and deletion) +} \ No newline at end of file diff --git a/Week 03/id_443/LeetCode_529_443.java b/Week 03/id_443/LeetCode_529_443.java new file mode 100644 index 000000000..bc86bc910 --- /dev/null +++ b/Week 03/id_443/LeetCode_529_443.java @@ -0,0 +1,137 @@ +//让我们一起来玩扫雷游戏! +// +// 给定一个代表游戏板的二维字符矩阵。 'M' 代表一个未挖出的地雷,'E' 代表一个未挖出的空方块,'B' 代表没有相邻(上,下,左,右,和所有4个对角线)地雷的已挖出的空白方块,数字('1' 到 '8')表示有多少地雷与这块已挖出的方块相邻,'X' 则表示一个已挖出的地雷。 +// +// 现在给出在所有未挖出的方块中('M'或者'E')的下一个点击位置(行和列索引),根据以下规则,返回相应位置被点击后对应的面板: +// +// +// 如果一个地雷('M')被挖出,游戏就结束了- 把它改为 'X'。 +// 如果一个没有相邻地雷的空方块('E')被挖出,修改它为('B'),并且所有和其相邻的方块都应该被递归地揭露。 +// 如果一个至少与一个地雷相邻的空方块('E')被挖出,修改它为数字('1'到'8'),表示相邻地雷的数量。 +// 如果在此次点击中,若无更多方块可被揭露,则返回面板。 +// +// +// +// +// 示例 1: +// +// 输入: +// +//[['E', 'E', 'E', 'E', 'E'], +// ['E', 'E', 'M', 'E', 'E'], +// ['E', 'E', 'E', 'E', 'E'], +// ['E', 'E', 'E', 'E', 'E']] +// +//Click : [3,0] +// +//输出: +// +//[['B', '1', 'E', '1', 'B'], +// ['B', '1', 'M', '1', 'B'], +// ['B', '1', '1', '1', 'B'], +// ['B', 'B', 'B', 'B', 'B']] +// +//解释: +// +// +// +// 示例 2: +// +// 输入: +// +//[['B', '1', 'E', '1', 'B'], +// ['B', '1', 'M', '1', 'B'], +// ['B', '1', '1', '1', 'B'], +// ['B', 'B', 'B', 'B', 'B']] +// +//Click : [1,2] +// +//输出: +// +//[['B', '1', 'E', '1', 'B'], +// ['B', '1', 'X', '1', 'B'], +// ['B', '1', '1', '1', 'B'], +// ['B', 'B', 'B', 'B', 'B']] +// +//解释: +// +// +// +// +// +// 注意: +// +// +// 输入矩阵的宽和高的范围为 [1,50]。 +// 点击的位置只能是未被挖出的方块 ('M' 或者 'E'),这也意味着面板至少包含一个可点击的方块。 +// 输入面板不会是游戏结束的状态(即有地雷已被挖出)。 +// 简单起见,未提及的规则在这个问题中可被忽略。例如,当游戏结束时你不需要挖出所有地雷,考虑所有你可能赢得游戏或标记方块的情况。 +// Related Topics 深度优先搜索 广度优先搜索 + +package com.modds.alltest.leetcode.editor.cn; + +public class LeetCode_529_443_Minesweeper { + public static void main(String[] args) { + Solution solution = new LeetCode_529_443_Minesweeper().new Solution(); + } + + //leetcode submit region begin(Prohibit modification and deletion) + class Solution { + int[] dx = {0, -1, 1}; + int[] dy = {0, -1, 1}; + + public char[][] updateBoard(char[][] board, int[] click) { + if (board.length == 0 || click.length == 0) return board; + + int x = click[0]; + int y = click[1]; + + // end condition + if (board[x][y] == 'M') { + board[x][y] = 'X'; + } else { + handle(board, x, y); + } + return board; + } + + private void handle(char[][] board, int x, int y) { + // end condition + if (x < 0 || x >= board.length || y < 0 || y >= board[0].length) return; + char c = board[x][y]; + if (c == 'E') { + int n = blockNum(board, x, y); + if (n == 0) { + board[x][y] = 'B'; + for (int i : dx) { + for (int j : dy) { + if (i != 0 || j != 0) { + handle(board, x+i, y+j); + } + } + } + } else { + board[x][y] = (char) ('0' + n); + } + } + } + + private int blockNum(char[][] board, int x, int y) { + int result = 0; + for (int i : dx) { + for (int j : dy) { + + if ((i != 0 || j != 0) + && x + i >= 0 && x + i < board.length + && y + j >= 0 && y + j < board[0].length + && board[x + i][y + j] == 'M') { + result++; + } + } + } + return result; + } + } +//leetcode submit region end(Prohibit modification and deletion) + +} \ No newline at end of file diff --git a/Week 03/id_448/LeetCode_127_448.java b/Week 03/id_448/LeetCode_127_448.java new file mode 100644 index 000000000..b45a879d5 --- /dev/null +++ b/Week 03/id_448/LeetCode_127_448.java @@ -0,0 +1,42 @@ +class Solution { + public int ladderLength(String beginWord, String endWord, List wordList) { + Map marked = new HashMap<>(); + Queue queue = new LinkedList(); + marked.put(beginWord, true); + queue.add(beginWord); + int len = 0; + while(!queue.isEmpty()){ + int size = queue.size(); + len++; + while(size-- > 0){ + String cur = queue.poll(); + for(String next: wordList){ + if(marked.get(next) != null){ + continue; + } + if(!canTransfer(cur, next)){ + continue; + } + if(endWord.equals(next)){ + return ++len; + } + queue.add(next); + marked.put(next, true); + } + } + } + + return 0; + } + private boolean canTransfer(String s1, String s2){ + int index = 0; + int cnt = 0; + while(index < s1.length()){ + if(s1.charAt(index) != s2.charAt(index)) + cnt++; + index++; + } + + return cnt == 1; + } +} diff --git a/Week 03/id_448/LeetCode_33_448.java b/Week 03/id_448/LeetCode_33_448.java new file mode 100644 index 000000000..c4c16f2cd --- /dev/null +++ b/Week 03/id_448/LeetCode_33_448.java @@ -0,0 +1,31 @@ +class Solution { + public int search(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return -1; + } + int start = 0; + int end = nums.length - 1; + int mid; + while (start <= end) { + mid = start + (end - start) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[start] <= nums[mid]) { + if (target >= nums[start] && target < nums[mid]) { + end = mid - 1; + } else { + start = mid + 1; + } + } else { + if (target <= nums[end] && target > nums[mid]) { + start = mid + 1; + } else { + end = mid - 1; + } + } + + } + return -1; + } +} diff --git a/Week 03/id_448/LeetCode_860_448.java b/Week 03/id_448/LeetCode_860_448.java new file mode 100644 index 000000000..c211e8126 --- /dev/null +++ b/Week 03/id_448/LeetCode_860_448.java @@ -0,0 +1,49 @@ +class Solution { + public boolean lemonadeChange(int[] bills) { + if (bills == null || bills.length < 1) { + return true; + } + + if (bills[0] != 5) { + return false; + } + + int money[] = new int[3];// 5 10 15 + money[0]++; + + for (int i = 1;i < bills.length;i++) { + switch (bills[i]) { + case 5: + money[0]++; + + break; + case 10: + money[1]++; + money[0]--; + + if (money[0] < 0) { + return false; + } + + break; + case 20: + money[2]++; + if (money[1] > 0) { + money[1]--; + } else { + money[0] -= 2; + } + + money[0]--; + + if (money[0] < 0) { + return false; + } + + break; + } + } + + return true; + } +} diff --git a/Week 03/id_468/leetcode102_3_468.java b/Week 03/id_468/leetcode102_3_468.java new file mode 100644 index 000000000..391de88ce --- /dev/null +++ b/Week 03/id_468/leetcode102_3_468.java @@ -0,0 +1,41 @@ +package week3; + +/** + * @program: leetcode + * @description: 层序遍历 + * @author: 王瑞全 + * @create: 2019-10-3111:07 + **/ + + +public class leetcode102_3_468 { + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + + public TreeNode buildTree(int[] preorder, int[] inorder) { + return helper(0,0,inorder.length,preorder,inorder); + } + + public TreeNode helper(int preStart, int inStart, int inEnd, int[] preOrder, int[] inOrder) { + if(preStart>preOrder.length-1||inStart>inEnd){ + return null; + } + TreeNode treeNode=new TreeNode(preOrder[preStart]); + int index=0; + for(int i=inStart;i<=inEnd;i++){ + if(treeNode.val==inOrder[i]){ + index=i; + } + } + treeNode.left=helper(preStart+1,inStart,index-1,preOrder,inOrder); + treeNode.right=helper(preStart+index-inStart+1,index+1,inEnd,preOrder,inOrder); + return treeNode; + } +} diff --git a/Week 03/id_468/leetcode122_3_468.java b/Week 03/id_468/leetcode122_3_468.java new file mode 100644 index 000000000..f63287865 --- /dev/null +++ b/Week 03/id_468/leetcode122_3_468.java @@ -0,0 +1,21 @@ +package week3; + +/** + * @program: leetcode + * @description: 买卖股票的最佳时机 II + * @author: 王瑞全 + * @create: 2019-11-0320:33 + **/ + + +public class leetcode122_3_468 { + public int maxProfit(int[] prices) { + int count=0; + for(int i=0;i> findLadders(String beginWord, String endWord, List wordList) { + HashSet dict = new HashSet<>(wordList); + List> res = new ArrayList<>(); + HashMap> nodeNeighbors = new HashMap<>(); + HashMap distance = new HashMap<>(); + ArrayList solution = new ArrayList<>(); + dict.add(beginWord); + bfs(beginWord, endWord, dict, nodeNeighbors, distance); + dfs(beginWord,endWord,dict,nodeNeighbors,distance,solution,res); + return res; + } + + //bfs:跟踪每个节点到其实节点的距离 + private void bfs(String start, String end, Set dict, HashMap> nodeNeighbors, HashMap distance) { + for (String str : dict) { + nodeNeighbors.put(str, new ArrayList<>()); + } + + Queue queue = new LinkedList<>(); + queue.offer(start); + distance.put(start, 0); + while (!queue.isEmpty()) { + int count = queue.size(); + boolean found = false; + for (int i = 0; i < count; i++) { + String cur = queue.poll(); + int curDistance = distance.get(cur); + ArrayList neughbors = getNeightbors(cur, dict); + + for (String neighbor : neughbors) { + nodeNeighbors.get(cur).add(neighbor); + if (!distance.containsKey(neighbor)) { + distance.put(neighbor, curDistance + 1); + if (end.equals(neighbor)) { + found = true; + } else { + queue.offer(neighbor); + } + } + } + } + if (found) { + break; + } + } + + } + + //找到所有的下一层节点 + private ArrayList getNeightbors(String node, Set dict) { + ArrayList res = new ArrayList<>(); + char[] chs = node.toCharArray(); + for (char ch = 'a'; ch <= 'z'; ch++) { + for (int i = 0; i < chs.length; i++) { + if (chs[i] == ch) continue; + char old_ch = chs[i]; + chs[i] = ch; + if (dict.contains(String.valueOf(chs))) { + res.add(String.valueOf(chs)); + } + chs[i] = old_ch; + } + } + return res; + } + + + //DFS : 输出所有path的最短路径 + private void dfs(String cur,String end,Set dict,HashMap> nodeNeighbors,HashMap distance,ArrayList solution,List> res){ + solution.add(cur); + if(end.equals(cur)){ + res.add(new ArrayList<>(solution)); + }else{ + for(String next:nodeNeighbors.get(cur)){ + if(distance.get(next)==distance.get(cur)+1){ + dfs(next,end,dict,nodeNeighbors,distance,solution,res); + } + } + } + solution.remove(solution.size()-1); + + } + +} diff --git a/Week 03/id_468/leetcode127_3_468.java b/Week 03/id_468/leetcode127_3_468.java new file mode 100644 index 000000000..5e0e499c2 --- /dev/null +++ b/Week 03/id_468/leetcode127_3_468.java @@ -0,0 +1,45 @@ +package week3; + +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * @program: leetcode + * @description: 给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则: + * @author: 王瑞全 + * @create: 2019-11-0219:01 + **/ + + +public class leetcode127_3_468 { + public int ladderLength(String beginWord, String endWord, List wordList) { + wordList.add(endWord); + Queue queue = new LinkedList<>(); + queue.add(beginWord); + int level = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + String cur = queue.remove(); + if (cur.equals(endWord)) return level + 1; + for (int j = 0; j < cur.length(); j++) { + char[] word = cur.toCharArray(); + for (char ch = 'a'; ch < 'z'; ch++) { + word[j] = ch; + String check = new String(word); + if (!check.equals(cur) && wordList.contains(check)) { + queue.add(check); + wordList.remove(check); + } + } + + } + } + level++; + } + return 0; + + } + +} diff --git a/Week 03/id_468/leetcode153_3_468.java b/Week 03/id_468/leetcode153_3_468.java new file mode 100644 index 000000000..7539aa7c9 --- /dev/null +++ b/Week 03/id_468/leetcode153_3_468.java @@ -0,0 +1,33 @@ +package week3; + +/** + * @program: leetcode + * @description: 寻找旋转排序数组中的最小值 + * @author: 王瑞全 + * @create: 2019-11-0323:37 + **/ + + +public class leetcode153_3_468 { + public int findMin(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + if (nums.length == 1) { + return nums[0]; + } + int start = 0, end = nums.length - 1; + while (start < end) { + int mid = (start + end) / 2; + if (mid > 0 && nums[mid] < nums[mid - 1]) { + return nums[mid]; + } + if (nums[start] <= nums[mid] && nums[mid] > nums[end]) { + start = mid + 1; + } else { + end = mid - 1; + } + } + return nums[start]; + } +} diff --git a/Week 03/id_468/leetcode200_3_468.java b/Week 03/id_468/leetcode200_3_468.java new file mode 100644 index 000000000..719d1b805 --- /dev/null +++ b/Week 03/id_468/leetcode200_3_468.java @@ -0,0 +1,40 @@ +package week3; + +/** + * @program: leetcode + * @description: Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water. + * @author: 王瑞全 + * @create: 2019-11-0317:06 + **/ + + +public class leetcode200_3_468 { + + private int n; + private int m; + public int numIslands(char[][] grid) { + int count=0; + n=grid.length; + if(n==0)return 0; + m=grid[0].length; + for(int i=0;i=n||j>=m||grid[i][j]!='1') return ; + grid[i][j]='0'; + DFS(grid,i+1,j); + DFS(grid,i-1,j); + DFS(grid,i,j+1); + DFS(grid,i,j-1); + } + +} diff --git a/Week 03/id_468/leetcode22_3_468.java b/Week 03/id_468/leetcode22_3_468.java new file mode 100644 index 000000000..e3a2fd97b --- /dev/null +++ b/Week 03/id_468/leetcode22_3_468.java @@ -0,0 +1,30 @@ +package week3; + +import java.util.ArrayList; +import java.util.List; + +/** + * @program: leetcode + * @description: 给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。 + * @author: 王瑞全 + * @create: 2019-11-0218:22 + **/ + + +public class leetcode22_3_468 { + public List generateParenthesis(int n) { + List array=new ArrayList<>(); + dfs(array,"",0,0,n); + return array; + } + private void dfs(List list,String str,int open,int close,int max){ + if(str.length()==max*2){ + list.add(str); + return; + } + if(open nums[m - 1]) ? minIdx : m - 1; + + while (start <= end) { + int mid = start + (end - start) / 2; + if (nums[mid] == target) return mid; + else if (target > nums[mid]) start = mid + 1; + else end = mid - 1; + } + return -1; + } + public int findMinIdx(int[] nums) { + int start = 0, end = nums.length - 1; + while (start < end) { + int mid = start + (end - start) / 2; + if (nums[mid] > nums[end]) start = mid + 1; + else end = mid; + } + return start; + } +} diff --git a/Week 03/id_468/leetcode367_3_468.java b/Week 03/id_468/leetcode367_3_468.java new file mode 100644 index 000000000..43cb57bd1 --- /dev/null +++ b/Week 03/id_468/leetcode367_3_468.java @@ -0,0 +1,26 @@ +package week3; + +/** + * @program: leetcode + * @description: 有效的平方数 + * @author: 王瑞全 + * @create: 2019-11-0322:47 + **/ + + +public class leetcode367_3_468 { + public boolean isPerfectSquare(int num) { + int low=1,hight=num; + while(low<=hight){ + long mid=(low+hight)>>>1; + if(mid*mid==num){ + return true; + }else if(mid*mid visited=new HashSet<>(); + Set bankSet=new HashSet<>(); + for(String b:bank){ + bankSet.add(b); + } + Queue queue=new LinkedList<>(); + visited.add(start); + queue.add(new Genehop(start,1)); + while(!queue.isEmpty()){ + Genehop next=queue.poll(); + if(next.mutation.equals(end)){ + return next.step-1; + } + // why is 8 + for(int i=0;i largestValues(TreeNode root) { + Queue queue=new LinkedList<>(); + List res=new ArrayList<>(); + queue.add(root); + int queueSize=root==null?0:1; + while(queueSize>0){ + int largest=Integer.MIN_VALUE; + for(int i=0;i0){ + board[x][y]=(char)(count+'0'); + } + else{ + updateBoard(board,x,y); + } + return board; + } + + public void updateBoard(char[][] board,int x,int y){ + if(x<0||y<0||y>=board[0].length||x>=board.length) return; + int count=countMines(board,x,y); + if(board[x][y]=='E'){ + if(count == 0) { + board[x][y] = 'B'; + for(int i = Math.max(x-1 , 0) ; i <= Math.min(x+1 , board.length-1) ; i++) { + for(int j = Math.max(y-1 , 0) ; j <= Math.min(y+1 , board[0].length-1) ; j++) { + updateBoard(board , i , j); + } + } + } + else { + board[x][y]=(char)(count+'0'); + } + } + + } + //计算位置相邻的地雷数量 + public int countMines(char[][] board,int x,int y ){ + int N=board.length; + int M=board[0].length; + int count=0; + for(int i=Math.max(x-1,0);i<=Math.min(x+1,N-1);i++){ + for(int j=Math.max(y-1,0);imax) return false; + max=Math.max(nums[i]+i,max); + } + return true; + } +} diff --git a/Week 03/id_468/leetcode74_3_468.java b/Week 03/id_468/leetcode74_3_468.java new file mode 100644 index 000000000..c0c635f97 --- /dev/null +++ b/Week 03/id_468/leetcode74_3_468.java @@ -0,0 +1,26 @@ +package week3; + +/** + * @program: leetcode + * @description: 搜索二维矩阵 + * @author: 王瑞全 + * @create: 2019-11-0323:19 + **/ + + +public class leetcode74_3_468 { + public boolean searchMatrix(int[][] matrix, int target) { + for(int[] index:matrix){ + if(index.length==0){ + return false; + } + if(index[index.length-1]=0;i--){ + if(index[i]==target){i--;return true;} + } + } + return false; + } +} diff --git a/Week 03/id_468/leetcode860_3_468.java b/Week 03/id_468/leetcode860_3_468.java new file mode 100644 index 000000000..fb99626e1 --- /dev/null +++ b/Week 03/id_468/leetcode860_3_468.java @@ -0,0 +1,23 @@ +package week3; + +/** + * @program: leetcode + * @description: 柠檬水找零 + * @author: 王瑞全 + * @create: 2019-11-0320:20 + **/ + + +public class leetcode860_3_468 { + public boolean lemonadeChange(int[] bills) { + int five=0,ten=0; + for(int i:bills){ + if(i==5)five++; + else if(i==10){five--;ten++;} + else if(ten>0){ten--;five--;} + else five-=3; + if(five<0)return false; + } + return true; + } +} diff --git a/Week 03/id_468/leetcode874_3_468.java b/Week 03/id_468/leetcode874_3_468.java new file mode 100644 index 000000000..409d634aa --- /dev/null +++ b/Week 03/id_468/leetcode874_3_468.java @@ -0,0 +1,43 @@ +package week3; + +import java.util.HashSet; +import java.util.Set; + +/** + * @program: leetcode + * @description: 模拟行走机器人 + * @author: 王瑞全 + * @create: 2019-11-0321:12 + **/ + + +public class leetcode874_3_468 { + public int robotSim(int[] commands, int[][] obstacles) { + Set set=new HashSet<>(); + for (int[] obs : obstacles) { + set.add(obs[0] + " " + obs[1]); + } + int[][] dirs = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; + int d=0,x=0,y=0,result=0; + for(int c:commands){ + if(c==-1){ + d++; + if(d==4){ + d=0; + } + }else if(c==-2){ + d--; + if(d==-1){ + d=3; + } + }else{ + while(c-- >0&&!set.contains((x+dirs[d][0])+" "+(y+dirs[d][1]))){ + x+=dirs[d][0]; + y+=dirs[d][1]; + } + } + result = Math.max(result, x * x + y * y); + } + return result; + } +} diff --git a/Week 03/id_473/LeetCode_33.java b/Week 03/id_473/LeetCode_33.java new file mode 100644 index 000000000..8e552ef97 --- /dev/null +++ b/Week 03/id_473/LeetCode_33.java @@ -0,0 +1,49 @@ +/** + * 33. 搜索旋转排序数组 + * @Author CJ + * @create 2019/11/3 + */ +class LeetCode_33 { + public static void main(String[] args) { + int[] nums = new int[]{4,5,6,7,0,1,2}; + int target = 3; + System.out.println(search(nums , target)); + } + + //二分查找法 + public static int search(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return -1; + } + int left = 0; + int right = nums.length - 1; + + while (left+1 < right){ + int mid = (left + right)>>>1; + if (nums[mid] == target) { + return mid; + } + if (nums[mid] > nums[left]) { + if (target <= nums[mid] && target >= nums[left]) { + right = mid; + }else { + left = mid; + } + }else if (nums[mid] < nums[right]) { + if (target <= nums[right] && target >= nums[mid]) { + left = mid; + }else { + right = mid; + } + } + } + if (nums[left] == target) { + return left; + }else if (nums[right] == target) { + return right; + } + return -1; + } + + +} \ No newline at end of file diff --git a/Week 03/id_473/LeetCode_74.java b/Week 03/id_473/LeetCode_74.java new file mode 100644 index 000000000..cb80a2ba9 --- /dev/null +++ b/Week 03/id_473/LeetCode_74.java @@ -0,0 +1,60 @@ +import java.util.ArrayList; +import java.util.List; +/** + * 74. 搜索二维矩阵 + * @Author CJ + * @create 2019/11/3 + */ +class LeetCode_74 { + public static void main(String[] args) { + int[][] matrix = new int[][]{{1,3,5}}; + //int[][] matrix = new int[][]{{},{1}}; + int target = 1; + System.out.println(searchMatrix(matrix, target)); + } + + //二分查找(二维数组转一维数组) + public static boolean searchMatrix(int[][] matrix, int target) { + if (matrix.length == 0 || matrix == null) { + return false; + } + + Integer[] nums = toOne(matrix); + if (nums.length == 0 || nums == null){ + return false; + } + int left = 0; + int right = nums.length - 1; + + while (left + 1 < right) { + int mid = (left + right)>>>1; + if (target == nums[mid]) { + return true; + } + if (nums[left] <= target && target <= nums[mid]){ + right = mid; + }else { + left = mid; + } + } + if (nums[left] == target || nums[right] == target){ + return true; + } + return false; + } + //二维数组转一维数组 + private static Integer[] toOne(int[][] matrix) { + List list = new ArrayList<>(); + int i,j = 0; + for ( i = 0; i < matrix.length; i++) { + if (matrix[i].length == 0){ + continue; + } + for ( j = 0; j < matrix[i].length; j++) { + list.add(matrix[i][j]); + } + } + return list.toArray(new Integer[list.size()]); + } + +} \ No newline at end of file diff --git a/Week 03/id_473/NOTE.md b/Week 03/id_473/NOTE.md index a6321d6e2..c209c8a33 100644 --- a/Week 03/id_473/NOTE.md +++ b/Week 03/id_473/NOTE.md @@ -1,4 +1,42 @@ -# NOTE +# 找出半有序数组被批断的位置 + +思路:与 搜索旋转排序数组 的解法类似,先判断哪一边为有序数,然后向反方向继续进行二分查找。 +``` + int left = 0; + int right = nums.length -1 ; + int target = 0; + while (left + 1 < right) { + int mid = (left + right)>>>1; + if (nums[left] < nums[mid]) { + left = mid; + }else if (nums[right] > mid){ + right = mid; + } + target = nums[mid]; + } + if (nums[left] == target ){ + return left; + }else if ( nums[right] == target) { + return right; + } + return -1; +``` +--- +# 第三周学习总结 + +- 深度优先搜索DFS:沿着树的深度遍历树的节点,当当前节点无子节点或不满足搜索条件时,回溯到上层节点的其他未搜索过的分支,直到搜索所有节点为止。 + +- 广度优先搜索BFS:以逐层式搜索遍历树的节点,当遍历至所有节点没有下一层子节点时则停止搜索。 + +- 贪心算法: + - 建立模型描述问题 + - 把求解的问题分解成若干个子问题 + - 针对没一个子问题进行求解,得到的子问题的局部最优解 + - 把子问题的局部最优解合成原来问题的一个解 + +- 二分查找: + - 需使用顺序存储机构或半序存储结构 + - 将数据一份为二,然后取中间的数据与前段或后段进行比较,当一段满足条件则缩小范围再次一分为二进行比较,直至找到匹配的数据为止。 diff --git "a/Week 03/id_493/127.\345\215\225\350\257\215\346\216\245\351\276\231.js" "b/Week 03/id_493/127.\345\215\225\350\257\215\346\216\245\351\276\231.js" new file mode 100644 index 000000000..8da608a5e --- /dev/null +++ "b/Week 03/id_493/127.\345\215\225\350\257\215\346\216\245\351\276\231.js" @@ -0,0 +1,55 @@ +/* + * @lc app=leetcode.cn id=127 lang=javascript + * + * [127] 单词接龙 + */ + +// @lc code=start +/** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ +var ladderLength = function(beginWord, endWord, wordList) { + + let dict = {}; + for (let word of wordList) { + for (let i = 0; i < word.length; i++) { + let key = word.substring(0, i) + "*" + word.substring(i + 1); + dict[key] = dict[key] || new Set(); + dict[key].add(word); + } + } + let visit = []; + let findSet = new Set([beginWord]); + let len = 1; + while (findSet.size > 0) { + let childList = new Set(); + len++; + for (let word of findSet) { + for (let i = 0; i < word.length; i++) { + let key = word.substring(0, i) + "*" + word.substring(i + 1); + if (dict.hasOwnProperty(key)) { + let ff = dict[key]; + if (ff.has(endWord)) { + return len; + } + for (let cc of ff) { + if (visit.indexOf(cc) == -1) { + childList.add(cc); + visit.push(cc) + } + + } + + } + } + } + findSet = childList; + } + return 0; +}; + +// @lc code=end + diff --git "a/Week 03/id_493/33.\346\220\234\347\264\242\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204.js" "b/Week 03/id_493/33.\346\220\234\347\264\242\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204.js" new file mode 100644 index 000000000..370a7c054 --- /dev/null +++ "b/Week 03/id_493/33.\346\220\234\347\264\242\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204.js" @@ -0,0 +1,42 @@ +/* + * @lc app=leetcode.cn id=33 lang=javascript + * + * [33] 搜索旋转排序数组 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ +var search = function(nums, target) { + let len = nums.length; + let begin = 0; + let end = len - 1; + while (begin <= end) { + let mid = begin + Math.floor((end - begin) / 2); + if (nums[mid] == target) { + return mid; + } + if (nums[begin] == target) { + return begin; + } + if (target < nums[mid]) { + if ((target > nums[begin] || nums[mid] < nums[begin])) { + end = mid - 1; + } else { + begin = mid + 1; + } + } else { + if (target > nums[end] && nums[mid] < nums[end]) { + end = mid - 1; + } else { + begin = mid + 1; + } + } + } + return -1; +}; +// @lc code=end + diff --git "a/Week 03/id_493/860.\346\237\240\346\252\254\346\260\264\346\211\276\351\233\266.js" "b/Week 03/id_493/860.\346\237\240\346\252\254\346\260\264\346\211\276\351\233\266.js" new file mode 100644 index 000000000..bf0ec91b5 --- /dev/null +++ "b/Week 03/id_493/860.\346\237\240\346\252\254\346\260\264\346\211\276\351\233\266.js" @@ -0,0 +1,47 @@ +/* + * @lc app=leetcode.cn id=860 lang=javascript + * + * [860] 柠檬水找零 + */ + +// @lc code=start +/** + * @param {number[]} bills + * @return {boolean} + */ +var lemonadeChange = function(bills) { + let fiveCount = 0; + let tenCount = 0; + let twenty = 0; + for (let bill of bills) { + // let locBill = + if (bill == 5) { + fiveCount++; + } else if (bill == 10) { + tenCount++; + fiveCount--; + if (fiveCount < 0) { + return false; + } + } else { + if (tenCount > 0) { + tenCount--; + if (fiveCount > 0) { + fiveCount--; + } else { + return false; + } + } else { + if (fiveCount > 2) { + fiveCount = fiveCount - 3; + } else { + return false; + } + } + twenty++; + } + } + return true; +}; +// @lc code=end + diff --git a/Week 03/id_503/NOTE.md b/Week 03/id_503/NOTE.md index a6321d6e2..23b705cf4 100644 --- a/Week 03/id_503/NOTE.md +++ b/Week 03/id_503/NOTE.md @@ -1,4 +1,163 @@ -# NOTE +# 深度优先搜索 DFS(Depth First Search) +* 用的是**回溯思想** +* 非常适合用**递归**实现,或者说**借助栈**来实现 +* 时间复杂度:O(E);E 表示边的个数 +* 空间复杂度:O(V);V 表示节点个数 +* 代码模板 + * 递归写法 + ```javascript + // 记录访问过的节点 + const visited = new Set(); + + function dsf(node, visited) { + + if (!root) { // 结束条件 + return; + } - + if (visited.has(node)) { // 已经访问过,不做处理 + return + } + visited.add(node); + + // 处理当前节点逻辑 + process(node) + + for (child of node.children){ // 递归处理子节点 + dsf(child, visited) + } + } + ``` + * 非递归写法 + ```javascript + function dsf(root) { + + if (!root) { + return; + } + + const visited = new Set(); // 记录已经访问过的节点 + let stack = [root] // 借助栈进行处理节点逻辑 + + while (stack.length) { + const node = stack.pop(); + visited.add(node); + + // 处理当前节点逻辑 + process(node) + + // 生成相关子节点,并过滤掉已经访问过的节点 + const childNodes = generateChildNodes(node, visited); + + // 子节点加入栈中进行下一次出栈处理 + stack = stack.concat(childNodes); + } + } + ``` +# 广度优先搜 BFS(Breadth First Search) +* 用的是**层层推进的搜索策略** +* 非常适合**借助队列**来实现 +* 时间复杂度:O(E);E 表示边的个数 +* 空间复杂度:O(V);V 表示节点个数 +* 代码模板 + * 写法1 + ```javascript + function bsf(root) { + + if (!root) { + return; + } + + const visited = new Set(); // 记录已经访问过的节点 + let queue = [root] // 借助队列进行处理节点逻辑 + + while (queue.length) { + const node = queue.shift(); + visited.add(node); + + // 处理当前节点 + process(node) + + // 生成相关子节点,并过滤掉已经访问过的节点 + const childNodes = generateChildNodes(node, visited); + + // 子节点加入队列中进行下一次出队处理 + queue = queue.concat(childNodes); + } + } + ``` + * 写法2 + ```javascript + function bsf(root) { + + if (!root) { + return; + } + + const visited = new Set(); // 记录已经访问过的节点 + let queue = [root] // 借助队列进行处理节点逻辑 + + while (queue.length) { + + let nextQueue = []; // 下一次队列 + for (let node of queue) { // 出队处理 + visited.add(node); + + // 处理当前节点 + process(node) + + // 生成相关子节点,并过滤掉已经访问过的节点 + const subNodes = generateChildNodes(node, visited); + nextQueue = nextQueue.concat(subNodes); + } + + // 作为下一次队列中进行出队处理 + queue = nextQueue; + } + } + ``` + +# 贪心算法(Greedy) +* 在**每一步**选择中都采取在**当前状态**下最好或最优的选择 +* **希望**导致结果是**全局**最好或最优的算法(事实情况并非能够全局最优) +* 与动态规划比较 + * 贪心算法对子问题做出选择后,不能回退 + * 动态规划则会保存以前的运算结果,有回退功能 +* 可以解决一些最优化问题 + * 求图中的最小生成树 + * 求哈夫曼编码 + * 可以当作辅助算法解决不特别精确的问题 +* 一般不能解决工程问题 +* 适用场景 + * 问题能够分解成子问题来解决 + * 子问题的最优解能递推到最终问题的最优解 + +# 二分查找(Binary Search) +* 前提 + * 目标函数单调性(单调递增或者递减) + * 存在上下界(bounded) + * 能够通过索引访问(index accessible) +* 时间复杂度是 O(logn) +* 代码模板 + ```javascript + function binarySearch(data, target) { + + let left = 0; + let right = data.length - 1; + + //找中间值,比较目标值,判断左查找还是右查找 + while (left <= right) { + let mid = Math.floor((left + right) / 2); + if (data[mid] == target) { + return + } + + if (data[mid] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + } + ``` \ No newline at end of file diff --git a/Week 03/id_503/leetcode_127_503.js b/Week 03/id_503/leetcode_127_503.js new file mode 100644 index 000000000..ea0e80e75 --- /dev/null +++ b/Week 03/id_503/leetcode_127_503.js @@ -0,0 +1,115 @@ +/** + * 双向 BFS + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ +var ladderLength = function (beginWord, endWord, wordList) { + + if (!wordList || !wordList.includes(endWord)) { + return 0 + } + + const comboWordsDic = generateComboWordsDic(wordList); + + const visitedBeginWords = {}; + visitedBeginWords[beginWord] = 1; + const visitedEndWords = {}; + visitedEndWords[endWord] = 1; + + let beginQueue = [{ word: beginWord, level: 1 }]; + let endQueue = [{ word: endWord, level: 1 }]; + + while (beginQueue.length || endQueue.length) { + + let res = -1; + + [res, beginQueue] = + processCurrent(beginQueue, comboWordsDic, visitedBeginWords, visitedEndWords); + if (res != -1) { + return res; + } + + [res, endQueue] = + processCurrent(endQueue, comboWordsDic, visitedEndWords, visitedBeginWords); + if (res != -1) { + return res; + } + } + + return 0; +} + +/** + * 处理当前字符 + * @param {string[]} queue + * @param {{}} comboWordsDic + * @param {{}} visitedWords + * @param {{}} targetVisitedWords + * @return {[number, string[]]} + */ +function processCurrent(queue, comboWordsDic, visitedWords, targetVisitedWords) { + + let nextQueue = []; + + for (let node of queue) { + const w = node.word; + const level = node.level; + + for (let i = 0; i < w.length; i++) { + + const key = generateKey(w, i); + const comboWords = comboWordsDic[key]; + + if (!comboWords) { + continue; + } + + for (let j = 0; j < comboWords.length; j++) { + + const newWord = comboWords[j]; + if (targetVisitedWords[newWord]) { + return [level + targetVisitedWords[newWord], []] + } + + if (!visitedWords[newWord]) { + + visitedWords[newWord] = level + 1; + nextQueue.push({ word: newWord, level: level + 1 }); + } + } + } + } + + return [-1, nextQueue]; +} + +/** + * 生产单词组合字典 + * @param {string[]} wordList + * @return {Object} + */ +function generateComboWordsDic(wordList) { + const dic = {}; + wordList.forEach(w => { + for (let i = 0; i < w.length; i++) { + const key = generateKey(w, i); + + dic[key] ? dic[key].push(w) : dic[key] = [w]; + } + }); + + return dic; +} + +/** + * 生产单词组合字典的 key + * @param {word} word + * @param {i} number + * @return {string} + */ +function generateKey(word, i) { + + return word.substring(0, i) + "*" + word.substring(i + 1, word.length); +} \ No newline at end of file diff --git a/Week 03/id_503/leetcode_74_503.js b/Week 03/id_503/leetcode_74_503.js new file mode 100644 index 000000000..346b676be --- /dev/null +++ b/Week 03/id_503/leetcode_74_503.js @@ -0,0 +1,41 @@ +/** + * T(n) = O(log(mn)) + * @param {number[][]} matrix + * @param {number} target + * @return {boolean} + */ +var searchMatrix = function (matrix, target) { + + if (!matrix) { + return false; + } + + const m = matrix.length; + if (m == 0) { + return false; + } + const n = matrix[0].length; + + let left = 0; + let right = m * n - 1; // 看成一个长数组 + + // 二分查找 + while (left <= right) { + const idx = Math.floor((left + right) / 2); + + const row = Math.floor(idx / n); + const col = idx % n; + const value = matrix[row][col]; + + if (value === target) { + return true; + } else if (value < target) { + left = idx + 1; + } else { + right = idx - 1; + } + } + + return false + +}; \ No newline at end of file diff --git a/Week 03/id_503/leetcode_874_503.js b/Week 03/id_503/leetcode_874_503.js new file mode 100644 index 000000000..b36ae99dd --- /dev/null +++ b/Week 03/id_503/leetcode_874_503.js @@ -0,0 +1,48 @@ +/** + * @param {number[]} commands + * @param {number[][]} obstacles + * @return {number} + */ +var robotSim = function (commands, obstacles) { + const obstaclesMap = {}; + obstacles.forEach(o => { + obstaclesMap[o[0] + "-" + o[1]] = true; + }); + + const directions = [[0, 1], [1, 0], [0, -1], [-1, 0]]; + let x = y = dir = 0; + + let distance = 0; + for (let i = 0; i < commands.length; i++) { + + switch (commands[i]) { + case -2: + dir = (dir + 3) % 4; + break; + case -1: + dir = (dir + 1) % 4; + break; + default: + let step = 0; + + while (step < commands[i]) { + + const nextX = x + directions[dir][0]; + const nextY = y + directions[dir][1]; + + const key = nextX + "-" + nextY; + if (obstaclesMap[key]) { + break; + } + + x = nextX; + y = nextY; + step++; + } + const newDistance = x * x + y * y; + distance = distance > newDistance ? distance : newDistance; + } + } + + return distance; +}; \ No newline at end of file diff --git a/Week 03/id_508/LeetCode_033_508.cpp b/Week 03/id_508/LeetCode_033_508.cpp new file mode 100644 index 000000000..a9b805173 --- /dev/null +++ b/Week 03/id_508/LeetCode_033_508.cpp @@ -0,0 +1,28 @@ +class Solution { +public: + int search(vector& nums, int target) { + int left = 0, right = nums.size() - 1, mid = right / 2; + int result = -1; + while (left <= right) { + mid = left + (right - left)/2; + if(target == nums[mid]) { + result = mid; + break; + } + if(nums[mid]>=nums[left]) { + if(target>=nums[left]&&target=nums[mid]&&target<=nums[right]) { + left = mid + 1; + } else { + right = mid - 1; + } + } + } + return result; + } +}; diff --git a/Week 03/id_508/LeetCode_045_508.cpp b/Week 03/id_508/LeetCode_045_508.cpp new file mode 100644 index 000000000..c071d0e00 --- /dev/null +++ b/Week 03/id_508/LeetCode_045_508.cpp @@ -0,0 +1,19 @@ +class Solution { +public: + int jump(vector& nums) { + int end=0; + int cur=0; + int maxPos = 0; + int res =0; + while(cur& nums) { + int N = nums.size(); + int stop = N-1; + int cur = stop-1; + while(cur>=0) { + if((stop - cur)<=nums[cur]) + stop = cur; + cur--; + } + return stop==0?true:false; + } +}; diff --git a/Week 03/id_508/LeetCode_074_508.cpp b/Week 03/id_508/LeetCode_074_508.cpp new file mode 100644 index 000000000..6236e01a3 --- /dev/null +++ b/Week 03/id_508/LeetCode_074_508.cpp @@ -0,0 +1,40 @@ +class Solution { +public: + bool searchMatrix(vector>& matrix, int target) { + int rows = matrix.size(); + int cols = 0; + if(rows>0) cols = matrix[0].size(); + int left = 0,right = cols -1; + int up = 0, bottom = rows -1; + int mid_row, mid_cols; + cout<=target) { + break; + } + if(matrix[mid_row][0]>target) { + bottom = mid_row -1; + } else { + up = mid_row + 1; + } + } + bool res = false; + + while(left<=right) { + mid_cols = left + (right - left)/2; + if(matrix[mid_row][mid_cols]== target) { + res = true; + break; + } + if(matrix[mid_row][mid_cols]>target) { + right = mid_cols -1; + } else { + left = mid_cols + 1; + } + } + return res; + } +}; diff --git a/Week 03/id_508/LeetCode_122_508.cpp b/Week 03/id_508/LeetCode_122_508.cpp new file mode 100644 index 000000000..0d5da8929 --- /dev/null +++ b/Week 03/id_508/LeetCode_122_508.cpp @@ -0,0 +1,12 @@ +class Solution { +public: + int maxProfit(vector& prices) { + int res=0; + for(int i = 1;iprices[i-1]) { + res += prices[i]- prices[i-1]; + } + } + return res; + } +}; diff --git a/Week 03/id_508/LeetCode_127_508.cpp b/Week 03/id_508/LeetCode_127_508.cpp new file mode 100644 index 000000000..c86143bfc --- /dev/null +++ b/Week 03/id_508/LeetCode_127_508.cpp @@ -0,0 +1,44 @@ +class Solution { +public: + int ladderLength(string beginWord, string endWord, vector& wordList) { + unordered_set visited; + unordered_set dict(wordList.begin(), wordList.end()); + deque que; + + que.push_back(beginWord); + int res = 0; + int result = 0; + while(!que.empty()) { + int size = que.size(); + res++; + while(size--) { + auto word = que.front(); + que.pop_front(); + if(word == endWord) { + que.clear(); + result = res; + break; + } + if(visited.find(word)==visited.end()) { + visited.insert(word); + string rem = word; + for(int index=0;index& nums) { + int left = 0; + int right = nums.size()-1; + int mid=0; + while(left<=right) { + mid = left + (right - left)/2; + if(nums[mid]=nums[left]&&nums[mid]>=nums[right]) { + left = mid +1; + } else { + right = mid-1; + } + } + return nums[mid]; + } +}; diff --git a/Week 03/id_508/LeetCode_200_508.cpp b/Week 03/id_508/LeetCode_200_508.cpp new file mode 100644 index 000000000..ccf5b4f28 --- /dev/null +++ b/Week 03/id_508/LeetCode_200_508.cpp @@ -0,0 +1,35 @@ +class Solution { +public: + int numIslands(vector>& grid) { + int k = 0; + int res = 0; + + deque> que; + for(int i = 0; i< grid.size() ; i++ ) { + for (int j = 0; j < grid[0].size(); j++) { + if(grid[i][j] == '1') { + k++; + que.push_back(pair(i,j)); + while(!que.empty()) { + int x = que.front().first; + int y = que.front().second; + que.pop_front(); + if(grid[x][y] == '0' ||grid[x][y] =='v') continue; + + grid[x][y] = 'v'; + if (x&&grid[x-1][y]=='1') {que.push_back(pair(x-1,y)); + } + if (x<(grid.size()-1)&&grid[x+1][y]=='1') {que.push_back(pair(x+1,y)); + } + if (y&&grid[x][y-1]=='1') {que.push_back(pair(x,y-1)); + } + if (y<(grid[0].size()-1)&&grid[x][y+1]=='1') { que.push_back(pair(x,y+1)); + } + } + } + + } + } + return k; + } +}; diff --git a/Week 03/id_508/LeetCode_455_508.cpp b/Week 03/id_508/LeetCode_455_508.cpp new file mode 100644 index 000000000..24d6de085 --- /dev/null +++ b/Week 03/id_508/LeetCode_455_508.cpp @@ -0,0 +1,17 @@ +class Solution { +public: + int findContentChildren(vector& g, vector& s) { + sort(g.begin(),g.end()); + sort(s.begin(),s.end()); + + int child =0; + int cookie = 0; + while(child> updateBoard(vector>& board, vector& click) { + deque> que; + que.push_back(pair(click[0],click[1])); + while(!que.empty()) { + int x = que.front().first; + int y = que.front().second; + + que.pop_front(); + + if(board[x][y]=='M') { + board[x][y] = 'X'; + } else if(board[x][y] == 'E' ) { + int n = 0; + for(int i = max(0,x-1);i(board.size()));i++) { + for (int j = max(0,y-1);j(board[0].size()));j++) { + if(board[i][j] == 'M') n++; + } + } + if(n) board[x][y] = '0'+n; + else { + board [x][y] = 'B'; + for(int i = max(0,x-1);i(board.size()));i++) { + for (int j = max(0,y-1);j(board[0].size()));j++) { + que.push_back(pair(i,j)); + } + } + } + } + } + return board; + } +}; diff --git a/Week 03/id_508/LeetCode_860_508.cpp b/Week 03/id_508/LeetCode_860_508.cpp new file mode 100644 index 000000000..f44395961 --- /dev/null +++ b/Week 03/id_508/LeetCode_860_508.cpp @@ -0,0 +1,31 @@ +class Solution { +public: + bool lemonadeChange(vector& bills) { + int five = 0, ten = 0; + bool res = true; + for(auto i:bills) { + if(i == 5) { + five++; + } else if(i == 10) { + ten++; + if(five>0) five--; + else { + res = false; + break; + } + } else { + if(ten&&five) { + ten--; + five--; + } else if(five>=3) { + five-=3; + } else { + res = false; + break; + } + } + } + return res; + } + +}; diff --git a/Week 03/id_508/LeetCode_874_508.cpp b/Week 03/id_508/LeetCode_874_508.cpp new file mode 100644 index 000000000..9b239c1f3 --- /dev/null +++ b/Week 03/id_508/LeetCode_874_508.cpp @@ -0,0 +1,32 @@ +class Solution { +public: + int robotSim(vector& commands, vector>& obstacles) { + int dx[] = {0,1,0,-1}; + int dy[] = {1,0,-1,0}; + int x = 0,y = 0; + int res = 0; + set> obs; + for(auto i:obstacles) { + obs.insert(pair(i[0],i[1])); + } + int dir = 0; + for(auto com:commands) { + + if(com>0) { + while(com--) { + x+=dx[dir]; + y+=dy[dir]; + if(obs.find(pair(x,y))!=obs.end()) { + x-=dx[dir]; + y-=dy[dir]; + break; + } + res = max(res,x*x+y*y); + } + } else { + dir = (dir + 2*com +7)%4; + } + } + return res; + } +}; diff --git a/Week 03/id_518/LeetCode_33_518.c b/Week 03/id_518/LeetCode_33_518.c new file mode 100644 index 000000000..4af4de96c --- /dev/null +++ b/Week 03/id_518/LeetCode_33_518.c @@ -0,0 +1,71 @@ +#include +#include + +int findSmallInOrder(int *nums, int left, int right) { + int idx = left; + int small = nums[left]; + for (int i = left + 1; i <= right; i++) { + if (nums[i] < small) { + small = nums[i]; + idx = i; + } + } + return idx; +} + +int findSmall(int *nums, int numSize) { + int left = 0; + int right = numSize - 1; + int mid = left; + while (nums[left] >= nums[right]) { + if (right - left == 1) { + mid = right; + break; + } + mid = (left + right) / 2; + if (nums[left] == nums[right] && nums[left] == nums[mid]) { + mid = findSmallInOrder(nums, left, right); + break; + } + if (nums[mid] >= nums[left]) + left = mid; + else if (nums[mid] <= nums[right]) + right = mid; + } + return mid; +} + +int search(int *nums, int numsSize, int target){ + if (nums == NULL || numsSize == 0) + return -1; + int idx = findSmall(nums, numsSize); + int left = 0; + int right = numsSize - 1; + int mid; + if ( idx && target <= nums[idx-1] && target >= nums[0]) + right = idx - 1; + else if (target >= nums[idx] && target <= nums[right]) + left = idx; + while (left <= right) { + mid = (left + right) / 2; + if (target == nums[mid]) return mid; + else if (target > nums[mid]) + left = mid + 1; + else + right = mid - 1; + } + return -1; +} + +int main() { + //int array[] = { 3, 4, 5, 6, 7 ,1, 2 }; + //int array[] = { 1, 0, 1, 1, 1 }; + //int array[] = { 1, 1, 1, 0, 1 }; + //int array[] = { 4, 5, 6, 7, 0, 1, 2 }; + //int array[] = {}; + int array[] = { 1 }; + //int array[] = { 5, 1, 3 }; + //int array[] = { 1, 3 }; + //printf("%d\n", findSmall(array, sizeof(array) / sizeof(array[0]))); + printf("%d\n", search(array, sizeof(array) / sizeof(array[0]), 1)); +} \ No newline at end of file diff --git a/Week 03/id_518/LeetCode_74_518.c b/Week 03/id_518/LeetCode_74_518.c new file mode 100644 index 000000000..6441b8e11 --- /dev/null +++ b/Week 03/id_518/LeetCode_74_518.c @@ -0,0 +1,42 @@ +#include + +bool searchMatrix(int **matrix, int matrixSize, int *matrixColSize, int target) { + if (!matrixSize) + return false; + int left = 0; + int right = matrixSize * matrixColSize[0] -1; + while (left <= right) { + int mid = (left + right) / 2; + int x = mid / matrixColSize[0]; + int y = mid - x * matrixColSize[0]; + if (matrix[x][y] == target) + return true; + else if (matrix [x][y] < target) + left = mid + 1; + else + right = mid - 1; + } + return false; +} + +// bool searchMatrix(int** matrix, int matrixSize, int* matrixColSize, int target){ +// if(matrixSize==0){ +// return false; +// } +// int left=0; +// int right=matrixSize*matrixColSize[0]; +// while(left nums[mid + 1]) + { + return mid; + } + else if (nums[mid] < nums[end]) + { + end = mid - 1; + } + else + { + start = mid + 1; + } + } + + return -1; + } +} \ No newline at end of file diff --git a/Week 03/id_523/LeetCode_153_523.cs b/Week 03/id_523/LeetCode_153_523.cs new file mode 100644 index 000000000..3ea5628a7 --- /dev/null +++ b/Week 03/id_523/LeetCode_153_523.cs @@ -0,0 +1,36 @@ +public class Solution +{ + public int FindMin(int[] nums) + { + int start = 0; + int end = nums.Length - 1; + + while (start < end) + { + int mid = (start + end) / 2; + + if (nums[mid] > nums[mid + 1]) + { + return nums[mid + 1]; + } + else + { + if (nums[start] > nums[mid]) + { + end = mid; + } + else if (nums[mid] > nums[end]) + { + start = mid; + } + else + { + return nums[start]; + + } + } + } + + return nums[start]; + } +} \ No newline at end of file diff --git a/Week 03/id_523/LeetCode_74_523.cs b/Week 03/id_523/LeetCode_74_523.cs new file mode 100644 index 000000000..2db19c71a --- /dev/null +++ b/Week 03/id_523/LeetCode_74_523.cs @@ -0,0 +1,28 @@ +public class Solution +{ + public bool SearchMatrix(int[,] matrix, int target) + { + var row = matrix.GetLength(0); + var column = matrix.GetLength(1); + + if (column == 0) return false; + + for (int i = row - 1; i >= 0; i--) + { + if (target >= matrix[i, 0]) + { + for (int j = 0; j < column; j++) + { + if (matrix[i, j] == target) + { + return true; + } + } + + return false; + } + } + + return false; + } +} \ No newline at end of file diff --git a/Week 03/id_533/LeetCode_102_533.js b/Week 03/id_533/LeetCode_102_533.js new file mode 100644 index 000000000..37e0b7297 --- /dev/null +++ b/Week 03/id_533/LeetCode_102_533.js @@ -0,0 +1,98 @@ +// https://leetcode-cn.com/problems/binary-tree-level-order-traversal/ + +/** + * @param {TreeNode} root + * @return {number[][]} + */ +// BFS解法 +// 时间复杂度O(N) 空间复杂度O(n) +var levelOrder = function (root) { + if (root === null) return []; + var queue = [], + visited = [], + level = 0; + queue.push(root); + while (queue.length > 0) { + if (!visited[level]) visited[level] = []; + var length = queue.length; + for (var i = 0; i < length; i++) { + var cur = queue.shift(); + visited[level].push(cur.val); + if (cur.left) queue.push(cur.left); + if (cur.right) queue.push(cur.right); + } + level++; + } + return visited; +}; + +/** + * @param {TreeNode} root + * @return {number[][]} + */ +// DFS解法(LeetCode上更快) +// 时间复杂度O(n) 空间复杂度O(n) +var levelOrder = function (root) { + var visited = []; + order(root, 0); + return visited; + function order (root, level) { + if (root === null) return; + if (!visited[level]) visited[level] = []; + visited[level].push(root.val); + order(root.left, level + 1); + order(root.right, level + 1); + } +}; + +// 节点 +function TreeNode (val) { + this.val = val; + this.left = null; + this.right = null; +} + +// 二叉搜索树 +function BinarySearchTree () { + this.root = null; +} +// 添加节点 +BinarySearchTree.prototype.insert = function (val) { + if(val === null || val === undefined) return; + var node = new TreeNode(val); + if (!this.root) { + this.root = node; + return; + } + var cur = this._getTreeNode(val); + if (val < cur.val) + cur.left = node; + else + cur.right = node; +} +// 在树中遍历查找可以添加val的节点 +BinarySearchTree.prototype._getTreeNode = function (val, find = false) { + var cur = this.root; + while (true) { + if (val < cur.val) { + if (!cur.left) break; + cur = cur.left; + } + if (val >= cur.val) { + if (!cur.right) break; + cur = cur.right; + } + } + return cur; +} + +var bst = new BinarySearchTree(); +bst.insert(9); +bst.insert(3); +bst.insert(2); +bst.insert(7); +bst.insert(20); +bst.insert(15); +bst.insert(22); + +console.log(levelOrder(bst.root)) \ No newline at end of file diff --git a/Week 03/id_533/LeetCode_122_533.js b/Week 03/id_533/LeetCode_122_533.js new file mode 100644 index 000000000..d29265d7b --- /dev/null +++ b/Week 03/id_533/LeetCode_122_533.js @@ -0,0 +1,36 @@ +// https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/ + +/** + * @param {number[]} prices + * @return {number} + */ +// 方法一 +// 时间复杂度O(n) 空间复杂度O(1) +var maxProfit = function(prices) { + var i = 0, + valley = prices[0], + peak = prices[0], + maxprofit = 0; + while (i < prices.length - 1) { + while (i < prices.length - 1 && prices[i] >= prices[i + 1]) i++; + valley = prices[i]; + while (i < prices.length - 1 && prices[i] <= prices[i + 1]) i++; + peak = prices[i]; + maxprofit += peak - valley; + } + return maxprofit; +}; + +// 方法二 +// 时间复杂度O(n) 空间复杂度O(1) +var maxProfit = function(prices) { + var maxprofit = 0, + length = prices.length - 1; + for (var i = 0; i < length; i++) { + if (prices[i + 1] > prices[i]) maxprofit += prices[i + 1] - prices[i] + } + return maxprofit; +}; + +console.log(maxProfit([7,1,5,3,6,4])); +console.log(maxProfit([7,6,4,3,1])); \ No newline at end of file diff --git a/Week 03/id_533/LeetCode_127_533.js b/Week 03/id_533/LeetCode_127_533.js new file mode 100644 index 000000000..e6d408f1e --- /dev/null +++ b/Week 03/id_533/LeetCode_127_533.js @@ -0,0 +1,39 @@ +// https://leetcode-cn.com/problems/word-ladder/ + +/** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ +// BFS解法 +var ladderLength = function(beginWord, endWord, wordList) { + var queue = [], + words = [], + listSet = new Set(); + for (var i = 0; i < 26; i++) words.push(String.fromCharCode(97 + i)); + while (wordList.length > 0) listSet.add(wordList.shift()); + if (beginWord) queue.push([beginWord, 1]); + while (queue.length > 0) { + var cur = queue.shift(), + times = cur[1]; + cur = cur[0]; + if (cur === endWord) return times; + for (var i = 0; i < cur.length; i++) { + var tmp = cur; + for (var j = 0; j < words.length; j++) { + if (cur[i] === words[j]) continue; + cur = cur.slice(0, i) + words[j] + cur.slice(i + 1); + if (listSet.has(cur)) { + queue.push([cur, times + 1]); + listSet.delete(cur); + } + } + cur = tmp; + } + } + return 0; +}; + +console.log(ladderLength("a", "c", ["a","b","c"])); +console.log(ladderLength("hit", "cog", ["hot","dot","dog","lot","log","cog"])); \ No newline at end of file diff --git a/Week 03/id_533/LeetCode_33_533.js b/Week 03/id_533/LeetCode_33_533.js new file mode 100644 index 000000000..c9f06db80 --- /dev/null +++ b/Week 03/id_533/LeetCode_33_533.js @@ -0,0 +1,32 @@ +// https://leetcode-cn.com/problems/search-in-rotated-sorted-array/ + +/** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ +var search = function(nums, target) { + var left = 0, + right = nums.length - 1; + while (left <= right) { + var mid = left + Math.floor((right - left) / 2); + if (nums[mid] === target) return mid; + // 划分后的数组总有一边是有序的 + if (nums[mid] >= nums[left]) { // 划分后的数组左边有序 + if (target >= nums[left] && target < nums[mid]) + right = mid - 1; + else + left = mid + 1; + } else { // 划分后的数组右边有序 + if (target > nums[mid] && target <= nums[right]) + left = mid + 1; + else + right = mid - 1; + } + } + return -1; +}; + +console.log(search([4,5,6,7,0,1,2], 0)) +console.log(search([4,5,6,7,0,1,2], 3)) + diff --git a/Week 03/id_533/LeetCode_367_533.js b/Week 03/id_533/LeetCode_367_533.js new file mode 100644 index 000000000..ae49987b4 --- /dev/null +++ b/Week 03/id_533/LeetCode_367_533.js @@ -0,0 +1,49 @@ +// https://leetcode-cn.com/problems/valid-perfect-square/ + +/** + * @param {number} num + * @return {boolean} + */ +// 二分查找法 +var isPerfectSquare = function(num) { + var left = 0, + right = num; + while (left < right) { + var mid = left + Math.ceil((right - left) / 2); + var square = mid * mid; + if (square <= num) + left = mid; + else + right = mid - 1; + } + return left * left === num; +}; + +/** + * @param {number} num + * @return {boolean} + */ +// 等差数列法 1 + 3 + 5 + 7 + ... + (2n−1) = n^2 +var isPerfectSquare = function(num) { + var i = 1; + while (num > 0) { + num -= i; + i += 2 + } + return num === 0; +}; + +/** + * @param {number} num + * @return {boolean} + */ +// 牛顿迭代法 +var isPerfectSquare = function(num) { + var r = num; + while (r * r > num) + r = ((r + num / r) / 2) | 0; + return r * r === num; +}; + +console.log(isPerfectSquare(16)); +console.log(isPerfectSquare(14)); \ No newline at end of file diff --git a/Week 03/id_533/LeetCode_433_533.js b/Week 03/id_533/LeetCode_433_533.js new file mode 100644 index 000000000..e307c5450 --- /dev/null +++ b/Week 03/id_533/LeetCode_433_533.js @@ -0,0 +1,48 @@ +// https://leetcode-cn.com/problems/minimum-genetic-mutation/ + +/** + * @param {string} start + * @param {string} end + * @param {string[]} bank + * @return {number} + */ +// BFS解法 +var minMutation = function (start, end, bank) { + var queue = [], + bankSet = new Set(), + genes = ['A', 'C', 'G', 'T']; + while (bank.length > 0) bankSet.add(bank.shift()); + if (start) queue.push([start, 0]) + while (queue.length > 0) { + var cur = queue.shift(), + times = cur[1]; + cur = cur[0] + if (cur === end) return times; + for (var i = 0; i < cur.length; i++) { + var tmp = cur; + for (var j = 0; j < genes.length; j++) { + if (cur[i] === genes[j]) continue; + cur = cur.slice(0, i) + genes[j] + cur.slice(i + 1); + if (bankSet.has(cur)) { + queue.push([cur, times + 1]); + bankSet.delete(cur) + } + } + cur = tmp; + } + } + return -1; +}; + +/** + * @param {string} start + * @param {string} end + * @param {string[]} bank + * @return {number} + */ +// DFS解法 +// var minMutation = function(start, end, bank) { +// }; +console.log(minMutation("AACCGGTT", "AAACGGTA", ["AACCGGTA", "AACCGCTA", "AAACGGTA"])) +console.log(minMutation("AACCGGTT", "AACCGCTA", ["AACCGGTA","AACCGCTA","AAACGGTA"])) + diff --git a/Week 03/id_533/LeetCode_455_533.js b/Week 03/id_533/LeetCode_455_533.js new file mode 100644 index 000000000..53533c544 --- /dev/null +++ b/Week 03/id_533/LeetCode_455_533.js @@ -0,0 +1,21 @@ +// https://leetcode-cn.com/problems/assign-cookies/ + +/** + * @param {number[]} g + * @param {number[]} s + * @return {number} + */ +var findContentChildren = function(g, s) { + var gi = 0, + si = 0; + g.sort(function(a, b) {return a - b}); + s.sort(function(a, b) {return a - b}); + while (gi < g.length && si < s.length) { + if (s[si] >= g[gi]) gi++; + si++ + } + return gi; +}; + +console.log(findContentChildren([1, 2], [1, 2, 3])); +console.log(findContentChildren([10,9,8,7], [5,6,7,8])); \ No newline at end of file diff --git a/Week 03/id_533/LeetCode_515_533.js b/Week 03/id_533/LeetCode_515_533.js new file mode 100644 index 000000000..2243ff37b --- /dev/null +++ b/Week 03/id_533/LeetCode_515_533.js @@ -0,0 +1,96 @@ +// https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/ + +/** + * @param {TreeNode} root + * @return {number[]} + */ +// BFS解法 +// 时间复杂度O(n) 空间复杂度O(logn) +var largestValues = function(root) { + var queue = [], + maxArr = [], + level = 0; + if (root) queue.push(root); + while (queue.length > 0) { + var length = queue.length; + for (var i = 0; i < length; i++) { + var cur = queue.shift(); + maxArr[level] = maxArr[level] === undefined ? cur.val : Math.max(maxArr[level], cur.val) + if (cur.left) queue.push(cur.left); + if (cur.right) queue.push(cur.right); + } + level++; + } + return maxArr; +}; + + +/** + * @param {TreeNode} root + * @return {number[]} + */ +// DFS解法(LeetCode上更快) +// 时间复杂度O(n) 空间复杂度O(logn) +var largestValues = function(root) { + var maxArr = []; + largest(root, 0) + return maxArr; + function largest (root, level) { + if (root === null) return root; + maxArr[level] = maxArr[level] === undefined ? root.val : Math.max(maxArr[level], root.val); + largest(root.left, level + 1); + largest(root.right, level + 1); + } +}; + +// 节点 +function TreeNode (val) { + this.val = val; + this.left = null; + this.right = null; +} + +// 二叉搜索树 +function BinarySearchTree () { + this.root = null; +} +// 添加节点 +BinarySearchTree.prototype.insert = function (val) { + if(val === null || val === undefined) return; + var node = new TreeNode(val); + if (!this.root) { + this.root = node; + return; + } + var cur = this._getTreeNode(val); + if (val < cur.val) + cur.left = node; + else + cur.right = node; +} +// 在树中遍历查找可以添加val的节点 +BinarySearchTree.prototype._getTreeNode = function (val, find = false) { + var cur = this.root; + while (true) { + if (val < cur.val) { + if (!cur.left) break; + cur = cur.left; + } + if (val >= cur.val) { + if (!cur.right) break; + cur = cur.right; + } + } + return cur; +} + +var bst = new BinarySearchTree(); +bst.insert(9); +bst.insert(3); +bst.insert(2); +bst.insert(7); +bst.insert(20); +bst.insert(15); +bst.insert(22); + +console.log(largestValues(bst.root)); \ No newline at end of file diff --git a/Week 03/id_533/LeetCode_55_533.js b/Week 03/id_533/LeetCode_55_533.js new file mode 100644 index 000000000..c27e07ea1 --- /dev/null +++ b/Week 03/id_533/LeetCode_55_533.js @@ -0,0 +1,24 @@ +// https://leetcode-cn.com/problems/jump-game/ + +/** + * @param {number[]} nums + * @return {boolean} + */ +// 贪心 +// 时间复杂度O(n) 空间复杂度O(1) +var canJump = function(nums) { + var lastPos = nums.length - 1; + for (var i = nums.length - 2; i >= 0; i--) { + if (i + nums[i] >= lastPos) lastPos = i; + } + return lastPos === 0; +}; + +lastPos = 1 + +// 0, 1 +// 2, 0 + +console.log(canJump([2, 3, 1, 1, 4])) +console.log(canJump([3, 2, 1, 0, 4])) +console.log(canJump([2, 0])) \ No newline at end of file diff --git a/Week 03/id_533/LeetCode_69_533.js b/Week 03/id_533/LeetCode_69_533.js new file mode 100644 index 000000000..d5dd0bfe5 --- /dev/null +++ b/Week 03/id_533/LeetCode_69_533.js @@ -0,0 +1,39 @@ +// https://leetcode-cn.com/problems/sqrtx/ + +/** + * @param {number} x + * @return {number} + */ +// 二分法 +// 时间复杂度O(logn) 空间复杂度O(1) +var mySqrt = function(x) { + var left = 0, + right = x; + while(left < right) { + var mid = left + Math.ceil((right - left) / 2); + var square = mid * mid; + if (square <= x) + left = mid + else + right = mid - 1 + } + return left; +}; + +/** + * @param {number} x + * @return {number} + */ +// 牛顿迭代法 +var mySqrt = function(x) { + var r = x; + while (r * r > x) { + r = ((r + x / r) / 2) | 0 + } + return r; +}; + +console.log(mySqrt(0)) +console.log(mySqrt(1)) +console.log(mySqrt(8)) +console.log(mySqrt(9)) \ No newline at end of file diff --git a/Week 03/id_533/LeetCode_74_533.js b/Week 03/id_533/LeetCode_74_533.js new file mode 100644 index 000000000..bfe1d7dd3 --- /dev/null +++ b/Week 03/id_533/LeetCode_74_533.js @@ -0,0 +1,28 @@ +// https://leetcode-cn.com/problems/search-a-2d-matrix/ + +/** + * @param {number[][]} matrix + * @param {number} target + * @return {boolean} + */ +var searchMatrix = function(matrix, target) { + if (!matrix.length) return false; + var m = matrix.length, + n = matrix[0].length, + left = 0, + right = m * n - 1; + while (left <= right) { + var mid = left + (Math.floor((right - left) / 2)), + midValue = matrix[Math.floor(mid / n)][mid % n]; + if (target === midValue) return true; + if (target > midValue) + left = mid + 1; + else + right = mid - 1; + } + return false; +}; + +var matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,50]]; +console.log(searchMatrix(matrix, 3)); +console.log(searchMatrix(matrix, 13)); \ No newline at end of file diff --git a/Week 03/id_533/LeetCode_860_533.js b/Week 03/id_533/LeetCode_860_533.js new file mode 100644 index 000000000..74ebd107e --- /dev/null +++ b/Week 03/id_533/LeetCode_860_533.js @@ -0,0 +1,32 @@ +// https://leetcode-cn.com/problems/lemonade-change/ + +/** + * @param {number[]} bills + * @return {boolean} + */ +var lemonadeChange = function(bills) { + var five = 0, + ten = 0; + for (bill of bills) { + if (bill === 5) { + five++; + } else if (bill === 10) { + if (five === 0) return false; + five--; + ten++; + } else { + if (five > 0 && ten > 0) { + five--; + ten--; + } else if(five >= 3) { + five -= 3; + } else { + return false; + } + } + } + return true; +}; + +console.log(lemonadeChange([5,5,5,10,20])) +console.log(lemonadeChange([5,5,10,10,20])) \ No newline at end of file diff --git a/Week 03/id_538/LeetCode_153_538.java b/Week 03/id_538/LeetCode_153_538.java new file mode 100644 index 000000000..4103fa313 --- /dev/null +++ b/Week 03/id_538/LeetCode_153_538.java @@ -0,0 +1,24 @@ +class Solution { + public int findMin(int[] nums) { + if (nums.length == 0){ + return -1; + } + int low = 0; + int height = nums.length - 1; + int middle = 0; + int min = nums[0]; + //效率很低啊,不过时间太晚了.明天要上班了 先这样吧 + while (low <= height){ + middle = low + (height - low) / 2; + min = Math.min(min,nums[middle]); + if (nums[middle] >= nums[low] && nums[height] < nums[low]){ + min = Math.min(min,nums[height]); + low = middle + 1; + }else { + min = Math.min(min,nums[low]); + height = middle - 1; + } + } + return min; + } +} diff --git a/Week 03/id_538/LeetCode_33_538.java b/Week 03/id_538/LeetCode_33_538.java new file mode 100644 index 000000000..6bc5c4d40 --- /dev/null +++ b/Week 03/id_538/LeetCode_33_538.java @@ -0,0 +1,28 @@ +class Solution { + public int search(int[] nums, int target) { + int low = 0; + int height = nums.length - 1; + + while(low <= height){ + int middle = (low + height) / 2; + if (nums[middle] == target){ + return middle; + } + //左半有序 + if(nums[low] <= nums[middle]){ + if(target >= nums[low] && target < nums[middle]){ + height = middle - 1; + }else{ + low = middle + 1; + } + }else {//右半有序 + if (target > nums[middle] && target <= nums[height]){ + low = middle + 1; + }else { + height = middle - 1; + } + } + } + return -1; + } +} diff --git a/Week 03/id_538/LeetCode_74_538.java b/Week 03/id_538/LeetCode_74_538.java new file mode 100644 index 000000000..8b01bbed4 --- /dev/null +++ b/Week 03/id_538/LeetCode_74_538.java @@ -0,0 +1,49 @@ +class Solution { + public boolean searchMatrix(int[][] matrix, int target) { + //terminal codintion 一维为空边界 + if (matrix.length == 0){ + return false; + } + int low = 0; + int height = matrix.length - 1; + //terminal codintion 二维为空边界 + if (matrix[low].length == 0){ + return false; + } + //先用二分法确定一维的位置 + while (low <= height){ + int middle = low + (height - low) / 2; + int x = 0; + int y = matrix[middle].length - 1; + int mid = x + (y - x) / 2; + //总是要碰下运气的,万一中了呢 + if(matrix[middle][mid] == target){ + return true; + } + //先确定一维数组的位置 + if (matrix[middle][y] < target){ + low = middle + 1; + }else if (matrix[middle][x] > target){ + height = middle - 1; + }else { + //确定二维数组的位置 + while (x <= y){ + mid = x + (y - x) / 2; + //返回结果 + if (matrix[middle][mid] == target){ + return true; + } + if (matrix[middle][mid] > target){ + y = mid - 1; + }else { + x = mid + 1; + } + } + //这里需要跳出一维的循环 + break; + } + } + //数组内不包含要寻找的元素 + return false; + } +} diff --git a/Week 03/id_543/LeetCode_127_543.java b/Week 03/id_543/LeetCode_127_543.java new file mode 100644 index 000000000..704acd8e1 --- /dev/null +++ b/Week 03/id_543/LeetCode_127_543.java @@ -0,0 +1,64 @@ +class Solution { + + //单向BFS 提交超时 + public int ladderLength(String beginWord, String endWord, List wordList) { + Set levelvisitSet = new HashSet(); + int length = 1; + levelvisitSet.add(beginWord); + while (!levelvisitSet.contains(endWord)){ + Set temSet = new HashSet(); + for(String word:levelvisitSet){ + for (int i = 0;i wordList) { + Set levelvisitSet = new HashSet(); + Set wordSet = new HashSet(wordList); + int length = 1; + levelvisitSet.add(beginWord); + while (!levelvisitSet.contains(endWord)){ + Set temSet = new HashSet(); + for(String word:levelvisitSet){ + for (int i = 0;i = nums[begin] && (nums[mid] < target || target < nums[begin])){ + begin = mid + 1; + }else if(target > nums[mid] && target < nums[begin]){ + begin = mid + 1; + }else{ + end = mid -1; +// end = mid; + } + } + return -1; + } + + 1.如果bgein<=end end=mid-1 + 2.如果begin=1){ + five--; + ten++; + }else { + return false; + } + } + if(pay == 20){ + if(five>=1&&ten>=1){ + five--; + ten--; + }else if(five>=3){ + five-=3; + }else { + return false; + } + } + } + return true; + } +} \ No newline at end of file diff --git a/Week 03/id_548/LeetCode_102_548.java b/Week 03/id_548/LeetCode_102_548.java new file mode 100644 index 000000000..a9f908a04 --- /dev/null +++ b/Week 03/id_548/LeetCode_102_548.java @@ -0,0 +1,113 @@ +import jdk.nashorn.internal.runtime.options.Option; +import net.minidev.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public class LeetCode_102_548 { + + + List > levels = new ArrayList > (); + + /** + * + * 给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。 + * + * 例如: + * 给定二叉树: [3,9,20,null,null,15,7], + * + * 3 + * / \ + * 9 20 + * / \ + * 15 7 + * 返回其层次遍历结果: + * + * [ + * [3], + * [9,20], + * [15,7] + * ] + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @param args + */ + + + /** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + public static void main(String[] args) { + TreeNode treeNode1 = new TreeNode ( 3 ); + TreeNode treeNode2 = new TreeNode ( 9 ); + TreeNode treeNode3 = new TreeNode ( 20 ); + TreeNode treeNode6 = new TreeNode ( 15 ); + TreeNode treeNode7 = new TreeNode ( 7 ); + + treeNode1.left = treeNode2; + treeNode1.right = treeNode3; + treeNode3.left = treeNode6; + treeNode3.right = treeNode7; + + + LeetCode_102_548 levelOrder = new LeetCode_102_548 (); + + List > levels = levelOrder.levelOrder ( treeNode1 ); + + System.out.println ( "levels: " + levels.toString () ); + + + } + + /** + * 1。输出列表称为 levels。 结构是 ArrayList>(); + *

+ * 2。 比较访问节点所在的层次 level 和当前最高层次 len(levels) 的大小 + * 当访问下一层的时候,level>len(levels)的时候。向level添加一个新的列表 + *

+ * 3。递归遍历左右子树 helper(node.left/node.rigth, level+1)(访问下一层) + */ + + + public List > levelOrder(TreeNode root) { + if (root == null) { + return levels; + } + helper ( root, 1 ); + return levels; + } + + + public void helper(TreeNode node, int level) { + + //1.比较访问节点所在的层次 level 和当前最高层次 len(levels) 的大小, 当访问下一层的时候,level>len(levels)的时候。向level添加一个新的列表 + if (levels.size () < level) { + levels.add ( new ArrayList () ); + } + + //2. 把当前节点的值,保留到本层 + levels.get ( level - 1 ).add ( node.val ); + + //遍历左右子树 + if (node.left != null) { + helper ( node.left, level + 1 ); + } + + if (node.right != null) { + helper ( node.right, level + 1 ); + } + + + } + + +} diff --git a/Week 03/id_548/LeetCode_860_548.java b/Week 03/id_548/LeetCode_860_548.java new file mode 100644 index 000000000..36d7bc0d4 --- /dev/null +++ b/Week 03/id_548/LeetCode_860_548.java @@ -0,0 +1,29 @@ +public class LeetCode_860_548 { + + + public boolean lemonadeChange(int[] bills) { + int five = 0, ten = 0; + for (int bill: bills) { + if (bill == 5) + five++; + else if (bill == 10) { + if (five == 0) return false; + five--; + ten++; + } else { + if (five > 0 && ten > 0) { + five--; + ten--; + } else if (five >= 3) { + five -= 3; + } else { + return false; + } + } + } + + return true; + } + + +} diff --git a/Week 03/id_558/LeetCode_102_558.java b/Week 03/id_558/LeetCode_102_558.java new file mode 100644 index 000000000..aefb57b60 --- /dev/null +++ b/Week 03/id_558/LeetCode_102_558.java @@ -0,0 +1,112 @@ +package Week03; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * @Date 2019/10/28. + * @see https://leetcode-cn.com/problems/binary-tree-level-order-traversal/#/description + * 二叉树的层次遍历 + */ +public class LeetCode_102_558 { + + /** + * 思路: + * 方法一:广度优先 + * 1、先遍历本层,把下一级元素存储起来 + * 2、递归遍历下一级元素 + * 方法二:深度优先 + * 递归遍历时传入层级 + */ + public static List> levelOrder(TreeNode root) { + List> lists = new ArrayList>(); + ArrayList nodes = new ArrayList(); + if (root != null) { + nodes.add(root); + } + traversal(lists, nodes); + return lists; + } + + + private static void traversal(List> lists, ArrayList nodes) { + if (nodes == null || nodes.size() <= 0) { + return; + } + List list = new ArrayList(); + ArrayList currentLevelNodes = new ArrayList(); + for (int i = 0; i < nodes.size(); i++) { + TreeNode node = nodes.get(i); + list.add(node.val); + if (node.left != null) { + currentLevelNodes.add(node.left); + } + if (node.right != null) { + currentLevelNodes.add(node.right); + } + } + lists.add(list); + traversal(lists, currentLevelNodes); + } + + + public static List> levelOrder2(TreeNode root) { + List> lists = new ArrayList>(); + if (root == null) { + return lists; + } + Queue> queue = new LinkedList>(); + ArrayList nodes = new ArrayList(); + nodes.add(root); + queue.add(nodes); + while (!queue.isEmpty()) { + List currentLevelNodes = queue.remove(); + ArrayList nodeList = new ArrayList(); + List list = new ArrayList(); + for (TreeNode node : currentLevelNodes) { + list.add(node.val); + if (node.left != null) { + nodeList.add(node.left); + } + if (node.right != null) { + nodeList.add(node.right); + } + } + if (list.size() > 0) { + lists.add(list); + } + if (nodeList.size() > 0) { + queue.add(nodeList); + } + } + return lists; + } + + static class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + + public static void main(String[] args) { + TreeNode treeNode3 = new TreeNode(3); + TreeNode treeNode9 = new TreeNode(9); + TreeNode treeNode20 = new TreeNode(20); + TreeNode treeNode15 = new TreeNode(15); + TreeNode treeNode7 = new TreeNode(7); + + treeNode3.left = treeNode9; + treeNode3.right = treeNode20; + + treeNode20.left = treeNode15; + treeNode20.right = treeNode7; + + System.out.println(levelOrder2(treeNode3)); + } +} diff --git a/Week 03/id_558/LeetCode_153_558.java b/Week 03/id_558/LeetCode_153_558.java new file mode 100644 index 000000000..f58a7c66d --- /dev/null +++ b/Week 03/id_558/LeetCode_153_558.java @@ -0,0 +1,37 @@ +package Week03; + +/** + * @Date 2019/11/3. + * @see https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/ + * 寻找旋转排序数组中的最小值 + */ +public class LeetCode_153_558 { + + public int findMin(int[] nums) { + if (nums.length == 1 || nums[0] < nums[nums.length - 1]) { + return nums[0]; + } + int left = 0; + int right = nums.length - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + if (nums[mid] > nums[mid + 1]) { + return nums[mid + 1]; + } + if (nums[mid] < nums[mid - 1]) { + return nums[mid]; + } + if (nums[left] > nums[mid]) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return -1; + } + + public static void main(String[] args) { + + } + +} diff --git a/Week 03/id_558/LeetCode_455_558.java b/Week 03/id_558/LeetCode_455_558.java new file mode 100644 index 000000000..26fd492bd --- /dev/null +++ b/Week 03/id_558/LeetCode_455_558.java @@ -0,0 +1,31 @@ +package Week03; + +import java.util.Arrays; + +/** + * @Date 2019/11/1. + * @see https://leetcode-cn.com/problems/assign-cookies/description/ + * 分发饼干 + */ +public class LeetCode_455_558 { + /** + * 步骤: + * 1、先排序 + * 2、遍历 + */ + public int findContentChildren(int[] g, int[] s) { + Arrays.sort(g); + Arrays.sort(s); + int ret = 0; + for (int i = 0, j = 0; i < g.length && j < s.length; ) { + if (g[i] <= s[j]) { + i++; + j++; + ret++; + } else { + j++; + } + } + return ret; + } +} diff --git a/Week 03/id_558/LeetCode_55_558.java b/Week 03/id_558/LeetCode_55_558.java new file mode 100644 index 000000000..56cb01684 --- /dev/null +++ b/Week 03/id_558/LeetCode_55_558.java @@ -0,0 +1,24 @@ +package Week03; + +/** + * @Date 2019/11/2. + * @see https://leetcode-cn.com/problems/jump-game/ + * 跳跃游戏 + */ +public class LeetCode_55_558 { + /** + * 核心步骤: + * 1、逆向(从后向前遍历) + * 2、nums[i] + i 当前位置最多可以跳的步数 + */ + public boolean canJump(int[] nums) { + if (nums == null) return false; + int endIndex = nums.length - 1; + for (int i = nums.length - 2; i >= 0; i--) { + if (nums[i] + i >= endIndex) { + endIndex = i; + } + } + return endIndex == 0; + } +} diff --git a/Week 03/id_558/LeetCode_69_558.java b/Week 03/id_558/LeetCode_69_558.java new file mode 100644 index 000000000..d112d899f --- /dev/null +++ b/Week 03/id_558/LeetCode_69_558.java @@ -0,0 +1,39 @@ +package Week03; + +/** + * @Date 2019/10/30. + * @see https://leetcode-cn.com/problems/sqrtx/submissions/ + * x 的平方根 + */ +public class LeetCode_69_558 { + + /** + * 注意事项: + * 1、数值越界 mid = left + ((right - left) / 2) + * square = mid * mid + * 2、二分查找边界处理 + */ + public static int mySqrt(int x) { + if (x == 0 || x == 1) { + return x; + } + long left = 1; + long right = x; + while (left <= right) { + long middle = left + ((right - left) / 2); + if (middle * middle == x) { + return (int) middle; + } else if (middle * middle > x) { + right = middle - 1; + } else { + left = middle + 1; + } + } + return (int) right; + } + + public static void main(String[] args) { + System.out.println(mySqrt(2147395599)); + } + +} diff --git a/Week 03/id_558/LeetCode_74_558.java b/Week 03/id_558/LeetCode_74_558.java new file mode 100644 index 000000000..480f18db3 --- /dev/null +++ b/Week 03/id_558/LeetCode_74_558.java @@ -0,0 +1,32 @@ +package Week03; + +/** + * @Date 2019/11/2. + * @see https://leetcode-cn.com/problems/search-a-2d-matrix/ + * 搜索二维矩阵 + */ +public class LeetCode_74_558 { + /** + * 核心: + * 二维矩阵中间值坐标:matrix[pivotIdx / n][pivotIdx % n] + */ + public boolean searchMatrix(int[][] matrix, int target) { + int m = matrix.length; + if (m == 0) return false; + int n = matrix[0].length; + // 二分查找 + int left = 0, right = m * n - 1; + int pivotIdx, pivotElement; + while (left <= right) { + pivotIdx = (left + right) / 2; + pivotElement = matrix[pivotIdx / n][pivotIdx % n]; + if (target == pivotElement) return true; + else { + if (target < pivotElement) right = pivotIdx - 1; + else left = pivotIdx + 1; + } + } + return false; + + } +} diff --git a/Week 03/id_558/LeetCode_860_558.java b/Week 03/id_558/LeetCode_860_558.java new file mode 100644 index 000000000..f4cd3a770 --- /dev/null +++ b/Week 03/id_558/LeetCode_860_558.java @@ -0,0 +1,39 @@ +package Week03; + +/** + * @Date 2019/11/2. + * @see https://leetcode-cn.com/problems/lemonade-change/description/ + * 柠檬水找零 + */ +public class LeetCode_860_558 { + /** + * 思维: + * 1、贪心算法只考虑当前步骤 + * 2、分类 + */ + public boolean lemonadeChange(int[] bills) { + int five = 0, ten = 0; + for (int bill : bills) { + if (bill == 5) { + five++; + } else if (bill == 10) { + if (five < 1) return false; + five--; + ten++; + } else if (bill == 20) { + if (five > 0 && ten > 0) { + five--; + ten--; + } else if (five >= 3) { + five -= 3; + } else { + return false; + } + } else { + return false; + } + } + return true; + } + +} diff --git a/Week 03/id_558/NOTE.md b/Week 03/id_558/NOTE.md index a6321d6e2..84930a5c8 100644 --- a/Week 03/id_558/NOTE.md +++ b/Week 03/id_558/NOTE.md @@ -1,4 +1,44 @@ # NOTE +#### 知识回顾 +* 算法思想:贪心算法 +* 算法:深度(DFS)、广度(BFS)搜索,二分查找 +* 二分查找 + * 前提 + 1. 目标函数单调性(单调递增或者递减) + 2. 存在上下界(bounded) + 3. 能够通过索引访问(index accessible) + * 代码模板 + ``` + int left = 0; + int right = arr.length - 1; + while(left <= right){ + int mid = left + (right - left) / 2; + if(target == arr[mid]){ + return mid; + }else if(target > arr[mid]){ + left = mid + 1; + }else{ + right = mid - 1; + } + } + ``` +#### 处理输出 + +* [二叉树层次遍历](https://leetcode-cn.com/problems/binary-tree-level-order-traversal/#/description) +* [柠檬水找零](https://leetcode-cn.com/problems/lemonade-change/description/) +* [分发饼干](https://leetcode-cn.com/problems/assign-cookies/description/) +* [跳跃游戏](https://leetcode-cn.com/problems/jump-game/) +* [x的平方根](https://leetcode-cn.com/problems/sqrtx/) +* [搜索旋转排序数组](https://leetcode-cn.com/problems/search-in-rotated-sorted-array/) +* [搜索二维矩阵](https://leetcode-cn.com/problems/search-a-2d-matrix/) +* [寻找旋转排序数组中的最小值](https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/) + +#### 遗留问题 + * 深度优先非递归实现 + +#### 总结改进 +* 题目分类,总结解题方法 +* 每日练习,写解题思路 diff --git a/Week 03/id_563/Leecode_153_563.go b/Week 03/id_563/Leecode_153_563.go new file mode 100644 index 000000000..3a7dfbefc --- /dev/null +++ b/Week 03/id_563/Leecode_153_563.go @@ -0,0 +1,19 @@ +func findMin(nums []int) int { + if len(nums) == 0 { + return -1 + } + i, j := 0, len(nums) - 1 + + for i <= j{ + if nums[i] <= nums[j] { + return nums[i] + } + mid := i + (j - i)/2 + if nums[mid] >= nums[i] { + i = mid + 1 + } else { + j = mid + } + } + return -1 +} \ No newline at end of file diff --git a/Week 03/id_563/Leetcode_33_563.go b/Week 03/id_563/Leetcode_33_563.go new file mode 100644 index 000000000..67c3459d4 --- /dev/null +++ b/Week 03/id_563/Leetcode_33_563.go @@ -0,0 +1,39 @@ +/* +* 根据老师讲的正解 +*/ + + +func search(nums []int, target int) int { + n := len(nums) + l := 0 + r := n - 1 + + for l <= r { + if nums[l] == target { + return l + } + + if nums[r] == target { + return r + } + + mid := (l + r) / 2 + + if nums[mid] == target { + return mid + } + + if nums[l] > nums[r] { + l += 1 + r -= 1 + } else { + if nums[mid] > target { + r = mid - 1 + } else { + l = mid + 1 + } + } + } + + return -1 +} \ No newline at end of file diff --git a/Week 03/id_563/Leetcode_74_563.go b/Week 03/id_563/Leetcode_74_563.go new file mode 100644 index 000000000..3f17790fc --- /dev/null +++ b/Week 03/id_563/Leetcode_74_563.go @@ -0,0 +1,78 @@ +/** +* 方法1 +* 比较好理解的 +*/ + +func searchMatrix(matrix [][]int, target int) bool { + l := len(matrix) + if l == 0 { + return false + } + + small := 0 + large := l * len(matrix[0]) -1 + + for small <= large { + mid := (small + large)/2 + row := mid / len(matrix[0]) + col := mid % len(matrix[0]) + + val := matrix[row][col] + + if val == target { + return true + } + + if val > target { + large = mid - 1 + } else { + small = mid + 1 + } + } + + return false +} + +/** + * 方法2 + * 二分查找 + */ + +func searchMatrix(matrix [][]int, target int) bool { + if len(matrix) == 0 || len(matrix[0]) == 0 { + return false + } + + n := len(matrix) + m := len(matrix[0]) + if target < matrix[0][0] || target > matrix[n-1][m-1] { + return false + } + + var mid int + low, high := 0, n-1 + for low <= high { + mid = (low + high) / 2 + if matrix[mid][0] > target { + high = mid - 1 + } else if matrix[mid][0] < target { + low = mid + 1 + } else { + return true + } + } + + left, right := 0, m-1 + for left <= right { + mid = (left + right) / 2 + if matrix[high][mid] > target { + right = mid - 1 + } else if matrix[high][mid] < target { + left = mid + 1 + } else { + return true + } + } + + return false +} \ No newline at end of file diff --git a/Week 03/id_563/Leetcode_860_563.go b/Week 03/id_563/Leetcode_860_563.go new file mode 100644 index 000000000..713288e69 --- /dev/null +++ b/Week 03/id_563/Leetcode_860_563.go @@ -0,0 +1,32 @@ +func lemonadeChange(bills []int) bool { + if (bills[0] != 5) { + return false + } + + change := make(map[int]int) + for _, each := range bills { + switch each { + case 5: + change[5] ++ + case 10: + if change[5] > 0 { + change[10] ++ + change[5] -- + } + case 20: + if change[10] > 0 && change[5] > 0 { + change[20] ++ + change[10] -- + change[5] -- + } else if change[5] > 2{ + change[20] ++ + change[5] -= 3 + } else { + return false + } + default: + return false + } + } + return true +} \ No newline at end of file diff --git a/Week 03/id_568/LeetCode_127_568.java b/Week 03/id_568/LeetCode_127_568.java new file mode 100644 index 000000000..36727f483 --- /dev/null +++ b/Week 03/id_568/LeetCode_127_568.java @@ -0,0 +1,47 @@ +import javafx.util.Pair; + +import java.util.*; + +/** + * @author kelvin + * @date 2019/11/3 9:46 PM + */ +public class LeetCode_127_568 { + public int ladderLength(String beginWord, String endWord, List wordList) { + int L = beginWord.length(); + HashMap> allComboDict = new HashMap>(); + wordList.forEach( + word -> { + for (int i = 0; i < L; i++) { + String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L); + ArrayList transformations = + allComboDict.getOrDefault(newWord, new ArrayList()); + transformations.add(word); + allComboDict.put(newWord, transformations); + } + }); + Queue> Q = new LinkedList>(); + Q.add(new Pair(beginWord, 1)); + HashMap visited = new HashMap(); + visited.put(beginWord, true); + while (!Q.isEmpty()) { + Pair node = Q.remove(); + String word = node.getKey(); + int level = node.getValue(); + for (int i = 0; i < L; i++) { + String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L); + for (String adjacentWord : allComboDict.getOrDefault(newWord, new ArrayList())) { + if (adjacentWord.equals(endWord)) { + return level + 1; + } + if (!visited.containsKey(adjacentWord)) { + visited.put(adjacentWord, true); + Q.add(new Pair(adjacentWord, level + 1)); + } + } + } + } + return 0; + } + +} diff --git a/Week 03/id_568/LeetCode_33_568.java b/Week 03/id_568/LeetCode_33_568.java new file mode 100644 index 000000000..e47ee7cfb --- /dev/null +++ b/Week 03/id_568/LeetCode_33_568.java @@ -0,0 +1,70 @@ +/** + * @author kelvin + * @date 2019/11/3 9:32 PM + */ +public class LeetCode_33_568 { + int[] nums; + int target; + + public int find_rotate_index(int left, int right) { + if (nums[left] < nums[right]) + return 0; + + while (left <= right) { + int pivot = (left + right) / 2; + if (nums[pivot] > nums[pivot + 1]) + return pivot + 1; + else { + if (nums[pivot] < nums[left]) + right = pivot - 1; + else + left = pivot + 1; + } + } + return 0; + } + + public int search(int left, int right) { + /* + Binary search + */ + while (left <= right) { + int pivot = (left + right) / 2; + if (nums[pivot] == target) + return pivot; + else { + if (target < nums[pivot]) + right = pivot - 1; + else + left = pivot + 1; + } + } + return -1; + } + + public int search(int[] nums, int target) { + this.nums = nums; + this.target = target; + + int n = nums.length; + + if (n == 0) { + return -1; + } + if (n == 1) { + return this.nums[0] == target ? 0 : -1; + } + int rotate_index = find_rotate_index(0, n - 1); + + if (nums[rotate_index] == target) { + return rotate_index; + } + if (rotate_index == 0) { + return search(0, n - 1); + } + if (target < nums[0]) { + return search(rotate_index, n - 1); + } + return search(0, rotate_index); + } +} diff --git a/Week 03/id_568/LeetCode_860_568.java b/Week 03/id_568/LeetCode_860_568.java new file mode 100644 index 000000000..45b806ad4 --- /dev/null +++ b/Week 03/id_568/LeetCode_860_568.java @@ -0,0 +1,34 @@ +/** + * @author kelvin + * @date 2019/11/3 9:36 PM + */ +public class LeetCode_860_568 { + public boolean lemonadeChange(int[] bills) { + int c5 = 0; + int c10 = 0; + for (int bill : bills) { + if (bill == 5) + c5 += 5; + else if (bill == 10) { + if (c5 != 0) { + c5 -= 5; + c10 += 10; + } else { + return false; + } + + } else if (c10 != 0) { + if (c5 != 0) { + c5 -= 5; + c10 -= 10; + } else return false; + } else if (c5 >= 15) { + c5 -= 15; + } else { + return false; + } + } + return true; + } + +} diff --git a/Week 03/id_573/NOTE.md b/Week 03/id_573/NOTE.md index a6321d6e2..2547f40b7 100644 --- a/Week 03/id_573/NOTE.md +++ b/Week 03/id_573/NOTE.md @@ -1,4 +1,33 @@ -# NOTE +# NOTE + +#### 寻找旋转排序数组中的最小值 + +#### 思路: + + 旋转排序数组 numsnums 可以被拆分为 2 个排序数组 nums1nums1 , nums2nums2 ,并且 nums1任一元素 >= nums2任一元素;因此,考虑二分法寻找此两数组的分界点 nums[i]nums[i] (即第 2 个数组的首个元素)。 + 设置 leftleft, rightright 指针在 numsnums 数组两端,midmid 为每次二分的中点: + 当 nums[mid] > nums[right]时,midmid 一定在第 1 个排序数组中,ii 一定满足 mid < i <= right,因此执行 left = mid + 1; + 当 nums[mid] < nums[right] 时,midmid 一定在第 2 个排序数组中,ii 一定满足 left < i <= mid,因此执行 right = mid; + 当 nums[mid] == nums[right] 时,是此题对比 153题 的难点(原因是此题中数组的元素可重复,难以判断分界点 ii 指针区间); + 例如 [1, 0, 1, 1, 1][1,0,1,1,1] 和 [1, 1, 1, 0, 1][1,1,1,0,1] ,在 left = 0, right = 4, mid = 2 时,无法判断 midmid 在哪个排序数组中。 + 我们采用 right = right - 1 解决此问题,证明: + 此操作不会使数组越界:因为迭代条件保证了 right > left >= 0; + 此操作不会使最小值丢失:假设 nums[right]nums[right] 是最小值,有两种情况: + 若 nums[right]nums[right] 是唯一最小值:那就不可能满足判断条件 nums[mid] == nums[right],因为 mid < right(left != right 且 mid = (left + right) // 2 向下取整); + 若 nums[right]nums[right] 不是唯一最小值,由于 mid < right 而 nums[mid] == nums[right],即还有最小值存在于 [left, right - 1][left,right−1] 区间,因此不会丢失最小值。 + + 以上是理论分析,可以代入以下数组辅助思考: + [1, 2, 3][1,2,3] + [1, 1, 0, 1][1,1,0,1] + [1, 0, 1, 1, 1][1,0,1,1,1] + [1, 1, 1, 1][1,1,1,1] + 时间复杂度 O(logN)O(logN),在特例情况下会退化到 O(N)O(N)(例如 [1, 1, 1, 1][1,1,1,1])。 + 旋转排序数组nums可以被拆分为2个排序数组nums1, nums2,并且nums1中所有元素比nums2大(因为nums中没有重复值); + 因此,考虑二分法寻找值nums[i],满足nums[i] < nums[i-1] and nums[i] < nums[i+1] + 设置left, right指针在nums数组两端,mid为中点: + 当nums[mid] > nums[right]时,一定满足mid < i <= right,因此left = mid + 1; + 当nums[mid] < nums[right]时,一定满足left< i <= mid,因此right = mid; + 当nums[mid] == nums[right]时,说明数组长度len(num) == 1(因为计算mid向下取整);当left = right也满足,但本题left == right时跳出循环。 diff --git a/Week 03/id_573/leetcode_153_573.java b/Week 03/id_573/leetcode_153_573.java new file mode 100644 index 000000000..5ac5ccaeb --- /dev/null +++ b/Week 03/id_573/leetcode_153_573.java @@ -0,0 +1 @@ +public class SolutionThree { public int findMin(int[] nums) { // If the list has just one element then return that element. if (nums.length == 1) { return nums[0]; } // initializing left and right pointers. int left = 0, right = nums.length - 1; // if the last element is greater than the first element then there is no rotation. // e.g. 1 < 2 < 3 < 4 < 5 < 7. Already sorted array. // Hence the smallest element is first element. A[0] if (nums[right] > nums[0]) { return nums[0]; } // Binary search way while (right >= left) { // Find the mid element int mid = left + (right - left) / 2; // if the mid element is greater than its next element then mid+1 element is the smallest // This point would be the point of change. From higher to lower value. if (nums[mid] > nums[mid + 1]) { return nums[mid + 1]; } // if the mid element is lesser than its previous element then mid element is the smallest if (nums[mid - 1] > nums[mid]) { return nums[mid]; } // if the mid elements value is greater than the 0th element this means // the least value is still somewhere to the right as we are still dealing with elements // greater than nums[0] if (nums[mid] > nums[0]) { left = mid + 1; } else { // if nums[0] is greater than the mid value then this means the smallest value is somewhere to // the left right = mid - 1; } } return -1; } public int findMinOne(int[] nums) { int left = 0; int right = nums.length - 1; while (left < right) { int mid = left + (right - left) / 2; if (nums[mid] > nums[right]) { left = mid + 1; } else { right = mid; } } return nums[left]; } public static void main(String[] args) { int[] integers = new int[]{4, 5, 6, 7, 8, 9, 0, 1, 2, 3}; SolutionThree solution = new SolutionThree(); System.out.println(solution.findMin(integers)); System.out.println(solution.findMinOne(integers)); } } \ No newline at end of file diff --git a/Week 03/id_573/leetcode_33_573.java b/Week 03/id_573/leetcode_33_573.java new file mode 100644 index 000000000..3433a9ba5 --- /dev/null +++ b/Week 03/id_573/leetcode_33_573.java @@ -0,0 +1 @@ +public class SolutionThree { int[] nums; int target; public int find_rotate_index(int left, int right) { if (nums[left] < nums[right]) return 0; while (left <= right) { int pivot = (left + right) / 2; if (nums[pivot] > nums[pivot + 1]) return pivot + 1; else { if (nums[pivot] < nums[left]) right = pivot - 1; else left = pivot + 1; } } return 0; } public int search(int left, int right) { /* Binary search */ while (left <= right) { int pivot = (left + right) / 2; if (nums[pivot] == target) return pivot; else { if (target < nums[pivot]) right = pivot - 1; else left = pivot + 1; } } return -1; } public int search(int[] nums, int target) { this.nums = nums; this.target = target; int n = nums.length; if (n == 0) return -1; if (n == 1) return this.nums[0] == target ? 0 : -1; int rotate_index = find_rotate_index(0, n - 1); // if target is the smallest element if (nums[rotate_index] == target) return rotate_index; // if array is not rotated, search in the entire array if (rotate_index == 0) return search(0, n - 1); if (target < nums[0]) // search in the right side return search(rotate_index, n - 1); // search in the left side return search(0, rotate_index); } public static void main(String[] args) { //int[][] integers = {{1, 3, 5, 7}, {10, 11, 16, 20}, {23, 30, 34, 50}}; int[] integers = new int[]{4, 5, 6, 7, 8, 9, 0, 1, 2, 3}; SolutionThree solution = new SolutionThree(); System.out.println(solution.search(integers, 5)); } } \ No newline at end of file diff --git a/Week 03/id_573/leetcode_74_573.java b/Week 03/id_573/leetcode_74_573.java new file mode 100644 index 000000000..7d23d4964 --- /dev/null +++ b/Week 03/id_573/leetcode_74_573.java @@ -0,0 +1 @@ +public class SolutionThree { public boolean searchMatrix(int[][] matrix, int target) { if (matrix == null || matrix.length == 0) { return false; } int m = matrix.length; int n = matrix[0].length; int i = 0; int j = n - 1; //从右上角开始,比较target与右上角的数据的大小,如果大于target,就可以往左进一行,如果小于target,就可以往下走一行 while (i < m && j >= 0) { if (matrix[i][j] > target) { j--; } else if (matrix[i][j] < target) { i++; } else { return true; } } return false; } public static void main(String[] args) { int[][] integers = {{1, 3, 5, 7}, {10, 11, 16, 20}, {23, 30, 34, 50}}; SolutionThree solution = new SolutionThree(); System.out.println(solution.searchMatrix(integers)); } } \ No newline at end of file diff --git a/Week 03/id_578/LeetCode_153_578.java b/Week 03/id_578/LeetCode_153_578.java new file mode 100644 index 000000000..4d4824d97 --- /dev/null +++ b/Week 03/id_578/LeetCode_153_578.java @@ -0,0 +1,23 @@ +package com.hand.week3; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/11/2 + */ +public class LeetCode_153_578 { + public int findMin(int[] nums) { + int low = 0; + int high = nums.length - 1; + if (nums[high] >= nums[0]) return nums[0]; + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (nums[mid + 1] < nums[mid]) return nums[mid + 1]; + if (nums[mid] < nums[mid - 1]) return nums[mid]; + if (nums[mid] > nums[0]) low = mid + 1; + else high = mid - 1; + } + return -1; + } +} diff --git a/Week 03/id_578/LeetCode_200_578.java b/Week 03/id_578/LeetCode_200_578.java new file mode 100644 index 000000000..fb16e010c --- /dev/null +++ b/Week 03/id_578/LeetCode_200_578.java @@ -0,0 +1,34 @@ +package com.hand.week3; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/11/2 + */ +public class LeetCode_200_578 { + public int numIslands(char[][] grid) { + if (grid.length == 0 || grid[0].length == 0) return 0; + int num = 0; + for (int i = 0; i < grid.length; ++i) { + for (int j = 0; j < grid[0].length; ++j) { + if (grid[i][j] == '1') { + num++; + helper(i, j, grid); + } + } + } + return num; + } + + private void helper(int i, int j, char[][] grid) { + int maxI = grid.length; + int maxJ = grid[0].length; + if (i < 0 || j < 0 || i >= maxI || j > maxJ || grid[i][j] == '0') return; + grid[i][j] = '0'; + helper(i + 1, j, grid); + helper(i - 1, j, grid); + helper(i, j + 1, grid); + helper(i, j - 1, grid); + } +} diff --git a/Week 03/id_578/LeetCode_33_578.java b/Week 03/id_578/LeetCode_33_578.java new file mode 100644 index 000000000..a46e477bf --- /dev/null +++ b/Week 03/id_578/LeetCode_33_578.java @@ -0,0 +1,26 @@ +package com.hand.week3; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/11/2 + */ +public class LeetCode_33_578 { + public int search(int[] nums, int target) { + int low = 0; + int high = nums.length - 1; + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (target == nums[mid]) return mid; + if (nums[mid] >= nums[low]) { + if (target >= nums[low] && target < nums[mid]) high = mid - 1; + else low = mid + 1; + } else { + if (target > nums[mid] && target <= nums[high]) low = mid + 1; + else high = mid - 1; + } + } + return -1; + } +} diff --git a/Week 03/id_578/LeetCode_74_578.java b/Week 03/id_578/LeetCode_74_578.java new file mode 100644 index 000000000..1f2771af4 --- /dev/null +++ b/Week 03/id_578/LeetCode_74_578.java @@ -0,0 +1,25 @@ +package com.hand.week3; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/11/2 + */ +public class LeetCode_74_578 { + public boolean searchMatrix(int[][] matrix, int target) { + int m = matrix.length; + if (m == 0) return false; + int n = matrix[0].length; + int low = 0; + int high = m * n - 1; + while (low <= high) { + int mid = low + ((high - low) >> 1); + int tmp = matrix[mid / n][mid % n]; + if (target == tmp) return true; + if (target > tmp) low = mid + 1; + else high = mid - 1; + } + return false; + } +} diff --git a/Week 03/id_583/LeetCode_200_583.php b/Week 03/id_583/LeetCode_200_583.php new file mode 100644 index 000000000..19baab7af --- /dev/null +++ b/Week 03/id_583/LeetCode_200_583.php @@ -0,0 +1,72 @@ +g = $grid; + + for ($i = 0; $i < count($grid); $i++) { + for ($j = 0; $j < count($grid[$i]); $j++) { + if ($this->g[$i][$j] == 0) { + continue; + } + + $islands += $this->sink($i, $j); + } + } + + return $islands; + } + + private function sink($i, $j) + { + if ($this->g[$i][$j] == 0) { + return 0; + } + + $this->g[$i][$j] = 0; + + for ($k = 0; $k < count($this->dx); $k++) { + $x = $i + $this->dx[$k]; + $y = $j + $this->dy[$k]; + + if ($x >= 0 && $x < count($this->g) && $y >= 0 && $y < count($this->g[$i])) { + if ($this->g[$x][$y] == 0) { + continue; + } + + $this->sink($x, $y); + } + } + + return 1; + } +} + +$s = new Solution(); +$grid = [ + [1, 1, 0, 0, 0], + [1, 1, 0, 0, 0], + [0, 0, 1, 0, 0], + [0, 0, 0, 1, 1], +]; + +$result = $s->numIslands($grid); + +echo $result; \ No newline at end of file diff --git a/Week 03/id_583/LeetCode_74_583.php b/Week 03/id_583/LeetCode_74_583.php new file mode 100644 index 000000000..8fdf7c303 --- /dev/null +++ b/Week 03/id_583/LeetCode_74_583.php @@ -0,0 +1,55 @@ + $target && $target_line >= 0) { + $target_line = $target_line - 1; + } + + $left = 0; + $right = count($matrix[$target_line]) - 1; + + //执行二分查找算法 + while ($left <= $right) { + $mid = intval(($left + $right) / 2); + + if ($matrix[$target_line][$mid] == $target) { + return true; + } elseif ($matrix[$target_line][$mid] < $target) { + $left = $mid + 1; + } else { + $right = $mid - 1; + } + } + + return false; + } +} + +$s = new Solution(); +$matrix = [ + [1, 3, 5, 7], + [10, 11, 16, 20], + [23, 30, 34, 50], +]; + +$target = 24; + +$result = $s->searchMatrix($matrix, $target); + +echo $result; \ No newline at end of file diff --git a/Week 03/id_583/LeetCode_860_583.php b/Week 03/id_583/LeetCode_860_583.php new file mode 100644 index 000000000..9b0432b94 --- /dev/null +++ b/Week 03/id_583/LeetCode_860_583.php @@ -0,0 +1,48 @@ + 0 && $ten > 0) { + $five--; + $ten--; + } else if ($five >= 3) { + $five -= 3; + } else { + return false; + } + } + } + + return true; + } +} + +$s = new Solution(); +$bills = [5,5,5,10,20]; + +$result = $s->lemonadeChange($bills); + +echo $result; \ No newline at end of file diff --git a/Week 03/id_588/LeetCode_122_588.java b/Week 03/id_588/LeetCode_122_588.java new file mode 100644 index 000000000..13ac6dbd3 --- /dev/null +++ b/Week 03/id_588/LeetCode_122_588.java @@ -0,0 +1,23 @@ +/** + * 买股票的最佳时机 + * https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/description/ + */ +public class LeetCode_122_588 { + + public int maxProfit(int[] prices) { + int max = 0; + for (int i = 1; i < prices.length; i++) { + // 只要是今天比前一天上涨,就进行买卖 + if (prices[i] > prices[i - 1]) { + max = max + (prices[i] - prices[i - 1]); + } + } + + return max; + } + + public static void main(String[] args) { + LeetCode_122_588 solution = new LeetCode_122_588(); + System.out.println(solution.maxProfit(new int[]{7, 1, 5, 3, 6, 4})); + } +} diff --git a/Week 03/id_588/LeetCode_153_588.java b/Week 03/id_588/LeetCode_153_588.java new file mode 100644 index 000000000..fcacd6054 --- /dev/null +++ b/Week 03/id_588/LeetCode_153_588.java @@ -0,0 +1,30 @@ +/** + * 寻找旋转数组中的最小值 + * https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/ + */ +public class LeetCode_153_588 { + + public int findMin(int[] nums) { + int left = 0, right = nums.length - 1; + while(left < right){ + int mid = (left + right) / 2; + if(nums[mid] > nums[right]) left = mid + 1; + else right = mid; + } + return nums[left]; + } + + public static void main(String[] args) { + LeetCode_33_588_BK solution = new LeetCode_33_588_BK(); + + int ret3 = solution.findMin(new int[]{4, 5, 6, 7, 0, 1, 2}); + int ret4 = solution.findMin(new int[]{8, 9, 0, 1, 4, 7}); + int ret5 = solution.findMin(new int[]{0, 1, 2, 3, 4, 5, 6}); + int ret6 = solution.findMin(new int[]{2, 3, 4, 5, 6, 7, 0}); + System.out.println(ret3); + System.out.println(ret4); + System.out.println(ret5); + System.out.println(ret6); + } + +} diff --git a/Week 03/id_588/LeetCode_200_588.java b/Week 03/id_588/LeetCode_200_588.java new file mode 100644 index 000000000..e5f2d6f60 --- /dev/null +++ b/Week 03/id_588/LeetCode_200_588.java @@ -0,0 +1,15 @@ +/** + * 岛屿数量 + * https://leetcode-cn.com/problems/number-of-islands/ + */ +public class LeetCode_200_588 { + + public int numIslands(char[][] grid) { + + return 0; + } + + public static void main(String[] args) { + + } +} diff --git a/Week 03/id_588/LeetCode_33_588.java b/Week 03/id_588/LeetCode_33_588.java new file mode 100644 index 000000000..34894751c --- /dev/null +++ b/Week 03/id_588/LeetCode_33_588.java @@ -0,0 +1,35 @@ +/** + * 搜索旋转排序数组 + * https://leetcode-cn.com/problems/search-in-rotated-sorted-array/ + */ +public class LeetCode_33_588 { + + public int search(int[] nums, int target) { + int left = 0, right = nums.length - 1; + while (left < right) { + int mid = left + (right - left) / 2; + // 如果左半部分是有序的并且target不在左半部分,则去右半部分查找 + if (nums[0] <= nums[mid] && (target > nums[mid] || target < nums[0])) { + left = mid + 1; + } else if (target > nums[mid] && target < nums[0]) { + // 如果[0,mid]之前有旋转点,则去去右半部分查找 + left = mid + 1; + } else { + right = mid; + } + + } + return left == right && nums[left] == target ? left : -1; + } + + public static void main(String[] args) { + LeetCode_33_588 solution = new LeetCode_33_588(); + int ret = solution.search(new int[]{8, 9, 0, 1, 4, 7}, 4); + int ret2 = solution.search(new int[]{8, 9, 0, 1, 4, 7}, 2); + int ret3 = solution.search(new int[]{5, 6, 7, 8, 9, 0, 1}, 6); + System.out.println(ret); + System.out.println(ret2); + System.out.println(ret3); + } + +} diff --git a/Week 03/id_588/LeetCode_455_588.java b/Week 03/id_588/LeetCode_455_588.java new file mode 100644 index 000000000..1374b8755 --- /dev/null +++ b/Week 03/id_588/LeetCode_455_588.java @@ -0,0 +1,37 @@ +import java.util.Arrays; + +/** + * 分饼干 + * https://leetcode-cn.com/problems/assign-cookies/description/ + */ +public class LeetCode_455_588 { + + public int findContentChildren(int[] g, int[] s) { + if (null == g || null == s) { + return 0; + } + + // 排序 + Arrays.sort(g); + Arrays.sort(s); + + int i = 0, j = 0; + while (i < g.length && j < s.length) { + if (s[j] >= g[i]) { + i ++; + } + j ++; + } + + return i; + } + + public static void main(String[] args) { + + LeetCode_455_588 solution = new LeetCode_455_588(); + System.out.println(solution.findContentChildren(new int[]{2, 3, 1}, new int[]{1, 1, 2})); + System.out.println(solution.findContentChildren(new int[]{2, 3, 2}, new int[]{1, 1, 1})); + System.out.println(solution.findContentChildren(new int[]{2, 3, 4}, new int[]{1, 3, 4, 5})); + + } +} diff --git a/Week 03/id_588/LeetCode_529_588.java b/Week 03/id_588/LeetCode_529_588.java new file mode 100644 index 000000000..14fc77dfd --- /dev/null +++ b/Week 03/id_588/LeetCode_529_588.java @@ -0,0 +1,64 @@ +/** + * 扫雷游戏 + * https://leetcode-cn.com/problems/minesweeper/description/ + */ +public class LeetCode_529_588 { + int[] dx = new int[]{1, -1, 1, 1, -1, -1, 0, 0}; + int[] dy = new int[]{0, 0, 1, -1, 1, -1, 1, -1}; + public char[][] updateBoard(char[][] board, int[] click) { + dfs(board, click[0], click[1]); + return board; + } + + public void dfs(char[][] board, int x, int y) { + // 如果是地雷,就结束 + if ('M' == board[x][y]) { + board[x][y] = 'X'; + return; + } + + // 如果点击的是E(未挖出的方块) + if ('E' == board[x][y]) { + // 计算该方块周围的地雷个数 + int count = countBoom(board, x, y); + // 如果地雷个数不为0,则在此方块内填上具体数字 + if (0 != count) { + board[x][y] = (char)(count + '0'); + } else { + // 如果此方块周围地雷个数为0,则此方块为B,并且对此方块周围的8个方块继续进行遍历 + board[x][y] = 'B'; + for (int i = 0; i < 8; i ++) { + int tempX = x + dx[i]; + int tempY = y + dy[i]; + if (tempX < 0 || tempX >= board.length || tempY < 0 || tempY >= board[0].length) { + continue; + } + dfs(board, tempX, tempY); + } + } + } + } + + // 计算某个点周围8个点的地雷个数 + public int countBoom(char[][] board, int x, int y) { + int count = 0; + for (int i = 0; i < 8; i ++) { + int tempX = x + dx[i]; + int tempY = y + dy[i]; + if (tempX < 0 || tempX >= board.length || tempY < 0 || tempY >= board[0].length) { + continue; + } + + if ('M' == board[tempX][tempY]) { + count ++; + } + } + + return count; + } + + public static void main(String[] args) { + + } + +} diff --git a/Week 03/id_588/LeetCode_74_588.java b/Week 03/id_588/LeetCode_74_588.java new file mode 100644 index 000000000..156cabbcf --- /dev/null +++ b/Week 03/id_588/LeetCode_74_588.java @@ -0,0 +1,78 @@ +/** + * 搜索二维矩阵 + * https://leetcode-cn.com/problems/search-a-2d-matrix/ + */ +public class LeetCode_74_588 { + + public boolean searchMatrix(int[][] matrix, int target) { + if (null == matrix || matrix.length <= 0 || matrix[0].length <= 0) { + return false; + } + + int column = matrix[0].length; + int row = matrix.length; + // 如果target小于左上角或者大于右下角,则找不到该target + if (target < matrix[0][0] || target > matrix[row - 1][column - 1]) { + return false; + } + + // 先在第一列里面进行二分查找,找到第一个大于target的值 + int rowIndex = 0; + int top = 0, bottom = row - 1; + while (top <= bottom) { + int mid = top + (bottom - top) / 2; + if (target == matrix[mid][0]) { + return true; + } + // 如果某一行的第一个元素小于target + if (matrix[mid][0] < target) { + // 如果这行是最后一行,则在最后一行里面进行二分搜索 + if (row - 1 == mid) { + rowIndex = mid; + break; + } else { + top = mid + 1; + } + } else { + if (0 == mid || matrix[mid - 1][0] < target) { + rowIndex = mid - 1; + break; + } else { + bottom = mid - 1; + } + } + } + + System.out.println(rowIndex); + return normalBinarySearch(matrix[rowIndex], target); + } + + public boolean normalBinarySearch(int[] nums, int target) { + int left = 0, right = nums.length - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + if (target == nums[mid]) { + return true; + } + if (nums[mid] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + return false; + } + + public static void main(String[] args) { + + int[][] matrix = { + {1, 3, 5, 7}, + {10, 11, 16, 20}, + {23, 30, 34, 50} + }; + + LeetCode_74_588 solution = new LeetCode_74_588(); + System.out.println(solution.searchMatrix(matrix, 3)); + } +} diff --git a/Week 03/id_588/LeetCode_860_588.java b/Week 03/id_588/LeetCode_860_588.java new file mode 100644 index 000000000..350a00b00 --- /dev/null +++ b/Week 03/id_588/LeetCode_860_588.java @@ -0,0 +1,55 @@ +/** + * 柠檬水找零 + * https://leetcode-cn.com/problems/lemonade-change/description/ + */ +public class LeetCode_860_588 { + + public boolean lemonadeChange(int[] bills) { + if (null == bills) { + return false; + } + + // 如果第一个付款的人给的不是5美元,则肯定不能找零成功,因为一开始手头没有任何零钱 + if (5 != bills[0]) { + return false; + } + + int num5 = 1; + int num10 = 0; + // 从第二个人开始遍历 + for (int i = 1; i < bills.length; i ++) { + // 如果付款是5美元,则不用找零,让5美元计数加一 + if (5 == bills[i]) { + num5 ++; + } else if (10 == bills[i]) { + // 如果付款是10美元,则只能找5美元,让5美元计数减一 + num10 ++; + if (num5 > 0) { + num5 --; + } else { + return false; + } + } else { + // 如果付款是20美元,则找零10+5或者5+5+5 + if (num10 >= 1 && num5 >= 1) { + num5 --; + num10 --; + } else if (num5 >= 3) { + num5 = num5 - 3; + } else { + return false; + } + } + } + + return true; + } + + public static void main(String[] args) { + LeetCode_860_588 solution = new LeetCode_860_588(); + System.out.println(solution.lemonadeChange(new int[]{5, 5, 5, 5, 5})); + System.out.println(solution.lemonadeChange(new int[]{10, 5, 5, 5, 5})); + System.out.println(solution.lemonadeChange(new int[]{5, 10, 5, 5, 5, 20})); + System.out.println(solution.lemonadeChange(new int[]{5, 5, 10, 10, 20})); + } +} diff --git a/Week 03/id_588/NOTE.md b/Week 03/id_588/NOTE.md index a6321d6e2..4088a9ebc 100644 --- a/Week 03/id_588/NOTE.md +++ b/Week 03/id_588/NOTE.md @@ -1,4 +1,38 @@ -# NOTE +# 第三周学习总结 + +## 思维 + +熟记DFS,BFS,二分查找的代码模板,然后根据具体情况慢慢调整。 + +## 二分查找的三个条件 + +1. 目标函数单调性(单调递增或者单调递减) +2. 存在上下便捷 +3. 能够通过索引访问 + +## 贪心算法 + +贪心算法是一种在每一步选择中都才去当前最好或最优的选择,从而希望导致结果是全局最好或最优的算法。(贪心算法不一定全局最优,只在可以被证明的情况下可达到全局最优) + +## 课后思考题 + +使用二分查找,寻找一个半有序数组[4, 5, 6, 7, 0, 1, 2]中间无序的地方 + +``` + public int searchRotateIndex(int[] nums) { + int left = 0, right = nums.length - 1; + while(left < right){ + int mid = left + (right - left) / 2; + if(nums[mid] > nums[right]) { + left = mid + 1; + } else { + right = mid; + } + } + return left; + } +``` + diff --git a/Week 03/id_593/LeetCode_102_593.java b/Week 03/id_593/LeetCode_102_593.java new file mode 100644 index 000000000..25576db23 --- /dev/null +++ b/Week 03/id_593/LeetCode_102_593.java @@ -0,0 +1,99 @@ + +/** + * 102. 二叉树的层次遍历 + * + *

+ * 给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。 + *

+ * 例如: + * 给定二叉树: [3,9,20,null,null,15,7], + *

+ *

3 + *

/ \ + *

9 20 + *

/ \ + *

15 7 + *

返回其层次遍历结果: + *

+ *

[ + *

[3], + *

[9,20], + *

[15,7] + *

] + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + * @version 1.0 实现两种思路 DFS 和 BFS + */ +public class LeetCode_102_593 { + + /** + * 通过 广度优先搜索 遍历树 + * + * @param root 目标树 + * @return 遍历后的数组 + */ + public List> levelOrderBfs(TreeNode root) { + if (root == null) { + return Collections.emptyList(); + } + List> result = new ArrayList<>(); + Deque deque = new LinkedList<>(); + Set visited = new HashSet<>(); + deque.addFirst(root); + while (!deque.isEmpty()) { + int size = deque.size(); + List list = new ArrayList<>(); + for (int i = 0; i < size; i++) { + TreeNode current = deque.poll(); + assert current != null; + if (!visited.contains(current)) { + visited.add(current); + list.add(current.val); + } + if (current.left != null) { + deque.addLast(current.left); + } + if (current.right != null) { + deque.addLast(current.right); + } + } + result.add(list); + } + return result; + } + + + public List> levelOrderByDfs(TreeNode root) { + if (root == null) { + return Collections.emptyList(); + } + List> result = new ArrayList<>(); + levelOrderByDfs(root, 0, result); + return result; + } + + private void levelOrderByDfs(TreeNode root, int level, List> result) { + // terminator + if (root == null) { + return; + } + // process + if (result.size() <= level) { + result.add(new ArrayList<>()); + } + result.get(level).add(root.val); + // drill down + if (root.left != null) { + levelOrderByDfs(root.left, level + 1, result); + } + if (root.right != null) { + levelOrderByDfs(root.right, level + 1, result); + } + } + + +} \ No newline at end of file diff --git a/Week 03/id_593/LeetCode_22_593.java b/Week 03/id_593/LeetCode_22_593.java new file mode 100644 index 000000000..6baf02958 --- /dev/null +++ b/Week 03/id_593/LeetCode_22_593.java @@ -0,0 +1,105 @@ + +/** + * 22. 括号生成 + *

+ * 给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。 + *

+ * 例如,给出 n = 3,生成结果为: + *

+ * [ + * "((()))", + * "(()())", + * "(())()", + * "()(())", + * "()()()" + * ] + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/generate-parentheses + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + * @version 3.0 BFS编写完成 + */ +public class LeetCode_22_593 { + + private List result = new ArrayList<>(); + + public List generateParenthesisByDfs(int n) { + if (n == 0) { + return Collections.emptyList(); + } + generateParenthesisByDfs("", 0, 0, n); + return result; + } + + /** + * 清空思路 + * + * @param current 当前的括号的情况 + * @param left 左括号数量 + * @param right 右括号数量 + * @param n 括号总数 + */ + private void generateParenthesisByDfs(String current, int left, int right, int n) { + if (current.length() == 2 * n) { + result.add(current); + } + + if (left < n) { + generateParenthesisByDfs(current + "(", left + 1, right, n); + } + if (right < left && right < n) { + generateParenthesisByDfs(current + ")", left, right + 1, n); + } + } + + + static class Node { + /** + * 当前得到的字符串 + */ + private String result; + /** + * 剩余左括号数量 + */ + private int left; + /** + * 剩余右括号数量 + */ + private int right; + + public Node(String result, int left, int right) { + this.result = result; + this.left = left; + this.right = right; + } + + public String getResult() { + return result; + } + } + + public List generateParenthesisByBfs(int n) { + if (n == 0) { + return Collections.emptyList(); + } + Deque nodeDeque = new LinkedList<>(); + nodeDeque.offer(new Node("", n, n)); + int total = 2 * n; + while (total > 0) { + int size = nodeDeque.size(); + for (int i = 0; i < size; i++) { + Node node = nodeDeque.poll(); + assert node != null; + if (node.left > 0) { + nodeDeque.offer(new Node(node.result + "(", node.left - 1, node.right)); + } + if (node.right > 0 && node.right > node.left) { + nodeDeque.offer(new Node(node.result + ")", node.left, node.right - 1)); + } + } + total--; + } + return nodeDeque.stream().map(Node::getResult).collect(Collectors.toList()); + } diff --git a/Week 03/id_593/LeetCode_33_593.java b/Week 03/id_593/LeetCode_33_593.java new file mode 100644 index 000000000..95aaa2b02 --- /dev/null +++ b/Week 03/id_593/LeetCode_33_593.java @@ -0,0 +1,63 @@ + +/** + * 33. 搜索旋转排序数组 + *

+ * 假设按照升序排序的数组在预先未知的某个点上进行了旋转。 + *

+ * ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 + *

+ * 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。 + *

+ * 你可以假设数组中不存在重复的元素。 + *

+ * 你的算法时间复杂度必须是 O(log n) 级别。 + *

+ * 示例 1: + *

+ * 输入: nums = [4,5,6,7,0,1,2], target = 0 + * 输出: 4 + * 示例 2: + *

+ * 输入: nums = [4,5,6,7,0,1,2], target = 3 + * 输出: -1 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * 四步刷题法: + * 1. clarification + * 数组是一个 升序 有序数组,在某个一个结点进行了旋转得到的,也就是整体部份为有序。 时间复杂度,log N ,提醒我们可以考虑一下二分查找。 + * 2. possible solution -> optimal(space / time) + * 思路:二分查找;先排序的方案,也能做到log N + * 3. code + * 4. test case + * + * @author jaryoung + * @version 1.0 参考了别人的写法,并且自己能理解的写法 + */ +public class LeetCode_33_593 { + + public int search(int[] nums, int target) { + int length = nums.length; + int left = 0, right = length - 1; + while (left < right) { + int mid = left + ((right - left) >> 1); + if (nums[mid] == target) { + return mid; + } else if (nums[left] <= nums[mid]) { + if (nums[left] <= target && target < nums[mid]) { + right = mid - 1; + } else { + left = mid + 1; + } + } else if (nums[mid] < nums[right]) { + if (nums[mid] < target && target <= nums[right]) { + left = mid + 1; + } else { + right = mid - 1; + } + } + } + return length > 0 && nums[left] == target ? left : -1; + } +} \ No newline at end of file diff --git a/Week 03/id_593/LeetCode_433_593.java b/Week 03/id_593/LeetCode_433_593.java new file mode 100644 index 000000000..3ed3b82e9 --- /dev/null +++ b/Week 03/id_593/LeetCode_433_593.java @@ -0,0 +1,126 @@ + +/** + * 433. 最小基因变化 + *

+ * 一条基因序列由一个带有8个字符的字符串表示,其中每个字符都属于 "A", "C", "G", "T"中的任意一个。 + *

+ * 假设我们要调查一个基因序列的变化。一次基因变化意味着这个基因序列中的一个字符发生了变化。 + *

+ * 例如,基因序列由"AACCGGTT" 变化至 "AACCGGTA" 即发生了一次基因变化。 + *

+ * 与此同时,每一次基因变化的结果,都需要是一个合法的基因串,即该结果属于一个基因库。 + *

+ * 现在给定3个参数 — start, end, bank,分别代表起始基因序列,目标基因序列及基因库, + * 请找出能够使起始基因序列变化为目标基因序列所需的最少变化次数。如果无法实现目标变化,请返回 -1。 + *

+ * 注意: + *

+ * 起始基因序列默认是合法的,但是它并不一定会出现在基因库中。 + * 所有的目标基因序列必须是合法的。 + * 假定起始基因序列与目标基因序列是不一样的。 + * 示例 1: + *

+ * start: "AACCGGTT" + * end: "AACCGGTA" + * bank: ["AACCGGTA"] + *

+ * 返回值: 1 + * 示例 2: + *

+ * start: "AACCGGTT" + * end: "AAACGGTA" + * bank: ["AACCGGTA", "AACCGCTA", "AAACGGTA"] + *

+ * 返回值: 2 + * 示例 3: + *

+ * start: "AAAAACCC" + * end: "AACCCCCC" + * bank: ["AAAACCCC", "AAACCCCC", "AACCCCCC"] + *

+ * 返回值: 3 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/minimum-genetic-mutation + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + */ +public class LeetCode_433_593 { + + private static final int EIGHT = 8; + + public int minMutationByDfs(String start, String end, String[] bank) { + Set dict = new HashSet<>(bank.length); + dict.addAll(Arrays.asList(bank)); + int result = minMutationByDfs(start, end, 0, dict); + return result == Integer.MAX_VALUE ? -1 : result; + } + + private int minMutationByDfs(String start, String end, int level, Set dict) { + if (start.equals(end)) { + return level; + } + int result = Integer.MAX_VALUE; + if (dict.isEmpty()) { + return result; + } + for (String s : dict) { + if (s.equals(end) && diff(s, start) == 1) { + return level + 1; + } else if (diff(s, start) == 1) { + Set tempDict = new HashSet<>(dict); + tempDict.remove(s); + result = Math.min(result, minMutationByDfs(s, end, level + 1, tempDict)); + } + + } + return result; + } + + public int minMutationByBfs(String start, String end, String[] bank) { + if (bank.length == 0) { + return -1; + } + Set dict = new HashSet<>(bank.length); + dict.addAll(Arrays.asList(bank)); + Set visited = new HashSet<>(dict.size()); + Deque deque = new LinkedList<>(); + deque.add(start); + visited.add(start); + int result = 0; + int level = 0; + while (!deque.isEmpty()) { + int size = deque.size(); + level++; + for (int i = 0; i < size; i++) { + String current = deque.poll(); + assert current != null; + for (String s : dict) { + if (visited.contains(s)) { + continue; + } + if (diff(current, s) == 1) { + if (end.equals(s)) { + return Math.min(result + 1, level); + } + deque.addLast(s); + visited.add(s); + result++; + } + } + } + } + return -1; + } + + private int diff(String s, String start) { + int diff = 0; + for (int i = 0; i < EIGHT; i++) { + if (s.charAt(i) != start.charAt(i)) { + ++diff; + } + } + return diff; + } +} diff --git a/Week 03/id_593/LeetCode_45_593.java b/Week 03/id_593/LeetCode_45_593.java new file mode 100644 index 000000000..20143dbd8 --- /dev/null +++ b/Week 03/id_593/LeetCode_45_593.java @@ -0,0 +1,49 @@ +/** + * 45. 跳跃游戏 II + *

+ * 给定一个非负整数数组,你最初位于数组的第一个位置。 + *

+ * 数组中的每个元素代表你在该位置可以跳跃的最大长度。 + *

+ * 你的目标是使用最少的跳跃次数到达数组的最后一个位置。 + *

+ * 示例: + *

+ * 输入: [2,3,1,1,4] + * 输出: 2 + * 解释: 跳到最后一个位置的最小跳跃数是 2。 + *   从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。 + * 说明: + *

+ * 假设你总是可以到达数组的最后一个位置。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/jump-game-ii + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * 思路: + * 1. 总是跳最远的步伐 + * + * @author jaryoung + * @version 1.0 默写了别人的写法,优化了一下提前结束的条件,等待下一次新思路 + */ +public class LeetCode_45_593 { + + public int jump(int[] nums) { + int end = 0; + int maxPosition = 0; + int steps = 0; + for (int i = 0; i < nums.length - 1; i++) { + maxPosition = Math.max(maxPosition, nums[i] + i); + // 提前结束 + if (maxPosition >= nums.length - 1) { + return ++steps; + } + if (i == end) { + end = maxPosition; + steps++; + } + } + return steps; + } + +} \ No newline at end of file diff --git a/Week 03/id_593/LeetCode_55_593.java b/Week 03/id_593/LeetCode_55_593.java new file mode 100644 index 000000000..212912980 --- /dev/null +++ b/Week 03/id_593/LeetCode_55_593.java @@ -0,0 +1,78 @@ + +/** + * 55. 跳跃游戏 + * 给定一个非负整数数组,你最初位于数组的第一个位置。 + *

+ * 数组中的每个元素代表你在该位置可以跳跃的最大长度。 + *

+ * 判断你是否能够到达最后一个位置。 + *

+ * 示例 1: + *

+ * 输入: [2,3,1,1,4] + * 输出: true + * 解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。 + * 示例 2: + *

+ * 输入: [3,2,1,0,4] + * 输出: false + * 解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/jump-game + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + * @version 1.0 贪心算法是默写,递归方法是根据贪心算法自己想到的 + */ +public class LeetCode_55_593 { + + private static final int TWO = 2; + + public boolean canJumpByForward(int[] nums) { + int length = nums.length; + if (length < TWO) { + return true; + } + int toJump = nums[0]; + for (int i = 1; i < nums.length; i++) { + if (toJump == 0) { + return false; + } + toJump = Math.max(toJump - 1, nums[i]); + } + return true; + } + + public boolean canJumpByBack(int[] nums) { + int length = nums.length; + if (length < TWO) { + return true; + } + int position = length - 1; + for (int i = length - TWO; i >= 0; i--) { + if (nums[i] + i >= position) { + position = i; + } + } + return position == 0; + } + + public boolean canJumpByRecursion(int[] nums) { + if (nums.length < TWO) { + return true; + } + return canJumpByRecursion(1, nums[0], nums); + } + + private boolean canJumpByRecursion(int level, int toJump, int[] nums) { + // terminator + if (level > nums.length - TWO || toJump == 0) { + return toJump > 0; + } + // process and drill down + toJump = Math.max(toJump - 1, nums[level]); + return canJumpByRecursion(level + 1, toJump, nums); + } + +} diff --git a/Week 03/id_593/LeetCode_69_593.java b/Week 03/id_593/LeetCode_69_593.java new file mode 100644 index 000000000..11a4f2b6d --- /dev/null +++ b/Week 03/id_593/LeetCode_69_593.java @@ -0,0 +1,127 @@ + +/** + * 69. x 的平方根 + *

+ * 实现 int sqrt(int x) 函数。 + *

+ * 计算并返回 x 的平方根,其中 x 是非负整数。 + *

+ * 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。 + *

+ * 示例 1: + *

+ * 输入: 4 + * 输出: 2 + * 示例 2: + *

+ * 输入: 8 + * 输出: 2 + * 说明: 8 的平方根是 2.82842..., + *   由于返回类型是整数,小数部分将被舍去。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/sqrtx + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * 四步刷题法: + * 1. clarification + * 计算并返回 x 的平方根,其中 x 是非负整数。只保留整数部分,也就是我们精确到整数位即可 + * 2. possible solution -> optimal(space / time) + * 穷尽,几乎不可能(时间复杂度太高了);二分查找不断逼近 log n;牛顿迭代法 + * 3. code + * 4. test case + * + * @author jaryoung + * @version 1.0 基本解法都是根据别人的方法改造而来,还需要多加练习 + */ +public class LeetCode_69_593 { + + /** + * 计算并返回 x 的平方根 + * + * @param x 待开平方根的数据 + * @return x 的平方根 + */ + public int mySqrtByBinarySearch(int x) { + long left = 0; + long right = Integer.MAX_VALUE; + while (left < right) { + long mid = (right + left + 1) >>> 1; + long squareMid = mid * mid; + if (squareMid == x) { + return (int) mid; + } + if (squareMid > x) { + right = mid - 1; + } else { + left = mid; + } + } + return (int) left; + } + + + public int mySqrtByNewtonIteration(int x) { + double x0 = 1; + double pre; + while (true) { + pre = x0; + x0 = (x0 + x / x0) / 2; + if (Math.abs(x0 - pre) < 1e-6) { + return (int) x0; + } + } + } + + public int mySqrtByNewtonIterationSecond(int x) { + long x0 = x; + while (x0 * x0 > x) { + x0 = (x0 + x / x0) / 2; + } + return (int) x0; + } + + public int mySqrtByNewtonIterationRecursion(int x) { + if (x == 0) { + return x; + } + return (int) (sqrt(x, x)); + } + + private double sqrt(double x, int a) { + double result = (x + a / x) / 2; + if (result - x == 0) { + System.out.println("result _" + result + " x:" + x + " a " + a); + return result; + } else { + return sqrt(result, a); + } + } + + public double mySqrtByBinarySearch(int x, int decimal) { + Assert.that(decimal < 9, "The sqrt only support decimal < 9."); + Assert.that(x < Integer.MAX_VALUE, "The sqrt only support decimal < Integer.MAX_VALUE."); + if (Math.abs(x) == 0 || Math.abs(x) == 1) { + return x; + } + double add = 1.0 / Math.pow(10, Math.min(decimal + 4, 16)); + double left = 0; + double right = x; + while (left < right) { + double mid = left + (right - left + add) / 2.000; + double squareMid = mid * mid; + if (squareMid - x == 0) { + BigDecimal bigDecimal = new BigDecimal(mid); + return bigDecimal.setScale(decimal, BigDecimal.ROUND_HALF_UP).doubleValue(); + } + if (squareMid < x) { + left = mid + add; + } else { + right = mid - add; + } + } + BigDecimal bigDecimal = new BigDecimal(left); + return bigDecimal.setScale(decimal, BigDecimal.ROUND_HALF_UP).doubleValue(); + } + + +} \ No newline at end of file diff --git a/Week 03/id_593/NOTE.md b/Week 03/id_593/NOTE.md index a6321d6e2..9dc2ea93c 100644 --- a/Week 03/id_593/NOTE.md +++ b/Week 03/id_593/NOTE.md @@ -1,4 +1,49 @@ -# NOTE +# 【593-Week 03】第三周总结 - +> 国际惯例,脑图先上: + +![Kj0XY4.png](https://s2.ax1x.com/2019/11/03/Kj0XY4.png) + + + +## 基本情况 + +### 上周复习完成度 10% + +仅仅完成了二叉树的前中后遍历的递归和迭代的代码的复习,其他均没有复习。 + +问题分析: + +1. 没有复习意识 + +解决办法:建立复习任务清单 + +### 理论知识完成度 100% + +感想:以前以为很熟悉的东西,真的懂吗?二分查找的前提记不住?(我是没记住,现在努力记住中)记不住,你敢说你能写出无误的二分查找吗?加强理论基础学习,该背的东西还得背。 + +### 实战题目完成度 57% + +尽自己最大的程度去完成了。 + +问题分析: + +1. 大部分题目是默写 +2. 大部分题目没有自己思路 + +解决办法:手熟而已,背,理解,练习,走起。 + +### 课后题目 10% + +主要周六一天都在玩游戏了,导致课后作业被压缩了一天都时间。 + +问题分析: + +1. 自律还不够 + +解决办法:制定先完成作业才能玩的规约 + +### 下周展望 + +复习学过的内容,努力巩固已经学习的内容,有时候复习也是一种获取新知识的好办法。 diff --git a/Week 03/id_598/LeetCode_102_598.java b/Week 03/id_598/LeetCode_102_598.java new file mode 100644 index 000000000..348be9fee --- /dev/null +++ b/Week 03/id_598/LeetCode_102_598.java @@ -0,0 +1,109 @@ +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * @author northleaf + * @create 2019年10月31日 + */ +public class LeetCode_102_598 { + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + + + /** + * 层次遍历:递归方式实现 + * + * @param root + * @return + */ + public List> levelOrder1(TreeNode root) { + List> ans = new ArrayList<>(); + if (root != null) { + levelOrder(0, root, ans); + } + + return ans; + } + + /** + * 层次遍历递归实现类 + * + * @param dept + * @param node + * @param ans + */ + private void levelOrder(int dept, TreeNode node, List> ans) { + //终止条件 + if (node == null) { + return; + } + //处理当前层的数据 + List tmp = null; + //由于dept是由0开始的,而list集合内如果有元素,就一定是大于0的 + //比如dept为0,那就判断,如果下一层的数量大于list集合的数据,说明一定没有当前层 + if (dept + 1 > ans.size()) { + tmp = new ArrayList<>(); + ans.add(tmp); + } + tmp = ans.get(dept); + tmp.add(node.val); + //下一层 + levelOrder(dept + 1, node.left, ans); + levelOrder(dept + 1, node.right, ans); + + //清理数据 + + } + + + /** + * 层序遍历:迭代法 + * 借助于队列实现 + * + * @param root + * @return + */ + public List> levelOrder(TreeNode root) { + List> ans = new ArrayList<>(); + if (root == null) { + return ans; + } + Queue queue = new LinkedList<>(); + //起始时,元素只有根节点 + queue.add(root); + //从第0层开始 + int level = 0; + //判断队列是否为空,不为空则持续循环 + //这个while每循环一次就是一层的数据 + while (!queue.isEmpty()){ + ans.add(new ArrayList<>()); + //根据当前队列的长度获取当前层的数据 + //同时将节点的子节点添加到队列中 + int num_length = queue.size(); + for(int i = 0;i preorderTraversal1(TreeNode root) { + List list = new ArrayList<>(); + preorderTraversal(root,list); + return list; + } + + /** + * 递归遍历以node为根的二叉树 + * @param node + * @param list + */ + private void preorderTraversal(TreeNode node, List list) { + if (node != null) { + list.add(node.val); + preorderTraversal(node.left,list); + preorderTraversal(node.right,list); + } + } + + + /** + * 前序遍历:迭代法 + * @param root + * @return + */ + public List preorderTraversal(TreeNode root) { + List list = new ArrayList<>(); + if (root == null) { + return list; + } + Stack stack = new Stack<>(); + stack.push(root); + while (!stack.isEmpty()){ + TreeNode tmp = stack.pop(); + list.add(tmp.val); + if(tmp.right!=null){ + stack.push(tmp.right); + } + if(tmp.left!=null){ + stack.push(tmp.left); + } + } + + + return list; + } +} diff --git a/Week 03/id_598/LeetCode_153_598.java b/Week 03/id_598/LeetCode_153_598.java new file mode 100644 index 000000000..1ff6a6bb5 --- /dev/null +++ b/Week 03/id_598/LeetCode_153_598.java @@ -0,0 +1,71 @@ +/** + * @author northleaf + * @create 2019年10月31日 + */ +public class LeetCode_153_598 { + /** + * 查找最小值:暴力搜索,从数组中查找最小的值,只需要遍历一次即可 + * @param nums + * @return + */ + public int findMin1(int[] nums) { + + //假设最小值为第一个 + int min = nums[0]; + for(int i = 1;i nums[0]){ + return nums[0]; + } + //如果不是,说明这个序列被处理过,按二分查找的方式,查找变化点 + // 比如原始序列为:1,2,3,4,5,6,7,8 + // 处理后的序列为:3,4,5,6,7,1,2,3 + // 那第处理后的序列中第5个元素(1)即为变化点,因为 + // 它左侧的元素比它大 + // 它右侧的元素也比它小 + int left = 0; + int right = nums.length - 1; + //中间元素 + int mid = 0; + while (left < right){ + mid = left + (right - left) /2 ; + //如果右侧的元素比它小 + if(nums[mid] > nums[mid+1]){ + return nums[mid+1]; + } + //如果左侧的元素比它大 + if(nums[mid] < nums[mid -1]){ + return nums[mid]; + } + + if(nums[mid] > nums[0]) { + left = mid+1; + }else{ + right = mid -1; + } + + } + return nums[mid]; + + } +} diff --git a/Week 03/id_598/LeetCode_22_598.java b/Week 03/id_598/LeetCode_22_598.java new file mode 100644 index 000000000..24c174a0b --- /dev/null +++ b/Week 03/id_598/LeetCode_22_598.java @@ -0,0 +1,49 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * @author northleaf + * @create 2019年10月31日 + */ +public class LeetCode_22_598 { + /** + * 递归法生成括号 + * @param n + * @return + */ + public List generateParenthesis(int n) { + List list = new ArrayList<>(); + helper(0,0,n,"",list); + return list; + } + + /** + * 递归生成 + * @param left + * @param n + * @param s + * @param list + */ + private void helper(int left,int right, int n, String s, List list) { + //终止条件 + //左右括号的数量与给定的括号对数一致的时候就是退出的时候 + if(left == n && right==n){ + list.add(s); + return; + } + + + //处理当前层 + //当左括号数小于括号对数的时候要添加左括号 + if(left < n ){ + helper(left+1,right,n,s+"(",list); + } + if(right < left){ + helper(left,right+1,n,s+")",list); + } + + //下一层 + //与当前层的处理合并了 + //清理数据 + } +} diff --git a/Week 03/id_598/LeetCode_429_598.java b/Week 03/id_598/LeetCode_429_598.java new file mode 100644 index 000000000..6825239eb --- /dev/null +++ b/Week 03/id_598/LeetCode_429_598.java @@ -0,0 +1,51 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * @author northleaf + * @create 2019年10月30日 + */ +public class LeetCode_429_598 { + class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val, List _children) { + val = _val; + children = _children; + } + } + + /** + * 层序遍历 + * @param root + * @return + */ + public List> levelOrder(Node root) { + List> ans = new ArrayList<>(); + helper(root,0,ans); + return ans; + } + + + + private void helper(Node node, int depth, List> res) { + //终止条件 + if (node == null) return; + //判断是否是新的一层 + if (depth + 1 > res.size()) { + res.add(new ArrayList<>()); + } + res.get(depth).add(node.val); + + //处理子节点 + for (Node node1 : node.children) { + if (node1 != null) { + helper(node1, depth + 1, res); + } + } + } + +} diff --git a/Week 03/id_598/LeetCode_515_598.java b/Week 03/id_598/LeetCode_515_598.java new file mode 100644 index 000000000..02b3d6668 --- /dev/null +++ b/Week 03/id_598/LeetCode_515_598.java @@ -0,0 +1,96 @@ +import java.util.*; + +/** + * @author northleaf + * @create 2019年10月31日 + */ +public class LeetCode_515_598 { + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + + /** + * 递归查找每一层最大的值 + * DFS算法 + * @param root + * @return + */ + public List largestValues1(TreeNode root) { + Map map = new HashMap<>(); + helper(0,root,map); + List list = new ArrayList(map.values()); + return list; + } + + /** + * 递归查找 + * @param depth + */ + private void helper(int depth, TreeNode node, Map map){ + //终止条件 + if (node == null) { + return; + } + //处理当前层 + if(map.get(depth) == null){ + map.put(depth,node.val); + }else{ + map.put(depth,Math.max(node.val,map.get(depth))); + } + //下一层 + helper(depth+1,node.left,map); + helper(depth+1,node.right,map); + //数据清理 + } + + /** + * 非递归方式,查找每一层最大的值 + * @param root + * @return + */ + public List largestValues(TreeNode root) { + List list = new ArrayList<>(); + if (root == null) { + return list; + } + + //借助一个队列实现 + Queue queue = new LinkedList<>(); + queue.add(root); + //从第0层开始 + int level = 0 ; + TreeNode tmpNode = null; + //遍历queue,每次遍历就是一层的数据 + while (!queue.isEmpty()){ + //获取当前层的元素数量 + int level_num_length = queue.size(); + int tmp = Integer.MIN_VALUE; + //遍历当前层的数据 + for(int i = 0;i children; + + public Node() {} + + public Node(int _val, List _children) { + val = _val; + children = _children; + } + } + + /** + * 前序遍历:根左右 + * 递归写法 + * @param root + * @return + */ + public List preorder1(Node root) { + List list = new ArrayList<>(); + preorder(root,list); + return list; + } + + /** + * 前序遍历 + * @param node + * @param list + */ + private void preorder(Node node, List list) { + if (node != null) { + list.add(node.val); + if(node.children!=null){ + for(Node n:node.children){ + preorder(n,list); + } + } + + } + } + + /** + * 前序遍历 概左右 + * @param root + * @return + */ + public List preorder(Node root) { + List list = new ArrayList<>(); + if (root == null) { + return list; + } + Stack stack = new Stack<>(); + stack.push(root); + while (!stack.isEmpty()){ + Node tmp = stack.pop(); + list.add(tmp.val); + List tmpList = tmp.children; + Collections.reverse(tmpList); + for(Node n:tmpList){ + stack.push(n); + } + } + return list; + } + +} diff --git a/Week 03/id_598/LeetCode_590_598.java b/Week 03/id_598/LeetCode_590_598.java new file mode 100644 index 000000000..119c2af2b --- /dev/null +++ b/Week 03/id_598/LeetCode_590_598.java @@ -0,0 +1,82 @@ +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Stack; + +/** + * @author northleaf + * @create 2019年10月30日 + */ +public class LeetCode_590_598 { + public class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val,List _children) { + val = _val; + children = _children; + } + } + + /** + * 后序遍历:左右根 + * @param root + * @return + */ + public List postorder1(Node root) { + List list = new ArrayList<>(); + postorder(root,list); + return list; + + } + + /** + * 递归遍历 + * @param node + * @param list + */ + private void postorder(Node node, List list) { + //终止条件 + if (node == null) { + return; + } + + //处理当前层数据 + if(node.children!=null) { + for (Node n : node.children) { + postorder(n,list); + } + } + list.add(node.val); + + } + + /** + * 后序遍历:左右根 + * 非递归 + * @param root + * @return + */ + public List postorder(Node root) { + LinkedList stack = new LinkedList<>(); + LinkedList output = new LinkedList<>(); + if (root == null) { + return output; + } + + stack.add(root); + while (!stack.isEmpty()) { + Node node = stack.pollLast(); + //反向添加 + output.addFirst(node.val); + for (Node item : node.children) { + if (item != null) { + stack.add(item); + } + } + } + return output; + } +} diff --git a/Week 03/id_598/LeetCode_860_598.java b/Week 03/id_598/LeetCode_860_598.java new file mode 100644 index 000000000..26fd4d6ac --- /dev/null +++ b/Week 03/id_598/LeetCode_860_598.java @@ -0,0 +1,59 @@ +/** + * @author northleaf + * @create 2019年10月31日 + */ +public class LeetCode_860_598 { + + /** + * 求柠檬水找零 + * + * @param bills + * @return + */ + public boolean lemonadeChange(int[] bills) { + boolean flag = true; + if (bills == null || bills.length < 1) { + return false; + } + //记录五美元的数量 + int num5 = 0; + //记录10美元的数量 + int num10 = 0; + //记录20美元的数量 + int num20 = 0; + //遍历数组 + for (int i = 0; i < bills.length; i++) { + if (bills[i] == 5) { + num5 += 1; + } else if (bills[i] == 10) { + //判断5美元的数量是否大于1 + if (num5 >= 1) { + num5 -= 1; + num10 += 1; + } else { + flag = false; + break; + } + } else { + //20美元要想找零,就必须有至少一个10美元一个五美元,或者至少三个5美元 + if (num5 >= 1 && num10 >= 1) { + num5 -= 1; + num10 -= 1; + } else if (num5 >= 3) { + num5 -= 3; + } else { + flag = false; + break; + } + } + + } + return flag; + } + + public static void main(String[] args) { + int[] nums = new int[]{5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 10, 5, 20, 20, 5, 5, 5, 5, 5, 10, 5, 5, 5, 20, 5, 5, 5, 10, 5, 5, 10, 5, 20, 5, 5, 20, 5, 10, 5, 5, 20, 5, 5, 5, 5, 5, 5, 10, 20, 5, 20, 20, 10, 5, 20, 20, 5, 10, 5, 5, 5, 5, 5, 5, 20, 20, 20, 20, 5, 5, 10, 5, 20, 5, 5, 5, 5, 10, 10, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 20, 5, 20, 10, 10, 20, 5, 5, 5, 5, 20, 20, 5, 5, 5, 5, 20, 5, 20, 20, 5, 5, 10, 5, 5, 5, 20, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 5, 10, 20, 20, 5, 5, 10, 20, 5, 5, 5, 5, 10, 20, 5, 5, 10, 20, 5, 10, 10, 5, 5, 5, 5, 5, 5, 10, 10, 10, 5, 10, 5, 10, 5, 5, 10, 10, 5, 5, 5, 20, 5, 20, 10, 20, 5, 5, 5, 20, 10, 5, 5, 20, 5, 5, 5, 10, 5, 5, 10, 5, 5, 20, 5, 10, 10, 5, 5, 10, 5, 5, 10, 5, 10, 5, 20, 10, 5, 10, 10, 5, 5, 5, 5, 10, 5, 5, 5, 20, 5, 5, 5, 5, 10, 5, 10, 10, 5, 20, 20, 5, 10, 10, 10, 5, 10, 5, 10, 5, 10, 5, 5, 10, 5, 5, 5, 20, 5, 5, 20, 5, 5, 5, 5, 5, 5, 10, 5, 5, 20, 20, 5, 5, 5, 5, 10, 5, 5, 5, 20, 5, 5, 5, 5, 10, 20, 5, 5, 5, 20, 20, 5, 10, 5, 5, 5, 10, 5, 10, 20, 20, 5, 5, 5, 5, 5, 5, 20, 10, 5, 10, 5, 5, 20, 10, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 5, 10, 5, 10, 20, 10, 10, 5, 5, 20, 10, 20, 5, 5, 5, 10, 5, 5, 5, 10, 5, 20, 20, 20, 10, 20, 5, 5, 5, 5, 20, 5, 20, 5, 10, 5, 5, 5, 5, 5, 5, 20, 5, 10, 5, 5, 5, 20, 5, 5, 5, 10, 10, 5, 5, 5, 5, 5, 20, 20, 20, 5, 5, 5, 5, 20, 5, 20, 5, 20, 20, 10, 10, 5, 5, 5, 20, 10, 20, 10, 20, 5, 20, 5, 5, 5, 10, 10, 5, 5, 5, 5, 10, 10, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 20, 10, 20, 20, 5, 20, 10, 5, 5, 20, 5, 5, 10, 5, 5, 5, 5, 10, 5, 5, 5, 20, 5, 10, 5, 10, 10, 20, 5, 20, 5, 20, 10, 5, 5, 5, 20, 5, 10, 10, 5, 5, 10, 5, 10, 5, 5, 20, 20, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 10, 5, 10, 20, 20, 5, 5, 20, 5, 10, 5, 20, 5, 20, 20, 5, 5, 5, 20, 20, 20, 5, 5, 5, 5, 20, 10, 5, 5, 10, 10, 10, 5, 10, 5, 10, 5, 20, 5, 5, 5, 5, 10, 20, 5, 5, 5, 5, 5, 5, 20, 20, 10, 10, 5, 5, 20, 5, 5, 5, 5, 20, 20, 20, 20, 5, 5, 20, 20, 5, 20, 5, 5, 5, 10, 20, 10, 5, 20, 5, 5, 5, 5, 10, 10, 5, 10, 5, 5, 10, 5, 5, 20, 10, 20, 5, 5, 5, 10, 5, 5, 10, 10, 5, 20, 5, 5, 20, 5, 5, 20, 10, 10, 5, 5, 10, 5, 5, 20, 5, 10, 5, 20, 20, 10, 10, 20, 5, 5, 5, 20, 5, 5, 20, 20, 10, 20, 10, 10, 5, 20, 10, 5, 10, 5, 10, 5, 5, 20, 5, 20, 20, 5, 5, 5, 5, 20, 10, 5, 5, 5, 5, 5, 20, 5, 5, 20, 10, 5, 5, 5, 5, 5, 5, 10, 5, 10, 5, 20, 20, 20, 20, 5, 5, 20, 5, 5, 5, 20, 5, 20, 5, 5, 5, 10, 5, 20, 5, 5, 5, 5, 20, 5, 20, 20, 5, 5, 5, 5, 10, 5, 20, 20, 5, 20, 5, 10, 5, 5, 5, 5, 20, 5, 5, 5, 20, 20, 5, 5, 5, 20, 10, 20, 5, 5, 10, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 20, 10, 20, 20, 5, 5, 10, 10, 5, 10, 10, 5, 5, 10, 10, 5, 5, 5, 5, 5, 10, 5, 20, 5, 10, 5, 20, 10, 5, 10, 5, 10, 20, 10, 5, 5, 5, 20, 5, 5, 20, 5, 5, 5, 5, 5, 10, 10, 20, 20, 20, 5, 20, 20, 5, 20, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 10, 5, 5, 5, 5, 10, 20, 20, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 20, 20, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 20, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 10, 5, 10, 5, 5, 5, 20, 5, 5, 5, 20, 5, 20, 5, 20, 10, 5, 5, 5, 5, 10, 5, 10, 5, 20, 20, 10, 5, 5, 20, 10, 10, 5, 10, 20, 5, 5, 5, 10, 5, 20, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 20, 5, 5, 10, 10, 5, 5, 10, 5, 10, 10, 20, 10, 20, 5, 5, 5, 10, 10, 5, 5, 20, 5, 20, 5, 5, 5, 5, 10, 5, 20, 5, 5, 5, 20, 5, 5, 10, 10, 5, 5, 5, 20, 5, 5, 10, 5, 5, 5, 5, 20, 5, 10, 5, 5, 10, 5, 10, 10, 5, 10, 10, 5, 20, 20, 5, 5, 20, 5, 5, 5, 5, 5, 20, 10, 10, 5, 10, 5, 20, 20, 5, 5, 5, 5, 5, 10, 5, 20, 10, 20, 5, 20, 5, 20, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 10, 5, 5, 20, 20, 5, 20, 20, 5, 5, 5, 10, 20, 5, 5, 10, 10, 5, 5, 20, 10, 20, 5, 10, 5, 5, 5, 5, 20, 5, 5, 5, 5, 20, 20, 5, 20, 5, 10, 10, 5, 10, 20, 20, 20, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 20, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 20, 10, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 20, 10, 5, 5, 5, 5, 5, 10, 5, 20, 5, 5, 5, 5, 10, 5, 10, 20, 10, 20, 5, 5, 5, 5, 10, 5, 20, 5, 20, 5, 20, 5, 10, 5, 10, 10, 10, 5, 5, 5, 20, 20, 5, 5, 10, 10, 10, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 20, 5, 20, 10, 5, 5, 5, 5, 5, 5, 5, 20, 10, 10, 20, 10, 5, 5, 5, 5, 5, 5, 20, 5, 5, 20, 5, 10, 5, 20, 5, 10, 5, 5, 20, 5, 5, 5, 10, 5, 5, 5, 10, 5, 5, 20, 20, 5, 5, 10, 20, 10, 20, 5, 10, 20, 5, 5, 10, 5, 20, 20, 5, 5, 5, 5, 5, 5, 5, 10, 10, 10, 5, 20, 20, 5, 5, 20, 20, 10, 20, 5, 5, 5, 5, 5, 5, 10, 5, 5, 20, 5, 20, 20, 10, 5, 5, 5, 5, 10, 5, 10, 5, 5, 5, 5, 10, 20, 20, 5, 5, 5, 5, 20, 5, 5, 5, 5, 10, 20, 5, 5, 10, 5, 5, 10, 5, 10, 20, 20, 5, 10, 5, 10, 5, 5, 10, 10, 5, 5, 5, 5, 5, 5, 20, 5, 20, 20, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 20, 10, 10, 5, 5, 10, 5, 20, 5, 5, 10, 10, 20, 5, 5, 5, 20, 10, 5, 5, 5, 10, 5, 5, 5, 5, 5, 20, 5, 10, 5, 5, 10, 20, 5, 5, 20, 10, 5, 5, 20, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 10, 10, 5, 5, 5, 5, 10, 5, 20, 5, 10, 10, 5, 10, 5, 5, 5, 5, 5, 5, 10, 20, 5, 5, 20, 20, 5, 20, 10, 5, 5, 5, 10, 5, 20, 5, 5, 10, 20, 5, 5, 20, 10, 20, 10, 5, 5, 5, 10, 5, 5, 5, 5, 5, 10, 20, 10, 5, 5, 5, 5, 5, 5, 5, 5, 20, 10, 5, 20, 10, 5, 20, 10, 20, 10, 5, 20, 5, 5, 5, 5, 5, 20, 20, 10, 10, 10, 10, 5, 5, 20, 5, 10, 5, 5, 10, 20, 5, 5, 5, 5, 20, 5, 5, 5, 10, 5, 5, 10, 5, 20, 20, 10, 10, 5, 5, 10, 5, 5, 10, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 10, 5, 5, 5, 5, 10, 5, 5, 20, 10, 10, 5, 20, 20, 20, 5, 5, 5, 5, 20, 5, 10, 5, 5, 5, 5, 5, 10, 20, 5, 20, 5, 20, 5, 10, 20, 5, 5, 5, 5, 5, 5, 5, 10, 5, 20, 5, 5, 20, 5, 5, 5, 5, 5, 20, 10, 5, 10, 10, 5, 20, 10, 5, 20, 20, 5, 5, 20, 5, 5, 10, 5, 5, 10, 5, 5, 10, 10, 5, 5, 20, 10, 5, 20, 5, 10, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 20, 5, 10, 5, 10, 20, 10, 10, 5, 5, 10, 5, 20, 10, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 10, 20, 20, 5, 5, 5, 20, 5, 5, 5, 5, 10, 5, 20, 5, 5, 5, 20, 10, 5, 5, 5, 5, 10, 5, 5, 5, 10, 5, 5, 5, 5, 20, 5, 5, 5, 10, 20, 20, 20, 5, 20, 5, 10, 5, 10, 20, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 20, 20, 10, 5, 20, 5, 5, 5, 5, 5, 5, 10, 5, 5, 10, 5, 5, 5, 5, 20, 5, 5, 5, 20, 5, 5, 10, 5, 10, 5, 10, 20, 5, 5, 5, 5, 20, 10, 5, 5, 5, 20, 5, 10, 10, 5, 5, 10, 20, 5, 10, 5, 5, 5, 20, 10, 5, 20, 5, 5, 5, 20, 20, 10, 20, 10, 5, 20, 5, 10, 10, 20, 10, 5, 5, 10, 5, 20, 20, 5, 5, 5, 5, 10, 5, 10, 20, 20, 20, 10, 20, 5, 10, 5, 5, 5, 10, 5, 5, 5, 5, 10, 5, 5, 5, 20, 10, 20, 5, 20, 5, 5, 5, 20, 10, 5, 5, 20, 5, 10, 5, 5, 5, 10, 5, 5, 10, 10, 5, 20, 20, 20, 5, 5, 5, 5, 20, 5, 20, 20, 5, 10, 5, 20, 5, 10, 20, 5, 5, 10, 5, 5, 5, 5, 5, 10, 10, 5, 5, 5, 20, 5, 10, 5, 5, 5, 5, 5, 20, 5, 10, 10, 5, 5, 10, 20, 10, 5, 5, 5, 5, 10, 5, 5, 10, 5, 10, 5, 10, 5, 20, 5, 5, 10, 5, 20, 5, 5, 10, 5, 20, 5, 20, 20, 10, 5, 20, 10, 20, 20, 5, 20, 5, 20, 5, 5, 20, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 20, 20, 10, 5, 5, 20, 20, 20, 10, 20, 20, 20, 10, 10, 20, 5, 10, 5, 5, 10, 5, 5, 5, 20, 10, 20, 20, 20, 5, 5, 5, 10, 5, 5, 5, 20, 10, 5, 10, 5, 5, 10, 5, 5, 5, 20, 5, 20, 5, 5, 5, 5, 5, 10, 20, 10, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 10, 20, 5, 10, 20, 10, 10, 5, 5, 20, 5, 20, 5, 5, 20, 5, 20, 20, 5, 5, 5, 10, 20, 5, 5, 10, 5, 5, 5, 5, 5, 10, 5, 5, 5, 20, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 10, 5, 20, 10, 5, 5, 10, 5, 5, 10, 10, 10, 5, 5, 10, 5, 10, 5, 20, 10, 20, 10, 20, 20, 10, 5, 10, 10, 10, 5, 20, 5, 5, 5, 5, 10, 5, 5, 20, 20, 10, 10, 10, 5, 10, 5, 5, 20, 5, 5, 5, 5, 5, 5, 20, 5, 5, 10, 20, 5, 5, 5, 20, 10, 10, 10, 20, 20, 10, 5, 5, 10, 20, 20, 10, 5, 5, 5, 10, 10, 5, 20, 5, 10, 20, 20, 5, 5, 20, 10, 5, 20, 5, 5, 10, 5, 5, 5, 10, 5, 5, 10, 5, 5, 10, 5, 20, 5, 5, 20, 5, 5, 5, 20, 20, 5, 20, 20, 5, 5, 10, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 10, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 20, 20, 5, 10, 5, 5, 5, 5, 5, 10, 5, 20, 10, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 20, 5, 10, 5, 5, 20, 5, 5, 20, 20, 20, 20, 5, 10, 5, 20, 5, 5, 20, 5, 5, 5, 10, 5, 10, 5, 20, 5, 5, 5, 5, 20, 5, 5, 20, 20, 5, 5, 20, 5, 5, 5, 20, 10, 5, 5, 5, 5, 5, 20, 20, 5, 5, 5, 5, 5, 10, 10, 5, 10, 10, 10, 5, 5, 5, 20, 5, 20, 10, 20, 20, 5, 5, 10, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 10, 20, 5, 5, 5, 5, 5, 5, 10, 10, 5, 10, 10, 5, 10, 5, 20, 10, 10, 5, 5, 20, 5, 5, 10, 10, 10, 5, 10, 10, 10, 5, 10, 5, 5, 10, 5, 10, 20, 20, 5, 20, 5, 5, 5, 10, 10, 20, 5, 5, 5, 10, 5, 5, 5, 5, 5, 20, 5, 5, 10, 20, 5, 20, 5, 5, 10, 20, 10, 20, 5, 10, 5, 5, 5, 10, 20, 10, 5, 10, 5, 5, 5, 20, 5, 5, 10, 5, 5, 5, 5, 5, 20, 10, 10, 5, 20, 5, 20, 5, 5, 10, 5, 5, 20, 5, 20, 20, 10, 20, 10, 5, 20, 5, 20, 20, 20, 5, 5, 5, 10, 5, 20, 20, 10, 5, 20, 5, 5, 5, 20, 10, 5, 5, 20, 20, 5, 20, 20, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 5, 5, 5, 20, 5, 5, 10, 20, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 20, 5, 5, 5, 5, 5, 20, 5, 5, 10, 10, 20, 5, 20, 5, 5, 5, 20, 10, 5, 5, 5, 5, 10, 5, 20, 5, 5, 5, 5, 20, 20, 10, 5, 5, 5, 10, 5, 20, 5, 10, 5, 20, 5, 10, 5, 5, 5, 5, 5, 20, 20, 5, 5, 5, 5, 5, 20, 10, 20, 5, 5, 5, 10, 5, 5, 5, 20, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 20, 5, 20, 5, 5, 20, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 5, 10, 5, 10, 5, 5, 10, 5, 5, 10, 10, 20, 5, 20, 5, 5, 10, 20, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 20, 5, 10, 5, 5, 10, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 20, 5, 5, 5, 20, 10, 5, 5, 10, 5, 10, 5, 20, 5, 10, 5, 5, 20, 5, 10, 5, 5, 5, 10, 5, 5, 5, 5, 5, 20, 5, 5, 5, 20, 5, 10, 5, 20, 5, 5, 5, 10, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 5, 20, 20, 5, 5, 10, 5, 5, 5, 5, 10, 10, 10, 5, 5, 20, 5, 10, 20, 5, 5, 5, 5, 5, 10, 10, 5, 20, 5, 10, 5, 5, 5, 5, 20, 5, 20, 10, 20, 10, 10, 20, 5, 5, 5, 5, 5, 10, 5, 5, 20, 5, 5, 10, 5, 5, 5, 20, 20, 20, 5, 10, 20, 20, 10, 20, 20, 20, 5, 10, 20, 5, 10, 5, 10, 5, 20, 20, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 20, 10, 5, 5, 20, 5, 5, 5, 20, 5, 10, 20, 10, 20, 5, 20, 5, 5, 5, 5, 20, 5, 5, 5, 5, 20, 5, 5, 5, 5, 20, 5, 5, 20, 5, 20, 10, 5, 5, 5, 20, 10, 5, 5, 5, 20, 5, 10, 5, 5, 5, 5, 10, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 10, 5, 5, 5, 10, 10, 5, 5, 10, 20, 20, 20, 20, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 5, 5, 20, 5, 20, 5, 10, 20, 5, 10, 20, 5, 20, 5, 10, 20, 5, 20, 5, 20, 20, 5, 10, 5, 20, 5, 5, 5, 20, 10, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 20, 5, 5, 5, 5, 5, 5, 5, 10, 20, 10, 10, 10, 20, 5, 5, 10, 20, 5, 20, 20, 10, 5, 5, 20, 5, 20, 5, 20, 10, 5, 10, 5, 5, 5, 5, 5, 10, 5, 5, 10, 5, 5, 20, 5, 20, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 10, 5, 10, 20, 5, 20, 5, 10, 10, 5, 5, 10, 5, 20, 20, 5, 5, 5, 10, 20, 5, 5, 5, 5, 10, 5, 5, 5, 5, 20, 5, 10, 10, 5, 5, 20, 5, 10, 5, 20, 10, 10, 5, 10, 20, 5, 5, 10, 5, 5, 10, 5, 5, 5, 5, 10, 5, 5, 5, 10, 5, 10, 20, 20, 5, 10, 5, 20, 5, 10, 5, 5, 20, 5, 5, 20, 5, 5, 20, 5, 5, 10, 20, 5, 5, 5, 20, 20, 20, 5, 20, 10, 5, 5, 5, 5, 5, 5, 10, 5, 20, 5, 10, 5, 20, 5, 20, 5, 20, 5, 10, 5, 20, 20, 10, 5, 5, 10, 20, 5, 5, 20, 10, 5, 5, 5, 5, 5, 5, 5, 5, 20, 10, 10, 10, 10, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 5, 20, 20, 5, 5, 5, 5, 5, 5, 10, 5, 20, 20, 10, 20, 5, 5, 5, 5, 10, 20, 5, 20, 5, 5, 20, 5, 5, 5, 5, 5, 20, 20, 5, 5, 5, 10, 5, 5, 10, 10, 10, 20, 10, 5, 20, 5, 5, 10, 20, 5, 5, 10, 5, 5, 20, 5, 20, 5, 5, 5, 5, 5, 5, 20, 5, 20, 5, 5, 20, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 20, 5, 5, 5, 10, 20, 10, 5, 20, 20, 5, 20, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 20, 5, 10, 10, 20, 10, 5, 5, 10, 5, 5, 10, 10, 5, 5, 5, 5, 5, 20, 10, 5, 5, 20, 5, 5, 20, 5, 10, 10, 20, 20, 20, 10, 20, 5, 20, 20, 5, 5, 20, 5, 20, 5, 5, 20, 5, 20, 5, 5, 10, 5, 20, 10, 5, 5, 5, 5, 10, 5, 20, 5, 20, 20, 20, 5, 10, 5, 5, 5, 5, 10, 20, 10, 10, 20, 5, 10, 5, 5, 10, 5, 20, 5, 20, 10, 20, 5, 5, 5, 5, 20, 20, 5, 5, 5, 10, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 20, 5, 5, 5, 20, 5, 10, 20, 5, 20, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 10, 5, 10, 20, 5, 5, 5, 5, 5, 5, 10, 5, 10, 5, 10, 5, 5, 5, 10, 10, 20, 5, 5, 10, 20, 5, 20, 5, 20, 10, 5, 5, 5, 20, 5, 10, 5, 5, 5, 5, 5, 20, 20, 10, 5, 5, 10, 5, 5, 5, 10, 5, 10, 10, 5, 5, 5, 10, 5, 10, 5, 5, 5, 5, 5, 10, 5, 10, 10, 10, 10, 5, 5, 5, 5, 5, 20, 10, 5, 5, 5, 10, 5, 5, 20, 5, 5, 10, 5, 5, 10, 10, 10, 5, 10, 20, 5, 5, 20, 5, 20, 5, 5, 20, 5, 5, 20, 20, 5, 5, 20, 10, 5, 5, 5, 5, 5, 5, 5, 10, 5, 20, 5, 10, 5, 5, 5, 5, 5, 5, 5, 10, 5, 20, 10, 5, 20, 5, 5, 5, 5, 5, 5, 20, 5, 10, 5, 5, 10, 20, 20, 5, 5, 20, 5, 5, 20, 5, 10, 20, 20, 5, 5, 5, 5, 5, 5, 20, 20, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 20, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 20, 20, 20, 5, 5, 5, 10, 10, 5, 5, 10, 5, 5, 5, 10, 10, 5, 5, 10, 10, 10, 20, 5, 20, 5, 5, 5, 5, 5, 5, 5, 10, 10, 20, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 10, 10, 5, 10, 5, 5, 5, 5, 10, 10, 20, 10, 20, 5, 10, 20, 20, 20, 5, 5, 10, 5, 20, 20, 5, 20, 20, 5, 10, 5, 5, 10, 20, 5, 5, 5, 5, 5, 10, 5, 10, 5, 10, 20, 5, 5, 5, 5, 5, 20, 10, 20, 10, 20, 20, 5, 5, 20, 20, 5, 5, 10, 5, 5, 5, 20, 10, 10, 5, 5, 20, 5, 5, 5, 20, 10, 10, 5, 5, 5, 10, 5, 10, 10, 20, 5, 10, 5, 20, 5, 5, 5, 20, 10, 20, 5, 10, 5, 20, 10, 5, 5, 5, 5, 5, 5, 5, 20, 5, 5, 20, 5, 20, 5, 5, 20, 5, 10, 5, 5, 5, 5, 5, 5, 10, 5, 10, 5, 5, 20, 5, 10, 5, 5, 5, 10, 10, 5, 5, 5, 20, 5, 5, 20, 20, 5, 5, 10, 20, 5, 5, 5, 5, 5, 10, 20, 5, 5, 10, 20, 10, 20, 5, 10, 5, 5, 20, 20, 5, 5, 5, 10, 5, 5, 10, 5, 5, 5, 20, 20, 10, 5, 5, 20, 20, 5, 20, 5, 10, 20, 5, 5, 5, 10, 5, 5, 5, 10, 5, 5, 20, 10, 5, 20, 20, 20, 5, 5, 10, 5, 20, 20, 20, 20, 10, 5, 10, 10, 10, 5, 5, 5, 20, 5, 10, 10, 20, 5, 5, 5, 5, 5, 20, 5, 5, 20, 10, 5, 5, 20, 10, 5, 10, 10, 20, 10, 20, 5, 5, 5, 10, 10, 5, 5, 10, 20, 20, 5, 5, 5, 10, 5, 5, 20, 5, 5, 5, 5, 5, 10, 20, 10, 5, 10, 5, 5, 5, 5, 5, 5, 10, 5, 5, 10, 10, 5, 20, 5, 10, 5, 5, 5, 10, 5, 5, 10, 20, 10, 10, 20, 10, 10, 20, 5, 5, 5, 5, 10, 5, 5, 20, 20, 5, 10, 20, 10, 5, 5, 20, 5, 20, 20, 5, 5, 5, 10, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 5, 5, 5, 20, 5, 5, 5, 10, 20, 5, 5, 5, 5, 10, 5, 10, 20, 5, 10, 5, 5, 5, 20, 5, 20, 10, 5, 5, 5, 5, 20, 5, 5, 5, 10, 5, 5, 5, 5, 10, 20, 5, 5, 5, 10, 20, 10, 10, 5, 5, 20, 20, 20, 10, 20, 20, 10, 10, 5, 5, 5, 10, 5, 20, 20, 5, 5, 20, 20, 5, 5, 20, 5, 20, 5, 5, 5, 5, 10, 5, 10, 5, 5, 10, 5, 20, 5, 5, 10, 10, 5, 10, 5, 5, 5, 5, 5, 10, 5, 5, 10, 10, 5, 5, 5, 5, 5, 20, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 20, 10, 20, 10, 5, 5, 10, 5, 5, 5, 5, 5, 10, 20, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 5, 5, 10, 5, 10, 20, 20, 10, 5, 5, 20, 5, 10, 5, 5, 5, 5, 20, 5, 10, 20, 5, 5, 10, 20, 5, 5, 10, 10, 20, 5, 5, 5, 5, 5, 20, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 20, 20, 5, 5, 5, 5, 5, 5, 10, 10, 20, 5, 5, 20, 10, 5, 5, 20, 20, 20, 20, 20, 5, 20, 10, 5, 20, 5, 5, 5, 5, 10, 5, 20, 5, 5, 10, 5, 5, 10, 10, 20, 5, 5, 20, 5, 20, 20, 5, 5, 5, 5, 5, 5, 5, 10, 20, 10, 20, 10, 5, 5, 5, 10, 5, 20, 20, 10, 10, 20, 20, 5, 5, 20, 5, 10, 10, 5, 5, 20, 5, 20, 5, 5, 5, 10, 5, 5, 20, 5, 10, 5, 10, 5, 10, 5, 10, 10, 5, 20, 5, 20, 5, 10, 5, 5, 5, 5, 20, 5, 20, 5, 5, 20, 10, 5, 10, 10, 5, 5, 5, 5, 10, 5, 10, 5, 10, 5, 10, 20, 5, 5, 10, 5, 10, 5, 10, 10, 20, 20, 20, 20, 10, 5, 5, 5, 20, 5, 5, 10, 10, 20, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 20, 10, 10, 10, 10, 20, 10, 5, 5, 5, 5, 10, 20, 5, 5, 5, 5, 20, 5, 5, 5, 10, 5, 5, 10, 5, 10, 10, 5, 5, 5, 5, 20, 20, 20, 5, 20, 5, 5, 10, 10, 20, 5, 5, 5, 5, 20, 5, 20, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 20, 20, 10, 20, 5, 5, 5, 20, 10, 5, 10, 10, 20, 20, 5, 20, 10, 5, 5, 5, 20, 5, 5, 10, 5, 5, 5, 20, 10, 5, 5, 5, 10, 20, 5, 20, 20, 5, 20, 5, 5, 10, 10, 10, 5, 5, 20, 20, 5, 5, 20, 5, 5, 10, 20, 10, 20, 5, 5, 20, 5, 10, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 20, 20, 5, 20, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 20, 5, 20, 5, 5, 20, 5, 10, 5, 20, 20, 5, 10, 5, 5, 5, 5, 10, 5, 5, 5, 5, 10, 5, 5, 5, 10, 10, 5, 20, 20, 5, 5, 5, 20, 10, 5, 5, 5, 5, 5, 10, 20, 20, 20, 10, 10, 5, 5, 20, 10, 5, 10, 5, 5, 5, 5, 5, 20, 5, 20, 20, 5, 5, 5, 10, 5, 10, 10, 10, 5, 5, 10, 5, 5, 5, 5, 5, 5, 20, 5, 5, 10, 5, 5, 5, 5, 5, 5, 10, 5, 20, 20, 5, 5, 5, 20, 5, 5, 5, 5, 10, 5, 5, 10, 5, 20, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 5, 10, 5, 5, 5, 20, 5, 10, 5, 5, 10, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 20, 5, 20, 20, 5, 5, 5, 5, 5, 10, 10, 20, 5, 5, 10, 5, 10, 20, 10, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 20, 20, 5, 5, 5, 5, 5, 20, 5, 5, 20, 5, 10, 20, 5, 5, 5, 10, 5, 10, 5, 20, 10, 5, 10, 5, 5, 20, 20, 5, 10, 5, 5, 5, 10, 5, 5, 20, 10, 10, 20, 5, 20, 5, 5, 20, 5, 5, 10, 20, 5, 10, 20, 5, 5, 5, 5, 5, 5, 20, 5, 10, 5, 5, 20, 5, 5, 20, 5, 10, 5, 10, 10, 5, 5, 20, 5, 5, 10, 5, 5, 5, 10, 5, 5, 5, 5, 20, 5, 20, 10, 5, 5, 20, 5, 20, 5, 5, 5, 5, 5, 5, 20, 5, 20, 20, 5, 10, 10, 20, 20, 5, 5, 5, 5, 5, 20, 20, 20, 5, 10, 5, 5, 10, 5, 20, 10, 10, 20, 5, 5, 10, 5, 5, 10, 5, 5, 5, 20, 5, 5, 20, 5, 5, 5, 5, 20, 10, 5, 10, 10, 10, 10, 10, 5, 10, 5, 5, 5, 20, 10, 20, 10, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 20, 10, 5, 5, 5, 5, 5, 20, 5, 20, 5, 5, 20, 5, 5, 20, 5, 5, 5, 20, 10, 20, 5, 5, 10, 5, 5, 5, 20, 20, 5, 10, 5, 5, 20, 20, 5, 5, 5, 10, 5, 10, 5, 5, 5, 20, 10, 10, 5, 5, 5, 5, 5, 5, 5, 20, 20, 5, 20, 5, 10, 5, 20, 10, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 20, 20, 10, 10, 5, 5, 20, 5, 10, 5, 5, 5, 10, 5, 20, 5, 5, 5, 5, 5, 5, 5, 10, 5, 10, 5, 5, 5, 5, 20, 20, 5, 20, 5, 20, 5, 20, 5, 5, 20, 5, 5, 10, 20, 5, 20, 5, 20, 5, 5, 5, 10, 10, 5, 5, 20, 5, 5, 10, 5, 5, 10, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 10, 10, 5, 5, 5, 5, 10, 5, 10, 10, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 10, 5, 5, 5, 10, 5, 5, 5, 10, 5, 20, 5, 20, 10, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 10, 20, 5, 10, 5, 5, 20, 10, 5, 5, 5, 10, 10, 10, 5, 5, 5, 5, 5, 5, 20, 10, 20, 20, 5, 5, 20, 5, 5, 5, 5, 20, 5, 10, 5, 10, 5, 5, 20, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 20, 10, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 10, 10, 5, 10, 5, 5, 20, 5, 5, 10, 5, 5, 10, 20, 10, 5, 20, 5, 5, 5, 20, 20, 5, 10, 5, 5, 5, 5, 10, 10, 10, 20, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 20, 5, 10, 5, 5, 5, 5, 10, 5, 5, 5, 20, 5, 5, 20, 5, 5, 5, 5, 5, 10, 20, 5, 20, 5, 10, 10, 5, 5, 20, 5, 5, 5, 5, 5, 20, 5, 5, 5, 20, 20, 5, 20, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 20, 5, 5, 10, 5, 5, 5, 10, 20, 20, 5, 5, 5, 5, 5, 10, 5, 20, 20, 5, 5, 5, 20, 10, 5, 5, 5, 10, 5, 5, 5, 5, 20, 20, 5, 20, 10, 10, 20, 5, 5, 20, 5, 10, 20, 20, 10, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 5, 20, 5, 5, 5, 20, 5, 20, 5, 5, 5, 10, 10, 20, 5, 5, 5, 5, 10, 10, 5, 20, 5, 20, 5, 5, 5, 5, 20, 5, 20, 10, 5, 20, 5, 10, 10, 5, 20, 5, 5, 5, 10, 20, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 20, 20, 5, 20, 5, 5, 10, 20, 5, 10, 5, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 10, 5, 20, 5, 5, 5, 10, 10, 20, 5, 5, 5, 5, 5, 5, 20, 5, 5, 10, 20, 20, 5, 10, 20, 5, 5, 20, 10, 20, 5, 10, 5, 5, 20, 10, 5, 5, 20, 10, 5, 10, 10, 5, 5, 20, 20, 5, 20, 10, 5, 5, 5, 5, 5, 20, 5, 5, 10, 10, 10, 10, 5, 5, 5, 5, 5, 5, 5, 20, 20, 5, 20, 5, 10, 20, 5, 10, 5, 5, 5, 10, 5, 20, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 10, 20, 5, 5, 10, 5, 20, 20, 10, 10, 5, 5, 5, 5, 10, 20, 5, 10, 5, 20, 10, 5, 5, 20, 5, 20, 10, 5, 5, 5, 5, 5, 10, 5, 5, 5, 20, 5, 20, 5, 20, 5, 5, 20, 10, 20, 5, 20, 5, 20, 5, 10, 20, 20, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 20, 5, 10, 5, 5, 20, 5, 5, 10, 5, 5, 5, 5, 20, 5, 10, 5, 5, 5, 5, 5, 5, 5, 10, 10, 5, 20, 20, 5, 5, 5, 5, 20, 5, 20, 5, 5, 5, 5, 5, 10, 10, 20, 20, 5, 5, 5, 20, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 10, 20, 5, 5, 5, 20, 5, 10, 5, 5, 5, 5, 5, 20, 5, 5, 5, 10, 20, 20, 5, 10, 10, 5, 5, 10, 5, 5, 20, 10, 10, 5, 5, 10, 5, 5, 20, 5, 5, 5, 5, 20, 5, 10, 10, 5, 5, 10, 20, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 10, 5, 10, 10, 10, 5, 20, 10, 5, 5, 5, 5, 5, 20, 5, 5, 5, 10, 20, 5, 5, 5, 5, 5, 10, 5, 5, 5, 10, 5, 20, 20, 5, 20, 20, 20, 5, 10, 5, 5, 5, 5, 20, 20, 5, 5, 5, 10, 5, 5, 5, 10, 10, 5, 5, 5, 5, 5, 5, 5, 10, 5, 10, 5, 5, 5, 5, 20, 5, 20, 5, 20, 5, 5, 20, 5, 5, 5, 20, 10, 5, 5, 5, 5, 5, 5, 5, 10, 5, 20, 5, 5, 5, 5, 20, 20, 5, 20, 5, 10, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 20, 20, 5, 20, 10, 10, 5, 5, 5, 10, 5, 20, 5, 20, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 20, 10, 5, 10, 5, 10, 10, 10, 5, 5, 5, 5, 5, 10, 20, 5, 5, 5, 5, 5, 20, 10, 10, 10, 5, 10, 20, 20, 20, 5, 5, 10, 10, 10, 20, 5, 20, 5, 20, 5, 5, 5, 5, 20, 5, 20, 5, 5, 20, 5, 10, 5, 20, 5, 5, 5, 5, 5, 5, 10, 5, 5, 10, 5, 20, 5, 5, 5, 20, 20, 10, 5, 5, 20, 5, 10, 5, 10, 5, 5, 5, 5, 20, 5, 20, 5, 10, 20, 10, 10, 5, 5, 5, 20, 5, 5, 5, 5, 5, 10, 5, 10, 5, 5, 5, 20, 20, 5, 5, 10, 10, 10, 5, 5, 5, 5, 10, 5, 5, 5, 5, 20, 20, 10, 5, 5, 5, 5, 5, 5, 20, 20, 5, 10, 5, 10, 10, 10, 10, 5, 10, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 10, 5, 5, 5, 5, 5, 20, 20, 10, 20, 10, 5, 5, 5, 20, 5, 5, 5, 20, 10, 5, 10, 5, 20, 5, 10, 5, 5, 5, 5, 5, 5, 20, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 10, 20, 10, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 20, 10, 10, 5, 20, 5, 5, 5, 10, 5, 5, 10, 20, 10, 5, 20, 5, 20, 5, 5, 20, 10, 5, 20, 10, 5, 5, 5, 5, 10, 20, 5, 20, 20, 5, 5, 20, 10, 5, 5, 5, 10, 5, 10, 20, 5, 5, 5, 5, 5, 10, 10, 10, 10, 5, 10, 20, 5, 5, 20, 10, 10, 10, 20, 20, 20, 5, 20, 10, 5, 5, 10, 5, 5, 10, 5, 5, 5, 10, 5, 10, 5, 20, 20, 20, 5, 10, 10, 5, 5, 10, 5, 5, 5, 10, 20, 5, 10, 10, 10, 10, 20, 20, 5, 5, 20, 5, 5, 5, 5, 10, 5, 10, 5, 10, 5, 10, 10, 20, 5, 10, 5, 10, 20, 5, 5, 5, 10, 5, 10, 5, 5, 10, 5, 5, 5, 5, 10, 5, 20, 5, 5, 5, 5, 10, 10, 5, 20, 5, 5, 5, 10, 5, 10, 20, 5, 5, 20, 10, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 10, 20, 5, 20, 5, 10, 5, 5, 10, 5, 10, 5, 5, 5, 20, 10, 5, 5, 5, 5, 20, 10, 5, 5, 5, 5, 20, 5, 5, 5, 10, 5, 10, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 10, 5, 20, 10, 20, 5, 5, 20, 10, 5, 5, 10, 10, 10, 5, 10, 20, 5, 5, 5, 5, 10, 5, 20, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 20, 5, 20, 20, 5, 5, 10, 5, 5, 20, 10, 10, 5, 10, 20, 5, 10, 5, 5, 5, 10, 5, 5, 20, 5, 20, 20, 5, 5, 5, 20, 5, 5, 20, 5, 5, 5, 20, 5, 5, 5, 10, 5, 10, 10, 20, 20, 5, 5, 20, 5, 10, 20, 10, 5, 5, 5, 5, 10, 5, 5, 20, 5, 5, 5, 5, 5, 5, 10, 20, 20, 5, 5, 10, 20, 5, 5, 5, 10, 5, 5, 5, 20, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 20, 20, 5, 5, 5, 5, 5, 5, 20, 5, 5, 20, 10, 5, 10, 5, 5, 20, 10, 10, 5, 20, 20, 5, 5, 5, 5, 5, 5, 5, 20, 10, 5, 5, 5, 20, 10, 10, 5, 5, 5, 10, 10, 5, 10, 5, 5, 5, 20, 10, 5, 10, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 20, 10, 5, 5, 20, 20, 5, 20, 5, 10, 5, 10, 20, 10, 5, 10, 5, 5, 20, 5, 5, 5, 5, 20, 5, 10, 5, 5, 5, 20, 20, 5, 10, 10, 5, 10, 10, 20, 10, 5, 5, 10, 20, 5, 5, 10, 5, 5, 5, 20, 5, 5, 5, 5, 5, 20, 5, 20, 10, 10, 5, 5, 5, 20, 5, 5, 20, 10, 5, 5, 5, 10, 5, 10, 5, 5, 5, 10, 5, 5, 20, 5, 10, 5, 10, 10, 5, 5, 10, 20, 20, 5, 5, 10, 10, 10, 5, 5, 5, 10, 5, 5, 5, 10, 5, 10, 5, 10, 20, 5, 20, 10, 20, 10, 5, 5, 5, 20, 5, 5, 20, 5, 5, 5, 5, 5, 10, 10, 5, 5, 5, 5, 10, 5, 5, 20, 20, 5, 10, 5, 5, 5, 5, 20, 5, 5, 20, 5, 10, 20, 20, 20, 5, 5, 20, 5, 10, 5, 5, 5, 5, 20, 10, 20, 10, 5, 5, 5, 10, 20, 5, 20, 5, 20, 10, 20, 20, 5, 10, 10, 5, 20, 5, 20, 5, 20, 5, 20, 5, 20, 20, 5, 5, 5, 5, 20, 20, 5, 5, 10, 5, 5, 20, 5, 5, 5, 10, 10, 5, 5, 5, 5, 5, 20, 20, 5, 10, 10, 10, 20, 5, 20, 20, 5, 5, 20, 20, 5, 5, 5, 10, 5, 20, 20, 10, 5, 5, 10, 5, 10, 10, 5, 5, 10, 5, 5, 5, 20, 5, 10, 5, 20, 5, 20, 5, 5, 20, 20, 10, 20, 5, 5, 5, 5, 5, 5, 20, 10, 5, 5, 5, 5, 5, 10, 20, 5, 5, 20, 5, 5, 5, 5, 5, 5, 20, 5, 5, 10, 10, 5, 5, 20, 20, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 10, 10, 5, 10, 10, 5, 5, 10, 10, 10, 20, 10, 10, 5, 5, 5, 10, 5, 5, 10, 10, 5, 10, 5, 5, 5, 10, 20, 5, 20, 20, 10, 5, 10, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 20, 5, 5, 20, 5, 5, 10, 20, 20, 20, 5, 5, 5, 5, 10, 10, 5, 5, 5, 5, 5, 20, 20, 5, 20, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 20, 5, 10, 20, 5, 5, 10, 5, 5, 5, 20, 20, 5, 10, 5, 10, 10, 5, 5, 10, 5, 5, 20, 20, 20, 20, 5, 5, 5, 10, 20, 10, 10, 10, 5, 10, 5, 5, 20, 5, 10, 5, 10, 5, 5, 20, 5, 5, 20, 5, 20, 5, 20, 20, 5, 20, 5, 5, 20, 5, 5, 5, 20, 5, 10, 5, 5, 20, 5, 20, 5, 20, 20, 20, 5, 5, 20, 5, 5, 10, 10, 5, 5, 5, 5, 10, 10, 5, 20, 10, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 10, 5, 20, 5, 5, 20, 5, 10, 20, 5, 5, 10, 5, 5, 5, 5, 10, 5, 20, 5, 20, 5, 5, 5, 10, 10, 5, 5, 10, 5, 20, 20, 10, 5, 10, 5, 5, 5, 10, 5, 10, 5, 5, 10, 10, 5, 5, 20, 10, 5, 5, 5, 20, 5, 5, 20, 5, 5, 5, 20, 10, 10, 20, 10, 5, 10, 5, 5, 10, 5, 10, 10, 5, 5, 20, 10, 5, 10, 5, 5, 20, 20, 5, 20, 5, 5, 5, 5, 5, 20, 10, 5, 5, 5, 10, 10, 10, 5, 5, 20, 5, 5, 10, 5, 5, 5, 20, 5, 5, 5, 10, 5, 20, 10, 5, 5, 10, 5, 5, 10, 5, 5, 10, 5, 20, 5, 5, 5, 5, 20, 5, 20, 20, 5, 5, 5, 5, 5, 10, 10, 5, 5, 5, 20, 5, 20, 10, 5, 20, 5, 5, 5, 5, 5, 5, 10, 10, 5, 10, 10, 5, 5, 20, 5, 5, 20, 20, 10, 20, 5, 10, 5, 20, 10, 10, 10, 5, 5, 10, 5, 10, 5, 20, 5, 10, 5, 5, 10, 5, 5, 5, 10, 5, 5, 10, 10, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 10, 5, 5, 5, 5, 5, 5, 5, 20, 10, 5, 5, 10, 5, 5, 10, 10, 20, 20, 5, 10, 10, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 10, 20, 20, 5, 5, 20, 20, 5, 10, 20, 10, 5, 5, 10, 20, 10, 5, 20, 10, 10, 5, 5, 5, 5, 10, 5, 5, 5, 5, 20, 5, 5, 10, 10, 5, 20, 20, 5, 20, 10, 5, 20, 5, 5, 5, 20, 5, 5, 20, 20, 20, 5, 20, 20, 5, 10, 5, 10, 5, 5, 5, 10, 5, 20, 10, 5, 5, 5, 5, 20, 10, 5, 10, 10, 5, 5, 5, 5, 5, 5, 20, 20, 10, 10, 5, 10, 20, 20, 5, 20, 5, 20, 10, 5, 5, 5, 5, 10, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 20, 5, 5, 5, 20, 10, 10, 20, 5, 5, 20, 10, 10, 5, 10, 5, 10, 5, 5, 20, 5, 5, 5, 5, 5, 10, 5, 10, 5, 5, 5, 5, 5, 20, 5, 10, 10, 10, 5, 5, 5, 10, 5, 20, 5, 5, 5, 10, 5, 5, 5, 5, 10, 5, 10, 5, 5, 10, 20, 5, 5, 10, 10, 20, 20, 20, 20, 10, 20, 5, 5, 5, 5, 5, 5, 5, 20, 5, 10, 5, 10, 20, 5, 20, 5, 5, 5, 5, 5, 20, 5, 5, 5, 20, 5, 20, 20, 20, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 20, 10, 5, 10, 10, 5, 5, 20, 20, 10, 5, 10, 10, 20, 10, 5, 5, 10, 10, 5, 10, 5, 5, 5, 10, 5, 5, 10, 10, 10, 5, 20, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 10, 5, 10, 10, 5, 5, 5, 20, 10, 5, 5, 10, 20, 20, 20, 5, 10, 10, 20, 20, 5, 20, 5, 5, 20, 5, 10, 5, 20, 20, 10, 5, 5, 10, 5, 5, 5, 10, 5, 5, 10, 10, 5, 20, 5, 10, 10, 5, 5, 10, 10, 5, 5, 5, 5, 10, 5, 10, 20, 5, 5, 10, 5, 20, 5, 20, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 20, 20, 20, 5, 20, 5, 20, 10, 5, 5, 5, 5, 5, 5, 20, 5, 20, 5, 5, 5, 5, 10, 5, 5, 5, 5, 20, 5, 5, 5, 10, 5, 5, 10, 20, 5, 20, 5, 10, 5, 20, 20, 10, 20, 5, 10, 5, 5, 10, 10, 20, 5, 20, 5, 20, 5, 20, 20, 20, 5, 20, 5, 20, 5, 5, 10, 20, 5, 10, 5, 5, 10, 5, 5, 20, 10, 10, 10, 5, 5, 5, 5, 5, 10, 5, 10, 10, 20, 5, 5, 5, 5, 5, 10, 10, 10, 5, 10, 20, 20, 5, 5, 20, 20, 5, 20, 5, 5, 5, 10, 10, 5, 5, 5, 10, 20, 5, 10, 5, 5, 10, 5, 10, 5, 10, 10, 5, 20, 5, 5, 10, 5, 5, 10, 10, 20, 5, 20, 20, 20, 5, 5, 10, 20, 5, 20, 5, 20, 5, 5, 10, 5, 20, 10, 20, 5, 10, 5, 10, 10, 5, 20, 5, 20, 5, 5, 5, 10, 5, 5, 20, 5, 5, 5, 5, 5, 10, 10, 20, 5, 20, 20, 5, 5, 10, 5, 5, 20, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 20, 10, 20, 5, 5, 5, 10, 5, 20, 5, 10, 5, 5, 10, 5, 5, 5, 10, 10, 5, 5, 10, 5, 5, 20, 20, 20, 5, 5, 20, 20, 10, 5, 5, 10, 5, 5, 5, 20, 5, 5, 20, 5, 20, 5, 20, 5, 5, 5, 10, 5, 20, 20, 5, 10, 10, 5, 5, 5, 5, 5, 5, 10, 20, 5, 5, 5, 20, 5, 5, 5, 20, 5, 5, 5, 5, 10, 20, 10, 5, 5, 5, 10, 5, 20, 20, 10, 10, 5, 10, 5, 20, 20, 5, 5, 5, 10, 5, 20, 5, 5, 20, 5, 5, 10, 20, 5, 20, 20, 5, 5, 20, 20, 20, 20, 5, 5, 10, 5, 20, 5, 5, 5, 5, 10, 5, 5, 5, 5, 10, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 10, 20, 20, 20, 5, 5, 10, 5, 5, 10, 5, 5, 20, 5, 10, 5, 10, 10, 5, 5, 20, 5, 10, 20, 5, 5, 10, 20, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 10, 5, 5, 10, 10, 5, 5, 20, 5, 10, 5, 20, 5, 20, 20, 5, 20, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 20, 10, 20, 10, 5, 20, 20, 10, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 10, 20, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 20, 10, 5, 5, 10, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 20, 20, 20, 5, 5, 20, 20, 10, 5, 10, 5, 10, 5, 5, 5, 10, 5, 5, 20, 5, 20, 5, 10, 10, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 20, 5, 20, 5, 5, 5, 20, 5, 20, 5, 5, 5, 5, 5, 20, 5, 5, 20, 5, 10, 5, 5, 20, 5, 5, 5, 5, 5, 10, 10, 10, 5, 5, 5, 10, 10, 5, 20, 5, 10, 5, 5, 5, 5, 20, 20, 20, 5, 5, 5, 20, 20, 10, 5, 5, 5, 10, 5, 5, 5, 5, 5, 20, 5, 5, 20, 5, 10, 20, 5, 10, 5, 10, 10, 10, 10, 5, 10, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 20, 5, 5, 20, 5, 5, 5, 20, 5, 5, 5, 5, 5, 10, 10, 5, 5, 5, 5, 5, 20, 10, 10, 5, 10, 10, 20, 10, 5, 20, 10, 10, 5, 5, 20, 5, 5, 5, 5, 20, 5, 5, 5, 5, 10, 20, 5, 20, 20, 5, 20, 5, 10, 10, 5, 5, 10, 20, 5, 5, 5, 5, 20, 20, 5, 5, 5, 5, 10, 5, 5, 10, 5, 5, 5, 5, 5, 10, 5, 5, 10, 5, 10, 10, 10, 10, 5, 20, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 20, 10, 10, 5, 5, 5, 5, 5, 20, 5, 5, 5, 20, 5, 10, 10, 5, 5, 5, 5, 5, 20, 5, 20, 5, 5, 5, 5, 5, 10, 5, 10, 5, 5, 5, 5, 5, 10, 5, 5, 10, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 10, 20, 20, 5, 20, 20, 10, 10, 5, 5, 5, 5, 10, 10, 5, 10, 5, 10, 10, 5, 20, 5, 5, 20, 5, 5, 10, 5, 20, 5, 5, 5, 5, 20, 5, 5, 20, 5, 10, 5, 5, 20, 10, 10, 20, 10, 20, 20, 10, 20, 5, 10, 5, 20, 10, 5, 10, 5, 5, 10, 5, 5, 20, 5, 5, 10, 10, 10, 5, 5, 20, 5, 5, 5, 20, 5, 10, 10, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 10, 5, 5, 5, 20, 5, 20, 10, 10, 20, 20, 5, 20, 20, 10, 20, 5, 5, 5, 20, 5, 5, 5, 5, 20, 5, 10, 5, 10, 5, 5, 5, 20, 20, 5, 5, 10, 20, 10, 5, 5, 5, 10, 10, 5, 10, 20, 10, 10, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 20, 5, 20, 20, 5, 5, 5, 20, 20, 5, 5, 20, 5, 10, 20, 20, 5, 20, 10, 10, 5, 5, 5, 10, 5, 10, 5, 5, 5, 20, 5, 20, 10, 5, 5, 5, 5, 5, 5, 5, 10, 10, 10, 5, 10, 5, 5, 5, 20, 20, 20, 5, 5, 5, 5, 5, 20, 5, 20, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 10, 10, 5, 10, 20, 5, 10, 20, 10, 5, 5, 5, 5, 20, 5, 10, 5, 20, 5, 5, 5, 20, 5, 5, 10, 5, 5, 10, 5, 10, 10, 5, 5, 5, 5, 20, 5, 20, 20, 10, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 20, 5, 10, 10, 5, 5, 5, 20, 5, 5, 5, 20, 10, 20, 5, 5, 20, 10, 5, 5, 5, 20, 10, 5, 20, 5, 5, 5, 20, 5, 5, 5, 20, 5, 20, 5, 10, 5, 5, 10, 10, 10, 10, 5, 5, 5, 20, 20, 5, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 20, 20, 5, 5, 20, 5, 5, 5, 5, 20, 5, 5, 10, 10, 5, 5, 5, 5, 10, 5, 5, 20, 5, 5, 5, 20, 5, 5, 5, 5, 20, 10, 5, 5, 5, 5, 10, 5, 5, 10, 20, 5, 5, 20, 5, 10, 5, 5, 20, 10, 5, 5, 20, 10, 20, 20, 5, 5, 20, 10, 10, 20, 5, 20, 5, 10, 10, 10, 5, 10, 5, 10, 5, 5, 5, 10, 5, 5, 10, 5, 5, 20, 20, 10, 10, 5, 5, 10, 20, 5, 5, 5, 10, 5, 10, 5, 5, 5, 5, 5, 5, 20, 5, 5, 10, 5, 5, 5, 5, 20, 5, 5, 5, 20, 5, 20, 5, 5, 5, 10, 5, 10, 20, 5, 20, 5, 10, 20, 10, 5, 5, 20, 10, 5, 10, 5, 10, 20, 20, 5, 20, 5, 5, 10, 5, 10, 20, 10, 20, 10, 5, 5, 5, 20, 5, 5, 5, 5, 5, 20, 5, 10, 10, 5, 20, 5, 5, 5, 20, 10, 5, 10, 5, 5, 5, 5, 5, 10, 10, 5, 5, 10, 20, 20, 20, 5, 10, 5, 5, 5, 20, 5, 5, 5, 20, 5, 5, 5, 5, 10, 5, 5, 5, 5, 10, 20, 5, 20, 5, 5, 5, 5, 20, 20, 20, 5, 5, 10, 20, 5, 5, 5, 5, 5, 5, 5, 10, 20, 5, 5, 5, 20, 5, 5, 10, 20, 20, 10, 5, 5, 5, 5, 20, 5, 5, 20, 20, 5, 5, 10, 20, 5, 5, 20, 5, 5, 10, 5, 10, 5, 10, 20, 10, 5, 5, 10, 5, 5, 5, 5, 5, 5, 20, 20, 20, 5, 5, 5, 5, 10, 20, 5, 10, 20, 5, 5, 5, 10, 20, 5, 10, 5, 5, 5, 5, 5, 10, 5, 5, 5, 20, 5, 5, 20, 5, 20, 5, 20, 10, 10, 5, 5, 20, 5, 5, 5, 5, 20, 20, 5, 5, 20, 20, 5, 5, 5, 10, 20, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 10, 5, 5, 5, 5, 10, 5, 5, 5, 20, 5, 5, 10, 5, 5, 5, 5, 20, 10, 10, 5, 10, 10, 5, 20, 5, 5, 5, 5, 10, 5, 20, 20, 5, 5, 10, 10, 20, 20, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 5, 20, 5, 5, 10, 10, 20, 5, 20, 5, 20, 10, 5, 20, 10, 5, 10, 5, 5, 5, 5, 5, 10, 5, 5, 20, 5, 20, 5, 5, 20, 10, 10, 5, 10, 10, 5, 10, 5, 5, 5, 10, 10, 5, 5, 20, 10, 10, 10, 5, 5, 20, 5, 5, 20, 5, 5, 5, 20, 5, 5, 5, 10, 10, 5, 5, 5, 20, 20, 20, 5, 20, 5, 20, 5, 10, 5, 5, 5, 20, 5, 20, 10, 20, 5, 10, 5, 5, 20, 5, 5, 20, 10, 5, 5, 5, 5, 10, 10, 5, 5, 20, 20, 5, 10, 20, 5, 10, 5, 20, 5, 5, 10, 10, 5, 5, 5, 20, 5, 5, 5, 5, 10, 20, 5, 10, 5, 20, 5, 5, 5, 10, 5, 5, 5, 5, 20, 20, 5, 5, 10, 20, 5, 5, 5, 20, 5, 5, 10, 5, 10, 20, 10, 20, 10, 20, 5, 5, 20, 5, 5, 5, 5, 20, 10, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 20, 5, 20, 10, 5, 10, 10, 20, 10, 20, 5, 10, 10, 20, 5, 5, 5, 5, 5, 5, 5, 20, 20, 5, 20, 20, 20, 5, 5, 5, 5, 10, 5, 5, 5, 5, 20, 5, 5, 20, 10, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 10, 10, 5, 20, 10, 20, 10, 20, 5, 5, 5, 20, 5, 5, 5, 20, 10, 20, 5, 5, 5, 20, 5, 5, 5, 5, 5, 20, 5, 5, 10, 5, 5, 5, 5, 5, 20, 20, 5, 5, 5, 5, 5, 5, 5, 5, 20, 10, 5, 5, 20, 5, 5, 20, 20, 20, 5, 10, 10, 5, 5, 5, 5, 10, 20, 10, 10, 5, 5, 5, 5, 5, 20, 10, 5, 20, 10, 5, 10, 5, 5, 20, 5, 5, 5, 10, 5, 5, 5, 20, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 5, 10, 10, 5, 5, 5, 20, 5, 5, 10, 5, 5, 5, 20, 20, 20, 20, 5, 5, 5, 5, 10, 5, 20, 5, 5, 20, 20, 5, 5, 20, 10, 10, 5, 5, 5, 20, 5, 5, 5, 5, 5, 10, 5, 20, 20, 5, 5, 5, 10, 20, 5, 20, 5, 20, 5, 5, 5, 5, 5, 5, 5, 20, 20, 20, 5, 5, 20, 20, 10, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 20, 10, 20, 5, 5, 5, 5, 5, 5, 10, 5, 5, 10, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 10, 5, 5, 10, 10, 5, 10, 10, 5, 5, 20, 20, 5, 5, 5, 5, 5, 10, 5, 20, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 20, 20, 5, 20, 5, 20, 20, 20, 10, 20, 10, 5, 5, 10, 10, 20, 5, 5, 5, 5, 5, 10, 5, 10, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 20, 20, 20, 10, 5, 5, 10, 5, 5, 10, 5, 5, 20, 10, 20, 5, 5, 20, 20, 10, 20, 20, 5, 5, 5, 5, 5, 5, 5, 10, 5, 10, 10, 5, 10, 5, 20, 20, 10, 10, 5, 5, 20, 10, 5, 10, 10, 5, 5, 5, 5, 10, 5, 5, 20, 5, 5, 5, 5, 10, 5, 10, 5, 10, 5, 5, 5, 5, 10, 5, 5, 20, 5, 5, 5, 5, 5, 10, 20, 5, 10, 5, 10, 20, 10, 20, 20, 5, 5, 5, 5, 5, 20, 5, 10, 10, 5, 20, 5, 20, 10, 20, 10, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 10, 5, 10, 10, 20, 5, 20, 5, 5, 20, 5, 20, 5, 5, 5, 20, 10, 10, 5, 5, 5, 5, 5, 20, 10, 10, 5, 5, 5, 20, 10, 5, 10, 5, 5, 20, 10, 5, 5, 5, 5, 5, 5, 20, 5, 20, 5, 10, 10, 5, 5, 5, 5, 10, 5, 5, 5, 5, 10, 5, 10, 5, 10, 5, 10, 5, 5, 5, 5, 20, 5, 5, 20, 5, 5, 10, 5, 5, 5, 5, 10, 5, 5, 10, 5, 20, 5, 5, 5, 10, 10, 5, 20, 5, 5, 10, 5, 5, 10, 20, 5, 5, 5, 5, 10, 20, 20, 20, 5, 5, 10, 20, 5, 5, 5, 10, 5, 20, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 20, 5, 20, 5, 10, 5, 20, 20, 10, 10, 5, 5, 10, 5, 5, 5, 5, 5, 5, 10, 20, 10, 10, 5, 5, 5, 5, 5, 20, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 20, 10, 20, 20, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 10, 20, 10, 20, 5, 10, 5, 5, 5, 5, 5, 5, 10, 10, 5, 5, 10, 20, 10, 5, 5, 5, 5, 20, 5, 5, 20, 5, 5, 10, 5, 5, 5, 5, 5, 5, 10, 20, 5, 20, 20, 10, 10, 10, 20, 10, 20, 5, 5, 10, 10, 5, 20, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 10, 20, 10, 10, 5, 10, 20, 5, 5, 20, 5, 10, 5, 5, 10, 5, 20, 10, 10, 10, 5, 5, 5, 5, 5, 5, 5, 20, 5, 5, 20, 10, 5, 5, 20, 5, 5, 5, 5, 20, 20, 5, 10, 5, 20, 5, 5, 5, 5, 5, 5, 20, 10, 20, 20, 10, 5, 5, 5, 5, 5, 5, 20, 5, 10, 5, 5, 5, 5, 5, 10, 5, 5, 5, 20, 20, 5, 10, 20, 20, 20, 10, 20, 5, 5, 5, 5, 20, 5, 5, 5, 5, 20, 5, 5, 5, 5, 20, 5, 20, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 10, 5, 10, 5, 10, 5, 5, 20, 10, 20, 10, 20, 5, 5, 5, 5, 5, 10, 20, 10, 20, 20, 10, 5, 5, 10, 5, 5, 10}; + LeetCode_860_598 leetCode_860_598 = new LeetCode_860_598(); + System.out.println(leetCode_860_598.lemonadeChange(nums)); + } +} diff --git a/Week 03/id_598/LeetCode_94_598.java b/Week 03/id_598/LeetCode_94_598.java new file mode 100644 index 000000000..5476c852e --- /dev/null +++ b/Week 03/id_598/LeetCode_94_598.java @@ -0,0 +1,76 @@ +import com.sun.jmx.remote.internal.ArrayQueue; + +import java.util.*; + +/** + * @author northleaf + * @create 2019年10月30日 + */ +public class LeetCode_94_598 { + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + + + /** + * 中序遍历:左根右 + * 递归做法 + * @param root + * @return + */ + public List inorderTraversal1(TreeNode root) { + List list = new ArrayList<>(); + inorderTraversal(root,list); + return list; + } + + + /** + * 中序遍历 + * @param node + * @param list + */ + private void inorderTraversal(TreeNode node, List list) { + if (node != null) { + inorderTraversal(node.left,list); + list.add(node.val); + inorderTraversal(node.right,list); + } + } + + + /** + * 中序遍历:迭代法 + * 左根右 + * @param root + * @return + */ + public List inorderTraversal(TreeNode root) { + List list = new ArrayList<>(); + Stack < TreeNode > stack = new Stack < > (); + TreeNode curr = root; + //当前的节点不为null + //并且栈不为空 + while (curr != null || !stack.isEmpty()) { + //节点不为空 + //持续循环,将节点的左子树全部入栈 + while (curr != null) { + //将节点入栈 + stack.push(curr); + curr = curr.left; + } + //将栈顶元素出栈,此时一定是左节点 + curr = stack.pop(); + list .add(curr.val); + //获取它的右子树 + curr = curr.right; + } + return list; + } +} diff --git a/Week 03/id_598/NOTE.md b/Week 03/id_598/NOTE.md index a6321d6e2..ae19626ca 100644 --- a/Week 03/id_598/NOTE.md +++ b/Week 03/id_598/NOTE.md @@ -1,4 +1,130 @@ -# NOTE +## 贪心算法相关记录 + + + +最优子结构:问题能够分解成子问题来解决,子问题的最优解能递推到最终问题的最优解,这种子问题最优解称为最优子结构 + +贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法。 + +贪心算法与动态规划的不同在于它对每个子问题的解决方案都做出选择,不能回退。动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。 + +贪心算法可以解决一些最优化问题,如求图中的最小生成树,等,然而对于工程和生活中的问题,贪心法一般不能得到我们所要求的答案。 + +一旦一个问题可以通过贪心法来解决,那么贪心法一般是解决这个问题的最好方法。由于贪心法的高效性以及期所求得的答案比较接近最优结果,贪心法也可以用作辅助算法或者直接解决一些要法度结果不特别精确的问题。 + + + + + +## 二分查找的前提 + +1. 目标函数的单调性(单调递减或单调递增) +2. 存在上下界(bounded) +3. 能够通过索引访问(index accessible) + + + +## 代码模板 + +##### 深度优先遍历 + +```python +visited = set() + +def dfs(node, visited): +if node in visited: # terminator + # already visited + return + + visited.add(node) + + # process current node here. + ... + for next_node in node.children(): + if not next_node in visited: + dfs(next_node, visited) +``` + + + +##### 广度优先遍历 + +```python +def BFS(graph, start, end): + + queue = [] + queue.append([start]) + + + while queue: + node = queue.pop() + visited.add(node) + + process(node) + nodes = generate_related_nodes(node) + queue.push(nodes) + + # other processing work + ... +``` + + + +##### 二分查找 + +```python +left, right = 0, len(array) - 1 +while left <= right: + #以下代码也可以写成这样:mid = left + (right - left ) /2 + mid = (left + right) / 2 + if array[mid] == target: + # find the target!! + break or return result + elif array[mid] < target: + left = mid + 1 + else: + right = mid - 1 +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/Week 03/id_603/LeetCode_33_603.py b/Week 03/id_603/LeetCode_33_603.py new file mode 100644 index 000000000..d3e910b7d --- /dev/null +++ b/Week 03/id_603/LeetCode_33_603.py @@ -0,0 +1,40 @@ +class Solution: + def search(self, nums: List[int], target: int) -> int: + def find_rotateindex(l, r): + if (nums[l] < nums[r]): + return 0 + while (l <= r): + rotate = (l + r) // 2 + if (nums[rotate] < nums[rotate - 1]): + return rotate + + if (nums[rotate] > nums[r]): + l = rotate + 1 + else: + r = rotate - 1 + + # print(find_rotateindex(0,len(nums)-1)) + def find(l, r): + while (l <= r): + mid = (l + r) // 2 + if (nums[mid] == target): + return mid + if (nums[mid] > target): + r = mid - 1 + else: + l = mid + 1 + return -1 + + n = len(nums) + if (n == 0): + return -1 + if (n == 1): + return 0 if (nums[0] == target) else -1 + rotateindex = find_rotateindex(0, n - 1) + if (rotateindex == 0): + return find(0, n - 1) + + if (target >= nums[0]): + return find(0, rotateindex) + else: + return find(rotateindex, n - 1) diff --git a/Week 03/id_603/LeetCode_860_603.py b/Week 03/id_603/LeetCode_860_603.py new file mode 100644 index 000000000..3bfb4be02 --- /dev/null +++ b/Week 03/id_603/LeetCode_860_603.py @@ -0,0 +1,19 @@ +class Solution(object): + def lemonadeChange(self, bills): + five = ten = 0 + for bill in bills: + if bill == 5: + five += 1 + elif bill == 10: + if not five: return False + five -= 1 + ten += 1 + else: + if ten and five: + ten -= 1 + five -= 1 + elif five >= 3: + five -= 3 + else: + return False + return True diff --git a/Week 03/id_608/search-a-2d-matrix.go b/Week 03/id_608/search-a-2d-matrix.go new file mode 100644 index 000000000..6b10d8e59 --- /dev/null +++ b/Week 03/id_608/search-a-2d-matrix.go @@ -0,0 +1,39 @@ +package main + +import "fmt" + +func main() { + matrix := [][]int{ + {1, 3, 5, 7}, + {10, 11, 16, 20}, + {23, 30, 34, 50}, + } + result := searchMatrix(matrix, 11) + fmt.Println(result) +} + +func searchMatrix(matrix [][]int, target int) bool { + for _, value := range matrix { + l := len(value) + if l == 0 { + return false + } + if target >= value[0] && target <= value[l-1] { + left := 0 + right := l - 1 + for left <= right { + mid := (left + right) / 2 + if target == value[mid] { + return true + } else { + if target < value[mid] { + right = mid - 1 + } else { + left = mid + 1 + } + } + } + } + } + return false +} diff --git a/Week 03/id_608/search-in-rotated-sorted-array.go b/Week 03/id_608/search-in-rotated-sorted-array.go new file mode 100644 index 000000000..d58267930 --- /dev/null +++ b/Week 03/id_608/search-in-rotated-sorted-array.go @@ -0,0 +1,56 @@ +package main + +import ( + "fmt" +) + +func main() { + nums := []int{4, 5, 6, 7, 8, 9, 0, 1, 2} + result := search(nums, 2) + fmt.Println(result) +} + +func search(nums []int, target int) int { + count := len(nums) + turnIndex := findTurnIndex(nums) + result := -1 + if turnIndex == 0 { + result = bsearch(nums, target, 0, count-1) + } else { + if target < nums[0] { + result = bsearch(nums, target, turnIndex, count-1) + } else if target > nums[0] { + result = bsearch(nums, target, 0, turnIndex-1) + } else { + result = 0 + } + } + return result +} + +func bsearch(nums []int, target int, left int, right int) int { + if left > right { + return -1 + } + mid := (left + right) >> 1 + if nums[mid] == target { + return mid + } else { + if nums[mid] < target { + return bsearch(nums, target, mid+1, right) + } else { + return bsearch(nums, target, left, mid-1) + } + } +} + +func findTurnIndex(nums []int) int { + turnIndex := 0 + count := len(nums) + for i := 1; i < count; i++ { + if nums[i] < nums[i-1] { + turnIndex = i + } + } + return turnIndex +} diff --git a/Week 03/id_613/NOTE.md b/Week 03/id_613/NOTE.md index a6321d6e2..df22965a2 100644 --- a/Week 03/id_613/NOTE.md +++ b/Week 03/id_613/NOTE.md @@ -1,4 +1,102 @@ # NOTE - +## dfs模板,多叉树递归写法 +``` +visited = set() +def dfs(node, visited): + # terminator + if node in visited: + # already visited + return + + # add to set + visited.add(node) + + # process current node here + ... + + # recursion + for next_node in node.children(): + if not next_node in visited: + dfs(next_node, visited) +``` +## dfs模板,多叉树非递归写法,手工维护一个栈 +``` +def dfs(root): + if root is None: + return [] + + visited, stack = [], [root] + + while stack: + node = stack.pop() + visited.add(node) + + process(node) + + nodes = generate_related_node(node) + stack.push(nodes) + + # other process + ... +``` + +## bfs模板, 队列 +``` +def bfs(graph, start, end): + queue = [] + queue.append([start]) + visited.add(start) + + while queue: + node = queue.pop() + visited.add(node) + + process(node) + + nodes = generate_related_nodes(node) + queue.push(nodes) + + # other process work + ... +``` + +# 贪心算法 +## 适用贪心算法的场景 +问题能分解为子问题来解决,子问题的最优解能递推到最终问题的最优解。这种子问题最优解 +称为最优子结构 + +## 贪心算法和动态规划的不同 +贪心算法对每个子问题都做出选择,且不能回退;动态规划会保存以前的运算结果,并根据以前的 +计算结果对当前进行选择,有回退功能 + +## coin change可以用贪心法来解决的场景 +大额的硬币是其他小额硬币面额的倍数,因此每一步选择小额的硬币不如选择较大面额的硬币,从 +而具有最优子结构 + +## 贪心算法的难点 +1、怎么证明题目可以用贪心法来解决 +2、贪心算法有很多变种,可以从前到后贪心,也可以从后到前贪心,甚至需要预先做一些变换 + + +# 二分法 +## 能用二分查找的三个条件 +1、目标函数单调性(单调递增或递减) +2、存在上下界(bounded) +3、能随机访问(index accessible) + +## 二分查找模板 +``` +left, right = 0, array(length-1) +while (left <= right) { + mid = (left + right) / 2 + if (array[mid] == target) { + break or return result + } else if (array[mid] < target) { + left = mid + 1 + } else { + right = mid - 1 + } +} +``` \ No newline at end of file diff --git a/Week 03/id_613/java/src/main/Leetcode102SolutionOne.java b/Week 03/id_613/java/src/main/Leetcode102SolutionOne.java new file mode 100644 index 000000000..95fbc9479 --- /dev/null +++ b/Week 03/id_613/java/src/main/Leetcode102SolutionOne.java @@ -0,0 +1,120 @@ +import java.util.*; + +/** + * 二叉树的层次遍历 + * + * 时间复杂度O(n),空间复杂度O(n) + * + * + */ +class Leetcode102SolutionOne { + + private class TreeNode { + int val; + int level; + TreeNode left; + TreeNode right; + + TreeNode(int x, int l) { + val = x; + level = l; + } + + @Override + public String toString() { + return "val = " + val + ", level = " + level; + } + } + + List> result; + + public List> levelOrder(TreeNode root) { + result = new ArrayList<>(); + if (root == null) { + return result; + } + + bfs(root); + return result; + } + + public void bfs(TreeNode root) { + Deque queue = new ArrayDeque<>(); + Set visited = new HashSet<>(); + queue.addLast(root); + visited.add(root); + + while (!queue.isEmpty()) { + TreeNode cur = queue.pollFirst(); + visited.add(cur); + addNodeResult(cur); + + // process cur node + System.out.println(cur); + + List children = getChildren(cur); + for (TreeNode n : children) { + queue.addLast(n); + } + } + } + + private List getChildren(TreeNode cur) { + List l = new ArrayList<>(); + if (cur.left != null) { + l.add(cur.left); + } + if (cur.right != null) { + l.add(cur.right); + } + return l; + } + + private void addNodeResult(TreeNode node) { + if (result.size() <= node.level) { + Vector v = new Vector<>(); + v.add(node.val); + result.add(v); + } else { + Vector v = (Vector)result.get(node.level); + v.add(node.val); + } + } + + private TreeNode buildTree(Integer[] nums) { + if (nums == null) { + return null; + } + + if (nums.length == 1) { + return new TreeNode(nums[0], 0); + } + + List nodes = new ArrayList<>(); + TreeNode root = new TreeNode(nums[0], 0); + nodes.add(root); + for (int i = 1; i < nums.length; i++) { + if (nums[i] == null) { + continue; + } + + TreeNode cur = new TreeNode(nums[i], (int) (Math.log(i + 1) / Math.log(2))); + nodes.add(cur); + if (i % 2 == 0) { + nodes.get(i / 2 - 1).right = cur; + } else { + nodes.get(i / 2).left = cur; + } + } + + return root; + } + + public static void main(String[] args) { + //Integer[] arrayTree = new Integer[]{3, 9, 20, null, null, 15, 7}; + Integer[] arrayTree = new Integer[]{1, 2, 3, 4, null, null, 5}; + Leetcode102SolutionOne solution = new Leetcode102SolutionOne(); + TreeNode root = solution.buildTree(arrayTree); + System.out.println(solution.levelOrder(root)); + } +} diff --git a/Week 03/id_613/java/src/main/Leetcode33SolutionOne.java b/Week 03/id_613/java/src/main/Leetcode33SolutionOne.java new file mode 100644 index 000000000..c58d54048 --- /dev/null +++ b/Week 03/id_613/java/src/main/Leetcode33SolutionOne.java @@ -0,0 +1,74 @@ +/** + * 搜索旋转排序数组 + * + * 二分查找 + * 时间复杂度O(logn), 空间复杂度O(1) + * + * 执行用时 : * 1 ms * , 在所有 java 提交中击败了 * 90.56% * 的用户 + * 内存消耗 : * 36.2 MB * , 在所有 java 提交中击败了 * 85.46% * 的用户 + */ +class Leetcode33SolutionOne { + public int search(int[] nums, int target) { + // 边界条件 + if (nums == null || nums.length == 0) { + return -1; + } + + // 只有一个元素 + if (nums.length == 1) { + if (nums[0] == target) { + return 0; + } else { + return -1; + } + } + + // 2个元素及以上 + int pivot = _findPivot(nums); + if (pivot == 0) { + return binarySearch(nums, 0, nums.length - 1, target); + } + + int result = binarySearch(nums, 0, pivot-1, target); + if (result != -1) { + return result; + } else { + result = binarySearch(nums, pivot, nums.length - 1, target); + } + + return result; + } + + // normal binary search + public int binarySearch(int[] nums, int left, int right, int target) { + while (left <= right) { + int mid = left + (right - left) / 2; + if (nums[mid] == target) { + return mid; + } else if (nums[mid] > target) { + right = mid - 1; + } else { + left = mid + 1; + } + } + + return -1; + } + + private int _findPivot(int[] nums) { + int left = 0; + int right = nums.length - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + if (mid < nums.length - 1 && nums[mid] > nums[mid + 1]) { + return mid + 1; + } else if (nums[left] <= nums[mid]) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + return 0; + } +} diff --git a/Week 03/id_613/java/src/main/Leetcode55SolutionOne.java b/Week 03/id_613/java/src/main/Leetcode55SolutionOne.java new file mode 100644 index 000000000..4bd9a600c --- /dev/null +++ b/Week 03/id_613/java/src/main/Leetcode55SolutionOne.java @@ -0,0 +1,45 @@ +/** + * 跳跃游戏 + * + * 用分治法求解 + * + * 时间复杂度O(m^n),m跟数组中数值有关,空间复杂度O(1) + * + * leetcode上运行超出时间限制 + */ +class Leetcode55SolutionOne { + public boolean canJump(int[] nums) { + if (nums.length <= 1) { + return true; + } + + return helper(nums, 0, nums.length - 1); + } + + private boolean helper(int[] nums, int idx, int size) { + if (size <= 0) { + return true; + } + + if (nums[idx] == 0) { + return false; + } + + boolean re; + for (int i = 1; i <= nums[idx]; i++) { + re = helper(nums, idx + i, size - i); + if (re) { + return true; + } + } + + return false; + } + + public static void main(String[] args) { + int nums[] = new int[]{2, 3, 1, 1, 4}; +// int nums[] = new int[]{2, 0, 0}; + Leetcode55SolutionOne solution = new Leetcode55SolutionOne(); + System.out.println(solution.canJump(nums)); + } +} diff --git a/Week 03/id_613/java/src/main/Leetcode55SolutionTwo.java b/Week 03/id_613/java/src/main/Leetcode55SolutionTwo.java new file mode 100644 index 000000000..2fef83cdc --- /dev/null +++ b/Week 03/id_613/java/src/main/Leetcode55SolutionTwo.java @@ -0,0 +1,41 @@ +/** + * 跳跃游戏 + * + * 两重循环,建立一个boolean数组,每一个位置根据能跳的步数把响应的能到达的位置标记为true, + * 最后查看最后一个位置是否为true + * + * 时间复杂度O(n^2),空间复杂度O(n) + * + * 执行用时 : * 414 ms * , 在所有 java 提交中击败了 * 10.43% * 的用户 + * 内存消耗 : * 41.5 MB * , 在所有 java 提交中击败了 * 72.10% * 的用户 + */ +class Leetcode55SolutionTwo { + public boolean canJump(int[] nums) { + if (nums.length <= 1) { + return true; + } + + boolean[] res = new boolean[nums.length]; + res[0] = true; + for (int i = 0; i < nums.length; i++) { + if (!res[i]) { + continue; + } + + for (int j = 0; j <= nums[i]; j++) { + if (i + j < nums.length) { + res[i + j] = true; + } + } + } + + return res[res.length - 1]; + } + + public static void main(String[] args) { +// int nums[] = new int[]{2, 3, 1, 1, 4}; + int nums[] = new int[]{2, 0, 0}; + Leetcode55SolutionTwo solution = new Leetcode55SolutionTwo(); + System.out.println(solution.canJump(nums)); + } +} diff --git a/Week 03/id_613/java/src/main/LeetcodeMaxDepthSolutionOne.java b/Week 03/id_613/java/src/main/LeetcodeMaxDepthSolutionOne.java new file mode 100644 index 000000000..1ca39b77f --- /dev/null +++ b/Week 03/id_613/java/src/main/LeetcodeMaxDepthSolutionOne.java @@ -0,0 +1,83 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * 二叉树,max depth + *

+ * 采用递归,时间复杂度O(logN),空间复杂度O(1) + */ +class LeetcodeMaxDepthSolutionOne { + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + + private int maxDepth; + + public int maxDepth(TreeNode root) { + if (root == null) { + return 0; + } + + this.maxDepth = 0; + _helper(root, 1); + return this.maxDepth; + } + + private void _helper(TreeNode root, int depth) { + if (root == null) { + return; + } + + this.maxDepth = this.maxDepth < depth ? depth : this.maxDepth; + + if (root.left != null) { + _helper(root.left, depth + 1); + } + + if (root.right != null) { + _helper(root.right, depth + 1); + } + } + + private TreeNode buildTree(Integer[] nums) { + if (nums == null) { + return null; + } + + if (nums.length == 1) { + return new TreeNode(nums[0]); + } + + List nodes = new ArrayList<>(); + TreeNode root = new TreeNode(nums[0]); + nodes.add(root); + for (int i = 1; i < nums.length; i++) { + if (nums[i] == null) { + continue; + } + + TreeNode cur = new TreeNode(nums[i]); + nodes.add(cur); + if (i % 2 == 0) { + nodes.get(i / 2 - 1).right = cur; + } else { + nodes.get(i / 2).left = cur; + } + } + + return root; + } + + public static void main(String[] args) { + Integer[] arrayTree = new Integer[]{3, 9, 20, null, null, 15, 7}; + LeetcodeMaxDepthSolutionOne solution = new LeetcodeMaxDepthSolutionOne(); + TreeNode root = solution.buildTree(arrayTree); + System.out.println(solution.maxDepth(root)); + } +} diff --git a/Week 03/id_613/java/src/test/Leetcode33Test.java b/Week 03/id_613/java/src/test/Leetcode33Test.java new file mode 100644 index 000000000..9f4d88367 --- /dev/null +++ b/Week 03/id_613/java/src/test/Leetcode33Test.java @@ -0,0 +1,36 @@ +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * + */ +public class Leetcode33Test { + @Test + public void testSolutionOne1() { + int nums[] = new int[]{4, 5, 6, 7, 0, 1, 2}; + Leetcode33SolutionOne solution = new Leetcode33SolutionOne(); + assertEquals(solution.search(nums, 0), 4); + } + + @Test + public void testSolutionOne2() { + int nums[] = new int[]{4, 5, 6, 7, 0, 1, 2}; + Leetcode33SolutionOne solution = new Leetcode33SolutionOne(); + assertEquals(solution.search(nums, 3), -1); + } + + @Test + public void testBinarySearch1() { + int nums[] = new int[]{0, 1, 2, 4, 5, 6, 7}; + Leetcode33SolutionOne solution = new Leetcode33SolutionOne(); + assertEquals(solution.binarySearch(nums, 0, nums.length - 1, 4), 3); + } + + @Test + public void testBinarySearch2() { + int nums[] = new int[]{0}; + Leetcode33SolutionOne solution = new Leetcode33SolutionOne(); + assertEquals(solution.binarySearch(nums, 0, nums.length - 1, 4), -1); + } +} diff --git a/Week 03/id_613/java/src/test/Leetcode55Test.java b/Week 03/id_613/java/src/test/Leetcode55Test.java new file mode 100644 index 000000000..a60ae4161 --- /dev/null +++ b/Week 03/id_613/java/src/test/Leetcode55Test.java @@ -0,0 +1,40 @@ +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * + */ +public class Leetcode55Test { + @Test + public void testSolutionOne1() { + int nums[] = new int[]{2, 3, 1, 1, 4}; + Leetcode55SolutionOne solution = new Leetcode55SolutionOne(); + solution.canJump(nums); + assertEquals(solution.canJump(nums), true); + } + + @Test + public void testSolutionOne2() { + int nums[] = new int[]{3, 2, 1, 0, 4}; + Leetcode55SolutionOne solution = new Leetcode55SolutionOne(); + solution.canJump(nums); + assertEquals(solution.canJump(nums), false); + } + + @Test + public void testSolutionOne3() { + int nums[] = new int[]{2, 0, 0}; + Leetcode55SolutionOne solution = new Leetcode55SolutionOne(); + solution.canJump(nums); + assertEquals(solution.canJump(nums), true); + } + + @Test + public void testSolutionOne4() { + int nums[] = new int[]{3, 2, 1, 0, 4}; + Leetcode55SolutionOne solution = new Leetcode55SolutionOne(); + solution.canJump(nums); + assertEquals(solution.canJump(nums), false); + } +} diff --git a/Week 03/id_618/LeetCode_122_618.java b/Week 03/id_618/LeetCode_122_618.java new file mode 100644 index 000000000..0a8cede8b --- /dev/null +++ b/Week 03/id_618/LeetCode_122_618.java @@ -0,0 +1,38 @@ +class Solution { + + public int maxProfit(int[] prices) { + // 是否有股票 + boolean hasStock = false; + // 收益 + int profit = 0; + // 买入的时间 + int buy = 0; + + // 从第一天开始要倒数第二天 + for (int i = 0; i < prices.length - 1; i++) { + if (prices[i] < prices[i + 1]) { + // 明天要涨,没买则买入,买入了则不动 + if (hasStock) { + continue; + } else { + buy = i; + hasStock = true; + } + } else { + // 明天要跌,卖掉股票 + if (hasStock) { + profit += prices[i] - prices[buy]; + + hasStock = false; + } + } + } + + // 最后还有股票,则卖出 + if (hasStock) { + profit += prices[prices.length - 1] - prices[buy]; + } + + return profit; + } +} \ No newline at end of file diff --git a/Week 03/id_618/LeetCode_127_618.java b/Week 03/id_618/LeetCode_127_618.java new file mode 100644 index 000000000..0bc6c9abb --- /dev/null +++ b/Week 03/id_618/LeetCode_127_618.java @@ -0,0 +1,87 @@ +class Solution { + + public int ladderLength(String beginWord, String endWord, List wordList) { + if (!wordList.contains(endWord)) { + return 0; + } + + Map/* words */> patternMap = this.buildPatternMap(wordList); + Set visited = new HashSet<>(); + // 记录每个节点遍历的深度 + Map levelMap = new HashMap<>(); + + Queue queue = new LinkedList<>(); + queue.add(beginWord); + levelMap.put(beginWord, 1); + + while (!queue.isEmpty()) { + String currentWord = queue.poll(); + visited.add(currentWord); + + // 当前深度+1 + int level = levelMap.get(currentWord) + 1; + + String[] patterns = this.getPatterns(currentWord); + for (String pattern : patterns) { + List linkedWords = patternMap.get(pattern); + + // 当前单词没有与之匹配的其他单词 + if (linkedWords == null) { + continue; + } + + // 找到目标 返回level + if (linkedWords.contains(endWord)) { + return level; + } + + for (String linkedWord : linkedWords) { + // 已访问的节点不再继续 + if (visited.add(linkedWord)) { + queue.add(linkedWord); + levelMap.put(linkedWord, level); + } + } + } + } + + return 0; + } + + private Map> buildPatternMap(List wordList) { + Map> map = new HashMap<>(); + + for (String word : wordList) { + String[] patterns = this.getPatterns(word); + + for (String pattern : patterns) { + List list = map.get(pattern); + if (list == null) { + list = new ArrayList(); + map.put(pattern, list); + } + + list.add(word); + } + } + + return map; + } + + private String[] getPatterns(String word) { + int length = word.length(); + String[] patterns = new String[length]; + + for (int i = 0; i < length; i++) { + String pattern = word.substring(0, i) + "*"; + if (i < length - 1) { + pattern += word.substring(i + 1, length); + } + + patterns[i] = pattern; + } + + return patterns; + } + +} \ No newline at end of file diff --git a/Week 03/id_618/LeetCode_33_618 .java b/Week 03/id_618/LeetCode_33_618 .java new file mode 100644 index 000000000..2934c864c --- /dev/null +++ b/Week 03/id_618/LeetCode_33_618 .java @@ -0,0 +1,191 @@ + +/** + * 循环nums获取最小最大值,得到关键位置,然后右移使数组有序,再用二分法,其中第一步时间复杂度位O(n)感觉不好 + * + * @author hangwen + * + */ +class Solution1 { + public int search(int[] nums, int target) { + // 找到断点,获取右移的步树,使之成为有序数组 + int rigthMoveStep = this.getRigthMoveStep(nums); + // 右移,使nums有序 + this.moveRight(nums, rigthMoveStep); + + // 二分查找 + int locate = this.searhBinary(nums, target); + if (locate > -1) { + // loacate左移步数 + return this.moveLeft(nums, locate, rigthMoveStep); + } + return -1; + } + + private int getRigthMoveStep(int[] nums) { + for (int i = 0; i < nums.length - 1; i++) { + if (nums[i] > nums[i + 1]) { + return nums.length - 1 - i; + } + } + + return 0; + } + + private int moveLeft(int[] nums, int locate, int step) { + int len = nums.length; + if (step == 0 || step == len) { + return locate; + } + + int ret = locate - step; + return ret < 0 ? nums.length + ret : ret; + } + + private void moveRight(int[] nums, int step) { + int len = nums.length; + if (step == 0 || step == len) { + return; + } + + swap(nums, 0, len - step - 1); + swap(nums, len - step, len - 1); + swap(nums, 0, len - 1); + } + + private int searhBinary(int[] nums, int target) { + int left = 0; + int right = nums.length - 1; + + if (target < nums[left] || target > nums[right]) { + return -1; + } + + while (left <= right) { + int mid = (left + right) / 2; + int midVal = nums[mid]; + + if (midVal == target) { + return mid; + } else if (midVal < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + return -1; + } + + private void swap(int[] nums, int start, int end) { + + while (start < end) { + int temp = nums[start]; + nums[start] = nums[end]; + nums[end] = temp; + + start++; + end--; + } + } +} + +/** + * 用二分法找到最小值所在的位置,将数组分为左右两边,然后分别对左右两边进行二分查找,最多执行3次 O(logn) + * + * @author hangwen + * + */ +class Solution2 { + private int ret = -1; + + public int search(int[] nums, int target) { + if (nums.length == 0) { + return -1; + } + + if (nums.length == 1) { + return target == nums[0] ? 0 : -1; + } + + // 利用二分法查找最小值所在的位置 + int minValIndex = this.findMinValIndex(nums, target); + if (this.ret > -1) { + // 如果在上述过程中已经找到target,直接返回 + return ret; + } + + // 在左边的有序数组里寻找 + if (minValIndex > 0) { + this.ret = this.searhBinary(nums, 0, minValIndex - 1, target); + if (this.ret > -1) { + return this.ret; + } + } + + // 在右边的有序数组里寻找 + return this.searhBinary(nums, minValIndex, nums.length - 1, target); + + } + + private int findMinValIndex(int[] nums, int target) { + int left = 0; + int right = nums.length - 1; + + while (left <= right) { + int leftVal = nums[left]; + if (leftVal == target) { + this.ret = left; + return -1; + } + + int rightVal = nums[right]; + if (rightVal == target) { + this.ret = right; + return -1; + } + + if (left + 1 == right) { + return leftVal < right ? left : right; + } + + int mid = (left + right) / 2; + int midVal = nums[mid]; + if (midVal == target) { + this.ret = mid; + return -1; + } + + if (midVal < leftVal) { + right = mid; + } else if (midVal > rightVal) { + left = mid; + } else { + return left; + } + } + + return -1; + } + + private int searhBinary(int[] nums, int left, int right, int target) { + if (target < nums[left] || target > nums[right]) { + return -1; + } + + while (left <= right) { + int mid = (left + right) / 2; + int midVal = nums[mid]; + + if (midVal == target) { + return mid; + } else if (midVal < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + return -1; + } + +} diff --git a/Week 03/id_618/LeetCode_860_618.java b/Week 03/id_618/LeetCode_860_618.java new file mode 100644 index 000000000..fd7e9d25d --- /dev/null +++ b/Week 03/id_618/LeetCode_860_618.java @@ -0,0 +1,44 @@ +class Solution { + + /** + * 记录手头5元和10元的数量,每次手帐,增加对应货币,减去找零货币 + */ + public boolean lemonadeChange(int[] bills) { + int five = 0; + int ten = 0; + + for (int bill : bills) { + if (bill == 5) { + // 无须找零 + five++; + } else if (bill == 10) { + // 找一张5元 + if (five <= 0) { + return false; + } + + five--; + ten++; + } else if (bill == 20) { + if (ten <= 0) { + // 找3张5元 + if (five <= 2) { + return false; + } else { + five = five - 3; + } + } else { + // 找一张10元一张5元 + if (five <= 0) { + return false; + } else { + ten--; + five--; + } + } + } + } + + return true; + } +} \ No newline at end of file diff --git a/Week 03/id_628/LeetCode_153_628.java b/Week 03/id_628/LeetCode_153_628.java new file mode 100644 index 000000000..60402f7ee --- /dev/null +++ b/Week 03/id_628/LeetCode_153_628.java @@ -0,0 +1,40 @@ +//谴Ԥδ֪ijϽת +// +// ( 磬 [0,1,2,4,5,6,7] ܱΪ [4,5,6,7,0,1,2] ) +// +// ҳСԪء +// +// ԼвظԪء +// +// ʾ 1: +// +// : [3,4,5,1,2] +//: 1 +// +// ʾ 2: +// +// : [4,5,6,7,0,1,2] +//: 0 +// Related Topics ֲ + + + +//leetcode submit region begin(Prohibit modification and deletion) +class LeetCode_153_628 { + public int findMin(int[] nums) { + + int left = 0; + int right = nums.length - 1; + + while (left < right) { + int mid = left + ((right - left) >> 1); + + if (nums[mid] < nums[right]) + right = mid; + else + left = mid + 1; + } + return nums[left]; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_628/LeetCode_33_628.java b/Week 03/id_628/LeetCode_33_628.java new file mode 100644 index 000000000..7c4120547 --- /dev/null +++ b/Week 03/id_628/LeetCode_33_628.java @@ -0,0 +1,60 @@ +//谴Ԥδ֪ijϽת +// +// ( 磬 [0,1,2,4,5,6,7] ܱΪ [4,5,6,7,0,1,2] ) +// +// һĿֵдĿֵ򷵻򷵻 -1 +// +// ԼвظԪء +// +// 㷨ʱ临Ӷȱ O(log n) +// +// ʾ 1: +// +// : nums = [4,5,6,7,0,1,2], target = 0 +//: 4 +// +// +// ʾ 2: +// +// : nums = [4,5,6,7,0,1,2], target = 3 +//: -1 +// Related Topics ֲ + + + +//leetcode submit region begin(Prohibit modification and deletion) +class LeetCode_33_628 { + public int search(int[] nums, int target) { + int left = 0; + int right = nums.length - 1; + + while (left < right) { + int mid = left + ((right - left) >> 1); + + if (nums[mid] < nums[right]) { + //ת + right = mid; + }else{ + left = mid + 1; + } + } + //תΪleft + int changePoint = left; + left = 0; + right = nums.length - 1; + + while (left <= right) { + //е + int mid = left + ((right - left) >> 1); + //е㣬ƫ֮ȡģ + int realmid=(mid+changePoint) % nums.length; + // + if(nums[realmid]==target)return realmid; + if(nums[realmid]=max||root.val<=min)return false; + return isValid(root.left,Long.valueOf(root.val),min)&&isValid(root.right,max,Long.valueOf(root.val)); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 03/id_628/NOTE.md b/Week 03/id_628/NOTE.md index a6321d6e2..1579074fa 100644 --- a/Week 03/id_628/NOTE.md +++ b/Week 03/id_628/NOTE.md @@ -1,4 +1,241 @@ -# NOTE +# ܽ - +## ֪ʶ㡿 + +1--ȣ + +2̰-ֲŽȫŽ + +3ֲ-ţ + +## ѧϰܽ᡿ + +ģ塿 + +1DFS + +ݹģ + +```python +visited = set() + +def dfs(node, visited): +if node in visited: # terminator + # already visited + return + + visited.add(node) + + # process current node here. + ... + for next_node in node.children(): + if not next_node in visited: + dfs(next_node, visited) +``` + +ǵݹģ + +```python +def DFS(self, tree): + + if tree.root is None: + return [] + + visited, stack = [], [tree.root] + + while stack: + node = stack.pop() + visited.add(node) + + process (node) + nodes = generate_related_nodes(node) + stack.push(nodes) + + # other processing work + ... +``` + + + +```java +//ݽṹ㷨֮ +boolean found = false; // ȫֱԱ + +public void dfs(int s, int t) { + found = false; + boolean[] visited = new boolean[v]; + int[] prev = new int[v]; + for (int i = 0; i < v; ++i) { + prev[i] = -1; + } + recurDfs(s, t, visited, prev); + print(prev, s, t); +} + +private void recurDfs(int w, int t, boolean[] visited, int[] prev) { + if (found == true) return; + visited[w] = true; + if (w == t) { + found = true; + return; + } + for (int i = 0; i < adj[w].size(); ++i) { + int q = adj[w].get(i); + if (!visited[q]) { + prev[q] = w; + recurDfs(q, t, visited, prev); + } + } +} +``` + +2BFS + +```python +def BFS(graph, start, end): + + queue = [] + queue.append([start]) + visited.add(start) + + while queue: + node = queue.pop() + visited.add(node) + + process(node) + nodes = generate_related_nodes(node) + queue.push(nodes) + + # other processing work + ... +``` + + + +```java +//ݽṹ㷨֮ + +public void bfs(int s, int t) { + if (s == t) return; + boolean[] visited = new boolean[v]; + visited[s]=true; + Queue queue = new LinkedList<>(); + queue.add(s); + int[] prev = new int[v]; + for (int i = 0; i < v; ++i) { + prev[i] = -1; + } + while (queue.size() != 0) { + int w = queue.poll(); + for (int i = 0; i < adj[w].size(); ++i) { + int q = adj[w].get(i); + if (!visited[q]) { + prev[q] = w; + if (q == t) { + print(prev, s, t); + return; + } + visited[q] = true; + queue.add(q); + } + } + } +} + +private void print(int[] prev, int s, int t) { // ݹӡs->t· + if (prev[t] != -1 && t != s) { + print(prev, s, prev[t]); + } + System.out.print(t + " "); +} +``` + +3 + +```python +//ģ +left, right = 0, len(array) - 1 +while left <= right: + mid = (left + right) / 2 + if array[mid] == target: + # find the target!! + break or return result + elif array[mid] < target: + left = mid + 1 + else: + right = mid - 1 +``` + + + +```java +//ݽṹ㷨֮ +// ֲҵĵݹʵ +public int bsearch(int[] a, int n, int val) { + return bsearchInternally(a, 0, n - 1, val); +} + +private int bsearchInternally(int[] a, int low, int high, int value) { + if (low > high) return -1; + + int mid = low + ((high - low) >> 1); + if (a[mid] == value) { + return mid; + } else if (a[mid] < value) { + return bsearchInternally(a, mid+1, high, value); + } else { + return bsearchInternally(a, low, mid-1, value); + } +} +``` + + + +## Žջ + +1ʽĿ̷ʽнάķ + +2ݽṹ㷨ӳ䵽ԭջ + +## ѧϰܽҵ⡿ + +> κҵĿ +> +> ʹöֲңѰһ [4, 5, 6, 7, 0, 1, 2] мĵط + +ԭ + +мתߣһһ㡣 + +мתұߣһСһ㡣 + +ʹʿԽжֲ + + + +мmidֵСұֵ߽˵תмmidߣ߽м俪ʼһζ֡ + +мmidֵұֵ߽˵תмmidұߣмұ߽俪ʼһζ֡ + + + +룺 + +```java +public int search(int[] nums) { + + int left = 0; + int right = nums.length - 1; + + while (left < right) { + int mid = left + ((right - left) >> 1); + + if (nums[mid] < nums[right]) + right = mid; + else + left = mid + 1; + } + return nums[left]; +} +``` diff --git a/Week 03/id_633/LeetCode_153_633.java b/Week 03/id_633/LeetCode_153_633.java new file mode 100644 index 000000000..befa9622f --- /dev/null +++ b/Week 03/id_633/LeetCode_153_633.java @@ -0,0 +1,34 @@ +package lesson10; + +public class Leetcode_153_633 { + public int findMin(int[] nums) { + if (nums.length == 1) { + return nums[0]; + } + + int left = 0, right = nums.length - 1; + + if (nums[right] > nums[0]) { + return nums[0]; + } + + while (right >= left) { + int mid = left + (right - left) / 2; + + if (nums[mid] > nums[mid + 1]) { + return nums[mid + 1]; + } + + if (nums[mid - 1] > nums[mid]) { + return nums[mid]; + } + + if (nums[mid] > nums[0]) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; + } +} \ No newline at end of file diff --git a/Week 03/id_633/LeetCode_860_633.java b/Week 03/id_633/LeetCode_860_633.java new file mode 100644 index 000000000..f651a85f6 --- /dev/null +++ b/Week 03/id_633/LeetCode_860_633.java @@ -0,0 +1,29 @@ +package lesson10; + +public class LeetCode_860_633 { + + public boolean lemonadeChange(int[] bills) { + int five = 0, ten = 0; + for (int bill : bills) { + if (5 == bill) { + five++; + } else if (10 == bill) { + if (0 == five) { + return false; + } + five--; + ten++; + } else { + if (ten > 0 && five > 0) { + five--; + ten--; + } else if (five >= 3) { + five -= 3; + } else { + return false; + } + } + } + return true; + } +} diff --git a/Week 03/id_638/Leetcode_126_638.java b/Week 03/id_638/Leetcode_126_638.java new file mode 100644 index 000000000..33ec0bf86 --- /dev/null +++ b/Week 03/id_638/Leetcode_126_638.java @@ -0,0 +1,59 @@ +package test1.week3; + +import javax.swing.*; +import java.util.*; + +public class LeetCode_126_638 { + + public static void main(String[] args) { + System.out.println(ladderLength("hit","cog",Arrays.asList("hot","dot","dog","lot","log","cog"))); + + } + + public static List> ladderLength(String beginWord, String endWord, List wordList) { + Queue queue = new LinkedList(); + List> res = new LinkedList<>(); + Set visited = new HashSet<>(); + if (!wordList.contains(endWord)) return res; + queue.offer(beginWord); + visited.add(beginWord); + int step = 0; + + while (!queue.isEmpty()){ + step ++; + int size = queue.size(); + List strings = new LinkedList<>(); + for (int q = 0;q < size;q++){ + String temp = queue.poll(); + if (temp.equals(endWord)){ + res.add(strings); + return res; + } + for(String word:wordList){ + if (visited.contains(word))continue; + char[] wordArr = word.toCharArray(); + char[] tempArr = temp.toCharArray(); + int i = 0; + int diffCount = 0; + int[] nums = null; + for (char c:wordArr){ + if (c != tempArr[i]){ + diffCount ++; + } + if (diffCount > 1){ + break; + } + i++; + } + if (diffCount == 1){ + queue.offer(word); + visited.add(word); + strings.add(word); + } + System.out.println(strings); + } + } + } + return res; + } +} diff --git a/Week 03/id_638/Leetcode_33_638.java b/Week 03/id_638/Leetcode_33_638.java new file mode 100644 index 000000000..1faadd713 --- /dev/null +++ b/Week 03/id_638/Leetcode_33_638.java @@ -0,0 +1,72 @@ +package test1.week3; + +public class LeetCode_33_638 { + + public int search(int[] nums, int target) { + int left = 0; + int right = nums.length -1; + while(left <= right){ + int mid = left + (right - left)/2; + if(nums[mid] == target){ + return mid; + } + if(nums[right] == target) return right; + if(nums[left] == target) return left; + if(nums[mid] < nums[right]){ + if(target > nums[mid] && target < nums[right]){ + left = mid+1; + }else{ + right = mid-1; + } + }else{ + if(target < nums[mid] && target > nums[left]){ + right = mid-1; + }else{ + left = mid+1; + } + } + } + return -1; + } + +// public int check(int i){ +// +// } +//public static void main(String[] args) { +// int[] arr = {4, 5, 0,0, 1, 2, 2}; +// +// +// int temp = binarySearch(arr,0,arr.length,arr[arr.length-1],-1); +// if (temp == -1){ +// +// } +// System.out.println("temp:"+temp); +// +// System.out.println(); +//} +// +// +// public static int binarySearch(int a[],int low,int arrLen,int target,int res) { +// int mod = arrLen % 2; +// System.out.println("mod:"+mod); +// int s = arrLen / 2; +// System.out.println("s:"+s); +// int newArrLen = mod == 0 ? s -1:s; +// System.out.println("index:"+newArrLen+"arr[index]:"+a[newArrLen]); +// if (res == -1){ +// +// } +// int high = newArrLen; +// while (low <= high) { +// int mid = low + (high - low) / 2; +// if (a[mid] > target) +// high = mid - 1; +// else if (a[mid] < target) +// low = mid + 1; +// else +// return mid; +// } +// // +// return -1; +// } +} diff --git a/Week 03/id_638/NOTE.md b/Week 03/id_638/NOTE.md index a6321d6e2..77c1e14b5 100644 --- a/Week 03/id_638/NOTE.md +++ b/Week 03/id_638/NOTE.md @@ -1,4 +1,76 @@ # NOTE +visited = set() + +def dfs(node, visited): +if node in visited: # terminator + # already visited + return + + visited.add(node) + + # process current node here. + ... + for next_node in node.children(): + if not next_node in visited: + dfs(next_node, visited) + +一:dfs + 递归方式: + 1:判断终止条件,是否被访问过 + 2:处理当前曾逻辑 + 3:下探下一层 + 非递归: + def DFS(self, tree): + + if tree.root is None: + return [] + + visited, stack = [], [tree.root] + + while stack: + node = stack.pop() + visited.add(node) + + process (node) + nodes = generate_related_nodes(node) + stack.push(nodes) + + # other processing work + + 二、bfs + def BFS(graph, start, end): + + queue = [] + queue.append([start]) + visited.add(start) + + while queue: + node = queue.pop() + visited.add(node) + + process(node) + nodes = generate_related_nodes(node) + queue.push(nodes) + + # other processing work + + +二分查找; + 模版: + left, right = 0, len(array) - 1 + while left <= right: //大部分情况需要等于 + mid = (left + right) / 2 + if array[mid] == target: + # find the target!! + break or return result + elif array[mid] < target: + left = mid + 1 + else: + right = mid - 1 + + + + diff --git a/Week 03/id_643/LeetCode_153_643.java b/Week 03/id_643/LeetCode_153_643.java new file mode 100644 index 000000000..aa2e5f5b5 --- /dev/null +++ b/Week 03/id_643/LeetCode_153_643.java @@ -0,0 +1,24 @@ + public int findMin(int[] nums) { + if (nums.length == 1) { + return nums[0]; + } + int left = 0, right = nums.length - 1; + if (nums[right] > nums[0]) { + return nums[0]; + } + while (right >= left) { + int mid = left + (right - left) / 2; + if (nums[mid] > nums[mid + 1]) { + return nums[mid + 1]; + } + if (nums[mid - 1] > nums[mid]) { + return nums[mid]; + } + if (nums[mid] > nums[0]) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; + } diff --git a/Week 03/id_643/LeetCode_33_643.java b/Week 03/id_643/LeetCode_33_643.java new file mode 100644 index 000000000..d49b4c7a9 --- /dev/null +++ b/Week 03/id_643/LeetCode_33_643.java @@ -0,0 +1,31 @@ +public int search(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return -1; + } + int start = 0; + int end = nums.length - 1; + int mid; + while (start <= end) { + mid = start + (end - start) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[start] <= nums[mid]) { + if (target >= nums[start] && target < nums[mid]) { + end = mid - 1; + } else { + start = mid + 1; + } + } else { + if (target <= nums[end] && target > nums[mid]) { + start = mid + 1; + } else { + end = mid - 1; + } + } + + } + return -1; + + } + diff --git a/Week 03/id_648/LeetCode_122_648.java b/Week 03/id_648/LeetCode_122_648.java new file mode 100644 index 000000000..9791c75ff --- /dev/null +++ b/Week 03/id_648/LeetCode_122_648.java @@ -0,0 +1,47 @@ +/** + *买卖股票的最佳时机 + */ +public class LeetCode_122_648 { + public int maxProfit(int[] prices) { + int i=0; + int total =0; + int peak = prices[0]; + int valley = prices[0]; + while(i=prices[i]){ + i++; + } + peak = prices[i]; + total+=peak-valley; + } + + return total; + } + + /** + * 贪心算法 + * @param prices + * @return + */ + public int maxProfit2(int[] prices) { + int total =0; + for(int i=1;iprices[i-1]){ + total+=prices[i]-prices[i-1]; + } + } + return total; + } + + public static void main(String[] args) { + LeetCode_122_648 leetCode_122_648 = new LeetCode_122_648(); + int[] prices = new int[]{7,1,5,3,6,4}; + int total = leetCode_122_648.maxProfit(prices); + System.out.println(total); + } + +} diff --git a/Week 03/id_648/LeetCode_126_648.java b/Week 03/id_648/LeetCode_126_648.java new file mode 100644 index 000000000..3ef3de8bc --- /dev/null +++ b/Week 03/id_648/LeetCode_126_648.java @@ -0,0 +1,89 @@ +import javafx.util.Pair; + +import java.util.*; + +/** + * 单词接龙 II + */ +public class LeetCode_126_648 { + public List> findLadders(String beginWord, String endWord, List wordList) { + List> ans = new ArrayList<>(); + HashMap distance = new HashMap<>(); + HashMap> map = new HashMap<>(); + bfs(beginWord,endWord,wordList,map,distance); + ArrayList temp = new ArrayList(); + temp.add(beginWord); + findLaddersHelper(beginWord,endWord,map,distance,temp,ans); + return ans; + } + private void findLaddersHelper(String beginWord,String endWord,HashMap> map,HashMap distance,ArrayList temp,List> ans){ + if(beginWord.equals(endWord)){ + ans.add(new ArrayList(temp)); + return; + } + ArrayList wordNeighbors = map.getOrDefault(beginWord,new ArrayList<>()); + for(String neighbor:wordNeighbors){ + if(distance.get(beginWord)+1 ==distance.get(neighbor)){ + temp.add(neighbor); + findLaddersHelper(neighbor,endWord,map,distance,temp,ans); + temp.remove(temp.size()-1); + } + } + + } + + private void bfs(String beginWord, String endWord, List wordList,HashMap> map,HashMap distance){ + Queue queue = new LinkedList<>(); + queue.offer(beginWord); + distance.put(beginWord,0); + boolean isFound =false; + HashSet set = new HashSet<>(wordList); + int depth = 0; + while(!queue.isEmpty()){ + int size = queue.size(); + depth++; + for(int i=0;i wordNeighbors = getNeighbors(qword,set); + map.put(qword,wordNeighbors); + for(String neighbor:wordNeighbors){ + if(!distance.containsKey(neighbor)){ + distance.put(neighbor,depth); + if(neighbor.equals(endWord)){ + isFound=true; + } + queue.offer(neighbor); + } + } + } + if(isFound){break;} + } + + } + private ArrayList getNeighbors(String word,HashSet dict){ + ArrayList res = new ArrayList(); + char[] chars = word.toCharArray(); + for(char ch='a';ch<'z';ch++){ + for(int i=0;i> ans =leetCode_126_648.findLadders(beginWord,endWord,Arrays.asList(wordList)); + ans.forEach(list ->{ + System.out.println(list); + }); + } +} diff --git a/Week 03/id_648/LeetCode_127_648.java b/Week 03/id_648/LeetCode_127_648.java new file mode 100644 index 000000000..869e89b23 --- /dev/null +++ b/Week 03/id_648/LeetCode_127_648.java @@ -0,0 +1,62 @@ +import javafx.util.Pair; + +import java.util.*; + +/** + * 单词接龙 + * 给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则: + * + * 每次转换只能改变一个字母。 + * 转换过程中的中间单词必须是字典中的单词。 + * + */ +public class LeetCode_127_648 { + + public int ladderLength(String beginWord, String endWord, List wordList) { + HashMap> allComboDict = new HashMap<>(); + int L = beginWord.length(); + wordList.forEach(word ->{ + for(int i=0; i list = allComboDict.getOrDefault(newWord,new ArrayList<>()); + list.add(word); + allComboDict.put(newWord,list); + } + }); + HashMap visted = new HashMap<>(); + visted.put(beginWord,true); + Queue> q = new LinkedList<>(); + q.add(new Pair(beginWord,1)); + while(!q.isEmpty()){ + Pair node = q.remove(); + String word =node.getKey(); + Integer level = node.getValue(); + for(int i=0;i())){ + System.out.println("advancedWord:"+advancedWord+" advancedWord:"+endWord); + if(advancedWord.equals(endWord)){ + return level+1; + } + if(!visted.containsKey(advancedWord)){ + visted.put(advancedWord,true); + q.add(new Pair(advancedWord,level+1)); + } + + } + } + } + return 0; + } + + public static void main(String[] args) { + LeetCode_127_648 leetCode_127_648 = new LeetCode_127_648(); + String[] wordList = new String[]{"hot","dot","dog","lot","log","cog"}; + String beginWord = "hit", endWord = "cog"; + int num =leetCode_127_648.ladderLength(beginWord,endWord,Arrays.asList(wordList)); + System.out.println(num); + } + +} + + diff --git a/Week 03/id_648/LeetCode_200_648.java b/Week 03/id_648/LeetCode_200_648.java new file mode 100644 index 000000000..989dea483 --- /dev/null +++ b/Week 03/id_648/LeetCode_200_648.java @@ -0,0 +1,101 @@ +/** + * 岛的数量 + * 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。 + * + */ +public class LeetCode_200_648 { + int[] dx =new int[]{-1,1,0,0}; + int[] dy =new int[]{0,0,-1,1}; + char[][] g; + + /** + * 方法一 flood fill + * @param grid + * @return + */ + public int numIslands(char[][] grid) { + g = grid; + int isLand = 0; + for(int i=0;i=grid.length||j<0||j>=grid[0].length||grid[i][j]=='0'){ + return; + } + grid[i][j]='0'; + bfs(grid,i+1,j); + bfs(grid,i-1,j); + bfs(grid,i,j-1); + bfs(grid,i,j+1); + } + + private int sink(int i,int j){ + if(g[i][j]=='0'){ + return 0; + } + g[i][j]='0'; + for(int k=0;k=0&&x=0&&ynums[pivot+1]){ + return pivot+1; + } + if(nums[pivot]>nums[left]){ + left=pivot+1; + }else { + right=pivot-1; + } + } + return 0; + } + + public int search(int left,int right){ + while(left<=right){ + int pivot = (left+right)/2; + if(nums[pivot]==target){ + return pivot; + }else { + if(nums[pivot]target){ + return search(0,rotate_index); + }else { + return search(rotate_index,nums.length-1); + } + } + public static void main(String[] args) { + LeetCode_33_648 leetCode_33_648 = new LeetCode_33_648(); + int[] nums = new int[]{4,5,6,7,0,1,2}; + int index = leetCode_33_648.search(nums,0); + System.out.println(index); + } +} diff --git a/Week 03/id_648/LeetCode_529_648.java b/Week 03/id_648/LeetCode_529_648.java new file mode 100644 index 000000000..45f931dfe --- /dev/null +++ b/Week 03/id_648/LeetCode_529_648.java @@ -0,0 +1,50 @@ +/** + * 扫雷游戏 + */ +public class LeetCode_529_648 { + public char[][] updateBoard(char[][] board, int[] click) { + int row = click[0];int col = click[1]; + int m = board.length;int n = board[0].length; + if(board[row][col]=='M'){ + board[row][col]='X'; + return board; + } + int count =0; + for(int i=-1;i<2;i++){ + for(int j=-1;j<2;j++){ + if(i==0&&j==0)continue; + int r = row+i;int c=col+j; + System.out.println(""+r+","+c); + if(r>=m||r<0||c<0||c>=n)continue; + if(board[r][c]=='M'||board[r][c]=='X'){ + count++; + } + } + } + if(count>0){ + board[row][col]=(char)(count+'0'); + }else { + board[row][col] = 'B'; + for(int i=-1;i<2;i++){ + for(int j=-1;j<2;j++){ + if(i==0&&j==0)continue; + int r = row+i;int c=col+j; + if(r>=m||r<0||c<0||c>=n)continue; + if(board[r][c]=='E')updateBoard(board,new int[]{r,c}); + } + } + } + return board; + } + + public static void main(String[] args) { + LeetCode_529_648 leetCode_529_648 = new LeetCode_529_648(); + String str = "EEEEE&EEMEE&EEEEE& EEEEE"; + LeetCode_200_648 leetCode_200_648 = new LeetCode_200_648(); + char[][] board = leetCode_200_648.buildArray(str); + int[] click = new int[]{3,1}; + char[][] board2 = leetCode_529_648.updateBoard(board,click); + leetCode_200_648.printArray(board); + + } +} diff --git a/Week 03/id_648/LeetCode_860_648.java b/Week 03/id_648/LeetCode_860_648.java new file mode 100644 index 000000000..bfcb0d135 --- /dev/null +++ b/Week 03/id_648/LeetCode_860_648.java @@ -0,0 +1,37 @@ +/** + * 柠檬水找零 + */ +public class LeetCode_860_648 { + public boolean lemonadeChange(int[] bills) { + int five =0; + int ten = 0; + for(int i=0;i0&&ten>0){ + five--; + ten--; + }else if(five>2){ + five-=3; + }else { + return false; + } + } + } + return true; + } + + public static void main(String[] args) { + LeetCode_860_648 leetCode_860_648 = new LeetCode_860_648(); + int[] bills = new int[]{5,10,5,20,5,10,5,20}; + boolean flag = leetCode_860_648.lemonadeChange(bills); + System.out.println(flag); + } +} diff --git a/Week 03/id_653/LeetCode_127_653.java b/Week 03/id_653/LeetCode_127_653.java new file mode 100644 index 000000000..b2d91058b --- /dev/null +++ b/Week 03/id_653/LeetCode_127_653.java @@ -0,0 +1,48 @@ +import javafx.util.Pair; + +public class Solution { + HashMap wildMap = new HashMap(); + + public int ladderLength(String beginWord, String endWord, List wordList) { + int L = beginWord.length(); + for (String word : wordList) { + for (int i = 0; i < L; i++) { + String wildWord = word.substring(0, i) + "*" + word.substring(i + 1, L); + List list = (List) wildMap.getOrDefault(wildWord,new ArrayList<>()); + list.add(word); + wildMap.put(wildWord, list); + } + } + Queue> queue = new LinkedList(); + + Pair root = new Pair(beginWord, 1); + queue.add(root); + + HashMap visitedMap = new HashMap(); + visitedMap.put(beginWord,0); + while (!queue.isEmpty()) { + Pair node = queue.remove(); + String word = node.getKey(); + int level = node.getValue(); + + for (int i = 0; i < L; i++) { + String wildWord = word.substring(0, i) + "*" + word.substring(i + 1, L); + List list = (List) wildMap.getOrDefault(wildWord,new ArrayList<>()); + for(String nextWord:list) { + if (nextWord.equals(endWord)) { + return level+1; + } + + if (!visitedMap.containsKey(nextWord)) { + queue.add(new Pair<>(nextWord,level+1)); + visitedMap.put(nextWord,0); + } + } + } + + + } + + return 0; + } +} \ No newline at end of file diff --git a/Week 03/id_653/LeetCode_200_653.kt b/Week 03/id_653/LeetCode_200_653.kt new file mode 100644 index 000000000..6ce568d43 --- /dev/null +++ b/Week 03/id_653/LeetCode_200_653.kt @@ -0,0 +1,44 @@ +class Solution { + + fun dfs(grid: Array, index: Int, k: Int): Int { + grid?.let { + var nr = it.size + var nc = it[0].size + if (index < 0 || k < 0 || index >=nr || k >=nc || it[index][k].equals('0')) { + return 0 + } + it[index][k] = '0' + dfs(grid, index + 1, k) + dfs(grid, index - 1, k) + dfs(grid, index, k + 1) + dfs(grid, index, k - 1) + + return 1 + } + } + + fun numIslands(grid: Array): Int { + if (grid.isEmpty()||grid==null) { + return 0 + } + + grid?.filter { grid.isNotEmpty()}.let { + var nr = it.size + var nc = it[0].size + var sum = 0; + for (index in grid.indices) { + var array = grid[index] + for (k in array.indices) { + var item = array[k] + if (item.equals('1')) { + var res = dfs(grid, index, k) + sum += res + } + } + } + return sum + } + + return 0 + } +} \ No newline at end of file diff --git a/Week 03/id_653/LeetCode_33_653.kt b/Week 03/id_653/LeetCode_33_653.kt new file mode 100644 index 000000000..1526bdfa7 --- /dev/null +++ b/Week 03/id_653/LeetCode_33_653.kt @@ -0,0 +1,91 @@ +class Solution { + fun search(nums: IntArray?, target: Int): Int { + if (nums == null) { + return -1 + } + + if (nums.isEmpty()) { + return -1 + } + + var size = nums?.size + var left = 0 + var right = size-1 +// if (nums.contains()) +// var pivot = 0 + if (nums.size==1) { + if (nums[0]==target) { + return 0 + } else { + return -1 + } + } + var pivot = binararySearch(nums,left,right) + + if (nums[pivot]==target) { + return pivot + } + + if (pivot==0) { + return binararySearchTarget(nums,pivot,right,target) + } + + return if (target nums!![pivot+1]) { + return pivot+1 + } else { + if (nums[pivot] < nums[left]) { + right = pivot - 1 + } else { + left = pivot + 1 + + } + } + } + + return pivot + } + + + + fun binararySearchTarget(nums: IntArray?, left: Int, right: Int, target: Int): Int { + + var left = left + var right = right + while (left <= right) { + + var pivot = (left + right) / 2 + if (nums!![pivot] ==target) { + + return pivot + } else { + if (nums[pivot] > target) { + right = pivot - 1 + } else { + left = pivot + 1 + + } + } + } + + return -1 + } +} \ No newline at end of file diff --git a/Week 03/id_658/LeetCode_122_658.java b/Week 03/id_658/LeetCode_122_658.java new file mode 100644 index 000000000..f98c49aa9 --- /dev/null +++ b/Week 03/id_658/LeetCode_122_658.java @@ -0,0 +1,55 @@ +/* + * @lc app=leetcode.cn id=122 lang=java + * + * [122] 买卖股票的最佳时机 II + * + * https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/description/ + * + * algorithms + * Easy (52.58%) + * Total Accepted: 50.8K + * Total Submissions: 96.2K + * Testcase Example: '[7,1,5,3,6,4]' + * + * 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 + * + * 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。 + * + * 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 + * + * 示例 1: + * + * 输入: [7,1,5,3,6,4] + * 输出: 7 + * 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 + * 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。 + * + * + * 示例 2: + * + * 输入: [1,2,3,4,5] + * 输出: 4 + * 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 + * 。 + * 注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。 + * 因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 + * + * + * 示例 3: + * + * 输入: [7,6,4,3,1] + * 输出: 0 + * 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 + * + */ +class Solution { + public int maxProfit(int[] prices) { + int sum = 0; + for(int i = 0; i < prices.length - 1; i++) { + if((prices[i + 1] - prices[i]) > 0) { + sum += prices[i + 1] - prices[i]; + } + } + return sum; + } +} diff --git a/Week 03/id_658/LeetCode_200_658.java b/Week 03/id_658/LeetCode_200_658.java new file mode 100644 index 000000000..27dc78990 --- /dev/null +++ b/Week 03/id_658/LeetCode_200_658.java @@ -0,0 +1,43 @@ +/* + * @lc app=leetcode.cn id=200 lang=java + * + * [200] 岛屿数量 + */ + +// @lc code=start +class Solution { + public int numIslands(char[][] grid) { + + if (grid == null || grid.length == 0) { + return 0; + } + int nr = grid.length; + int nc = grid[0].length; + int num_islands = 0; + for (int r = 0; r < nr; ++r) { + for (int c = 0; c < nc; ++c) { + if (grid[r][c] == '1') { + ++num_islands; + dfs(grid, r, c); + } + } + } + return num_islands; + } + + private void dfs(char[][] grid, int r, int c) { + + int nr = grid.length; + int nc = grid[0].length; + + if (r < 0 || c < 0 || r >= nr || c >= nc || grid[r][c] == '0') { + return; + } + + grid[r][c] = '0'; + dfs(grid, r - 1, c); + dfs(grid, r + 1, c); + dfs(grid, r, c - 1); + dfs(grid, r, c + 1); + } +} diff --git a/Week 03/id_658/LeetCode_33_658.java b/Week 03/id_658/LeetCode_33_658.java new file mode 100644 index 000000000..ff0a427fc --- /dev/null +++ b/Week 03/id_658/LeetCode_33_658.java @@ -0,0 +1,30 @@ +/* + * @lc app=leetcode.cn id=33 lang=java + * + * [33] 搜索旋转排序数组 + */ + +// @lc code=start +class Solution { + public int search(int[] nums, int target) { + int lo = 0, hi = nums.length - 1; + while (lo <= hi) { + int mid = lo + (hi - lo) / 2; + int num = nums[mid]; + if ((nums[mid] < nums[0]) == (target < nums[0])) { + num = nums[mid]; + } else { + num = target < nums[0] ? Integer.MIN_VALUE : Integer.MAX_VALUE; + } + + if (num < target) + lo = mid + 1; + else if (num > target) + hi = mid - 1; + else + return mid; + } + return -1; + } +} +// @lc code=end diff --git a/Week 03/id_658/LeetCode_455_658.java b/Week 03/id_658/LeetCode_455_658.java new file mode 100644 index 000000000..893658cb1 --- /dev/null +++ b/Week 03/id_658/LeetCode_455_658.java @@ -0,0 +1,20 @@ +/* + * @lc app=leetcode.cn id=455 lang=java + * + * [455] 分发饼干 + */ + +// @lc code=start +class Solution { + public int findContentChildren(int[] g, int[] s) { + Arrays.sort(g); + Arrays.sort(s); + int l = 0, r = 0; + while (l < g.length && r < s.length) { + if (g[l] <= s[r++]) + l++; + } + return l; + } +} +// @lc code=end diff --git a/Week 03/id_658/LeetCode_860_658.java b/Week 03/id_658/LeetCode_860_658.java new file mode 100644 index 000000000..852b7a5c8 --- /dev/null +++ b/Week 03/id_658/LeetCode_860_658.java @@ -0,0 +1,37 @@ +/* + * @lc app=leetcode.cn id=860 lang=java + * + * [860] 柠檬水找零 + */ + +// @lc code=start +class Solution { + public boolean lemonadeChange(int[] bills) { + int five = 0, ten = 0; + for (int bill : bills) { + if (bill == 5) { + five++; + } else if (bill == 10) { + ten++; + five--; + if (five < 0) + return false; + } else { + if (ten > 0) { + ten--; + five--; + if (five < 0) + return false; + } else if (five > 0) { + five = five - 3; + if (five < 0) + return false; + } else { + return false; + } + } + } + return true; + } +} +// @lc code=end diff --git a/Week 03/id_658/NOTE.md b/Week 03/id_658/NOTE.md index a6321d6e2..d2cef5db0 100644 --- a/Week 03/id_658/NOTE.md +++ b/Week 03/id_658/NOTE.md @@ -1,4 +1,103 @@ -# NOTE +# 第三周学习总结 - +## 遍历搜索 +- 绝大多数情况下我们使用的搜索都是比较简单的暴力搜索,即将所有节点都遍历一次 +- 如果这个数据结构是没有任何特点的普通的树或者图,我们要做的就是保证所有的点都遍历一次并且仅访问一次 +- 对于节点的访问顺序不同可以分为 `深度优先(depth first search)` 和 `广度优先(breadth first search)` +- 还有其他的优先级不同的搜索方式可以定义,例如优先级优先 + +### 深度优先搜索 + +- 各种递归 +- 在处理当前层时,访问当前node,将node加入到已访问的结点中 +- 终止条件就是当前结点已经被访问过 +- 下转就是进入到它的子结点中 + - 二叉树就是左孩子和右孩子 + - 图就是它联通的相邻结点 + - 多叉树是它的children,把所有的children遍历一次 +- DFS递归写法 + + ```python + visited = set() + def dfs(node, visited): + # terminator + if node in visited: + # already visited + return + + visited.add(node) + # process current node here + ... + for next_node in node.chldren(): + if not next_node in visited: + dfs(next_node, visited) + ``` + +- DFS非递归写法,手动维护一个栈,模拟一个递归 + + ```python + def dfs(self, tree): + if tree.root is Node: + return [] + + visited, stack = [], [tree.node] + while stack: + node = stack.pop() + visited.add(node) + + process(node) + nodes = generate_related_nodes(node) + stack.push(nodes) + # other processing work + ... + ``` + +### 广度优先搜索 + +- 用数组队列来进行遍历 +- 在Java中更多用一个链表或者是双端队列(deque)来表示 +- BFS代码 + + ```python + def bfs(graph, start, end): + queue = [] + queue.append([start]) + visited.add(start) + + while queue: + node = queue.pop() + visited.add(node) + + process(node) + nodes = generate_related_nodes(node) + queue.push(nodes) + + # other porcessing work + ... + ``` + +## 贪心算法 + +- 贪心算法(Greedy)是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法 +- 贪心算法对每个子问题的解决方案都做出选择,不能回退 +- 动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能 + +### 适用性 + +- 贪心算法可以解决一些最优化问题,如:求图中的最小生成树、求哈夫曼编码等 +- 如果问题可以用贪心算法解决,那么一般是解决这个问题的最好办法 +- 由于贪心算法的高效性以及其所求得的答案比较接近最优结果,贪心算法也可以用作辅助算法或者直接解决一些要求结果不特别精确的问题 +- 一个问题能够分解成子问题来解决,子问题的最优解能递推到最终问题的最优解,这种子问题最优解称为最优子结构 + +## 最近重复性 + +- 最近重复性就是最近相似性,较远的相似性很多性质也会有很大不同 +- 要找最近重复性才最有可能复用代码 +- 最大公约数 + +## 学习方法上 + +- 改变自己的学习喜欢,不去死磕题目 +- 五毒神掌重要的是五遍,每遍不要花太长时间 +- 一定要去看高票代码和题解 diff --git a/Week 03/id_668/leetcode_102_668.py b/Week 03/id_668/leetcode_102_668.py new file mode 100644 index 000000000..cb30e50a7 --- /dev/null +++ b/Week 03/id_668/leetcode_102_668.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: binary_tree_level_order_traversal.py + @time: 2019/10/29 21:26 +""" + + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution(object): + """ + 给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。 + + 例如: + 给定二叉树: [3,9,20,null,null,15,7], + + 3 + / \ + 9 20 + / \ + 15 7 + 返回其层次遍历结果: + + [ + [3], + [9,20], + [15,7] + ] + """ + + def level_order(self, root): + """ + :type root: TreeNode + :rtype: List[List[int]] + """ + levels = [] + + if not root: + return levels + + def helper(node, level): + if len(levels) == level: + ''' + level层从0开始计数,而levels存储每一层的结点值,因此这里很巧妙的处理是依据当前 + levels中元素的个数判断是否需要添加新的一层;比如个数为0,表示正处理第0层,个数为3 + 表示正处理第3层。 + ''' + levels.append([]) + + levels[level].append(node.val) + + if node.left: + helper(node.left, level + 1) + + if node.right: + helper(node.right, level + 1) + + helper(root, 0) + + return levels + + def level_order2(self, root): + """ + :type root: TreeNode + :rtype: List[List[int]] + """ + if not root: + return [] + + # level表示这一层装了多少个节点 + ans, level = [], [root] + + while level: + ans.append([node.val for node in level]) + temp = [] + + for node in level: + temp.extend([node.left, node.right]) + + level = [leaf for leaf in temp if leaf] + + return ans + + def level_order3(self, root): + """ + :type root: TreeNode + :rtype: List[List[int]] + """ + if not root: + return [] + + res, level = [], [root] # 起始层为根节点所在的层 + + # level表示当前层里存储的节点,当当前层不存在结点时,处理结束 + while level: + res.append([node.val for node in level]) + level = [kid for node in level for kid in (node.left, node.right) if kid] + + return res + diff --git a/Week 03/id_668/leetcode_122_668.py b/Week 03/id_668/leetcode_122_668.py new file mode 100644 index 000000000..7197aa65d --- /dev/null +++ b/Week 03/id_668/leetcode_122_668.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: best_time_buy_sell_stock.py + @time: 2019/11/1 08:04 +""" + + +class Solution(object): + """ + 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 + 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。 + 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 + + 示例 1: + 输入: [7,1,5,3,6,4] + 输出: 7 + 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 +   随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。 + + 示例 2: + 输入: [1,2,3,4,5] + 输出: 4 + 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 +   注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。 +   因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 + + 示例 3: + 输入: [7,6,4,3,1] + 输出: 0 + 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 + + """ + + def max_profit(self, prices): + """ + :type prices: List[int] + :rtype: int + """ + profit = 0 + + for i in range(len(prices) - 1): + if prices[i + 1] > prices[i]: + profit += prices[i + 1] - prices[i] + + return profit + diff --git a/Week 03/id_668/leetcode_33_668.py b/Week 03/id_668/leetcode_33_668.py new file mode 100644 index 000000000..4f15ca9ab --- /dev/null +++ b/Week 03/id_668/leetcode_33_668.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: search_in_rorated_sorted_array.py + @time: 2019/11/2 15:34 +""" + + +class Solution(object): + """ + 假设按照升序排序的数组在预先未知的某个点上进行了旋转。 + ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 + + 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。 + 你可以假设数组中不存在重复的元素。 + 你的算法时间复杂度必须是 O(log n) 级别。 + + 示例 1: + 输入: nums = [4,5,6,7,0,1,2], target = 0 + 输出: 4 + + 示例 2: + 输入: nums = [4,5,6,7,0,1,2], target = 3 + 输出: -1 + """ + + def search(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: int + + 数组从任意位置劈开,至少有一半是有序的 + """ + left, right = 0, len(nums) - 1 + + while left <= right: + mid = left + (right - left) / 2 + + if target == nums[mid]: + return mid + + # 开始判断以mid为分界线的前段是否有序 + if nums[left] <= nums[mid]: + if nums[left] <= target <= nums[mid]: + right = mid - 1 + else: + # target > nums[mid] + left = mid + 1 + else: + if nums[mid] <= target <= nums[right]: + left = mid + 1 + else: + # target < nums[mid] + right = mid - 1 + + return -1 + + def search2(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: int + + 光头哥的代码,不容易理解 + """ + left, right = 0, len(nums) - 1 + + while left < right: + mid = left + (right - left) / 2 + + if (nums[0] > target) ^ (nums[0] > nums[mid]) ^ (target > nums[mid]): + left = mid + 1 + else: + right = mid + + return left if target in nums[left:left + 1] else -1 + + def search3(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: int + + """ + left, right = 0, len(nums) - 1 + + while left <= right: + mid = left + (right - left) / 2 + + if nums[mid] == target: + return mid + if nums[0] <= target < nums[mid] or target < nums[mid] < nums[0] or nums[mid] < nums[0] <= target: + right = mid - 1 + else: + left = mid + 1 + + return -1 + + def search4(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: int + + 分析: + 1. 先用二分查找法找到数组中逆序的位置 + 2. 以逆序的索引为界,将数组分解为两个有序的数组 + 3. 对前半有序数组执行标准的二分查找target + 4. 若找到,则直接返回值;若没有找到则继续对后半有序数组执行标准的二分查找target + 5. 返回查找的结果 + + 时间复杂度:O(logN)包含二分查找逆序的位置,以及后面一次或两次的标准二分查找 + 空间复杂度:O(1) + """ + + def bsearch(low, high): + """ + 标准的二分查找法 + """ + while low <= high: + m = low + (high - low) / 2 + + if nums[m] == target: + return m + elif nums[m] < target: + low = m + 1 + else: + high = m - 1 + + return -1 + + left, right, mid, = 0, len(nums) - 1, 0 + + while left <= right: + mid = left + (right - left) / 2 + + if nums[mid] < nums[0]: + right = mid - 1 + else: + left = mid + 1 + + ''' + mid有可能是前半有序数组的最后一个元素的索引,也有可能是后半有序数组的第一个元素的索引 + ''' + _index = mid + + if mid < len(nums) - 1: + # 获取后半有序数组的第一个元素的索引 + _index = mid if nums[mid] < nums[mid + 1] else mid + 1 + elif mid == len(nums) - 1: + if mid >= 1 and nums[mid] > nums[0]: + # 原数组是一个有序数组 + _index += 1 + else: + # 原数组是一个空数组 + _index = 0 + + res = bsearch(0, _index - 1) + return res if res != -1 else bsearch(_index, len(nums) - 1) + diff --git a/Week 03/id_668/leetcode_367_668.py b/Week 03/id_668/leetcode_367_668.py new file mode 100644 index 000000000..9af2c3629 --- /dev/null +++ b/Week 03/id_668/leetcode_367_668.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: valid_perfect_square.py + @time: 2019/11/2 13:34 +""" + + +class Solution(object): + """ + 给定一个正整数 num,编写一个函数,如果 num 是一个完全平方数,则返回 True,否则返回 False。 + 说明:不要使用任何内置的库函数,如  sqrt。 + + 示例 1: + 输入:16 + 输出:True + + 示例 2: + 输入:14 + 输出:False + + """ + + def is_perfect_square(self, num): + """ + :type num: int + :rtype: bool + + 分析: + 一个完全平方数一定是一个正整数的二次方,而非完全平方数一定是介于一个值的二次方与这个值加1的二次方之间 + + 时间复杂度:O(logn) + 空间复杂度:O(1) + """ + left, right, res = 0, num, False + + while left <= right: + mid = left + (right - left) / 2 + square = mid * mid + square_one = (mid + 1) * (mid + 1) + + if square < num < square_one: + # 非完全平方数 + break + elif square == num: + # 完全平方数 + res = True + break + elif num < square: + right = mid + else: + left = mid + 1 + + return res + + def is_perfect_square2(self, num): + """ + :type num: int + :rtype: bool + + 时间复杂度:这个不太容易算出来,我测试了几组数据,num -> while循环次数:16->3, 25->3, 529->6, 459684->12, 31741956->13 + 从中可发现,牛顿迭代法的时间复杂度接近O(1) + 空间复杂度:O(1) + """ + val = num + + while val * val > num: + # 依然采用从x坐标轴右侧逼近真实值的做法,循环结束的条件是val * val <= num,因此判断val * val 是否等于 num即可 + val = (val + num / val) / 2 + + return val * val == num + + def is_perfect_square3(self, num): + """ + :type num: int + :rtype: bool + + 对num求平方根、取整、求平方,如果最终值与num相同则表示num为完全平方数 + 如果num为非完全平方数,则求出的平方根为float数,取整后再求平方的值一定是小于num的值 + + 时间复杂度:O(1) + 空间复杂度:O(1) + """ + res = False + + if int(num ** .5) ** 2 == num: + res = True + + return res + diff --git a/Week 03/id_668/leetcode_69_668.py b/Week 03/id_668/leetcode_69_668.py new file mode 100644 index 000000000..32f5395d7 --- /dev/null +++ b/Week 03/id_668/leetcode_69_668.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: sqrt.py + @time: 2019/11/2 08:25 +""" + + +class Solution(object): + """ + 实现 int sqrt(int x) 函数。 + 计算并返回 x 的平方根,其中 x 是非负整数。 + 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。 + + 示例 1: + 输入: 4 + 输出: 2 + + 示例 2: + 输入: 8 + 输出: 2 + 说明: 8 的平方根是 2.82842..., +   由于返回类型是整数,小数部分将被舍去。 + + """ + + def mysqrt(self, x): + """ + :type x: int + :rtype: int + + 遍历法,这里用的是xrange,xrange是个生成器,因此节省空间但效率不如range快;其次需要遍历的元素从0到sqrt(x) + 时间复杂度:O(sqrt(x)) + 空间复杂度:O(1) + """ + val = 0 + + for i in xrange(x + 1): # 这里不可用range函数,否则2147395599这个数会产生MemoryError + if i * i <= x < (i + 1) * (i + 1): + val = i + break + + return val + + def mysqrt2(self, x): + """ + :type x: int + :rtype: int + + 二分查找法 + 时间复杂度:O(logx),x的值大小,也即O(logn) + 空间复杂度:O(1) + """ + l, r, mid = 0, x, 0 + + while l <= r: + mid = l + (r - l) / 2 # 这里不能用位运算,会超时 + square, square_one = mid * mid, (mid + 1) * (mid + 1) + + # 下面的逻辑判断,可通过画坐标法准确判断 + if square <= x < square_one: # 关键是这一步的判断,因为只保留整数部分,所以x < (mid + 1) * (mid + 1)的取值都是mid + break + elif x < square: + r = mid + else: + # 表示 x >= (mid + 1) * (mid + 1) + l = mid + 1 + + return mid + + def mysqrt3(self, x): + """ + :type x: int + :rtype: int + + 牛顿迭代法 + f(x) = x * x - a 当f(x) = 0,即为求平方根 + + 时间复杂度:接近O(1) + 空间复杂度:O(1) + """ + val = x # 求解x的正实根,假设正实根val起始值为x + + while val * val > x: + ''' + 这种处理相当于是从曲线右侧远大于正解x的地方不断去逼近真实值x + 当逼近值刚好等于正解x或刚到正解值左侧,此时这个逼近值就是只需要整数部分的正实根 + ''' + val = (val + x / val) / 2 + + return val + diff --git a/Week 03/id_668/leetcode_74_668.py b/Week 03/id_668/leetcode_74_668.py new file mode 100644 index 000000000..6f9be73da --- /dev/null +++ b/Week 03/id_668/leetcode_74_668.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: search_2d_matrix.py + @time: 2019/11/3 08:38 +""" + + +class Solution(object): + """ + 编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性: + 1. 每行中的整数从左到右按升序排列。 + 2. 每行的第一个整数大于前一行的最后一个整数。 + + 示例 1: + 输入: + matrix = [ + [1, 3, 5, 7], + [10, 11, 16, 20], + [23, 30, 34, 50] + ] + target = 3 + 输出: true + + 示例 2: + 输入: + matrix = [ + [1, 3, 5, 7], + [10, 11, 16, 20], + [23, 30, 34, 50] + ] + target = 13 + 输出: false + """ + + def search_matrix(self, matrix, target): + """ + :type matrix: List[List[int]] + :type target: int + :rtype: bool + + 暴力破解:将二维数组转成一维有序数组,然后使用二分查找判断target是否在其中 + 时间复杂度:遍历二维数组O(m*n),二分查找O(log(m*n)),因此最终的时间复杂度为O(m*n) + 空间复杂度:O(m*n) + """ + elems = [e for row in matrix for e in row] + + def search(): + left, right = 0, len(elems) - 1 + + while left <= right: + mid = left + (right - left) / 2 + + if elems[mid] == target: + return True + elif elems[mid] < target: + left = mid + 1 + else: + right = mid - 1 + + return False + + return search() + + def search_matrix2(self, matrix, target): + """ + :type matrix: List[List[int]] + :type target: int + :rtype: bool + + 分析: + 1. 先用二分查找确定target会出现在哪一行 + 2. 在那个行中,再用二分查找判断target是否在其中 + + 时间复杂度:判断target在哪个行的操作是O(logm),如果确定具体的行,然后在该行中查找target的操作是O(logn);因此时间复杂度为 + O(logm) + O(logn) + 空间复杂度:O(1) + """ + left, right, res = 0, len(matrix) - 1, False + + while left <= right: + mid = left + (right - left) / 2 + + if not matrix[mid]: + # 当前行为[] + break + + if matrix[mid][0] < target < matrix[mid][-1]: + # 在这一行中查找 + low, high = 0, len(matrix[mid]) - 1 + + while low <= high: + m = low + (high - low) / 2 + + if matrix[mid][m] == target: + res = True + break + elif matrix[mid][m] < target: + low = m + 1 + else: + high = m - 1 + break + elif target in [matrix[mid][0], matrix[mid][-1]]: + res = True + break + elif target < matrix[mid][0]: + right = mid - 1 + else: + left = mid + 1 + + return res + + def search_matrix3(self, matrix, target): + """ + :type matrix: List[List[int]] + :type target: int + :rtype: bool + + 把二维矩阵看成是有序的一维数组,再有序数组进行标准的二分查找。当需要获取一维数组中mid索引的值时, + 由于一维数组索引index对应二维矩阵的 row = index / n, col = index % n;因此能够方便的获取 + 二维矩阵中的值与target进行比较 + + 时间复杂度:O(log(m*n)) + 空间复杂度:O(1) + """ + if not matrix: + return False + + n = len(matrix[0]) # 获取矩阵列数 + left, right = 0, len(matrix) * n - 1 + + while left <= right: + mid = left + (right - left) / 2 + val = matrix[mid / n][mid % n] + + if val == target: + return True + elif val < target: + left = mid + 1 + else: + right = mid - 1 + + return False + diff --git a/Week 03/id_668/leetcode_860_668.py b/Week 03/id_668/leetcode_860_668.py new file mode 100644 index 000000000..1a8e005ce --- /dev/null +++ b/Week 03/id_668/leetcode_860_668.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: lemonade_change.py + @time: 2019/10/31 20:03 +""" +from collections import defaultdict + + +class Solution(object): + """ + 在柠檬水摊上,每一杯柠檬水的售价为 5 美元。 + 顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。 + 每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。 + 注意,一开始你手头没有任何零钱。 + 如果你能给每位顾客正确找零,返回 true ,否则返回 false 。 + + 示例 1: + 输入:[5,5,5,10,20] + 输出:true + 解释: + 前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。 + 第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。 + 第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。 + 由于所有客户都得到了正确的找零,所以我们输出 true。 + + 示例 2: + 输入:[5,5,10] + 输出:true + + 示例 3: + 输入:[10,10] + 输出:false + + 示例 4: + 输入:[5,5,10,10,20] + 输出:false + 解释: + 前 2 位顾客那里,我们按顺序收取 2 张 5 美元的钞票。 + 对于接下来的 2 位顾客,我们收取一张 10 美元的钞票,然后返还 5 美元。 + 对于最后一位顾客,我们无法退回 15 美元,因为我们现在只有两张 10 美元的钞票。 + 由于不是每位顾客都得到了正确的找零,所以答案是 false。 + + 提示: + 0 <= bills.length <= 10000 + bills[i] 不是 5 就是 10 或是 20 + + """ + + def lemonade_change(self, bills): + """ + :type bills: List[int] + :rtype: bool + + 暴力破解 + """ + d = defaultdict(int) + + # 5, 10, 20 + for bill in bills: + val = bill - 5 + + if val == 0: + # 5,直接往里添加 + d[bill] += 1 + elif val == 5: + # 10,+10, -5 + d[bill] += 1 + d[5] -= 1 + + if d[5] < 0: + return False + else: + # 20, 10+5, 5+5+5 + if d[10] > 0: + if d[5] < 1: + return False + + d[10] -= 1 + d[5] -= 1 + else: + if d[5] < 3: + return False + + d[5] -= 3 + + return True + + def lemonade_change2(self, bills): + """ + :type bills: List[int] + :rtype: bool + + 改进官方题解答案 + """ + five = ten = 0 + + for bill in bills: + if bill == 10: + ten += 1 + five -= 1 + elif bill == 20: + if ten >= 1: + # 如果有10,才会使用10,所以在找零中对10的使用不会异常 + ten -= 1 + five -= 1 + else: + # 如果没有10,则直接去掉3个5找零 + five -= 3 + else: + five += 1 + + if five < 0: + return False + + return True + + def lemonade_change3(self, bills): + """ + :type bills: List[int] + :rtype: bool + + 国际站高票 + """ + c = defaultdict(int) + + for bill in bills: + if bill == 10: + c[bill] += 1 + c[5] -= 1 + elif bill == 20: + # 此处不添加20,因为20无法参与找零 + if c[10] >= 1: + c[10] -= 1 + c[5] -= 1 + else: + c[5] -= 3 + else: + c[bill] += 1 + + if c[5] < 0: + return False + + return True + diff --git a/Week 03/id_673/best-time-to-buy-and-sell-stock-ii.cpp b/Week 03/id_673/best-time-to-buy-and-sell-stock-ii.cpp new file mode 100644 index 000000000..811bb0a6c --- /dev/null +++ b/Week 03/id_673/best-time-to-buy-and-sell-stock-ii.cpp @@ -0,0 +1,9 @@ +int maxProfit(vector& prices) { + int totalProfite = 0; + for (size_t i = 1; i < prices.size(); i++) + { + if (prices[i - 1] < prices[i]) + totalProfite += (prices[i]-prices[i-1]); + } + return totalProfite; +} diff --git a/Week 03/id_673/search-in-rotated-sorted-array.cpp b/Week 03/id_673/search-in-rotated-sorted-array.cpp new file mode 100644 index 000000000..7eb47500e --- /dev/null +++ b/Week 03/id_673/search-in-rotated-sorted-array.cpp @@ -0,0 +1,14 @@ +class Solution { +public: + int search(vector& nums, int target) { + int lo = 0, hi = nums.size() - 1; + while (lo < hi) { + int mid = (lo + hi) / 2; + if ((nums[0] > target) ^ (nums[0] > nums[mid]) ^ (target > nums[mid])) + lo = mid + 1; + else + hi = mid; + } + return lo == hi && nums[lo] == target ? lo : -1; + } +}; diff --git a/Week 03/id_673/word-ladder-ii.cpp b/Week 03/id_673/word-ladder-ii.cpp new file mode 100644 index 000000000..0d2aa46be --- /dev/null +++ b/Week 03/id_673/word-ladder-ii.cpp @@ -0,0 +1,64 @@ +class Solution { +public: + bool sim(const string& s1, const string& s2) { + int diff = 0; + for (int i = 0; i < s1.size(); ++i) { + diff += s1[i] != s2[i]; + } + return diff <= 1; + } + void dfs(const vector >& g, const vector& dfn, const vector& wordList, + int i, vector& path, vector >& paths) { + if (dfn[i] == 0) { + vector v(path); + reverse(v.begin(), v.end()); + paths.push_back(v); + return; + } + for (auto j : g[i]) { + if (dfn[j] == dfn[i] - 1) { + path.push_back(wordList[j]); + dfs(g, dfn, wordList, j, path, paths); + path.pop_back(); + } + } + } + vector> findLadders(string beginWord, string endWord, vector& wordList) { + wordList.push_back(beginWord); + int N = wordList.size(); + vector > g(N); + int endi = -1; + // 构图 + for (int i = 0; i < N; ++i) { + if (wordList[i] == endWord) endi = i; + for (int j = i + 1; j < N; ++j) { + if (sim(wordList[i], wordList[j])) { + g[i].push_back(j); + g[j].push_back(i); + } + } + } + if (endi == -1) return {}; + // 层级编号 + vector dfn(N, -1); + queue q; + q.push(N - 1); + dfn[N - 1] = 0; + while (!q.empty()) { + auto i = q.front(); + q.pop(); + for (auto j : g[i]) { + if (dfn[j] == -1) { + dfn[j] = dfn[i] + 1; + q.push(j); + } + } + } + if (dfn[endi] == -1) return {}; + // 回溯路径 + vector path{endWord}; + vector > paths; + dfs(g, dfn, wordList, endi, path, paths); + return paths; + } +}; diff --git a/Week 03/id_683/LeetCode_102_683.java b/Week 03/id_683/LeetCode_102_683.java new file mode 100644 index 000000000..e2e356877 --- /dev/null +++ b/Week 03/id_683/LeetCode_102_683.java @@ -0,0 +1,59 @@ +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +class Solution { + + List> result; + + // DFS + public List> levelOrder(TreeNode root) { + result = new ArrayList<>(); + helper(root, 0); + return result; + } + + private void helper(TreeNode root, int level) { + if (root == null) { + return; + } + if (result.size() == level) { + result.add(new ArrayList<>()); + } + result.get(level).add(root.val); + if (root.left != null) { + helper(root.left, level + 1); + } + if (root.right != null) { + helper(root.right, level + 1); + } + } + + // BFS + public List> levelOrderBFS(TreeNode root) { + List> result = new ArrayList<>(); + if (root == null) { + return result; + } + LinkedList queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + result.add(new ArrayList<>()); + + int level_length = queue.size(); + for (int i = 0; i < level_length; ++i) { + TreeNode node = queue.poll(); + + result.get(result.size() - 1).add(node.val); + + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + } + return result; + } +} \ No newline at end of file diff --git a/Week 03/id_683/LeetCode_122_683.java b/Week 03/id_683/LeetCode_122_683.java new file mode 100644 index 000000000..7113b52eb --- /dev/null +++ b/Week 03/id_683/LeetCode_122_683.java @@ -0,0 +1,13 @@ +class Solution { + public int maxProfit(int[] prices) { + int profit = 0; + if (prices == null) return profit; + + for (int i = 1; i < prices.length; ++i) { + if (prices[i] > prices[i - 1]) { + profit += prices[i] - prices[i - 1]; + } + } + return profit; + } +} \ No newline at end of file diff --git a/Week 03/id_683/LeetCode_153_683.java b/Week 03/id_683/LeetCode_153_683.java new file mode 100644 index 000000000..31dcc880d --- /dev/null +++ b/Week 03/id_683/LeetCode_153_683.java @@ -0,0 +1,25 @@ +class Solution { + public int findMin(int[] nums) { + if (nums == null) return -1; + int left = 0; + int right = nums.length - 1; + if (left == right) return nums[left]; + if (nums[right] > nums[0]) return nums[0]; + int mid = 0; + while (left <= right) { + mid = left + (right - left) / 2; + if (mid > 0 && nums[mid - 1] > nums[mid]) { + return nums[mid]; + } + if (mid < nums.length - 1 && nums[mid] > nums[mid + 1]) { + return nums[mid + 1]; + } + if (nums[0] < nums[mid]) { + left = mid + 1; + } else if (nums[0] > nums[mid]) { + right = mid - 1; + } + } + return -1; + } +} \ No newline at end of file diff --git a/Week 03/id_683/LeetCode_33_683.java b/Week 03/id_683/LeetCode_33_683.java new file mode 100644 index 000000000..951953b59 --- /dev/null +++ b/Week 03/id_683/LeetCode_33_683.java @@ -0,0 +1,22 @@ +class Solution { + public int search(int[] nums, int target) { + if (nums == null) { + return -1; + } + int left = 0; + int right = nums.length - 1; + int mid; + while (left < right) { + mid = left + ((right - left) >> 1); + if (target == nums[mid]) return mid; + else if (nums[0] <= nums[mid] && (target > nums[mid] || target < nums[0])) { + left = mid + 1; + } else if (nums[0] > nums[mid] && target < nums[0] && target > nums[mid]) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return left == right && nums[left] == target ? left : -1; + } +} \ No newline at end of file diff --git a/Week 03/id_683/LeetCode_433_683.java b/Week 03/id_683/LeetCode_433_683.java new file mode 100644 index 000000000..b66ceee51 --- /dev/null +++ b/Week 03/id_683/LeetCode_433_683.java @@ -0,0 +1,50 @@ +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Set; + +class Solution { + + public int minMutation(String start, String end, String[] bank) { + if (start == null || end == null || bank == null) { + return -1; + } + if (start.equals(end)) { + return 0; + } + int times = 0; + char[] mutations = new char[] {'T', 'G', 'A', 'C'}; + Set bankSet = new HashSet<>(); + for (int i = 0; i < bank.length; ++i) { + bankSet.add(bank[i]); + } + Set visited = new HashSet<>(); + LinkedList queue = new LinkedList<>(); + queue.offer(start); + visited.add(start); + while (!queue.isEmpty()) { + int size = queue.size(); + while (size > 0) { + String cur = queue.poll(); + if (end.equals(cur)) { + return times; + } + char[] curChars = cur.toCharArray(); + for (int i = 0; i < curChars.length; ++i) { + char old = curChars[i]; + for (char mutation : mutations) { + curChars[i] = mutation; + String next = new String(curChars); + if (bankSet.contains(next) && !visited.contains(next)) { + visited.add(next); + queue.offer(next); + } + } + curChars[i] = old; + } + size--; + } + times++; + } + return -1; + } +} \ No newline at end of file diff --git a/Week 03/id_683/LeetCode_455_683.java b/Week 03/id_683/LeetCode_455_683.java new file mode 100644 index 000000000..a43f148d0 --- /dev/null +++ b/Week 03/id_683/LeetCode_455_683.java @@ -0,0 +1,20 @@ +import java.util.Arrays; + +class Solution { + public int findContentChildren(int[] g, int[] s) { + if (g == null || s == null) { + return 0; + } + + Arrays.sort(g); + Arrays.sort(s); + int i = 0; + for (int j = 0; i < g.length && j < s.length;) { + if (s[j] >= g[i]) { + i++; + } + j++; + } + return i; + } +} \ No newline at end of file diff --git a/Week 03/id_683/LeetCode_515_683.java b/Week 03/id_683/LeetCode_515_683.java new file mode 100644 index 000000000..7f4655f74 --- /dev/null +++ b/Week 03/id_683/LeetCode_515_683.java @@ -0,0 +1,42 @@ +import java.util.LinkedList; +import java.util.List; + +import javax.swing.tree.TreeNode; + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List largestValues(TreeNode root) { + List result = new ArrayList<>(); + if (root == null) { + return result; + } + LinkedList queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + int size = queue.size(); + int max = Integer.MIN_VALUE; + while (size-- > 0) { + TreeNode node = queue.poll(); + if (node.val > max) { + max = node.val; + } + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + result.add(max); + } + return result; + } +} \ No newline at end of file diff --git a/Week 03/id_683/LeetCode_69_683.java b/Week 03/id_683/LeetCode_69_683.java new file mode 100644 index 000000000..6f020e57e --- /dev/null +++ b/Week 03/id_683/LeetCode_69_683.java @@ -0,0 +1,18 @@ +class Solution { + public int mySqrt(int x) { + if (x == 0) return 0; + long left = 1L; + long right = x >> 1l; + long mid; + while (left < right) { + mid = left + ((right - left) >> 1); + if (mid * mid <= x) { + if ((mid + 1) * (mid + 1) > x || mid * mid == x) return (int) mid; + else left = mid + 1; + } else if (mid * mid > x) { + right = mid - 1; + } + } + return (int) left; + } +} \ No newline at end of file diff --git a/Week 03/id_683/LeetCode_860_683.java b/Week 03/id_683/LeetCode_860_683.java new file mode 100644 index 000000000..91f5e7277 --- /dev/null +++ b/Week 03/id_683/LeetCode_860_683.java @@ -0,0 +1,32 @@ +import java.util.Arrays; + +class Solution { + public boolean lemonadeChange(int[] bills) { + if (bills == null) { + return true; + } + + int fiveDoller = 0, tenDoller = 0; + for (int i = 0; i < bills.length; ++i) { + if (bills[i] == 5) fiveDoller++; + else if (bills[i] == 10) { + if (fiveDoller > 0) { + fiveDoller--; + tenDoller++; + } else { + return false; + } + } else if (bills[i] > 10) { + if (fiveDoller > 0 && tenDoller > 0) { + fiveDoller--; + tenDoller--; + } else if (fiveDoller >= 3) { + fiveDoller -= 3; + } else { + return false; + } + } + } + return true; + } +} \ No newline at end of file diff --git a/Week 03/id_683/NOTE.md b/Week 03/id_683/NOTE.md index a6321d6e2..e35b4d976 100644 --- a/Week 03/id_683/NOTE.md +++ b/Week 03/id_683/NOTE.md @@ -1,4 +1,66 @@ -# NOTE +# 第三周总结 + +## 深度优先与广度优先搜索 + +在遍历树和图时要保证 + +- 每个节点都要访问到 +- 每个节点最好仅访问一次 +- 对节点的访问顺序没有要求 + +此时就可以用到深度优先搜索和广度优先搜索 + +比如二叉树的中序遍历就是深度优先搜索,先沿着一个分治一直走到尽头再回溯进行另一个分之的遍历。而广度优先搜索顾名思义,就是像水波纹一样,先将所有分支访问到,再对访问到的分支一次进行同样的操作。 + + +## 贪婪算法 + +贪婪算法的用武之地比较少,要求的条件比较苛刻,其应用最大的问题往往是证明贪婪可以得到最优解。在运用贪婪算法时重要的是找好贪婪的方式,是从头开始还是从末尾往回进行,甚至从中间某个切入点进行贪婪。 + +## 二分法 + +二分查找应用的前提: + +- 目标存在单调性 +- 存在上下界 +- 能够通过索引访问 + + + +## 使用二分查找,找到半有序数组中间无序的地方 + +这个问题,本质上就是查找数组中最小值的下标,第一个问题使在二分的过程中讨论向左规约还是向右规约: + +- 当mid左边是单调递增时(nums[0] < nums[mid]),向右规约 +- 当mid左边不具有单调性时(nums[0] > nums[mid]),向坐规约 + +每次循环通过判断mid和左右相邻值的大小来判断是否找到最小值 + +代码如下所示: + + public static int widget(int[] nums) { + if (nums == null) return -1; + int left = 0; + int right = nums.length - 1; + if (left == right) return nums[left]; + if (nums[right] > nums[0]) return 0; + int mid = 0; + while (left <= right) { + mid = left + (right - left) / 2; + if (mid > 0 && nums[mid - 1] > nums[mid]) { + return mid; + } + if (mid < nums.length - 1 && nums[mid] > nums[mid + 1]) { + return mid + 1; + } + if (nums[0] < nums[mid]) { + left = mid + 1; + } else if (nums[0] > nums[mid]) { + right = mid - 1; + } + } + return -1; + } + - diff --git a/Week 03/id_693/LeetCode_122_693.java b/Week 03/id_693/LeetCode_122_693.java new file mode 100644 index 000000000..f86b1d3f9 --- /dev/null +++ b/Week 03/id_693/LeetCode_122_693.java @@ -0,0 +1,69 @@ +package id_693; + +/** + * @Desc 122. 买卖股票的最佳时机 II https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/description/ + * @Auther 李雷(KyLin) + * @Date 2019/11/02 + */ +public class LeetCode_122_693 { + //效率不是太好,贪心思路,看到明天的比现在好就买,但是容易出现后天的比明天的好。这样买卖交易有点多余了 O(N)时间复杂度 + public int maxProfit(int[] prices) { + int spend = 0; + int earn = 0; + for (int i = 0; i < prices.length; i++) { + int price = prices[i]; + //买 + if (i < prices.length - 1 && prices[i + 1] > price) { + spend += price; + } + + //卖 + if (i > 0 && price > prices[i - 1]) { + earn += price - spend; + spend = 0; + } + + } + return earn; + } + + //网上思路,直接计算差值,然后就是赚的。每天都交易。有点不符合我的思维,时间复杂度O(n),但是比上面计算简单,真正效率高于上面 + public int maxProfit2(int[] prices) { + int earn = 0; + for (int i = 1; i < prices.length; i++) { + int temp = prices[i] - prices[i - 1]; + if (temp > 0) { + earn += temp; + } + } + return earn; + } + + public int maxProfit3(int[] prices) { + int earn = 0; + for (int i = 1; i < prices.length; i++) { + if (prices[i] > prices[i - 1]) { + earn += prices[i] - prices[i - 1]; + } + } + return earn; + } + + //官方的峰谷法,不错,这是最喜欢的,符合人类思维,第一个while找出前面的第一个可买的,然后在第二个一直找出相邻最高的值。漂亮 + public int maxProfit4(int[] prices) { + int spend = 0; + int earn = 0; + int i = 0; + while (i < prices.length - 1) { + while (i < prices.length - 1 && prices[i] >= prices[i + 1]) { + i++; + } + spend = prices[i]; + while (i < prices.length - 1 && prices[i] <= prices[i + 1]) { + i++; + } + earn += prices[i] - spend; + } + return earn; + } +} diff --git a/Week 03/id_693/LeetCode_126_693.java b/Week 03/id_693/LeetCode_126_693.java new file mode 100644 index 000000000..da3f096e2 --- /dev/null +++ b/Week 03/id_693/LeetCode_126_693.java @@ -0,0 +1,98 @@ +package id_693; + +import java.util.*; + +/** + * @Author 李雷(KyLin) + * @Desc 126. 单词接龙 II https://leetcode-cn.com/problems/word-ladder-ii/ + * @Date 2019/10/31 + */ +public class LeetCode_126_693 { + //BFS构造map图,然后dfs获取结果 + public List> findLadders(String beginWord,String endWord,List wordList) { + List> result = new ArrayList<>(); + Set words = new HashSet<>(wordList); + if (!words.contains(endWord)) { + return result; + } + Set start = new HashSet<>(); + start.add(beginWord); + words.remove(beginWord); + Set end = new HashSet<>(); + end.add(endWord); + + Map> mapTree = new HashMap<>(); + if (findBuildTree(mapTree,start,end,words,true)) { + dfs(result,mapTree,beginWord,endWord,new LinkedList<>()); + } + return result; + } + + + private boolean findBuildTree(Map> mapTree,Set start,Set end,Set words,boolean isFront) { + if (start.size() == 0) { + return false; + } + if (start.size() > end.size()) { + return findBuildTree(mapTree,end,start,words,!isFront); + } + words.removeAll(start); + boolean isOk = false; + Set nextSet = new HashSet<>(); + for (String each : start) { + char[] chars = each.toCharArray(); + for (int i = 0; i < chars.length; i++) { + char temp = chars[i]; + for (char j = 0; j <= 'z'; j++) { + chars[i] = j; + String word = String.valueOf(chars); + if (words.contains(word)) { + nextSet.add(word); + String key = isFront ? each : word; + String value = !isFront ? each : word; + if (!mapTree.containsKey(key)) { + mapTree.put(key,new ArrayList<>()); + } + mapTree.get(key).add(value); + if (end.contains(word)) { + isOk = true; + } + } + } + chars[i] = temp; + } + } + if (isOk) { + return true; + } + return findBuildTree(mapTree,nextSet,end,words,isFront); + } + + private void dfs(List> result,Map> mapTree,String beginWord,String endWord,LinkedList temp) { + temp.add(beginWord); + if (endWord.equals(beginWord)) { + result.add(new ArrayList<>(temp)); + } + if (!mapTree.containsKey(beginWord)) { + temp.removeLast(); + return; + } + List next = mapTree.get(beginWord); + for (String str : next) { + dfs(result,mapTree,str,endWord,temp); + } + temp.removeLast(); + } + + public static void main(String[] args) { + String ss = "/home/zcloud/db/masterdb_glass/masterdb_glass01"; + String ss2 = "/zcloud/"; + String ss3 = "/123/"; + String ss4 = "zcloud/"; + System.out.println(ss.substring(0,ss.indexOf("/zcloud"))); + System.out.println(ss2.substring(0,ss2.indexOf("zcloud"))); + System.out.println(ss3.substring(0,ss3.indexOf("zcloud"))); + System.out.println(ss4.substring(0,ss4.indexOf("zcloud"))); + //System.out.println(new LeetCode_126_693().findLadders("hit","cog",Arrays.asList("hot","dot","dog","lot","log","cog"))); + } +} \ No newline at end of file diff --git a/Week 03/id_693/LeetCode_127_693.java b/Week 03/id_693/LeetCode_127_693.java new file mode 100644 index 000000000..a51a9d293 --- /dev/null +++ b/Week 03/id_693/LeetCode_127_693.java @@ -0,0 +1,159 @@ +package id_693; + +import java.util.*; + +/** + * @Desc 127. 单词接龙 https://leetcode-cn.com/problems/word-ladder/description/ + * @Auther 李雷(KyLin) + * @Date 2019/10/30 + */ +public class LeetCode_127_693 { + + //迭代bfs + // 注意: char [] chars = each.toCharArray(); 这个必须每次变化的时候重新生成,引用对象 + public int ladderLength(String beginWord, String endWord, List wordList) { + Set words = new HashSet<>(wordList); + Set end = new HashSet<>(); + end.add(beginWord); + int distance = 1; + while (!end.contains(endWord)) { + Set toAdd = new HashSet<>(); + for (String each : end) { + char[] chars = each.toCharArray(); + for (int i = 0; i < each.length(); i++) { + char temp = chars[i]; + for (char j = 'a'; j <= 'z'; j++) { + chars[i] = j; + String word = new String(chars); + if (words.contains(word)) { + toAdd.add(word); + words.remove(word); + } + } + chars[i] = temp; + } + } + distance++; + if (toAdd.isEmpty()) { + return 0; + } + end = toAdd; + } + return distance; + } + + /** + * 个人学习使用 + * 在提交里看到的最优解,看懂了,解读一下分享出来: + * 需要掌握的知识递进: + * 1.BFS。 + * 2.双端BFS。 + * 3.临近点查找方式: + * 首先将所有的字符存到结构为HashSet的dic字典里去,然后将字符串的每一位挨个从a变到z, + * 在变化的时候实时去字典里查,因为是hashset,所以复杂度是O(1),非常快。 + * 如果查到了,则就是找到了临近点。 + */ + class Solution { + //递归 + public int ladderLength(String beginWord, String endWord, List wordList) { + if (wordList == null || wordList.size() == 0) return 0; + //hashset的好处:去重也完成了 + //开始端 + HashSet start = new HashSet<>(); + //结束端 + HashSet end = new HashSet<>(); + //所有字符串的字典 + HashSet dic = new HashSet<>(wordList); + start.add(beginWord); + end.add(endWord); + if (!dic.contains(endWord)) return 0; + //经历过上面的一系列判定,到这里的时候,若是有路径,则最小是2,所以以2开始 + return bfs(start, end, dic, 2); + + } + + public int bfs(HashSet start, HashSet end, HashSet words, int l) { + //双端查找的时候,若是有任意一段出现了“断裂”,也就是说明不存在能够连上的路径,则直接返回0 + if (start.size() == 0) return 0; + if (start.size() > end.size()) {//双端查找,为了优化时间,永远用少的去找多的,比如开始的时候塞进了1000个,而结尾只有3个,则肯定是从少的那一端开始走比较好 + return bfs(end, start, words, l); + } + //BFS的标记行为,即使用过的不重复使用 + words.removeAll(start); + //收集下一层临近点 + HashSet next = new HashSet<>(); + for (String s : start) { + char[] arr = s.toCharArray(); + for (int i = 0; i < arr.length; i++) { + char tmp = arr[i]; + //变化 + for (char c = 'a'; c <= 'z'; c++) { + arr[i] = c; + String word = new String(arr); + if (words.contains(word)) { + if (end.contains(word)) return l; + else next.add(word); + } + } + //复原 + arr[i] = tmp; + } + } + return bfs(next, end, words, l + 1); + } + + } + + public static void main(String[] args) { + test1(); + test2(); + test3(); + test4(); + } + + private static void test1() { + + } + + private static void test2() { + System.out.println(new LeetCode_127_693().ladderLength( + "hit", + "cog", + Arrays.asList( + "hot", "dot", "dog", "lot", "log" + )) == 5); + } + + private static void test3() { + System.out.println(new LeetCode_127_693().ladderLength( + "hit", + "cog", + Arrays.asList( + "hot", "dot", "dog", "lot", "log" + )) == 0); + } + + private static void test4() { + System.out.println(new LeetCode_127_693().ladderLength( + "hit", + "cog", + Arrays.asList( + "hot", "dot", "dog", "lot", "log", "cog" + )) == 5); + } + + + + /* + 《规则说明》 + 如果不存在这样的转换序列,返回 0。 + 所有单词具有相同的长度。 + 所有单词只由小写字母组成。 + 字典中不存在重复的单词。 + 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/word-ladder +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +} diff --git a/Week 03/id_693/LeetCode_153_693.java b/Week 03/id_693/LeetCode_153_693.java new file mode 100644 index 000000000..d34f4f4a7 --- /dev/null +++ b/Week 03/id_693/LeetCode_153_693.java @@ -0,0 +1,49 @@ +package id_693; + +/** + * @Desc 153. 寻找旋转排序数组中的最小值 https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/ + * @Auther 李雷(KyLin) + * @Date 2019/11/03 + */ +public class LeetCode_153_693 { + //二分法,和老师布置的找有序得无序一样哈, + public int findMin(int[] nums) { + //基准值,看mid索引的值小于基准值,大于基准点,说明mid左边是有序的,直接看mid右边,否则反之 + int point = nums[0]; + int i = 0; + int j = nums.length - 1; + while (i < j) { + int mid = i + (j - i) / 2; + //如果i + 1 = j,说明j的下标值就是最小值, + //但是还需要判断i j的 索引值,如果j值大于i值,说明这里就是无序点,返回j即可 + if (i + 1 == j) { + if (nums[i] > nums[j]) { + return nums[j]; + } + //j 值不大于i 说明就是一个完全有序数组。反正0即可 + return nums[0]; + } + if (point > nums[mid]) { + j = mid; + } else { + i = mid; + } + } + return nums[0]; + } + + //不错 ,我自己想复杂了、 + public int findMin2(int[] nums) { + int i = 0; + int j = nums.length - 1; + while (i < j) { + int mid = i + (j - i) / 2; + if (nums[mid] < nums[j]) { + j = mid; + } else { + i = mid + 1; + } + } + return nums[i]; + } +} diff --git a/Week 03/id_693/LeetCode_200_693.java b/Week 03/id_693/LeetCode_200_693.java new file mode 100644 index 000000000..2933645d3 --- /dev/null +++ b/Week 03/id_693/LeetCode_200_693.java @@ -0,0 +1,41 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 200. 岛屿数量 https://leetcode-cn.com/problems/number-of-islands/ + * @Date 2019/11/01 + */ +public class LeetCode_200_693 { + //实录:沉到模式 + public int numIslands(char[][] grid) { + int count = 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == '1') { + count++; + dfs(grid,i,j); + } + } + } + return count; + } + + private void dfs(char[][] grid,int i,int j) { + if (i >= grid.length || j >= grid[0].length || i < 0 || j < 0) { + return; + } + if (grid[i][j] == '1') { + grid[i][j] = '0'; + dfs(grid,i - 1,j); + dfs(grid,i + 1,j); + dfs(grid,i,j + 1); + dfs(grid,i,j - 1); + } + } + + public static void main(String[] args) { + System.out.println(new LeetCode_200_693().numIslands(new char[][]{{'1','1','0','0','0'},{'1','1','0','0','0'},{'0','0','1','0','0'},{'0','0','0','1','1'}})); + System.out.println(new LeetCode_200_693().numIslands(new char[][]{{'1'},{'1'}})); + System.out.println(new LeetCode_200_693().numIslands(new char[][]{{'1','1','1','1','0'},{'1','1','0','1','0'},{'1','1','0','0','0'},{'0','0','0','0','0'}})); + } +} diff --git a/Week 03/id_693/LeetCode_33_693.java b/Week 03/id_693/LeetCode_33_693.java new file mode 100644 index 000000000..eff3a1b1b --- /dev/null +++ b/Week 03/id_693/LeetCode_33_693.java @@ -0,0 +1,88 @@ +package id_693; + +import org.junit.Assert; + +/** + * @Desc 33. 搜索旋转排序数组 https://leetcode-cn.com/problems/search-in-rotated-sorted-array/ + * @Auther 李雷(KyLin) + * @Date 2019/11/03 + */ +public class LeetCode_33_693 { + //暴力法:O(n) ,但是要求是O(log n)哈。。。 + public int search2(int[] nums, int target) { + for (int i = 0; i < nums.length; i++) { + if (target == nums[i]) { + return i; + } + } + return -1; + } + + //暴力:还原(0(long n) -->升序 ->二分:(0(long n) + public int search3(int[] nums, int target) { + int point = nums[0]; + int i = 0; + int j = nums.length - 1; + int index = 0; + while (i < j) { + int mid = i + (j - i) / 2; + if (i + 1 == j) { + if (nums[i] > nums[j]) { + index = j; + } + break; + } + if (point > nums[mid]) { + j = mid; + } else { + i = mid; + } + } + i = 0; + j = nums.length - 1; + if (index == 0) { + + } else if (target < nums[i]) { + i = index; + } else { + j = index - 1; + } + while (i <= j) { + int mid = i + (j - i) / 2; + if (nums[mid] == target) { + return mid; + } else if (nums[mid] > target) { + j = mid - 1; + } else { + i = mid + 1; + } + } + return -1; + } + + //优化:根据查看题解,发现人家写法,正好可以延伸。 + public int search(int[] nums, int target) { + int lo = 0; + int hi = nums.length - 1; + while (lo < hi) { + int mid = lo + (hi - lo) / 2; + //看0-mid是否是有序的,如果是,则判断target 大于mid索引值,或小于0索引值,确定范围在右边 + if (nums[0] <= nums[mid] && + (target > nums[mid] || target < nums[0])) { + lo = mid + 1; + //直接判断是否大于mid索引值且也小于0索引值//确定范围在右边 + } else if (target > nums[mid] && target < nums[0]) { + lo = mid + 1; + } else { + hi = mid; + } + } + return lo == hi && nums[lo] == target ? lo : -1; + } + + public static void main(String[] args) { + Assert.assertEquals(4, new LeetCode_33_693().search(new int[]{4, 5, 6, 7, 0, 1, 2}, 0)); + Assert.assertEquals(1, new LeetCode_33_693().search(new int[]{1, 3}, 3)); + Assert.assertEquals(0, new LeetCode_33_693().search(new int[]{1}, 1)); + } +} diff --git a/Week 03/id_693/LeetCode_455_693.java b/Week 03/id_693/LeetCode_455_693.java new file mode 100644 index 000000000..1db2c07b3 --- /dev/null +++ b/Week 03/id_693/LeetCode_455_693.java @@ -0,0 +1,62 @@ +package id_693; + +import java.util.Arrays; + +/** + * @Desc 455. 分发饼干 https://leetcode-cn.com/problems/assign-cookies/description/ + * @Auther 李雷(KyLin) + * @Date 2019/11/02 + */ +public class LeetCode_455_693 { + // g 胃口最低值 + // s 饼干能量值 + + //暴力解法,效率很低 :执行用时 : 164 ms , 在所有 java 提交中击败了 5.04% 的用户 内存消耗 : 39.9 MB , 在所有 java 提交中击败了 94.85% 的用户 + //时间复杂度是O(MN) 空间复杂度O(N) + public int findContentChildren(int[] g, int[] s) { + if (s.length == 0) { + return 0; + } + Arrays.sort(g); + Arrays.sort(s); + int max = 0; + boolean[] used = new boolean[g.length]; + for (int i = 0; i < s.length; i++) { + int si = s[i]; + for (int j = 0; j < g.length; j++) { + int gj = g[j]; + if (si >= gj && !used[j]) { + used[j] = true; + max++; + break; + } + } + } + return max; + } + + //贪心计数把。这样时间复杂度O(N) 空间复杂度o(1) + public int findContentChildren2(int[] g, int[] s) { + Arrays.sort(g); + Arrays.sort(s); + int max = 0; + int gi = 0; + int si = 0; + while (gi < g.length && si < s.length) { + if (s[si] >= g[gi]) { + max++; + gi++; + si++; + } else { + si++; + } + + } + return max; + } + + public static void main(String[] args) { + System.out.println(new LeetCode_455_693().findContentChildren(new int[]{1, 2}, new int[]{1, 2, 3})); + System.out.println(new LeetCode_455_693().findContentChildren2(new int[]{10, 9, 8, 7}, new int[]{5, 6, 7, 8})); + } +} diff --git a/Week 03/id_693/LeetCode_45_693.java b/Week 03/id_693/LeetCode_45_693.java new file mode 100644 index 000000000..850241c05 --- /dev/null +++ b/Week 03/id_693/LeetCode_45_693.java @@ -0,0 +1,30 @@ +package id_693; + + +import org.junit.Assert; + +/** + * @Desc 45. 跳跃游戏 II https://leetcode-cn.com/problems/jump-game-ii/ + * @Auther 李雷(KyLin) + * @Date 2019/11/02 + */ +public class LeetCode_45_693 { + //参考顺藤摸瓜,每次看自己可以跳的范围最大值,然后跳到那个可跳跃的最大值位置,然后继续看下一个可跳跃范围 + public int jump(int[] nums) { + int end = 0; + int maxPosition = 0; + int steps = 0; + for (int i = 0; i < nums.length - 1; i++) { + maxPosition = Math.max(maxPosition, nums[i] + i); + if (i == end) { + end = maxPosition; + steps++; + } + } + return steps; + } + + public static void main(String[] args) { + Assert.assertEquals(2, new LeetCode_45_693().jump(new int[]{2, 3, 1, 1, 4})); + } +} diff --git a/Week 03/id_693/LeetCode_529_693.java b/Week 03/id_693/LeetCode_529_693.java new file mode 100644 index 000000000..1a8d81804 --- /dev/null +++ b/Week 03/id_693/LeetCode_529_693.java @@ -0,0 +1,76 @@ +package id_693; + +import java.util.Arrays; + +/** + * @Desc 529. 扫雷游戏 https://leetcode-cn.com/problems/minesweeper/description/ + * @Auther 李雷(KyLin) + * @Date 2019/11/01 + */ +public class LeetCode_529_693 { + int[] xForWord = {-1, 1, -1, 1, 0, 1, -1, 0}; + int[] yForWord = {-1, 1, 1, -1, 1, 0, 0, -1}; + + //dfs , + /* + 思路: + 1、判断当前是否有雷,有则修改返回 + 2、专门检查周围雷的数量,如果有雷,修改当前坐标为雷数 并且返回 + 2、如果没雷,则直接dfs修改周围的数据为对应的 + */ + public char[][] updateBoard(char[][] board, int[] click) { + int x = click[0]; + int y = click[1]; + if (board[x][y] == 'M') { + board[x][y] = 'X'; + return board; + } + dfs(board, x, y); + return board; + } + + private void dfs(char[][] board, int x, int y) { + if (x < 0 || x >= board.length || y < 0 || y >= board[0].length || board[x][y] != 'E') { + return; + } + int count = getThunderNumber(board, x, y); + if (count == 0) { + board[x][y] = 'B'; + for (int i = 0; i < xForWord.length; i++) { + int qx = x + xForWord[i]; + int qy = y + yForWord[i]; + dfs(board, qx, qy); + } + } else { + board[x][y] = (char) ('0' + count); + } + } + + private int getThunderNumber(char[][] board, int x, int y) { + int count = 0; + for (int i = 0; i < xForWord.length; i++) { + int qx = x + xForWord[i]; + int qy = y + yForWord[i]; + if (qx < 0 || qx >= board.length || qy < 0 || qy >= board[0].length) { + continue; + } + if (board[qx][qy] == 'M' || board[qx][qy] == 'X') { + count++; + } + } + return count; + } + + public static void main(String[] args) { + char[][] b = new LeetCode_529_693().updateBoard(new char[][]{ + {'E', 'E', 'E', 'E', 'E'}, + {'E', 'E', 'M', 'E', 'E'}, + {'E', 'E', 'E', 'E', 'E'}, + {'E', 'E', 'E', 'E', 'E'}}, new int[]{3, 0}); + for (int i = 0; i < b.length; i++) { + + + System.out.println(Arrays.toString(b[i])); + } + } +} diff --git a/Week 03/id_693/LeetCode_55_693.java b/Week 03/id_693/LeetCode_55_693.java new file mode 100644 index 000000000..6e6fbf968 --- /dev/null +++ b/Week 03/id_693/LeetCode_55_693.java @@ -0,0 +1,59 @@ +package id_693; + +import org.junit.Assert; + +/** + * @Desc 55. 跳跃游戏 https://leetcode-cn.com/problems/jump-game/ + * @Auther 李雷(KyLin) + * @Date 2019/11/02 + */ +public class LeetCode_55_693 { + //贪心思路,从后往前看,太他妈神奇了 + //获得当前的值,加上需要的步数(i),如果大于等于当前的步数,那么肯定能跳出去,那就更新数据。就继续下去 + public boolean canJump(int[] nums) { + if (nums == null) { + return false; + } + int endReachable = nums.length - 1; + for (int i = nums.length - 2; i >= 0; i--) { + if (nums[i] + i >= endReachable) { + endReachable = i; + } + } + return endReachable == 0; + } + + //网上看到的c++写法,很秀。但是因为计算步骤多,效率没上一个快 + /* + 1、如果某一个作为 起跳点 的格子可以跳跃的距离是 3,那么表示后面 3 个格子都可以作为 起跳点。 + 2、可以对每一个能作为 起跳点 的格子都尝试跳一次,把 能跳到最远的距离 不断更新。 + 3、如果可以一直跳到最后,就成功了。 + */ + public boolean canJump2(int[] nums) { + int k = 0; + for (int i = 0; i < nums.length; i++) { + if (i > k) { + return false; + } + k = Math.max(k, i + nums[i]); + } + return true; + } + + public static void main(String[] args) { + Assert.assertTrue(new LeetCode_55_693().canJump(new int[]{2, 5, 0, 0})); + Assert.assertTrue(new LeetCode_55_693().canJump(new int[]{2, 0, 0})); + Assert.assertTrue(new LeetCode_55_693().canJump(new int[]{2, 0})); + Assert.assertFalse(new LeetCode_55_693().canJump(new int[]{0, 1})); + Assert.assertTrue(new LeetCode_55_693().canJump(new int[]{2, 3, 1, 1, 4})); + Assert.assertFalse(new LeetCode_55_693().canJump(new int[]{3, 2, 1, 0, 4})); + + + Assert.assertTrue(new LeetCode_55_693().canJump2(new int[]{2, 5, 0, 0})); + Assert.assertTrue(new LeetCode_55_693().canJump2(new int[]{2, 0, 0})); + Assert.assertTrue(new LeetCode_55_693().canJump2(new int[]{2, 0})); + Assert.assertFalse(new LeetCode_55_693().canJump2(new int[]{0, 1})); + Assert.assertTrue(new LeetCode_55_693().canJump2(new int[]{2, 3, 1, 1, 4})); + Assert.assertFalse(new LeetCode_55_693().canJump2(new int[]{3, 2, 1, 0, 4})); + } +} diff --git a/Week 03/id_693/LeetCode_74_693.java b/Week 03/id_693/LeetCode_74_693.java new file mode 100644 index 000000000..44912b2bc --- /dev/null +++ b/Week 03/id_693/LeetCode_74_693.java @@ -0,0 +1,116 @@ +package id_693; + +import org.junit.Assert; + +/** + * @Desc 74. 搜索二维矩阵 https://leetcode-cn.com/problems/search-a-2d-matrix/ + * @Auther 李雷(KyLin) + * @Date 2019/11/03 + */ +public class LeetCode_74_693 { + //暴力求解0(M N) + public boolean searchMatrix2(int[][] matrix,int target) { + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[i].length; j++) { + if (matrix[i][j] == target) { + return true; + } + } + } + return false; + } + + //暴力求解优化:因为有序,所以跳过最大值大于当前列的 0(M N) + public boolean searchMatrix3(int[][] matrix,int target) { + for (int i = 0; i < matrix.length; i++) { + + for (int j = 0; j < matrix[i].length; j++) { + if (target > matrix[i][matrix[i].length - 1]) { + break; + } + if (matrix[i][j] == target) { + return true; + } + } + } + return false; + } + + //二分法; 找出区间范围,然后进行二分查找 O(M + log(N)) 当m远远大于n 时 这个效率很低 + public boolean searchMatrix4(int[][] matrix,int target) { + for (int i = 0; i < matrix.length; i++) { + int a = matrix[i].length - 1; + if (a >= 0 && target >= matrix[i][0] && target <= matrix[i][a]) { + int lo = 0; + int hi = a; + while (lo <= hi) { + int mid = lo + (hi - lo) / 2; + if (target == matrix[i][mid]) { + return true; + } else if (target > matrix[i][mid]) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return false; + } + + } + return false; + } + + //指针法,直接用行列指针, O(M + N) + // 1、第一行最后一列开始, 如果行的 最后值小于目标值,行指针下移移行 + // 2、 如果列的 当前值大于目标值,列指针左移一列 + public boolean searchMatrix5(int[][] matrix,int target) { + if (matrix.length == 0) { + return false; + } + int row = 0; + int col = matrix[0].length - 1; + while (row < matrix.length && col >= 0) { + if (matrix[row][col] < target) { + row++; + } else if (matrix[row][col] > target) { + col--; + } else { + return true; + } + } + return false; + } + + //标准二分查找 O(log(mn)) 这个效率比方法4 要更好一些 + public boolean searchMatrix(int[][] matrix,int target) { + int cow = matrix.length; + if (cow == 0) { + return false; + } + int col = matrix[0].length; + // 二分查找 + int left = 0; + int right = cow * col - 1; + while (left <= right) { + int mid = (left + right) / 2; + //pivotIdx / 列就可以索引在哪一行了。private % 列 就可以知道索引再哪一列,太完美了。超帅气 + int val = matrix[mid / col][mid % col]; + if (target == val) { + return true; + } else { + if (target < val) { + right = mid - 1; + } else { + left = mid + 1; + } + } + } + return false; + } + + public static void main(String[] args) { + Assert.assertTrue(new LeetCode_74_693().searchMatrix(new int[][]{{1},{3}},3)); + Assert.assertFalse(new LeetCode_74_693().searchMatrix(new int[][]{{}},1)); + Assert.assertTrue(new LeetCode_74_693().searchMatrix(new int[][]{{1,3,5,7},{10,11,16,20},{23,30,34,50}},3)); + } +} diff --git a/Week 03/id_693/LeetCode_860_693.java b/Week 03/id_693/LeetCode_860_693.java new file mode 100644 index 000000000..0a5c27b66 --- /dev/null +++ b/Week 03/id_693/LeetCode_860_693.java @@ -0,0 +1,113 @@ +package id_693; + +/** + * @Desc 322. 零钱兑换 https://leetcode-cn.com/problems/coin-change/ + * @Auther 李雷(KyLin) + * @Date 2019/11/02 + */ +public class LeetCode_860_693 { + //第一次解法,效率很低3秒。再反复看题和题解,发现我不用考虑多个, + public boolean lemonadeChange(int[] bills) { + int five = 0; + int ten = 0; + for (int i = 0; i < bills.length; i++) { + int money = bills[i]; + if (money == 5) { + five++; + continue; + } + if (money == 10 || money == 20) { + if (money == 10) { + ten++; + } + int index = (money - 5) / 10; + if (index != 0) { + if (ten >= index) { + money = money - index * 10; + ten -= index; + } else { + money = money - ten * 10; + ten -= ten; + } + } + index = (money - 5) / 5; + if (index != 0) { + if (five >= index) { + money = money - index * 5; + five -= index; + } else { + money = money - five * 5; + five -= five; + } + } + if (money != 5) { + return false; + } + } + } + return true; + } + + //根据题解进行优化 + public boolean lemonadeChange2(int[] bills) { + int five = 0; + int ten = 0; + for (int i = 0; i < bills.length; i++) { + int money = bills[i]; + if (money == 5) { + five++; + } else if (money == 10) { + if (five > 0) { + five--; + ten++; + } else { + return false; + } + } else if (money == 20) { + if (five > 0 && ten > 0) { + ten--; + five--; + } else { + if (ten == 0 && five >= 3) { + five -= 3; + } else { + return false; + } + } + } + } + return true; + } + + //浏览国际站代码净化,但是国际站是用的foreach java中数组遍历推荐用for。更高效 + public boolean lemonadeChange3(int[] bills) { + int five = 0; + int ten = 0; + for (int i = 0; i < bills.length; i++) { + if (bills[i] == 5) { + five++; + } else if (bills[i] == 10) { + five--; + ten++; + } else if (ten > 0) { + ten--; + five--; + } else { + five -= 3; + } + if (five < 0) { + return false; + } + } + return true; + } + + public static void main(String[] args) { + System.out.println(new LeetCode_860_693().lemonadeChange2(new int[]{5, 5, 5, 10, 20})); + System.out.println(new LeetCode_860_693().lemonadeChange2(new int[]{5, 5, 10})); + System.out.println(new LeetCode_860_693().lemonadeChange2(new int[]{10, 10})); + System.out.println(new LeetCode_860_693().lemonadeChange2(new int[]{5, 5, 10, 10, 20})); + System.out.println(new LeetCode_860_693().lemonadeChange2(new int[]{5, 5, 5, 10, 5, 5, 10, 20, 20, 20})); + } +} + diff --git a/Week 03/id_693/practise/LeetCode_102_693.java b/Week 03/id_693/practise/LeetCode_102_693.java new file mode 100644 index 000000000..8d434799b --- /dev/null +++ b/Week 03/id_693/practise/LeetCode_102_693.java @@ -0,0 +1,43 @@ +package id_693.practise; + +import id_693.TreeNode; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; + +/** + * @Desc 102. 二叉树的层次遍历 https://leetcode-cn.com/problems/binary-tree-level-order-traversal/ + * @Auther 李雷(KyLin) + * @Date 2019/10/29 + */ +public class LeetCode_102_693 { + //bfs (类似第二周的 429.N叉树的层序遍历0 + public List> levelOrder(TreeNode root) { + List> list = new ArrayList<>(); + if (root == null) { + return list; + } + Queue queue = new ArrayDeque<>(); + queue.offer(root); + while (!queue.isEmpty()) { + List res = new ArrayList<>(); + int size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode temp = queue.poll(); + res.add(temp.val); + if (temp.left != null) { + queue.add(temp.left); + } + if (temp.right != null) { + queue.add(temp.right); + } + } + + list.add(res); + } + return list; + } +} + diff --git a/Week 03/id_693/practise/LeetCode_367_693.java b/Week 03/id_693/practise/LeetCode_367_693.java new file mode 100644 index 000000000..820231388 --- /dev/null +++ b/Week 03/id_693/practise/LeetCode_367_693.java @@ -0,0 +1,70 @@ +package id_693.practise; + +import org.junit.Assert; + +/** + * @Desc 367. 有效的完全平方数 https://leetcode-cn.com/problems/valid-perfect-square/ + * @Auther 李雷(KyLin) + * @Date 2019/11/03 + */ +public class LeetCode_367_693 { + //牛顿法,因为刚做了那个题,干脆直接带过来哈 + //注意:这样的r 做运算的时候一定要注意基本数据类型的一出问题 + public boolean isPerfectSquare1(int num) { + long r = num; + while (r * r > num) { + r = (r + num / r) / 2; + } + return r * r == num; + } + + //二分:其实写法也和69题一样,但是 + /* + 1、题目是为了找出num是否存在有效平方根,所以找到就返回,num说明了是正整数,所以直接1开始 + 2、再其次,再判断left任何一个数的平方是否等于num 就好了。 + 3、上面相对理解有点绕。可以直接用69的写法。然后只需要对结果判断是否相等就好了 + */ + public boolean isPerfectSquare2(int num) { + long left = 1; + long right = num >> 1; + while (left < right) { + long mid = left + ((right - left) >> 1); + long square = mid * mid; + if (square == num) { + return true; + } else if (square > num) { + right = mid - 1; + } else { + left = mid + 1; + } + + } + return left * left == num; + } + + //看了国际站,解决上面理解有点绕的问题- + // -------这样left和right相等的时候也去判断一下是否等于num即可,因为判断再while内,所以right不能除以2了 + public boolean isPerfectSquare(int num) { + long left = 1; + long right = num; + while (left <= right) { + long mid = left + ((right - left) >> 1); + long square = mid * mid; + if (square == num) { + return true; + } else if (square > num) { + right = mid - 1; + } else { + left = mid + 1; + } + + } + return false; + } + + public static void main(String[] args) { + Assert.assertTrue(new LeetCode_367_693().isPerfectSquare(1)); + Assert.assertTrue(new LeetCode_367_693().isPerfectSquare(808201)); + Assert.assertFalse(new LeetCode_367_693().isPerfectSquare(2342)); + } +} \ No newline at end of file diff --git a/Week 03/id_693/practise/LeetCode_433_693.java b/Week 03/id_693/practise/LeetCode_433_693.java new file mode 100644 index 000000000..8291a99cf --- /dev/null +++ b/Week 03/id_693/practise/LeetCode_433_693.java @@ -0,0 +1,152 @@ +package id_693.practise; + +import java.util.*; + +/** + * @Desc 433. 最小基因变化 https://leetcode-cn.com/problems/minimum-genetic-mutation/#/description + * @Auther 李雷(KyLin) + * @Date 2019/10/29 + */ +public class LeetCode_433_693 { + + //dfs + public int minMutation(String start,String end,String[] bank) { + if (start == null || end == null || bank == null || start.length() == 0 || end.length() == 0) { + return 0; + } + Set backString = new HashSet<>(); + Collections.addAll(backString,bank); + int result = dfs(start,end,backString,0); + return result == Integer.MAX_VALUE ? -1 : result; + } + + private int dfs(String start,String end,Set backString,int depth) { + if (start.equals(end)) { + return depth; + } + int res = Integer.MAX_VALUE; + if (backString.isEmpty()) { + return res; + } + for (String s : backString) { + //如果有s和end相等,且只和自身只有一个变化,那么就返回变化+1; + if (s.equals(end) && diff(s,start)) { + return depth + 1; + //如果有和start存在一个字母变化的,那么就进入下一步 + } else if (diff(s,start)) { + Set backString2 = new HashSet<>(backString); + backString2.remove(s); + res = Math.min(res,dfs(s,end,backString2,depth + 1)); + } + } + return res; + } + + //验证是否是只有一个字母变化 + private boolean diff(String s,String start) { + int res = 0; + for (int i = 0; i < start.length(); i++) { + if (s.charAt(i) != start.charAt(i)) { + res++; + if (res > 1) { + return false; + } + } + } + return res == 1; + } + + + //sfs+backtarck 优化空间和效率 + public int minMutation2(String start,String end,String[] bank) { + if (start == null || end == null || bank == null || start.length() == 0 || end.length() == 0) { + return 0; + } + int result = dfs2(start,end,bank,new boolean[bank.length]); + return result > bank.length ? -1 : result; + } + + private int dfs2(String start,String end,String[] bank,boolean[] used) { + if (start.equals(end)) { + return 0; + } + int len = bank.length; + int res = bank.length + 1; + for (int i = 0; i < len; i++) { + if (!used[i] && diff(start,bank[i])) { + used[i] = true; + res = Math.min(res,1 + dfs2(bank[i],end,bank,used)); + used[i] = false; + } + } + return res; + } + + //test + public static void main(String[] args) { + LeetCode_433_693.test1();//1 + LeetCode_433_693.test2();//2 + LeetCode_433_693.test3();//3 + LeetCode_433_693.test4();//4 + LeetCode_433_693.test5();//-1 + LeetCode_433_693.test6();//4 + } + + public static void test1() { + String start = "AACCGGTT"; + String end = "AACCGGTA"; + String[] back = {"AACCGGTA"}; + System.out.println(new LeetCode_433_693().minMutation2(start,end,back)); + } + + public static void test2() { + String start = "AACCGGTT"; + String end = "AAACGGTA"; + String[] back = {"AACCGGTA","AACCGCTA","AAACGGTA"}; + System.out.println(new LeetCode_433_693().minMutation2(start,end,back)); + } + + public static void test3() { + String start = "AAAAACCC"; + String end = "AACCCCCC"; + String[] back = {"AAAACCCC","AAACCCCC","AACCCCCC"}; + System.out.println(new LeetCode_433_693().minMutation2(start,end,back)); + } + + public static void test4() { + String start = "AACCGGTT"; + String end = "AAACGGTA"; + String[] back = { + "AACCGATT", + "AACCGATA", + "AAACGATA", + "AAACGGTA"}; + System.out.println(new LeetCode_433_693().minMutation2(start,end,back)); + } + + public static void test5() { + String start = "AACCTTGG"; + String end = "AATTCCGG"; + String[] back = { + "AATTCCGG", + "AACCTGGG", + "AACCCCGG", + "AACCTACC"}; + System.out.println(new LeetCode_433_693().minMutation2(start,end,back)); + } + + public static void test6() { + String start = "AAAACCCC"; + String end = "CCCCCCCC"; + String[] back = { + "AAAACCCA", + "AAACCCCA", + "AACCCCCA", + "AACCCCCC", + "ACCCCCCC", + "CCCCCCCC", + "AAACCCCC", + "AACCCCCC"}; + System.out.println(new LeetCode_433_693().minMutation2(start,end,back)); + } +} \ No newline at end of file diff --git a/Week 03/id_693/practise/LeetCode_515_693.java b/Week 03/id_693/practise/LeetCode_515_693.java new file mode 100644 index 000000000..4e1b4ee8c --- /dev/null +++ b/Week 03/id_693/practise/LeetCode_515_693.java @@ -0,0 +1,59 @@ +package id_693.practise; + +import com.sun.jmx.remote.internal.ArrayQueue; +import id_693.TreeNode; + +import java.util.*; + +/** + * @Author 李雷(KyLin) + * @Desc 515. 在每个树行中找最大值 https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/ + * @Date 2019/10/30 + */ +public class LeetCode_515_693 { + //迭代法bfs + public List largestValues(TreeNode root) { + List result = new ArrayList<>(); + if (root == null) { + return result; + } + Deque deque = new ArrayDeque<>(); + deque.offer(root); + while (!deque.isEmpty()) { + int size = deque.size(); + int currentMax = Integer.MIN_VALUE; + for (int i = 0; i < size; i++) { + TreeNode temp = deque.poll(); + currentMax = Math.max(currentMax,temp.val); + if (temp.left != null) { + deque.offer(temp.left); + } + if (temp.right != null) { + deque.offer(temp.right); + } + + } + result.add(currentMax); + } + return result; + } + + //递归法bfs + public List largestValues2(TreeNode root) { + List result = new ArrayList<>(); + recurrsion(result,root,0); + return result; + } + + private void recurrsion(List result,TreeNode root,int level) { + if (root != null) { + if (level == result.size()) { + result.add(root.val); + } else if (root.val > result.get(level)) { + result.set(level,Math.max(root.val,result.get(level))); + } + recurrsion(result,root.left,level + 1); + recurrsion(result,root.right,level + 1); + } + } +} diff --git a/Week 03/id_693/practise/LeetCode_69_693.java b/Week 03/id_693/practise/LeetCode_69_693.java new file mode 100644 index 000000000..b28beb6d9 --- /dev/null +++ b/Week 03/id_693/practise/LeetCode_69_693.java @@ -0,0 +1,73 @@ +package id_693.practise; + +import org.junit.Assert; + +/** + * @Desc 69. x 的平方根 https://leetcode-cn.com/problems/sqrtx/ + * @Auther 李雷(KyLin) + * @Date 2019/11/03 + */ +public class LeetCode_69_693 { + //二分法: 返回left或right 都可以 + public int mySqrt(int x) { + if (x == 0 || x == 1) { + return x; + } + long left = 1; + long right = x >> 1; + while (left < right) { + long mid = left + ((right - left) >> 1); + if (mid * mid > x) { + right = mid - 1; + } else { + left = mid + 1; + //不要死磕老师的视频上的解法,不然会浪费很多时间,不过也值得了。 + if (left * left > x) { + return (int) mid; + } + } + } + return (int) left; + } + + //二分法:稍微有点绕,就是为了保证死循环,所以就有右边界+1;保证了mid*mid 肯定大于x + public int mySqrt2(int x) { + if (x == 0 || x == 1) { + return x; + } + long left = 1; + long right = (x >> 1) + 1; + while (left < right) { + long mid = left + ((right - left + 1) >> 1); + if (mid * mid > x) { + right = mid - 1; + } else { + left = mid; + } + } + return (int) left; + } + + //牛顿迭代法 + public int mySqrt3(int x) { + long r = x; + while (r * r > x) { + r = (r + (x / r)) >>> 1; + } + return (int) r; + } + + public static void main(String[] args) { + Assert.assertEquals(0, new LeetCode_69_693().mySqrt(0)); + Assert.assertEquals(1, new LeetCode_69_693().mySqrt(1)); + Assert.assertEquals(1, new LeetCode_69_693().mySqrt(3)); + Assert.assertEquals(2, new LeetCode_69_693().mySqrt(4)); + Assert.assertEquals(2, new LeetCode_69_693().mySqrt(6)); + Assert.assertEquals(2, new LeetCode_69_693().mySqrt(8)); + Assert.assertEquals(3, new LeetCode_69_693().mySqrt(9)); + Assert.assertEquals(3, new LeetCode_69_693().mySqrt(15)); + Assert.assertEquals(4, new LeetCode_69_693().mySqrt(16)); + Assert.assertEquals(5, new LeetCode_69_693().mySqrt(26)); + Assert.assertEquals(46339, new LeetCode_69_693().mySqrt(2147395599)); + } +} diff --git a/Week 03/id_698/LeetCode_200_698.java b/Week 03/id_698/LeetCode_200_698.java new file mode 100644 index 000000000..956333f03 --- /dev/null +++ b/Week 03/id_698/LeetCode_200_698.java @@ -0,0 +1,66 @@ + +/** + * 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。 + * 一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。 + * 你可以假设网格的四个边均被水包围。 + * + * @author gning (id=698) + */ + + public class LeetCode_200_698 { + + private static final int[][] directions = {{-1,0},{0,-1},{1,0},{0,1}}; + + private boolean[][] marked; + + private int rows; + + private int cols; + + private char[][] grid; + + public int numIslands(char[][] grid) { + rows = grid.length; + + if(rows == 0) { + return 0; + } + + cols = grid[0].length; + + this.grid = grid; + + marked = new boolean[rows][cols]; + + int count = 0; + for(int i=0; i=0 && x=0 && y nums[mid] || target < nums[0])) { + left = mid + 1; + } else if ( target > nums[mid] && target < nums[0]) { + left = mid + 1; + }else { + right = mid; + } + + } + + return left == right && nums[left] == target ? left : -1; + } + + +} \ No newline at end of file diff --git a/Week 03/id_698/LeetCode_45_698.java b/Week 03/id_698/LeetCode_45_698.java new file mode 100644 index 000000000..db902278e --- /dev/null +++ b/Week 03/id_698/LeetCode_45_698.java @@ -0,0 +1,33 @@ +/** + * Given an array of non-negative integers, you are initially positioned at the first index of the array. + * Each element in the array represents your maximum jump length at that position. + * Your goal is to reach the last index in the minimum number of jumps. + * + * @author gning (id=698) + */ + + public class LeetCode_45_698 { + + public int jump(int[] nums) { + if(nums == null || nums.length ==0) { + return 0; + } + + int end = 0; + int maxPosition = 0; + int steps = 0; + + for (int i=0; i=0; i--) { + if(nums[i] + i >= reachable ) { + reachable = i; + } + } + + return reachable == 0; + + } + +} \ No newline at end of file diff --git a/Week 03/id_703/LeetCode_22_703.py b/Week 03/id_703/LeetCode_22_703.py new file mode 100644 index 000000000..a2b45ac55 --- /dev/null +++ b/Week 03/id_703/LeetCode_22_703.py @@ -0,0 +1,34 @@ +# +# @lc app=leetcode.cn id=22 lang=python3 +# +# [22] 括号生成 +# + +# @lc code=start +from typing import List + +class Solution: + def generateParenthesis(self, n: int) -> List[str]: + if n == 0: + return [] + total_l = [] + total_l.append([None]) + total_l.append(["()"]) + for i in range(2,n+1): + l = [] + for j in range(i): + now_list1 = total_l[j] + now_list2 = total_l[i-1-j] + for k1 in now_list1: + for k2 in now_list2: + if k1 == None: + k1 = "" + if k2 == None: + k2 = "" + el = "(" + k1 + ")" + k2 + l.append(el) + total_l.append(l) + return total_l[n] + +# @lc code=end + diff --git a/Week 03/id_703/LeetCode_69_703.py b/Week 03/id_703/LeetCode_69_703.py new file mode 100644 index 000000000..d7ee89340 --- /dev/null +++ b/Week 03/id_703/LeetCode_69_703.py @@ -0,0 +1,23 @@ +# +# @lc app=leetcode.cn id=69 lang=python3 +# +# [69] x 的平方根 +# + +# @lc code=start +class Solution: + def mySqrt(self, x: int) -> int: + left = 0 + right = x // 2 + 1 + while left < right: + mid = (left + right + 1) >> 1 + square = mid * mid + if square > x: + right = mid - 1 + else: + left = mid + return left + + +# @lc code=end + diff --git a/Week 03/id_708/LeetCode_102_708.java b/Week 03/id_708/LeetCode_102_708.java new file mode 100644 index 000000000..e1a93b3cb --- /dev/null +++ b/Week 03/id_708/LeetCode_102_708.java @@ -0,0 +1,29 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List> levelOrder(TreeNode root) { + List> list = new LinkedList<>(); + Queue queue = new LinkedList<>(); + + if (root != null) queue.offer(root); + while (!queue.isEmpty()) { + int size = queue.size(); + List currList = new LinkedList<>(); + while (size-- > 0) { + TreeNode curr = queue.remove(); + currList.add(curr.val); + if (curr.left != null) queue.offer(curr.left); + if (curr.right != null) queue.offer(curr.right); + } + list.add(currList); + } + return list; + } +} \ No newline at end of file diff --git a/Week 03/id_708/LeetCode_127_708.java b/Week 03/id_708/LeetCode_127_708.java new file mode 100644 index 000000000..8845999e7 --- /dev/null +++ b/Week 03/id_708/LeetCode_127_708.java @@ -0,0 +1,32 @@ +class Solution { + public int ladderLength(String beginWord, String endWord, List wordList) { + int level = 1; + Set visited = new HashSet<>(); + Set dictSet = new HashSet<>(wordList); + + Queue queue = new LinkedList<>(); + queue.offer(beginWord); + while (!queue.isEmpty()) { + int size = queue.size(); + while (size-- > 0) { // 因为我们是按层级遍历,所以是多条路径并行查找,当找到最小路径时,直接返回 + String curr = queue.poll(); + if (curr.equals(endWord)) return level; + char[] currChars = curr.toCharArray(); + for (int i = 0; i < currChars.length; i++) { + char old = currChars[i]; + for (char j = 'a'; j <= 'z'; j++) { + currChars[i] = j; + String word = new String(currChars); + if (!visited.contains(word) && dictSet.contains(word)) { + queue.offer(word); + visited.add(word); + } + } + currChars[i] = old; + } + } + level++; + } + return 0; + } +} \ No newline at end of file diff --git a/Week 03/id_708/LeetCode_200_708.java b/Week 03/id_708/LeetCode_200_708.java new file mode 100644 index 000000000..3d2836d9e --- /dev/null +++ b/Week 03/id_708/LeetCode_200_708.java @@ -0,0 +1,25 @@ +// dfs +class Solution { + public int numIslands(char[][] grid) { + int count = 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == '1') { + count++; + dfs(grid, i, j); + } + } + } + return count; + } + + private void dfs(char[][] grid, int i, int j) { + if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == '0') return ; + + grid[i][j] = '0'; + dfs(grid, i+1, j); + dfs(grid, i, j+1); + dfs(grid, i-1, j); + dfs(grid, i, j-1); + } +} \ No newline at end of file diff --git a/Week 03/id_708/LeetCode_433_708.java b/Week 03/id_708/LeetCode_433_708.java new file mode 100644 index 000000000..34d4f65c1 --- /dev/null +++ b/Week 03/id_708/LeetCode_433_708.java @@ -0,0 +1,37 @@ +class Solution { + public int minMutation(String start, String end, String[] bank) { + Set visited = new HashSet<>(); + Queue queue = new LinkedList<>(); + char[] charArray = new char[]{ 'A', 'C', 'G', 'T' }; + Set bankSet = new HashSet<>(); + for (String str : bank) bankSet.add(str); + int level = 0; + + queue.offer(start); + while (!queue.isEmpty()) { + int size = queue.size(); + // 这一轮循环里只改变了一个字符 + while (size-- > 0) { + String curr = queue.poll(); + if (curr.equals(end)) return level; + char[] chars = curr.toCharArray(); + // 每次循环都是针对其中一个字符的操作 + for (int i = 0; i < chars.length; i++) { + char old = chars[i]; + // 对某个字符遍历所有可能 + for (char c : charArray) { + chars[i] = c; + String str = new String(chars); + if (!visited.contains(str) && bankSet.contains(str)) { + queue.offer(str); + visited.add(str); + } + } + chars[i] = old; + } + } + level++; + } + return -1; + } +} \ No newline at end of file diff --git a/Week 03/id_708/LeetCode_515_708.java b/Week 03/id_708/LeetCode_515_708.java new file mode 100644 index 000000000..9073eebc7 --- /dev/null +++ b/Week 03/id_708/LeetCode_515_708.java @@ -0,0 +1,30 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +// BSF +class Solution { + public List largestValues(TreeNode root) { + Queue queue = new LinkedList<>(); + List ret = new LinkedList<>(); + if (root != null) queue.offer(root); + TreeNode curr = null; + while (!queue.isEmpty()) { + int size = queue.size(); + int max = Integer.MIN_VALUE; + while (size-- > 0) { + curr = queue.poll(); + max = Math.max(max, curr.val); + if (curr.left != null) queue.offer(curr.left); + if (curr.right != null) queue.offer(curr.right); + } + ret.add(max); + } + return ret; + } +} \ No newline at end of file diff --git a/Week 03/id_708/LeetCode_529_708.java b/Week 03/id_708/LeetCode_529_708.java new file mode 100644 index 000000000..761d832cd --- /dev/null +++ b/Week 03/id_708/LeetCode_529_708.java @@ -0,0 +1,45 @@ +// dfs +// 仔细看条件,访问只有一轮:踩到地雷则退出,否则找出当前位置相邻的所有没有地雷的位置(注意:此时查找的是没有地雷的节点!) +class Solution { + public char[][] updateBoard(char[][] board, int[] click) { + int row = click[0], col = click[1]; + if (board[row][col] == 'M') board[row][col] = 'X'; + else findAdjacentBlankNode(board, row, col); + + return board; + } + + private void findAdjacentBlankNode(char[][] board, int row, int col) { + int bRow = board.length; + int bCol = board[0].length; + + // 不是地雷,就分两种情况:周围有地雷;周围无地雷 + // 遍历九宫格 + int count = 0; + for (int i = -1; i < 2; i++) { + for (int j = -1; j < 2; j++) { + int r = row+i, c = col+j; + // 周围挖出来一颗地雷 + if (r >= 0 && c >= 0 && r < bRow && c < bCol && board[r][c] == 'M') { + count++; + } + } + } + + if (count > 0) { + board[row][col] = (char)('0' + count); + } else { + // 当前位置周围无地雷,继续遍历周围节点 + board[row][col] = 'B'; + for (int i = -1; i < 2; i++) { + for (int j = -1; j < 2; j++) { + int r = row+i, c = col+j; + // 周围挖出来一颗地雷 + if (r >= 0 && c >= 0 && r < bRow && c < bCol && board[r][c] == 'E') { + findAdjacentBlankNode(board, r, c); + } + } + } + } + } +} \ No newline at end of file diff --git a/Week 03/id_713/AssignCookies.java b/Week 03/id_713/AssignCookies.java new file mode 100644 index 000000000..43529245f --- /dev/null +++ b/Week 03/id_713/AssignCookies.java @@ -0,0 +1,64 @@ +package id_713; + +import java.util.Arrays; + +/** + * 455. 分发饼干 + *

+ * 假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。 + *

+ * 注意: + *

+ * 你可以假设胃口值为正。 + * 一个小朋友最多只能拥有一块饼干。 + *

+ * 示例 1: + *

+ * 输入: [1,2,3], [1,1] + *

+ * 输出: 1 + *

+ * 解释: + * 你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。 + * 虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。 + * 所以你应该输出1。 + *

+ * 示例 2: + *

+ * 输入: [1,2], [1,2,3] + *

+ * 输出: 2 + *

+ * 解释: + * 你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。 + * 你拥有的饼干数量和尺寸都足以让所有孩子满足。 + * 所以你应该输出2. + */ +public class AssignCookies { + + /* + 思路: + 1. 两个数组先排序, 以便达到 用饼干, 满足需求小的小朋友 + 2. 双指针, 满足则都往后移动, 否则饼干往后移动 + */ + + public int findContentChildren(int[] g, int[] s) { + Arrays.sort(g); + Arrays.sort(s); + + int i = 0; + int j = 0; + int cnt = 0; + + while (i < g.length && j < s.length) { + if (g[i] <= s[j]) { + i++; + j++; + cnt++; + } else { + j++; + } + } + return cnt; + } +} diff --git a/Week 03/id_713/BestTimeToBuyAndSellStockII.java b/Week 03/id_713/BestTimeToBuyAndSellStockII.java new file mode 100644 index 000000000..d99707327 --- /dev/null +++ b/Week 03/id_713/BestTimeToBuyAndSellStockII.java @@ -0,0 +1,46 @@ +package id_713; + +/** + * 122. 买卖股票的最佳时机 II + *

+ * 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 + * 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。 + * 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 + *

+ * 输入: [7,1,5,3,6,4] + * 输出: 7 + * 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 + * 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。 + *

+ * 输入: [1,2,3,4,5] + * 输出: 4 + * 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 + * 注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。 + * 因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 + *

+ * 输入: [7,6,4,3,1] + * 输出: 0 + * 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 + */ +public class BestTimeToBuyAndSellStockII { + + /* + 思路: + 1. 因为在有限空间, 追求最大利润. 所以只要后面一天比前一天价格高, 就交易, 否则不交易 + 7 1 5 3 6 4 + 歇 买 卖 买 卖 歇 + 2. 从而转化为, SUM(后面-前面), 当且仅当 后面-前面>0 + + */ + + public int maxProfit(int[] prices) { + int p = 0; + + for (int i = prices.length; i > 0; i--) { + int tmp = prices[i] - prices[i - 1]; + p += (tmp > 0 ? tmp : 0); + } + + return p; + } +} diff --git a/Week 03/id_713/BinaryTreeLevelOrderTraversal.java b/Week 03/id_713/BinaryTreeLevelOrderTraversal.java new file mode 100644 index 000000000..c91dfb812 --- /dev/null +++ b/Week 03/id_713/BinaryTreeLevelOrderTraversal.java @@ -0,0 +1,51 @@ +package id_713; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * 102. 二叉树的层次遍历 + */ +public class BinaryTreeLevelOrderTraversal { + + + + + public List> levelOrder(TreeNode root) { + List> ans = new ArrayList<>(); + if (root == null) return ans; + + Queue queue = new LinkedList<>(); + queue.add(root); + + int level = 0; + + while (!queue.isEmpty()) { + int size = queue.size(); + + ans.add(new ArrayList<>()); + + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + ans.get(level).add(node.val); + + if (node.left != null) queue.add(node.left); + if (node.right != null) queue.add(node.right); + } + level++; + } + return ans; + } + + private class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } +} \ No newline at end of file diff --git a/Week 03/id_713/LemonadeChange.java b/Week 03/id_713/LemonadeChange.java new file mode 100644 index 000000000..f5a21929e --- /dev/null +++ b/Week 03/id_713/LemonadeChange.java @@ -0,0 +1,41 @@ +package id_713; + +/** + * 860. 柠檬水找零 + */ +public class LemonadeChange { + + /* + 思路: + 模拟真实场景即可 + 注意, 客户只会使用 5, 10, 20 + */ + + public boolean lemonadeChange(int[] bills) { + int five = 0; + int ten = 0; + + for (Integer i : bills) { + if (i == 5) { + five++; + } else if (i == 10) { + if (five == 0) return false; + ten++; + five--; + } else { + if (five > 0 && ten > 0) { + five--; + ten--; + } else if (five >= 3) { + five -= 3; + } else { + return false; + } + } + } + return true; + } + + + +} \ No newline at end of file diff --git a/Week 03/id_713/NOTE.md b/Week 03/id_713/NOTE.md index a6321d6e2..86e41133d 100644 --- a/Week 03/id_713/NOTE.md +++ b/Week 03/id_713/NOTE.md @@ -2,3 +2,270 @@ +### DFS深度优先 + +#### 算法模板 + +##### python版递归写法 + +```python +visited = set() + +def dfs(node, visited): + # 终止条件: 已经访问过的, 就不再访问了 + if node in visited: + return + + visited.add(node) + + # 对当前节点进行处理 + process(node) + + # 针对多叉树的操作 + for sub_node in node.children(): + if not sub_node in visited: + dfs(sub_node, visited) + + # 针对二叉树的操作 + dfs(node.left, visited) + dfs(node.right, visited) + +``` + +##### Java版递归写法 + +```java +private Set visited = new HashSet<>(); // 定义已经访问过的数据集 + +public void dfs(Node node) { + // 终止条件: 已经访问过的, 就不要再访问了 + if (visited.contains(node)) return; + + visited.add(node); + + // 对当前节点进行处理(如添加到结果集中) + this.process(node); + + // 针对多叉树 + for (Node subNode : node.children()) { + if (!visited.contains(subNode)) { + dfs(subNode); + } + } + + // 针对二叉树 + dfs(node.left); + dfs(node.right); +} +``` + +##### python版遍历(非递归)写法 + +* 需要手动维护一个栈 + +```python +def dfs(self, tree): + if tree.root is None: + return [] + + # 定义访问过的集合 与 栈, 并初始化栈 + visited, stack = [], [tree.root] + + while stack: + node = stack.pop() # 弹出栈顶元素 + visited.add(node) # 加入到访问记录 + + # 处理当前节点(如添加到结果集中) + process(node) + sub_nodes = get_sub_nodes(node) # 获取子节点 + stack.push(sub_nodes) # 将子节点压入栈中 + +``` + +##### Java版遍历(非递归)写法 + +```java +public void dfs(Tree tree) { + + if (tree.root == null) return; + + // 定义访问记录结果集 + Set visited = new HashSet<>(); + // 定义栈, 并初始化栈 + Stack stack = new Stack<>(); + stack.push(tree.root); + + while (!stack.empty()) { + Node node = stack.pop(); // 弹出栈顶元素 + visited.add(node); // 加入到访问记录 + + // 处理当前节点(如添加到结果集中) + process(node); + List subNodes = getSubNodes(node); // 获取子节点 + // 放入到栈中 + for (Node subNode : subNodes) { + if (visited.contains(subNode)) { + stack.push(); + } + } + } +} +``` + + + +### BFS广度优先 + +#### 算法模板 + +##### python版本 + +```python +visited = set() + +def bfs(graph, start, end): + queue = [] + queue.append([start]) + visited.add(start) + + while queue: + node = queue.pop() + visited.add(node) + # 处理当前节点 + process(node) + sub_nodes = get_sub_nodes(node) + queue.push(nodes) + +``` + +##### java版本写法 + +```java +private Set visited = new HashSet<>(); // 定义已经访问过的数据集 + +public void bfs(Graph graph, Node start, Node end) { + Queue queue = new LinkedList<>(); + queue.offer(start); + visited.add(start); + + while (!queue.isEmpty()) { + Node node = queue.poll(); + visited.add(node); + + // 处理当前节点 + process(node); + List subNodes = this.getSubNodes(node); + for (Node subNode : subNode) { + if (!visited.contains(subNode)){ + queue.add(subNode); + } + } + } +} +``` + + + +### 贪心算法 + +* 局部最优解 + * 每一步选择中, 寻找最优 +* 通常不是全局最优解 +* 与动态规划的区别 + +| 贪心 | 回溯 | 动态规划 | +| ------------ | -------- | ------------------ | +| 当下局部最优 | 能够回退 | 记录以前的运算结果 | + +#### 贪心算法的适用条件 + +* 问题能够分解成子问题 +* **子问题最优解** 能递推为 **全局最优解** +* 从后往前贪心, 或者从局部切入贪心 + + + + + +### 二分查找 + +#### 使用条件 + +* 目标函数的单调性 (单调递增或者递减) +* 存在上下界 (bounded) +* 能够沟通过索引访问 (index accessible) + +#### 算法模板 + +##### python版本 + +```python +left, right = 0, len(array) - 1 +while left <= right: + mid = (left + right) / 2 + if array[mid] == target: + # find the target! + break or return result + elif array[mid] < target: + left = mid + 1 + else: + right = mid + 1 + +``` + +#### 作业题 + +```java + /* + 思路: + 时间复杂度 O(logN) 则意味着: 要使用二分查找了 + + 1 2 3 4 5 6 7 可以分成2种情况 + 2 3 4 5 6 7 1 + 6 7 1 2 3 4 5 + + 第一类 2 3 4 5 6 7 1 也就 nums[left] <= nums[mid] 2 <= 5 + 这种情况下, 前半部分有序 + 如果 nums[left] <= target < nums[mid], 则在前半部分查找, 否则后半部分 + + 第二类 6 7 1 2 3 4 5 也就是 nums[left] > nums[mid] 6 > 2 + 这种情况下, 后半部分有序 + 如果 nums[mid] < target <= nums[right], 则在后半部分查找, 否则前半部分 + + */ + public int search(int[] nums, int target) { + + if (nums == null || nums.length == 0) return -1; + + int left = 0, right = nums.length - 1, mid = 0; + + while (left <= right) { + mid = (right - left) / 2 + left; // 相当于 (right + left) / 2 + + if (nums[mid] == target) return mid; + + // 前半部分有序, 包含mid + if (nums[left] <= nums[mid]) { + // target在前半部分 + if (target >= nums[left] && target < nums[mid]) { + right = mid - 1; + } else { + left = mid + 1; + } + } else { // 后半部分有序 + // target 后半部分 + if (target > nums[mid] && target < nums[right]) { + left = mid + 1; + } else { + right = mid -1; + } + } + + + } + + return -1; + } + +``` + diff --git a/Week 03/id_713/NumberOfIslands.java b/Week 03/id_713/NumberOfIslands.java new file mode 100644 index 000000000..1e7e16717 --- /dev/null +++ b/Week 03/id_713/NumberOfIslands.java @@ -0,0 +1,103 @@ +package id_713; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.Queue; + +/** + * 200. 岛屿数量 + */ +public class NumberOfIslands { + + + // 使用 dfs + public int numIslandsDfs(char[][] grid) { + + if (grid == null || grid.length == 0) { + return 0; + } + + int nr = grid.length; // 行数 + int nc = grid[0].length; // 列数 + int num_islands = 0; // 总计岛屿数 + + // 先行后列扫描 + for (int r = 0; r < nr; ++r) { + for (int c = 0; c < nc; ++c) { + if (grid[r][c] == '1') { // 发现有岛屿, 将自己和周边都赋值0 + ++num_islands; // 增加毗连的岛屿数量 + dfs(grid, r, c); // 递归赋值0 + } + } + } + + return num_islands; + } + + private void dfs(char[][] grid, int r, int c) { + int nr = grid.length; // 行数 + int nc = grid[0].length; // 列数 + + // 行数<0, 列数<0, 行数>最大行数, 列数>最大列数, 当前值==0 + if (r < 0 || c < 0 || r >= nr || c >= nc || grid[r][c] == '0') { + return; + } + + grid[r][c] = '0'; // 填平(染色) + + dfs(grid, r - 1, c); // 左 + dfs(grid, r + 1, c); // 右 + dfs(grid, r, c - 1); // 上 + dfs(grid, r, c + 1); // 下 + } + + + + // 使用bfs + public int numIslandsBfs(char[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + + int nr = grid.length; + int nc = grid[0].length; + int num_islands = 0; + + for (int r = 0; r < nr; ++r) { + for (int c = 0; c < nc; ++c) { + if (grid[r][c] == '1') { + ++num_islands; + grid[r][c] = '0'; // mark as visited + + Queue neighbors = new LinkedList<>(); + neighbors.add(r * nc + c); + while (!neighbors.isEmpty()) { + int id = neighbors.remove(); + int row = id / nc; + int col = id % nc; + if (row - 1 >= 0 && grid[row-1][col] == '1') { + neighbors.add((row-1) * nc + col); + grid[row-1][col] = '0'; + } + if (row + 1 < nr && grid[row+1][col] == '1') { + neighbors.add((row+1) * nc + col); + grid[row+1][col] = '0'; + } + if (col - 1 >= 0 && grid[row][col-1] == '1') { + neighbors.add(row * nc + col-1); + grid[row][col-1] = '0'; + } + if (col + 1 < nc && grid[row][col+1] == '1') { + neighbors.add(row * nc + col+1); + grid[row][col+1] = '0'; + } + } + } + } + } + + return num_islands; + } + + +} diff --git a/Week 03/id_713/SearchInRotatedSortedArray.java b/Week 03/id_713/SearchInRotatedSortedArray.java new file mode 100644 index 000000000..55f8cb7be --- /dev/null +++ b/Week 03/id_713/SearchInRotatedSortedArray.java @@ -0,0 +1,60 @@ +package id_713; + +/** + * 33. 搜索旋转排序数组 + */ +public class SearchInRotatedSortedArray { + + + /* + 思路: + 时间复杂度 O(logN) 则意味着: 要使用二分查找了 + + 1 2 3 4 5 6 7 可以分成2种情况 + 2 3 4 5 6 7 1 + 6 7 1 2 3 4 5 + + 第一类 2 3 4 5 6 7 1 也就 nums[left] <= nums[mid] 2 <= 5 + 这种情况下, 前半部分有序 + 如果 nums[left] <= target < nums[mid], 则在前半部分查找, 否则后半部分 + + 第二类 6 7 1 2 3 4 5 也就是 nums[left] > nums[mid] 6 > 2 + 这种情况下, 后半部分有序 + 如果 nums[mid] < target <= nums[right], 则在后半部分查找, 否则前半部分 + + */ + public int search(int[] nums, int target) { + + if (nums == null || nums.length == 0) return -1; + + int left = 0, right = nums.length - 1, mid = 0; + + while (left <= right) { + mid = (right - left) / 2 + left; // 相当于 (right + left) / 2 + + if (nums[mid] == target) return mid; + + // 前半部分有序, 包含mid + if (nums[left] <= nums[mid]) { + // target在前半部分 + if (target >= nums[left] && target < nums[mid]) { + right = mid - 1; + } else { + left = mid + 1; + } + } else { // 后半部分有序 + // target 后半部分 + if (target > nums[mid] && target < nums[right]) { + left = mid + 1; + } else { + right = mid -1; + } + } + + + } + + return -1; + } + +} diff --git a/Week 03/id_713/WordLadder.java b/Week 03/id_713/WordLadder.java new file mode 100644 index 000000000..099984cca --- /dev/null +++ b/Week 03/id_713/WordLadder.java @@ -0,0 +1,101 @@ +package id_713; + +import javafx.util.Pair; + +import java.util.*; + +/** + * 127. 单词接龙 + * 给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则: + *

+ * 每次转换只能改变一个字母。 + * 转换过程中的中间单词必须是字典中的单词。 + *

+ * 示例 1: + * 输入: + * beginWord = "hit", + * endWord = "cog", + * wordList = ["hot","dot","dog","lot","log","cog"] + * 输出: 5 + * 解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", + * 返回它的长度 5。 + *

+ * 示例 2: + * 输入: + * beginWord = "hit" + * endWord = "cog" + * wordList = ["hot","dot","dog","lot","log"] + * 输出: 0 + * 解释: endWord "cog" 不在字典中,所以无法进行转换。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/word-ladder + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class WordLadder { + + + /* + 思路: 广度优先搜索 + 利用广度优先搜索搜索从 beginWord 到 endWord 的路径。 + + 1. 对给定的 wordList 做预处理,找出所有的通用状态。将通用状态记录在字典中,键是通用状态,值是所有具有通用状态的单词。 + 2. 将包含 beginWord 和 1 的元组放入队列中,1 代表节点的层次。我们需要返回 endWord 的层次也就是从 beginWord 出发的最短距离。 + 3. 为了防止出现环,使用访问数组记录。 + 4. 当队列中有元素的时候,取出第一个元素,记为 current_word。 + 5. 找到 current_word 的所有通用状态,并检查这些通用状态是否存在其它单词的映射,这一步通过检查 all_combo_dict 来实现。 + 6. 从 all_combo_dict 获得的所有单词,都和 current_word 共有一个通用状态,所以都和 current_word 相连,因此将他们加入到队列中。 + 7. 对于新获得的所有单词,向队列中加入元素 (word, level + 1) 其中 level 是 current_word 的层次。 + 8. 最终当你到达期望的单词,对应的层次就是最短变换序列的长度。 + 标准广度优先搜索的终止条件就是找到结束单词。 + */ + + public int ladderLength(String beginWord, String endWord, List wordList) { + int length = beginWord.length(); // 因为题目描述, 长度都一样 + + Map> dict = new HashMap<>(); // 定义字典, key是泛化字符串, value是原字符串的集合 + + for (String word : wordList) { // 构造泛化字符与原字符的映射关系 (无向无权图) + for (int i = 0; i < length; i++) { + String newKey = word.substring(0, i) + '*' + word.substring(i + 1, length); // hit -> *it + ArrayList list = dict.getOrDefault(newKey, new ArrayList<>()); + list.add(word); + dict.put(newKey, list); + } + } + // 最终成为 *ot -> [hot, dot, lot]这样 + + // 定义队列, 元素为 原始词和层级, 并放入beginWord + Queue> queue = new LinkedList<>(); + queue.add(new Pair<>(beginWord, 1)); + + // 定义访问记录 + Map visited = new HashMap<>(); + visited.put(beginWord, true); + + // BFS + while (!queue.isEmpty()) { + Pair node = queue.poll(); + String word = node.getKey(); + Integer level = node.getValue(); + + for (int i = 0; i < length; i++) { + // 构造泛化词, 并从泛化词的图中获取相邻节点 + String newKey = word.substring(0, i) + '*' + word.substring(i + 1, length); + ArrayList list = dict.getOrDefault(newKey, new ArrayList<>()); + + for (String str : list) { + if (word.equals(str)) return level + 1; // 如果找到对应的原始词, 则返回当前层+1, 也就是多少步 + + if (!visited.containsKey(str)) { // 继续深度优选搜索 + visited.put(str, true); // 添加访问记录 + queue.add(new Pair<>(str, level + 1)); // 下一层级的元素, level需要加一 + } + } + } + + } + return 0; + } + +} \ No newline at end of file diff --git a/Week 03/id_713/not_finish/WordLadderII.java b/Week 03/id_713/not_finish/WordLadderII.java new file mode 100644 index 000000000..7789cc038 --- /dev/null +++ b/Week 03/id_713/not_finish/WordLadderII.java @@ -0,0 +1,35 @@ +package id_713.not_finish; + +import java.util.List; + +/** + * 126. 单词接龙 II + *

+ * 给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列。转换需遵循如下规则: + * 每次转换只能改变一个字母。 + * 转换过程中的中间单词必须是字典中的单词。 + *

+ * 说明: + * 如果不存在这样的转换序列,返回一个空列表。 + * 所有单词具有相同的长度。 + * 所有单词只由小写字母组成。 + * 字典中不存在重复的单词。 + * 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。 + *

+ * 示例 1: + * 输入: + * beginWord = "hit", + * endWord = "cog", + * wordList = ["hot","dot","dog","lot","log","cog"] + * 输出: + * [ + * ["hit","hot","dot","dog","cog"], + * ["hit","hot","lot","log","cog"] + * ] + */ +public class WordLadderII { + + public List> findLadders(String beginWord, String endWord, List wordList) { + return null; + } +} diff --git a/Week 03/id_718/126.word-ladder-ii.py b/Week 03/id_718/126.word-ladder-ii.py new file mode 100644 index 000000000..3fb862353 --- /dev/null +++ b/Week 03/id_718/126.word-ladder-ii.py @@ -0,0 +1,51 @@ +# +# @lc app=leetcode id=126 lang=python3 +# +# [126] Word Ladder II +# + +# @lc code=start +from collections import deque +class Solution: + def findLadders(self, beginWord: str, endWord: str, wordList: List[str]) -> List[List[str]]: + # BFS to save dict, DFS to print path + dic = set(wordList) + dic.add(beginWord) + dist = {} + self.bfs(endWord, beginWord, dic, dist) + ret = [] + self.dfs(beginWord, endWord, dic, dist, [beginWord], ret) + return ret + + def bfs(self, start, end, dic, dist): + dist[start] = 0 + q = deque([start]) + while q: + word = q.popleft() + for next_word in self.generate_next_words(word, dic): + if next_word not in dist: + dist[next_word] = dist[word] + 1 + q.append(next_word) + + def generate_next_words(self, word, dic): + ret = [] + for i in range(len(word)): + for c in 'abcdefghijklmnopqrstuvwxyz': + next_word = word[:i] + c + word[i+1:] + if next_word != word and next_word in dic: + ret.append(next_word) + return ret + + def dfs(self, start, end, dic, dist, path, ret): + if start == end: + ret.append(list(path)) + return + + for word in self.generate_next_words(start, dic): + if dist[word] != dist[start] - 1: + continue + path.append(word) + self.dfs(word, end, dic, dist, path, ret) + path.pop() +# @lc code=end + diff --git a/Week 03/id_718/127.word-ladder.py b/Week 03/id_718/127.word-ladder.py new file mode 100644 index 000000000..01ac789ab --- /dev/null +++ b/Week 03/id_718/127.word-ladder.py @@ -0,0 +1,36 @@ +# +# @lc app=leetcode id=127 lang=python3 +# +# [127] Word Ladder +# + +# @lc code=start +from collections import deque +class Solution: + def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: + wordList = set(wordList) + visited = set([beginWord]) + q = deque([beginWord]) + ret = 0 + while q: + ret += 1 + for _ in range(len(q)): + word = q.popleft() + if word == endWord: + return ret + for w in self.generate_next_words(word, wordList): + if w not in visited: + q.append(w) + visited.add(w) + return 0 + + def generate_next_words(self, word, wordList): + ret = [] + for i in range(len(word)): + for c in 'abcdefghijklmnopqrstuvwxyz': + next_word = word[:i] + c + word[i+1:] + if next_word != word and next_word in wordList: + ret.append(next_word) + return ret +# @lc code=end + diff --git a/Week 03/id_718/200.number-of-islands.py b/Week 03/id_718/200.number-of-islands.py new file mode 100644 index 000000000..efc89ef72 --- /dev/null +++ b/Week 03/id_718/200.number-of-islands.py @@ -0,0 +1,35 @@ +# +# @lc app=leetcode id=200 lang=python3 +# +# [200] Number of Islands +# + +# @lc code=start +from collections import deque +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + if not grid or not grid[0]: return 0 + M, N = len(grid), len(grid[0]) + q = deque() + res = 0 + for i in range(M): + for j in range(N): + if grid[i][j] == '1': + res += 1 + self.sink(grid, i, j, M, N) + return res + + def sink(self, grid, i, j, M, N): + grid[i][j] = '0' + q = deque([(i, j)]) + directions = ((0, 1), (0, -1), (1, 0), (-1, 0)) + while q: + x, y = q.pop() + for d in directions: + nx, ny = x + d[0], y + d[1] + if 0 <= nx < M and 0 <= ny < N and grid[nx][ny] == '1': + grid[nx][ny] = '0' + q.append((nx, ny)) + +# @lc code=end + diff --git a/Week 03/id_718/322.coin-change.py b/Week 03/id_718/322.coin-change.py new file mode 100644 index 000000000..1aa6ac30d --- /dev/null +++ b/Week 03/id_718/322.coin-change.py @@ -0,0 +1,24 @@ +# +# @lc app=leetcode id=322 lang=python3 +# +# [322] Coin Change +# + +# @lc code=start +class Solution: + def coinChange(self, coins: List[int], amount: int) -> int: + # i, j means use i coins, total amoung is j + MAX = 100000000000000 + dp = [MAX for _ in range(amount+1)] + dp[0] = 0 + for amt in range(1, amount + 1): + for coin in coins: + if amt - coin < 0: + continue + dp[amt] = min(dp[amt], dp[amt-coin] + 1) + if dp[amount] == MAX: + return - 1 + return dp[amount] + +# @lc code=end + diff --git a/Week 03/id_718/33.search-in-rotated-sorted-array.py b/Week 03/id_718/33.search-in-rotated-sorted-array.py new file mode 100644 index 000000000..a6002fdc4 --- /dev/null +++ b/Week 03/id_718/33.search-in-rotated-sorted-array.py @@ -0,0 +1,34 @@ +# +# @lc app=leetcode id=33 lang=python3 +# +# [33] Search in Rotated Sorted Array +# + +# @lc code=start +class Solution: + def search(self, nums: List[int], target: int) -> int: + if not nums: + return -1 + l , r = 0, len(nums) - 1 + while l + 1 < r: + mid = l + (r-l) // 2 + if nums[mid] == target: + return mid + if nums[l] <= nums[mid]: # left sorted + if nums[l] <= target < nums[mid]: + r = mid + else: + l = mid + else: + if nums[r] >= target > nums[mid]: + l = mid + else: + r = mid + if nums[l] == target: + return l + if nums[r] == target: + return r + return -1 + +# @lc code=end + diff --git a/Week 03/id_718/429.n-ary-tree-level-order-traversal.py b/Week 03/id_718/429.n-ary-tree-level-order-traversal.py new file mode 100644 index 000000000..dd18c5d21 --- /dev/null +++ b/Week 03/id_718/429.n-ary-tree-level-order-traversal.py @@ -0,0 +1,31 @@ +# +# @lc app=leetcode id=429 lang=python3 +# +# [429] N-ary Tree Level Order Traversal +# + +# @lc code=start +""" +# Definition for a Node. +class Node: + def __init__(self, val, children): + self.val = val + self.children = children +""" +class Solution: + def levelOrder(self, root: 'Node') -> List[List[int]]: + ret = [] + if not root: + return ret + q = [root] + while len(q): + n, level = len(q), [] + for _ in range(n): + node = q.pop(0) + level.append(node.val) + for c in node.children: + q.append(c) + ret.append(level) + return ret +# @lc code=end + diff --git a/Week 03/id_718/433.minimum-genetic-mutation.py b/Week 03/id_718/433.minimum-genetic-mutation.py new file mode 100644 index 000000000..5516886a0 --- /dev/null +++ b/Week 03/id_718/433.minimum-genetic-mutation.py @@ -0,0 +1,37 @@ +# +# @lc app=leetcode id=433 lang=python3 +# +# [433] Minimum Genetic Mutation +# + +# @lc code=start +from collections import deque +class Solution: + def minMutation(self, start: str, end: str, bank: List[str]) -> int: + ret = -1 + bank = set(bank) + q = deque([start]) + visited = set([start]) + while q: + ret += 1 + for _ in range(len(q)): + word = q.popleft() + if word == end: + return ret + for nw in self.generate_next_words(word, bank): + if nw not in visited: + q.append(nw) + visited.add(nw) + return -1 + + def generate_next_words(self, word, bank): + ret = [] + for i in range(len(word)): + for c in 'AGCT': + nw = word[:i] + c + word[i+1:] + if nw != word and nw in bank: + ret.append(nw) + bank.remove(nw) + return ret +# @lc code=end + diff --git a/Week 03/id_718/45.jump-game-ii.py b/Week 03/id_718/45.jump-game-ii.py new file mode 100644 index 000000000..8cec81bc4 --- /dev/null +++ b/Week 03/id_718/45.jump-game-ii.py @@ -0,0 +1,20 @@ +# +# @lc app=leetcode id=45 lang=python3 +# +# [45] Jump Game II +# + +# @lc code=start +class Solution: + def jump(self, nums: List[int]) -> int: + n = len(nums) + cur, cnt, pos = 0, 0, 0 + while cur < n - 1: + cnt += 1 + pre = cur + while pos <= pre: + cur = max(cur, pos + nums[pos]) + pos += 1 + return cnt +# @lc code=end + diff --git a/Week 03/id_718/455.assign-cookies.py b/Week 03/id_718/455.assign-cookies.py new file mode 100644 index 000000000..710ca930b --- /dev/null +++ b/Week 03/id_718/455.assign-cookies.py @@ -0,0 +1,21 @@ +# +# @lc app=leetcode id=455 lang=python3 +# +# [455] Assign Cookies +# + +# @lc code=start +class Solution: + def findContentChildren(self, g: List[int], s: List[int]) -> int: + s.sort() + g.sort() + cnt = 0 + for cookie in s: + if cnt > len(g) - 1: + break + if cookie >= g[cnt]: + cnt += 1 + return cnt + +# @lc code=end + diff --git a/Week 03/id_718/529.minesweeper.py b/Week 03/id_718/529.minesweeper.py new file mode 100644 index 000000000..84f8225e9 --- /dev/null +++ b/Week 03/id_718/529.minesweeper.py @@ -0,0 +1,47 @@ +# +# @lc app=leetcode id=529 lang=python3 +# +# [529] Minesweeper +# + +# @lc code=start +class Solution: + def updateBoard(self, board: List[List[str]], click: List[int]) -> List[List[str]]: + if not board or not board[0]: + return board + x, y = click + if board[x][y] == "M":# dig into the mine, you can modify it directly and then return + board[x][y] = "X" + return board + m, n = len(board), len(board[0]) + visited = [[False for _ in range(n + 1)] for j in range(m + 1)] + self.dfs(x, y, board, visited) + return board + + def dfs(self, x0, y0, board, visited): + if board[x0][y0] == "M" or visited[x0][y0]: + return + m, n = len(board), len(board[0]) + visited[x0][y0] = True + mineCnt = 0 + dx = (1, 1, -1, -1, 0, 0, -1, 1) + dy = (1, 0, -1, 0, 1, -1, 1, -1) + for k in range(8): + x = x0 + dx[k] + y = y0 + dy[k] + if 0 <= x < m and 0 <= y < n and board[x][y] == "M": + mineCnt += 1 + + if mineCnt > 0: + board[x0][y0] = str(mineCnt) + else: + board[x0][y0] = 'B' + + for k in range(8): + x = x0 + dx[k] + y = y0 + dy[k] + if 0 <= x < m and 0 <= y < n and not visited[x][y]: + self.dfs(x, y, board, visited) + +# @lc code=end + diff --git a/Week 03/id_718/55.jump-game.py b/Week 03/id_718/55.jump-game.py new file mode 100644 index 000000000..b6f429e8a --- /dev/null +++ b/Week 03/id_718/55.jump-game.py @@ -0,0 +1,18 @@ +# +# @lc app=leetcode id=55 lang=python3 +# +# [55] Jump Game +# + +# @lc code=start +class Solution: + def canJump(self, nums: List[int]) -> bool: + n, reach = len(nums), 0 + for i, jump in enumerate(nums): + if reach >= n - 1: + return True + if i > reach: + return False + reach = max(reach, i + jump) +# @lc code=end + diff --git a/Week 03/id_718/69.sqrt-x.py b/Week 03/id_718/69.sqrt-x.py new file mode 100644 index 000000000..8ce57cf7f --- /dev/null +++ b/Week 03/id_718/69.sqrt-x.py @@ -0,0 +1,23 @@ +# +# @lc app=leetcode id=69 lang=python3 +# +# [69] Sqrt(x) +# + +# @lc code=start +class Solution: + def mySqrt(self, x: int) -> int: + if x == 0 or x == 1: + return x + # binary search + l, r = 1, x + while l < r: + mid = l + (r-l) // 2 + if mid * mid <= x < (mid + 1) * (mid + 1): + return mid + if mid * mid > x: + r = mid + else: + l = mid +# @lc code=end + diff --git a/Week 03/id_718/860.lemonade-change.py b/Week 03/id_718/860.lemonade-change.py new file mode 100644 index 000000000..d3ecb235a --- /dev/null +++ b/Week 03/id_718/860.lemonade-change.py @@ -0,0 +1,28 @@ +# +# @lc app=leetcode id=860 lang=python3 +# +# [860] Lemonade Change +# + +# @lc code=start +class Solution: + def lemonadeChange(self, bills: List[int]) -> bool: + five = ten = 0 + for bill in bills: + if bill == 5: + five += 1 + elif bill == 10: + if not five: return False + five -= 1 + ten += 1 + else: + if ten and five: + ten -= 1 + five -= 1 + elif five >= 3: + five -= 3 + else: + return False + return True +# @lc code=end + diff --git a/Week 03/id_728/lemonadeChange_860.java b/Week 03/id_728/lemonadeChange_860.java new file mode 100644 index 000000000..af3404df9 --- /dev/null +++ b/Week 03/id_728/lemonadeChange_860.java @@ -0,0 +1,25 @@ +class Solution { + public boolean lemonadeChange(int[] bills) { + int c5=0; + int c10=0; + for(int bill:bills){ + if(bill==5) + c5+=5; + else if(bill==10){ + if(c5!=0){ + c5-=5; + c10+=10; + }else return false; + + }else if(c10!=0){ + if(c5!=0){ + c5-=5; + c10-=10; + }else return false; + }else if(c5>=15) + c5-=15; + else return false; + } + return true; + } +} diff --git a/Week 03/id_728/search_33.java b/Week 03/id_728/search_33.java new file mode 100644 index 000000000..3d167a441 --- /dev/null +++ b/Week 03/id_728/search_33.java @@ -0,0 +1,30 @@ +class Solution { + public int search(int[] nums, int target) { + int start = 0; + int end = nums.length - 1; + while (start <= end) { + int mid = (start + end) / 2; + if (target == nums[mid]) { + return mid; + } + //左半段是有序的 + if (nums[start] <= nums[mid]) { + //target 在这段里 + 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; + } +} diff --git a/Week 03/id_733/LeetCode_122_733.go b/Week 03/id_733/LeetCode_122_733.go new file mode 100644 index 000000000..9d7ed704c --- /dev/null +++ b/Week 03/id_733/LeetCode_122_733.go @@ -0,0 +1,10 @@ +func maxProfit(prices []int) int { + profit := 0 + for i := 1; i < len(prices); i++ { + if prices[i] > prices[i-1] { + profit += prices[i] - prices[i-1] + } + } + + return profit +} diff --git a/Week 03/id_733/LeetCode_126_733.go b/Week 03/id_733/LeetCode_126_733.go new file mode 100644 index 000000000..1f65ad783 --- /dev/null +++ b/Week 03/id_733/LeetCode_126_733.go @@ -0,0 +1,101 @@ +func findLadders(beginWord string, endWord string, wordList []string) [][]string { + var res [][]string + wordDict := make(map[string]struct{}) + for _, w := range wordList { + wordDict[w] = struct{}{} + } + + if _, ok := wordDict[endWord]; !ok { + return res + } + + delete(wordDict, endWord) + + visited := make(map[string]struct{}) + begin := make(map[string][][]string) + end := make(map[string][][]string) + flip := false + + begin[beginWord] = [][]string{{beginWord}} + end[endWord] = [][]string {{endWord}} + + done := false + for !done && len(begin) > 0 && len(end) > 0 { + if len(end) < len(begin) { + begin, end = end, begin + flip = !flip + } + + curVisited := make(map[string]struct{}) + newBegin := make(map[string][][]string) + for word, beginPaths := range begin { + chars := []rune(word) + for i := 0; i < len(chars); i++ { + origin := chars[i] + for c := 'a'; c <= 'z'; c++ { + chars[i] = c + s:= string(chars) + if endPath, ok := end[s]; ok { + done = true + addIntoFinalPaths(&res, beginPaths, endPath, flip) + } + + if done { + continue + } + + if _, ok := wordDict[s]; !ok { + continue + } + + if _, ok := visited[s]; ok { + continue + } + + curVisited[s] = struct{}{} + newBegin[s] = addIntoWordPaths(newBegin[s], beginPaths, s) + } + + chars[i] = origin + } + } + + begin = newBegin + for v, _ := range curVisited { + visited[v] = struct{}{} + } + } + + return res +} + +func addIntoWordPaths(wordPaths [][]string, paths [][]string, path string) [][]string { + for _, ps := range paths { + nps := make([]string, len(ps)+1) + copy(nps, ps) + nps[len(nps)-1] = path + wordPaths = append(wordPaths, nps) + } + + return wordPaths +} + +func addIntoFinalPaths(finalPaths *[][]string,beginPaths, endPaths [][]string , flip bool) { + if flip { + beginPaths, endPaths = endPaths, beginPaths + } + + for _, b := range beginPaths { + for _, e := range endPaths { + path := make([]string, len(b)+len(e)) + copy(path, b) + i := len(path) - 1 + for _, p := range e { + path[i] = p + i-- + } + + *finalPaths = append(*finalPaths, path) + } + } +} diff --git a/Week 03/id_733/LeetCode_127_733.go b/Week 03/id_733/LeetCode_127_733.go new file mode 100644 index 000000000..3de9c9489 --- /dev/null +++ b/Week 03/id_733/LeetCode_127_733.go @@ -0,0 +1,59 @@ +func ladderLength(beginWord string, endWord string, wordList []string) int { + wordDict := make(map[string]struct{}) + for _, w := range wordList { + wordDict[w] = struct{}{} + } + + if _, ok := wordDict[endWord]; !ok { + return 0 + } + + delete(wordDict, endWord) + + visited := make(map[string]struct{}) + begin := make(map[string]struct{}) + end := make(map[string]struct{}) + + begin[beginWord] = struct{}{} + end[endWord] = struct{}{} + + step := 2 + for len(begin) > 0 && len(end) > 0 { + if len(end) < len(begin) { + begin, end = end, begin + } + + newBegin := make(map[string]struct{}) + for w, _ := range begin { + chars := []rune(w) + for i := 0; i < len(chars); i++ { + origin := chars[i] + for c := 'a'; c <= 'z'; c++ { + chars[i] = c + s := string(chars) + if _, ok := end[s]; ok { + return step + } + + if _, ok := visited[s]; ok { + continue + } + + if _, ok := wordDict[s]; !ok { + continue + } + + visited[s] = struct{}{} + newBegin[s] = struct{}{} + } + + chars[i] = origin + } + } + + begin = newBegin + step++ + } + + return 0 +} \ No newline at end of file diff --git a/Week 03/id_733/LeetCode_153_733.go b/Week 03/id_733/LeetCode_153_733.go new file mode 100644 index 000000000..912f67750 --- /dev/null +++ b/Week 03/id_733/LeetCode_153_733.go @@ -0,0 +1,17 @@ +func findMin(nums []int) int { + left, right := 0, len(nums)-1 + for left < right { + if nums[left] < nums[right] { + break + } + + mid := left + (right-left)>>1 + if nums[mid] < nums[0] { + right = mid + } else { + left = mid + 1 + } + } + + return nums[left] +} diff --git a/Week 03/id_733/LeetCode_200_733.go b/Week 03/id_733/LeetCode_200_733.go new file mode 100644 index 000000000..883fde2ea --- /dev/null +++ b/Week 03/id_733/LeetCode_200_733.go @@ -0,0 +1,47 @@ +const water = '0' + +var dx = []int{-1, 1, 0, 0} +var dy = []int{0, 0, -1, 1} + +func NumIslands(grid [][]byte) int { + return numIslands(grid) +} + +func numIslands(grid [][]byte) int { + height := len(grid) + if height == 0 { + return 0 + } + + width := len(grid[0]) + if width == 0 { + return 0 + } + + cnt := 0 + for i := 0; i < width; i++ { + for j := 0; j < height; j++ { + cnt += searchIsland(grid, i, j, width, height) + } + } + + return cnt +} + +func searchIsland(grid [][]byte, i, j, w, h int) int { + if grid[j][i] == water { + return 0 + } + + grid[j][i] = water + for k := 0; k < len(dx); k++ { + x, y := i+dx[k], j+dy[k] + if x < 0 || x >= w || y < 0 || y >= h { + continue + } + + searchIsland(grid, x, y, w, h) + } + + return 1 +} diff --git a/Week 03/id_733/LeetCode_33_733.go b/Week 03/id_733/LeetCode_33_733.go new file mode 100644 index 000000000..9ac2a87cd --- /dev/null +++ b/Week 03/id_733/LeetCode_33_733.go @@ -0,0 +1,25 @@ +func search(nums []int, target int) int { + left, right := 0, len(nums)-1 + for left <= right { + mid := left + (right-left)>>1 + if nums[mid] == target { + return mid + } + + if nums[mid] < target { + if nums[mid] < nums[0] && target >= nums[0] { + right = mid - 1 + } else { + left = mid + 1 + } + } else { + if nums[mid] >= nums[0] && target < nums[0] { + left = mid + 1 + } else { + right = mid - 1 + } + } + } + + return -1 +} diff --git a/Week 03/id_733/LeetCode_455_733.go b/Week 03/id_733/LeetCode_455_733.go new file mode 100644 index 000000000..19c04bbf9 --- /dev/null +++ b/Week 03/id_733/LeetCode_455_733.go @@ -0,0 +1,13 @@ +func findContentChildren(g []int, s []int) int { + sort.Ints(g) + sort.Ints(s) + + i := 0 + for j := 0; i < len(g) && j < len(s); j++ { + if g[i] <= s[j] { + i++ + } + } + + return i +} diff --git a/Week 03/id_733/LeetCode_45_733.go b/Week 03/id_733/LeetCode_45_733.go new file mode 100644 index 000000000..b18d1651f --- /dev/null +++ b/Week 03/id_733/LeetCode_45_733.go @@ -0,0 +1,16 @@ +func jump(nums []int) int { + steps := 0 + for i, curEnd, nextEnd := 0, 0, 0; i < len(nums) - 1; i++ { + farthest := i + nums[i] + if farthest > nextEnd { + nextEnd = farthest + } + + if i == curEnd { + steps++ + curEnd = nextEnd + } + } + + return steps +} diff --git a/Week 03/id_733/LeetCode_529_733.go b/Week 03/id_733/LeetCode_529_733.go new file mode 100644 index 000000000..005a18c08 --- /dev/null +++ b/Week 03/id_733/LeetCode_529_733.go @@ -0,0 +1,83 @@ +const ( + mine = 'M' + empty = 'E' + blank = 'B' + bomb = 'X' +) + +func updateBoard(board [][]byte, click []int) [][]byte { + height := len(board) + if height == 0 { + return board + } + + width := len(board[0]) + if width == 0 { + return board + } + + if len(click) != 2 { + return board + } + + x, y := click[1], click[0] + if x < 0 || x >= width || y < 0 || y >= height { + return board + } + + if board[y][x] == mine { + board[y][x] = bomb + return board + } + + spread(board, x, y, width, height) + return board +} + +func spread(board [][]byte, x, y, w, h int) { + if board[y][x] != empty { + return + } + + xl := max(x-1, 0) + xr := min(x+1, w-1) + yu := max(y-1, 0) + yd := min(y+1, h-1) + + cnt := 0 + for i := xl; i <= xr; i++ { + for j := yu; j <= yd; j++ { + if board[j][i] == mine { + cnt++ + } + } + } + + if cnt > 0 { + board[y][x] = byte('0' + cnt) + return + } + + board[y][x] = blank + for i := xl; i <= xr; i++ { + for j := yu; j <= yd; j++ { + spread(board, i, j, w, h) + } + } +} + +func max(x, y int) int { + if x > y { + return x + } + + return y +} + +func min(x, y int) int { + if x > y { + return y + } + + return x +} diff --git a/Week 03/id_733/LeetCode_55_733.go b/Week 03/id_733/LeetCode_55_733.go new file mode 100644 index 000000000..d126b06a2 --- /dev/null +++ b/Week 03/id_733/LeetCode_55_733.go @@ -0,0 +1,10 @@ +func canJump(nums []int) bool { + reachable := len(nums) - 1 + for i := len(nums) - 2; i >= 0; i-- { + if nums[i]+i >= reachable { + reachable = i + } + } + + return reachable == 0 +} diff --git a/Week 03/id_733/LeetCode_74_733.go b/Week 03/id_733/LeetCode_74_733.go new file mode 100644 index 000000000..d2d2ce82f --- /dev/null +++ b/Week 03/id_733/LeetCode_74_733.go @@ -0,0 +1,64 @@ +func searchMatrix(matrix [][]int, target int) bool { + if len(matrix) == 0 || len(matrix[0]) == 0 { + return false + } + + left, right := 0, len(matrix)-1 + for left < right { + mid := right - (right-left)>>1 + value := matrix[mid][0] + if value == target { + return true + } + + if value < target { + left = mid + } else { + right = mid - 1 + } + } + + col := left + left, right = 0, len(matrix[0])-1 + for left <= right { + mid := left + (right-left)>>1 + value := matrix[col][mid] + if value == target { + return true + } + + if value < target { + left = mid + 1 + } else { + right = mid - 1 + } + } + + return false +} + +func searchMatrixV2(matrix [][]int, target int) bool { + colLen := len(matrix) + if colLen == 0 { + return false + } + + rowLen := len(matrix[0]) + left, right := 0, colLen*rowLen-1 + for left <= right { + mid := left + (right-left)>>1 + col, row := mid/rowLen, mid%rowLen + value := matrix[col][row] + if value == target { + return true + } + + if value < target { + left = mid + 1 + } else { + right = mid - 1 + } + } + + return false +} diff --git a/Week 03/id_733/LeetCode_860_733.go b/Week 03/id_733/LeetCode_860_733.go new file mode 100644 index 000000000..7c3b9f6cb --- /dev/null +++ b/Week 03/id_733/LeetCode_860_733.go @@ -0,0 +1,27 @@ +const ( + five = 5 + ten = 10 +) + +func lemonadeChange(bills []int) bool { + fiveCnt, tenCnt := 0, 0 + for _, bill := range bills { + if bill == five { + fiveCnt++ + } else if bill == ten { + tenCnt++ + fiveCnt-- + } else if tenCnt > 0 { + tenCnt-- + fiveCnt-- + } else { + fiveCnt -= 3 + } + + if fiveCnt < 0 { + return false + } + } + + return true +} diff --git a/Week 03/id_733/LeetCode_874_733.go b/Week 03/id_733/LeetCode_874_733.go new file mode 100644 index 000000000..5ed2929b4 --- /dev/null +++ b/Week 03/id_733/LeetCode_874_733.go @@ -0,0 +1,48 @@ +const ( + turnLeft = -2 + turnRight = -1 +) + +func robotSim(commands []int, obstacles [][]int) int { + dx := []int{0, 1, 0, -1} + dy := []int{1, 0, -1, 0} + + obs := make(map[int64]struct{}, len(obstacles)) + for _, co := range obstacles { + obs[getKey(co[0], co[1])] = struct{}{} + } + + idx := 0 + x, y, maxDis := 0, 0, 0 + for _, c := range commands { + switch c { + case turnLeft: + idx = (idx + 3) % 4 + + case turnRight: + idx = (idx + 1) % 4 + + default: + for c > 0 { + nx, ny := x+dx[idx], y+dy[idx] + if _, ok := obs[getKey(nx, ny)]; ok { + break + } + + c-- + x, y = nx, ny + } + + dis := x*x + y*y + if dis > maxDis { + maxDis = dis + } + } + } + + return maxDis +} + +func getKey(x, y int) int64 { + return (int64(x) + 30000) << 16 + int64(y) + 30000 +} diff --git a/Week 03/id_738/LeetCode_126_738.py b/Week 03/id_738/LeetCode_126_738.py new file mode 100644 index 000000000..bc2cfb3e8 --- /dev/null +++ b/Week 03/id_738/LeetCode_126_738.py @@ -0,0 +1,110 @@ +class Solution(object): + def findLadders(self, beginWord, endWord, wordList): + """ + 单词接龙2:https://leetcode-cn.com/problems/word-ladder-ii/description/ + + :type beginWord: str + :type endWord: str + :type wordList: List[str] + :rtype: List[List[str]] + """ + # BFS,写法1: + #从开始到结尾逐层探索 + wordList = set(wordList) + layer = {} + #因为一个单词节点到达可能为多个路径,所以这里用二维数组 + #layer只存储当前探索层的单词 + layer[beginWord] = [[beginWord]] + result = [] + #下面一层一层探索,直到探索到layer的key等于endWord终止,此key对应的value列表为最终结果 + #探索一层后,要将layer更新为下一层的单词 + #直到layer没有单词,终止 + while layer: + newlayer = collections.defaultdict(list) + #对layer的每个单词进行下探一层 + for w in layer: + if w == endWord: + #此时找到最短路径,将最短路径的节点列表添加到result列表里面,因为最短路径的节点列表为列表,所以用extend + result.extend(k for k in layer[w]) + else: + #还没到达终点,那么对这个单词进行逐位替换,变为下一层的节点 + for i in range(len(w)): + for c in 'abcdefghijklmnopqrstuvwxyz': + neww = w[:i] + c + w[i + 1:] + if neww in wordList: + #这句话就是将这一层的单词的路径列表每个都加上下一层的单词,作为下一层单词的可达路径 + newlayer[neww].extend(k + [neww] for k in layer[w]) + # newlayer[neww] += [k + [neww] for k in layer[w]] + #探索过的单词,从wordList中移除,防止重复踩点 + wordList -= set(newlayer.keys()) + layer = newlayer + + return result +########################################################################################################## + + # 单向BFS:写法2 + queue = [beginWord] + wordList = set(wordList) + visited = collections.defaultdict(list) + visited[beginWord] = [[beginWord]] + while queue: + curs = [] + while queue: + n = queue.pop() + if n == endWord: + return visited[n] + curs.append(n) + for cur in curs: + for i in range(len(cur)): + for k in "abcdefghijklmnopqrstuvwxyz": + if k == cur[i]: + continue + new = cur[:i] + k + cur[i + 1:] + if new in wordList: + visited[new].extend([p + [new] for p in visited[cur]]) + if new not in queue: + queue.append(new) + wordList -= set(queue) + return [] + +########################################################################################################## + + + # BFS双向求解 + if endWord not in wordList: + return [] + + forward = {beginWord} + backward = {endWord} + wordList = set(wordList) + dic = collections.defaultdict(set) + + flag = True + letters = set('qwertyuioplkjhgfdsazxcvbnm') + length = len(endWord) + + while forward: + if len(forward) > len(backward): + forward, backward, flag = backward, forward, not flag + cur = set() + wordList -= forward + for word in forward: + for idx in range(length): + x,y = word[:idx],word[idx + 1:] + for letter in letters: + temp = x + letter + y + if temp in wordList: + cur.add(temp) + if flag: dic[temp].add(word) + else: dic[word].add(temp) + if cur & backward: + res = [[endWord]] + while res[0][0] != beginWord: + res = [[x] + y for y in res for x in dic[y[0]]] + return res + forward = cur + return [] + + + + diff --git a/Week 03/id_738/LeetCode_127_738.py b/Week 03/id_738/LeetCode_127_738.py new file mode 100644 index 000000000..b640f5c0f --- /dev/null +++ b/Week 03/id_738/LeetCode_127_738.py @@ -0,0 +1,177 @@ +class Solution(object): + def ladderLength(self, beginWord, endWord, wordList): + """ + 单词接龙:https://leetcode-cn.com/problems/word-ladder/description/ + + :type beginWord: str + :type endWord: str + :type wordList: List[str] + :rtype: int + """ + # BFS-1:完全遍历替代每个位置为26个字母 + # wordList = set(wordList) + # change = "abcdefghijklmnopqrstuvwxyz" + # queue = [beginWord] + # step = 0 + # while queue: + # step += 1 + # curs = [] + # while queue: + # n = queue.pop() + # if n == endWord: + # return step + # curs.append(n) + # for cur in curs: + # for i in range(len(cur)): + # for k in change: + # c = cur[:i] + k + cur[i + 1:] + # if c in wordList: + # queue.append(c) + # wordList.remove(c) + # return 0 +########################################################################################################## + # BFS-2:单词的变换为不用26个字母,而是设置为*,代表这个位置的都可以变换为标准词库里的目标单词 + # 所以要初始化一下这个映射关系的map + # change = collections.defaultdict(list) + # L = len(endWord) + # for word in wordList: + # for i in range(L): + # c = word[:i] + '*' + word[i + 1:] + # change[c].append(word) + # queue = [beginWord] + # step = 0 + # # visited做剪枝,访问过的节点,无需再重复访问,因为再重复访问step也是比之前的大 + # visited = set() + # while queue: + # step += 1 + # curs = [] + # while queue: + # n = queue.pop() + # if n == endWord: + # return step + # curs.append(n) + # for cur in curs: + # for i in range(L): + # c = cur[:i] + '*' + cur[i + 1:] + # for new in change[c]: + # if new not in visited: + # visited.add(new) + # queue.append(new) + # change[c] = [] + # return 0 +########################################################################################################## + # BFS-3:究极解法,双端BFS,写法1 + # 头部广度搜索,尾部广度搜索,头尾交替进行,头尾最近一次到达的节点如果有交集,那么就表示最短路径第一次出现 + + # bfs每次下探一层 + # def bfs(level, queue, visited, vilid_visited): + # level += 1 + # curs = [] + # while queue: + # n = queue.pop() + # if n in vilid_visited: + # return level + # curs.append(n) + # for cur in curs: + # for i in range(L): + # c = cur[:i] + '*' + cur[i + 1:] + # for new in change[c]: + # if new not in visited: + # visited.add(new) + # queue.append(new) + # change[c] = [] + + # change = collections.defaultdict(list) + # L = len(endWord) + # for word in wordList: + # for i in range(L): + # c = word[:i] + '*' + word[i + 1:] + # change[c].append(word) + + # if endWord not in wordList: + # return 0 + # head = [beginWord] + # end = [endWord] + # head_level = 0 + # end_level = 0 + # head_visited = set() + # end_visited = set() + # while head and end: + # l = bfs(head_level, head, head_visited, end_visited) + # if l: return l + end_level + # else: head_level += 1 + # l = bfs(end_level, end, end_visited, head_visited) + # if l: return l + head_level + # else: end_level += 1 + # return 0 +########################################################################################################## + # BFS-3:究极解法,双端BFS,写法2 + # 头部广度搜索,尾部广度搜索,头尾交替进行,头尾最近一次到达的节点如果有交集,那么就表示最短路径第一次出现 + # 时间空间复杂度比上面所有解法都低 + def visitWordNode(queue, visited, others_visited): + level, current_word = queue.pop(0) + for i in range(self.L): + intermediate_word = current_word[:i] + "*" + current_word[i + 1:] + for word in m[intermediate_word]: + if word in others_visited: + return level + others_visited[word] + if word not in visited: + visited[word] = level + 1 + queue.append((level + 1, word)) + return None + self.L = len(beginWord) + m = collections.defaultdict(list) + for word in wordList: + for k in range(0, self.L): + key = word[:k] + '*' + word[k + 1:] + m[key].append(word) + queue_tail = [(1, endWord)] + queue_head = [(1, beginWord)] + visited_head = {beginWord: 1} + visited_tail = {endWord: 1} + result = None + if endWord not in wordList: + return 0 + while queue_tail and queue_head: + result = visitWordNode(queue_head, visited_head, visited_tail) + if result: + return result + result = visitWordNode(queue_tail, visited_tail, visited_head) + if result: + return result + return 0 +########################################################################################################## + # DFS 表示超出时间限制 +# def dfs(step, beginWord, endWord, wordList, last_index): +# if beginWord == endWord: +# if self.min_step is None or self.min_step > step: +# self.min_step = step +# return +# if not wordList: +# return +# for i in range(len(beginWord)): +# if i != last_index: +# letter = beginWord[i] +# for change_letter in change[i]: +# if letter != change_letter: +# new = beginWord[:i] + change_letter + beginWord[i + 1:] +# if new in wordList: +# wordList.remove(new) +# # print(wordList) +# # print(change) +# dfs(step + 1, new, endWord, wordList, i) +# wordList.add(new) + +# #存储每个位置可以转换为的字母 +# change = collections.defaultdict(set) +# for letter in wordList: +# for j in range(len(letter)): +# change[j].add(letter[j]) +# wordList = set(wordList) +# self.min_step = None +# dfs(0, beginWord, endWord, wordList, -1) +# return 0 if not self.min_step else self.min_step + 1 + + + + diff --git a/Week 03/id_738/LeetCode_153_738.py b/Week 03/id_738/LeetCode_153_738.py new file mode 100644 index 000000000..fdfe7f990 --- /dev/null +++ b/Week 03/id_738/LeetCode_153_738.py @@ -0,0 +1,62 @@ +class Solution(object): + def findMin(self, nums): + """ + 寻找旋转排序数组中的最小值:https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/ + + :type nums: List[int] + :rtype: int + """ + # 二分法写法2 + # 先排除掉特殊数组,否则下面循环会越界错误 + if len(nums) == 1 or nums[0] < nums[-1]: + return nums[0] + + left, right = 0, len(nums) - 1 + while left <= right: + mid = (right + left) >> 1 + if nums[mid] < nums[mid - 1]: + return nums[mid] + if nums[mid] > nums[mid + 1]: + return nums[mid + 1] + if nums[0] > nums[mid]: # 左边乱序 + right = mid - 1 + elif nums[0] < nums[mid]: # 右边乱序 + left = mid + 1 + + +################################################### + + # 二分法,写法1,判断mid左右两边哪一边是有序的,那么最小值肯定落在无序的一边 + # left, right = 0, len(nums) - 1 + # while left <= right: + # # 如果是left 和 right相邻的情况,那么不是left就是right + # if right - left == 1: + # return min(nums[left], nums[right]) + # mid = (left + right) >> 1 + # # 左边(含mid)无序,值在左边 + # if nums[left] > nums[mid]: + # right = mid + # # 右边(含mid)无序,值在右边 + # elif nums[right] < nums[mid]: + # left = mid + # # Mid,left,right重合 + # elif left == right: + # return nums[mid] + # # 最后剩下nums[left] < nums[mid] < nums[right]的情况,明显就是一个没有被旋转的数组,直接返回left + # else: + # return nums[left] + +################################################### + + # 直接解答。。平均时间复杂度最大O(n/2), + # if not nums: + # return None + # if nums[0] <= nums[-1]: + # return nums[0] + # result = nums[0] + # for n in nums: + # if result < n: + # continue + # result = n + # return result + \ No newline at end of file diff --git a/Week 03/id_738/LeetCode_200_738.py b/Week 03/id_738/LeetCode_200_738.py new file mode 100644 index 000000000..e044ce324 --- /dev/null +++ b/Week 03/id_738/LeetCode_200_738.py @@ -0,0 +1,29 @@ +class Solution(object): + def numIslands(self, grid): + """ + 岛屿数量:https://leetcode-cn.com/problems/number-of-islands/ + + :type grid: List[List[str]] + :rtype: int + """ + # 导弹轰炸法,每遇到一个岛屿,dfs法炸平整个岛 + # 到最后看看用了几个导弹就是有几个岛屿 + def dfs(x, y): + if grid[x][y] == '0': + return 0 + grid[x][y] = '0' + for i in range(len(dx)): + new_x, new_y = x + dx[i], y + dy[i] + if 0 <= new_x < len(grid) and 0 <= new_y < len(grid[0]): + dfs(new_x, new_y) + return 1 + dx = [-1, 1, 0, 0] + dy = [0 ,0 ,-1, 1] + islands = 0 + for i in range(len(grid)): + for j in range(len(grid[i])): + if grid[i][j] == '1': + islands += dfs(i, j) + return islands + + \ No newline at end of file diff --git a/Week 03/id_738/LeetCode_33_738.py b/Week 03/id_738/LeetCode_33_738.py new file mode 100644 index 000000000..db3edf417 --- /dev/null +++ b/Week 03/id_738/LeetCode_33_738.py @@ -0,0 +1,62 @@ +class Solution(object): + def search(self, nums, target): + """ + 搜索旋转排序数组:https://leetcode-cn.com/problems/search-in-rotated-sorted-array/ + + :type nums: List[int] + :type target: int + :rtype: int + """ + # 直接二分查找 + # 解题关键:如何判断target在数组的左半部分还是右半部分 + # 一步一步分解,按不同情况移动left和right。思路完全清晰,但是代码if else比较多(不影响执行效率) + left, right = 0, len(nums) - 1 + while left <= right: + mid = left + (right - left) // 2 + if nums[mid] == target: + return mid + # 判断左右部分哪一部分是升序的 + if nums[0] < nums[mid]: # 左半部分是升序的 + if nums[0] <= target < nums[mid]: # target落在左半部分 + right = mid - 1 + else: + left = mid + 1 + elif nums[0] > nums[mid]: # 右半部分是升序的 + if nums[mid] < target <= nums[-1]: # target落在右边部分 + left = mid + 1 + else: + right = mid - 1 + else: # nums[0] == nums[mid]表示left为0,right为1 + if nums[left] == target: + return left + elif nums[right] == target: + return right + else: + return -1 + return -1 + + # 二分查找写法2:这是别人题解的写法,思想和上面的一致,不过个人觉得思路难理解,步骤不清晰。 + # left, right = 0, len(nums) - 1 + # while left < right: + # if nums[0] <= nums[mid] and (target > nums[mid] or target < nums[0]): + # left = mid + 1 + # elif target < nums[0] and target > nums[mid]: + # left = mid + 1 + # else: + # right = mid + # return left if left == right and nums[left] == target else -1 + + + + + + + + + + + + + + + diff --git a/Week 03/id_738/LeetCode_529_738.py b/Week 03/id_738/LeetCode_529_738.py new file mode 100644 index 000000000..ac3afca31 --- /dev/null +++ b/Week 03/id_738/LeetCode_529_738.py @@ -0,0 +1,49 @@ +class Solution(object): + def updateBoard(self, board, click): + """ + 扫雷游戏:https://leetcode-cn.com/problems/minesweeper/solution/cyan-du-you-xian-sou-suo-jie-jue-by-cailfbit/ + + :type board: List[List[str]] + :type click: List[int] + :rtype: List[List[str]] + """ + # 点击地雷--显示X,返回; + # 点空方块--若周围有地雷,显示数字n,返回; + # 点空方块--若周围没有地雷,显示空白‘B’,并递归显示相连的’M‘方块; + # 不需要考虑其余input; + # => 如果一个方块显示数字,则不再递归其相邻节点 + + def dfs(x, y): + if board[x][y] != 'E' and board[x][y] != 'M': + return + if board[x][y] == 'M': + if x == click[0] and y == click[1]: + board[x][y] = 'X' + return + else: + board[x][y] = 0 + for i in range(len(dx)): + new_x = x + dx[i] + new_y = y + dy[i] + if 0 <= new_x < len(board) and 0 <= new_y < len(board[0]): + if board[new_x][new_y] == 'M' or board[new_x][new_y] == 'X': + board[x][y] += 1 + if board[x][y] == 0: + board[x][y] = 'B' + else: + board[x][y] = str(board[x][y]) + return + for i in range(len(dx)): + new_x = x + dx[i] + new_y = y + dy[i] + if 0 <= new_x < len(board) and 0 <= new_y < len(board[0]): + # if board[new_x][new_y] == 'M' or board[new_x][new_y] == 'E': + dfs(new_x, new_y) + + + dx = [1, -1, 0, 0, 1, -1, -1, 1] + dy = [0, 0, -1, 1, -1, 1, -1, 1] + x = click[0] + y = click[1] + dfs(x, y) + return board \ No newline at end of file diff --git a/Week 03/id_738/LeetCode_74_738.py b/Week 03/id_738/LeetCode_74_738.py new file mode 100644 index 000000000..9fb7bc767 --- /dev/null +++ b/Week 03/id_738/LeetCode_74_738.py @@ -0,0 +1,65 @@ +class Solution(object): + def searchMatrix(self, matrix, target): + """ + 搜索二维矩阵:https://leetcode-cn.com/problems/search-a-2d-matrix/ + + :type matrix: List[List[int]] + :type target: int + :rtype: bool + """ + # 二分查找:直接二分查找 + if not matrix or not matrix[0]: + return False + + height = len(matrix) + width = len(matrix[0]) + left, right = 0, height * width - 1 + while left <= right: + mid = (left + right) // 2 + val = matrix[mid // width][mid % width] + if val == target: + return True + elif val < target: + left = mid + 1 + else: + right = mid - 1 + return False + + + # 二分查找1:先找出在哪一行进行二分,再二分 +# def b(nums): +# left, right = 0, len(nums) - 1 +# while left <= right: +# mid = (left + right) >> 1 +# if nums[mid] == target: +# return True +# if nums[mid] < target: +# left = mid + 1 +# if nums[mid] > target: +# right = mid - 1 +# return False + +# if not matrix or not matrix[0]: +# return False + +# first_list = [matrix[i][0] for i in range(len(matrix))] +# # 第几行查找 +# s = 0 +# for i in range(len(first_list)): +# if first_list[i] == target: +# return True +# if i + 1 > len(first_list) - 1 or first_list[i] < target < first_list[i + 1]: +# s = i +# break +# return b(matrix[s]) + + + + + + + + + + + \ No newline at end of file diff --git a/Week 03/id_738/LeetCode_860_738.py b/Week 03/id_738/LeetCode_860_738.py new file mode 100644 index 000000000..bdc5d0186 --- /dev/null +++ b/Week 03/id_738/LeetCode_860_738.py @@ -0,0 +1,32 @@ +class Solution(object): + def lemonadeChange(self, bills): + """ + 柠檬水找零:https://leetcode-cn.com/problems/lemonade-change/description/ + + :type bills: List[int] + :rtype: bool + """ + five = ten = 0 + for bill in bills: #找零0 + if bill == 5: # 5直接收下 + five += 1 + elif bill == 10: #找零5 + if five > 0: # 要求身上5的个数>0才能找零 + five -= 1 + ten += 1 + else: + return False + elif bill == 20: #找零15 + #这个分多个情况分析 + if 5 * five + 10 * ten < 15: #身上零钱少于20,失败 + return False + if five <= 0: # 身上如果没有5,失败,因为找零15必须里面有个5 + return False + if ten <= 0: # 来到这个if表示身上钱大于等于20,而且至少有一个5,这个判断表示身上没有10,都是5,那么直接找零3张5 + five -= 3 + if ten > 0: # 来到这个if表示身上钱大于等于20,而且至少有一个5,这个判断表示身上有10,那么直接找零1张10和一张5 + five -= 1 + ten -= 1 + #20不能找任何零,不计入 + return True + diff --git a/Week 03/id_738/LeetCode_874_738.py b/Week 03/id_738/LeetCode_874_738.py new file mode 100644 index 000000000..20c9d0c1e --- /dev/null +++ b/Week 03/id_738/LeetCode_874_738.py @@ -0,0 +1,33 @@ +class Solution(object): + def robotSim(self, commands, obstacles): + """ + 模拟机器人走路:https://leetcode-cn.com/problems/walking-robot-simulation/description/ + + :type commands: List[int] + :type obstacles: List[List[int]] + :rtype: int + """ + # dx,dy每个元素一一对应,顺序从0到4方向表示向右转command == -1,顺序从4到0方向表示向左转command == -2 + dx = [0, 1, 0, -1] + dy = [1, 0, -1, 0] + obset = set([(v[0], v[1]) for v in obstacles]) # 等价于 obset = set(map(tuple, obstacles)) + + # df表示目前dx,dy的下标 + x = y = di = 0 + result = 0 + for command in commands: + if command == -1: # 右转90度,(di + 1) % 4 + di = (di + 1) % 4 + elif command == -2: # 左转90度,(di - 1) % 4 + di = (di - 1) % 4 + else: # 移动步数 + for i in range(1, command + 1): + if (x + dx[di], y + dy[di]) not in obset: + x += dx[di] + y += dy[di] + else: + break + result = max(result, x * x + y * y) + return result + + \ No newline at end of file diff --git a/Week 03/id_738/NOTE.md b/Week 03/id_738/NOTE.md index a6321d6e2..aab237535 100644 --- a/Week 03/id_738/NOTE.md +++ b/Week 03/id_738/NOTE.md @@ -1,4 +1,223 @@ -# NOTE +# 第三周总结 + 分析解答二分查找半序数组最小值下标问题 + +## 第三周知识点总结 + +### 深度优先搜索、广度优先搜索的实现和特性 + +- 深度优先: + - 如果一个题目,能分解成像一棵树一样的分叉,然后一直分叉下去,那么都可以归结到深度优先里面 +- 广度优先: + - 用于寻最短路径 +- 深度优先搜索代码模板 + - 递归(N叉树) + ```python + visited = set() +def dfs(node, visited): + # terminator 和递归形式一致 + if node in visited: + #already visited + return + + visited.add(node) + # process current node here + … + for next_node in node.children(): + if not next_node in visited: + dfs(next_node, visited) + ``` + - 非递归 + ```python + def DFS(self, tree): + + if tree.root is None: + return [] + + visited, stack = [], [tree.root] + + while stack: + node = stack.pop() + visited.add(node) + process(node) + #获取Node的子节点 + nodes = generate_related_nodes(node) + stack.push(nodes) + + # other processing work + ... + ``` +- 广度优先代码模板(链表,双端队列) +```python +def BFS(graph, start, end): + queue = [] + queue.append([start]) + visited.add(start) + + while queue: + node = queue.pop() + visited.add(node) + + process(node) + # 获取node的所有子节点 + nodes = generate_related_nodes(node) + queue.push(nodes) + # other processing work + ... +``` +### 贪心算法Greedy + +>> 贪心算法是一种在每一步选择中都采取在当前状态下最好或者最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法。 +>> 贪心算法与动态规划的不通在于它对每个子问题的解决方案都作出选择,不能回退。 +>> 动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。 + +- 贪心、回溯、动态规划区别 + - 贪心:当下做局部最优判断 + - 贪心算法不会得到全局最优,但是,某些情况下可以。 + - 在某一步使用贪心算法,在全局加上搜索,递归或者全局规划 + - 回溯:能够回退 + - 动态规划:最优判断 + 回退 +- 贪心算法解决可以解决一些最优化问题 + - 求图中的最小生成树 + - 求哈夫曼编码 + - 等 +- 适用贪心算法的场景:(主要是能证明它是最优子结构) + - 最优子结构:问题能够分解成子问题来解决,子问题的最优解能递推到最终问题的最优解 + +### 二分查找 + +- 二分查找的前提 + - 1.目标函数单调性(单调递增或者递减) + - 2.存在上下界(bounded) + - 3.能够通过索引访问(index accessible) +- 代码模板:(肌肉式记忆) +```pyton +left, right = 0, len(array) - 1 +while left <= right: + mid = (left + right) / 2 + if array[mid] == target: + # find the target! + break or return result + elif array[mid] < target: + left = mid + 1 + else: + right = mid - 1 +``` + +## 第三周学习感想 + +本周学习了 + +1. 深度广度优先遍历,其实在之前的树遍历课程中就已经或多或少涉及了。只不过这里更加全面的介绍了算法,而且应用场景也不局限于二叉树了,还扩展到图,N叉树以及各种递归层层深入的程序中。 + +2. 贪心算法。严格来讲,应该说是贪心思想。一个问题要能使用贪心思想,那么肯定问题能够分解成子问题来解决,子问题的最优解能递推到最终问题的最优解。另外,贪心并不代表蠢,有时候也是一种智慧的选择。比如5,10,15的找零问题,贪心的作出能用10找零就不用5这个决定,其实是很有智慧的。。 + +3. 二分查找,通过大量练习,完全记忆了二分查找的代码模板,并且在练习中,深刻体会了各种二分查找的变异形式。还是要记住这几个条件: + +其中目标函数单调性是考验解题灵活变换的关键。不一定是非要整个数组单调递增或者递减,有时候一半数组单调递增或者递减也是可以的。 + +``` +目标函数单调性(单调递增或者递减) +存在上下界(bounded) +能够通过索引访问(index accessible) +``` + +## 课后思考题:分析解答二分查找半序数组最小值下标问题 +### 使用二分查找,寻找一个半有序数组 [4, 5, 6, 7, 0, 1, 2] 中间无序的地方 + +问题和下面两个题目对比: +- [寻找旋转排序数组中的最小值](https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/) +- [搜索旋转排序数组]https://leetcode-cn.com/problems/search-in-rotated-sorted-array/ + +要将半有序数组重新排序,也就是找出半有序数组的中间无序的下标,此下标就是原有序数组的0下标。 + +所以问题就是找一个半有序数组中间无序的地方。 + +其实问题就是找`旋转排序数组中的最小值`,只不过是找出最小值的下标。 + +#### 解法 + +二分查找的三个条件: +1. 目标函数单调性(单调递增或者递减) +2. 存在上下界(bounded) +3. 能够通过索引访问(index accessible) + +题目2,3点都明显满足,第一点实质是要确定查找的值是在数组的左半部分或者右半部分。 + +对于一个半有序数组nums,要确定某个值是在左半部分或者右半部分,是有办法的,虽然不是简单的和有序数组一样判断mid(mid = (left + right) / 2)和target(目标值,这里是最小值)的大小即可。 + +所以该题目适合用二分查找解决。 + +- 问题转化为:如何判断待查找的数target(这里就是最小值)是在mid的左右哪一部分? + +对于一个半有序数组,左右两部分,存在下面两种情况:(排除数组从头到尾都有序 和 数组大小为1 的情况) + +1. 一部分是有序的,一部分是乱序的。很明显,数组的最小值肯定是落在无序部分。 + +2. 左右两部分都是有序的。很明显,mid 或者 mid + 1 或者 mid -1 中的最小者的下标就是我们要找的最小值下标。 + +- 问题转化为:如何判断两边都是有序的,如何判断mid左右部分哪一半边是无序的。 + + - 对于左右两边都有序的情况,判断条件很明显: + +1. nums[mid] > nums[mid + 1] +2. nums[mid] < nums[mid - 1] + + - 对于左右两边有一边无序的情况:有序部分,肯定存在性质:最左边的数小于最右边的数。反之如果是乱序部分,那么最左边的数肯定大于最右边的数。 + +我们用原数组nums第一个数nums[0]和mid比较即可判断数组是左右两边哪边有序。 + +1. if nums[0] < nums[mid],表示mid左边的数组有序,即最小值落在mid右边部分 +2. elif nums[0] > nums[mid],表示mid右边的数组有序,即最小值落在mid左边部分 + +所以分析到这里,我们只要利用上述方法,不断循环,直到: + +1. if nums[mid + 1] < nums[mid]: return mid + 1 +2. if nums[mid] < nums[mid - 1]: return mid +3. if nums[0] < nums[mid]: left = mid + 1 +4. elif nums[0] > nums[mid]: right = mid - 1 + +记得排查特殊数组情况才可以用上面的逻辑: + +1. nums有序 +2. nums大小为0或者为1 + +#### 程序实现 + +```python +class Solution(object): + def findMin(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + if not nums: + return None + if len(nums) == 1 or nums[0] < nums[-1]: + return nums[0] + left, right = 0, len(nums) - 1 + while left <= right: + mid = left + ((right - left) >> 1) + if nums[mid] > nums[mid + 1]: + return mid + 1 + if nums[mid] < nums[mid - 1]: + return mid + if nums[0] > nums[mid]: + right = mid - 1 + elif nums[0] < nums[mid]: + left = mid + 1 +``` + + + + + + + + + + + + + diff --git "a/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_102_738.py" "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_102_738.py" new file mode 100644 index 000000000..2d1322a8e --- /dev/null +++ "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_102_738.py" @@ -0,0 +1,63 @@ +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def levelOrder(self, root): + """ + 二叉树的层次遍历:https://leetcode-cn.com/problems/binary-tree-level-order-traversal/#/description + + :type root: TreeNode + :rtype: List[List[int]] + """ + # BFS + if not root: + return [] + result = [] + queue = [root] + while queue: + cur = [] + while queue: + cur.append(queue.pop(0)) + result.extend([[i.val for i in cur]]) + for n in cur: + if n.left: queue.append(n.left) + if n.right: queue.append(n.right) + return result + + + # DFS + # def dfs(level, root): + # if not root: + # return + # if level == len(self.result): + # self.result.append([root.val]) + # else: + # self.result[level].append(root.val) + # if root.left: dfs(level + 1, root.left) + # if root.right: dfs(level + 1, root.right) + + # self.result = [] + # dfs(0, root) + # return self.result + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git "a/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_122_738.py" "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_122_738.py" new file mode 100644 index 000000000..08597bdc7 --- /dev/null +++ "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_122_738.py" @@ -0,0 +1,38 @@ +class Solution(object): + def maxProfit(self, prices): + """ + 买卖股票的最佳时机:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/description/ + + :type prices: List[int] + :rtype: int + """ + # 完全模拟交易过程 + # result = 0 + # keep = None + # for i in range(len(prices) - 1): + # if keep is not None and keep < prices[i]: + # result += prices[i] - keep + # keep = None + # if prices[i] < prices[i + 1]: + # keep = prices[i] + # if keep is not None: + # result += prices[-1] - keep + # return result + + # 提炼出交易过程的关键: + result = 0 + for i in range(1, len(prices)): + if prices[i] > prices[i - 1]: + result += prices[i] - prices[i - 1] + return result + + + + + + + + + + + \ No newline at end of file diff --git "a/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_22_738.py" "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_22_738.py" new file mode 100644 index 000000000..26f499038 --- /dev/null +++ "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_22_738.py" @@ -0,0 +1,47 @@ +class Solution(object): + def generateParenthesis(self, n): + """ + 括号生成:https://leetcode-cn.com/problems/generate-parentheses/#/description + + :type n: int + :rtype: List[str] + """ + # DFS + # def dfs(l_num, r_num, s, n = n): + # if len(s) == n * 2: + # self.result.append(s) + # return + # if l_num < n: + # dfs(l_num + 1, r_num, s + '(') + # if r_num < l_num and r_num < n: + # dfs(l_num, r_num + 1, s + ')') + # self.result = [] + # dfs(1, 0, '(') + # return self.result + + # BFS + queue = [(1, 0, '(')] + result = [] + while queue: + l_num, r_num, cur = queue.pop(0) + if len(cur) == n * 2: + result.append(cur) + continue + if l_num < n: + queue.append((l_num + 1, r_num, cur + '(')) + if r_num < n and r_num < l_num: + queue.append((l_num, r_num + 1, cur + ')')) + return result + + + + + + + + + + + + + \ No newline at end of file diff --git "a/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_367_738.py" "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_367_738.py" new file mode 100644 index 000000000..8ee7bfb72 --- /dev/null +++ "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_367_738.py" @@ -0,0 +1,23 @@ +class Solution(object): + def isPerfectSquare(self, num): + """ + 有效的完全平方数:https://leetcode-cn.com/problems/valid-perfect-square/submissions/ + + :type num: int + :rtype: bool + """ + # 二分法直接求解Log(n) + left, right = 1, num + while left <= right: + mid = left + (right - left) // 2 + print(mid) + mid2 = mid * mid + if mid2 == num: + return True + elif mid2 > num: + right = mid - 1 + else: + left = mid + 1 + return False + + \ No newline at end of file diff --git "a/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_433_738.py" "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_433_738.py" new file mode 100644 index 000000000..c994434b5 --- /dev/null +++ "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_433_738.py" @@ -0,0 +1,87 @@ +class Solution(object): + def minMutation(self, start, end, bank): + """ + 最小基因变化:https://leetcode-cn.com/problems/minimum-genetic-mutation/#/description + + :type start: str + :type end: str + :type bank: List[str] + :rtype: int + """ + # BFS 代码写法1 + # change = { + # 'A': ['C', 'G', 'T'], + # 'C': ['A', 'G', 'T'], + # 'G': ['C', 'A', 'T'], + # 'T': ['C', 'G', 'A'] + # } + # step = 0 + # queue = [start] + # while queue: + # curs = [] + # while queue: + # cur = queue.pop() + # if cur == end: return step + # curs.append(cur) + # step += 1 + # for cur in curs: + # for i in range(len(cur)): + # for c in change[cur[i]]: + # new = cur[:i] + c + cur[i + 1:] + # if new in bank: + # queue.append(new) + # bank.remove(new) + # return -1 + + # BFS 代码写法2 + # change = {'A': 'TCG', 'T': 'ACG', 'C': 'ATG', 'G': 'ATC'} + # bank = set(bank) + # q = [(start, 0)] + # while q: + # node, level = q.pop(0) + # if node == end: + # return level + # for i, val in enumerate(node): + # c = change[val] + # for j in c: + # new = node[:i] + j + node[i + 1:] + # if new in bank: + # q.append((new, level + 1)) + # bank.remove(new) + # return -1 + + # DFS + change = {'A': 'TCG', 'T': 'ACG', 'C': 'ATG', 'G': 'ATC'} + def dfs(level, cur, bank = bank, pre_chenge_index = -1): + if cur == end: + if self.min_step == -1 or self.min_step > level: + self.min_step = level + # 标准库里面的都已经visited了,那么表示不可达 + if not bank: + return + for i in range(len(cur)): + # 这里是一个剪枝优化,如果上一层处理的位置,在本层是不需要处理的,因为上一层有后续循环会处理,这一层的Level肯定比上一层的大 + if i == pre_chenge_index: + continue + for c in change[cur[i]]: + new = cur[:i] + c + cur[i + 1:] + if new in bank: + # 标准库中已经visited的元素,如果不移除,下次如果重复到达这个状态,还是会重复走一样的路 + bank.remove(new) + dfs(level + 1, new, bank, i) + # 恢复现场进入下个循环 + bank.append(new) + self.min_step = -1 + dfs(0, start) + return self.min_step + + + + + + + + + + + \ No newline at end of file diff --git "a/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_455_738.py" "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_455_738.py" new file mode 100644 index 000000000..634502cff --- /dev/null +++ "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_455_738.py" @@ -0,0 +1,46 @@ +class Solution(object): + def findContentChildren(self, g, s): + """ + 分发饼干:https://leetcode-cn.com/problems/assign-cookies/description/ + + :type g: List[int] + :type s: List[int] + :rtype: int + """ + # 解法 - 写法1 + # result = 0 + # g.sort() + # s.sort() + # for i in g: + # while s and i > s[0]: + # s.pop(0) + # if s: + # s.pop(0) + # result += 1 + # return result + + # 解法 - 写法2 + result = 0 + g.sort() + s.sort() + i = 0 + j = 0 + while i < len(g) and j < len(s): + if g[i] <= s[j]: + result += 1 + i += 1 + j += 1 + else: + j += 1 + return result + + + + + + + + + + + \ No newline at end of file diff --git "a/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_515_738.py" "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_515_738.py" new file mode 100644 index 000000000..a9b31789d --- /dev/null +++ "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_515_738.py" @@ -0,0 +1,53 @@ +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def largestValues(self, root): + """ + 在每个树行中找最大值:https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/#/description + + :type root: TreeNode + :rtype: List[int] + """ + # BFS + if not root: + return [] + result = [] + queue = [root] + while queue: + curs = [] + while queue: + curs.append(queue.pop()) + result.append(max([cur.val for cur in curs])) + for cur in curs: + if cur.left: queue.append(cur.left) + if cur.right: queue.append(cur.right) + return result + + # DFS + # def dfs(level, root): + # if not root: + # return + # if level == len(result): result.append(root.val) + # else: result[level] = max(result[level], root.val) + # if root.left: dfs(level + 1, root.left) + # if root.right: dfs(level + 1, root.right) + # result = [] + # dfs(0, root) + # return result + + + + + + + + + + + + \ No newline at end of file diff --git "a/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_55_738.py" "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_55_738.py" new file mode 100644 index 000000000..e64da88f7 --- /dev/null +++ "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_55_738.py" @@ -0,0 +1,52 @@ +class Solution(object): + def canJump(self, nums): + """ + 跳跃游戏:https://leetcode-cn.com/problems/jump-game/ + + :type nums: List[int] + :rtype: bool + """ + #动态规划 - (最后一个超大数组的测试用例)超出内存限制 + # def canJumpFromPosition(position, nums): + # if memo[position] != -1: + # return True if memo[position] == 1 else False + # furthestJump = min(position + nums[position], len(nums) - 1) + # for nextPosition in range(position + 1, furthestJump + 1): + # if canJumpFromPosition(nextPosition, nums): + # memo[position] = 1 + # return True + # memo[position] = 0; + # return False; + + + # memo = [] + # for i in range(len(nums)): + # memo.append(-1) + # memo[-1] = 1; + # return canJumpFromPosition(0, nums); + +################################################################################## + + # (最后一个超大数组的测试用例)超出时间限制 + # queue = [0] + # end = len(nums) - 1 + # while queue: + # cur = queue.pop(0) + # if cur + nums[cur] >= end: + # return True + # for i in range(1, nums[cur] + 1): + # n = cur + i + # if n <= end and n not in queue: + # queue.append(n) + # return False + +################################################################################## + + # 贪心法 + cur = len(nums) - 1 + for i in range(len(nums) - 1)[::-1]: + if cur - i <= nums[i]: + cur = i + if cur == 0: + return True + return False \ No newline at end of file diff --git "a/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_69_738.py" "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_69_738.py" new file mode 100644 index 000000000..ae88c1a77 --- /dev/null +++ "b/Week 03/id_738/\350\257\276\345\240\202\345\256\236\346\210\230\351\242\230\347\233\256\350\256\255\347\273\203/LeetCode_69_738.py" @@ -0,0 +1,65 @@ +class Solution(object): + def mySqrt(self, x): + """ + x的平方根:https://leetcode-cn.com/problems/sqrtx/ + + :type x: int + :rtype: int + """ + # 二分查找写法2: + if x == 0 : + return 0 + left, right = 0, x + while left < right: + mid = left + (right - left) // 2 + t = mid * mid + if t == x: + return mid + elif t > x: + right = mid - 1 + else: + left = mid + 1 + + # 这里的return:当while循环最后一次是 left mid right 都相差一的时候,比如8的平方根最后第二次while的时候是 left = 2, mid = 3, right = 4,跳出while后就是 left = right = 3,这时候3并不是最后结果。 + # 因为right并不能确定是否是大于x(当然right后面数字平方的肯定大于x),所以应该改成 return (int)(right * right > x ? right - 1 : right); 如果right的平方大于x,则right-1肯定是结果。如果right^2 < x,那么返回right。 + return left if left * left <= x else left - 1 + + + # 二分查找写法1:因为x的平方根r:0 < r <= x,我们可以从递增序列0到x中,通过二分查找,定位到r的位置 + # 这里二分查找需要一些技巧,尤其是最后的返回,有一定逻辑,看下面注释 + # if x == 0 or x == 1: + # return x + # L, R = 1, x + # # 下面while循环 + # # 1. 找到平方根,返回 + # # 2. 没有整数平方根,最终到达L = R或者L > R的时候会退出循环 + # while L < R: + # # mid为 L和R的中间值取下界整数 + # mid = L + (R - L) // 2 + # mid2 = mid * mid + # if mid2 == x: # 此时找到平方根直接返回 + # return mid + # elif mid2 > x: # 中点mid^2 > x表示x的平方根落在L到mid-1的位置 + # R = mid - 1 + # elif mid2 < x: # 中点mid^2 < x表示x的平方根落在mid + 1到L的位置 + # L = mid + 1 + # # 能够来到这里,表示没有整数平方根,而且 L >= R + # if L == R: + # # 如果 L == R的情况,表示结果不是L就是L - 1只要取L 和 L-1 中平方最接近x的那个即可 + # return L if L * L <= x else L - 1 + # elif L > R: + # # 如果L > R的情况,表示循环最后一个mid肯定是下面情况之一 + # # 1. mid为L,而且L^2 == mid^2 > x,而且L左边的数字的平方肯定L的数字的平方都>x + # # 2. mid为R,而且R^2 == mid^2 < x,而且R右边的数字的平方肯定>x,也就是<=R的数字的平方都R的数字的平方都>x + # # 只有上面两种情况之一,才会出现L > R的情况 + # # 而此时R肯定落在上面1情况的L的左边,或者情况2的R位置 + # # 所以此时R肯定是最接近x的平方根且小于x的平方根的整数,所以返回R。。 + # return R + + + + + + + + \ No newline at end of file diff --git a/Week 03/id_748/LeetCode_127_748_ladderLength.java b/Week 03/id_748/LeetCode_127_748_ladderLength.java new file mode 100644 index 000000000..70493514d --- /dev/null +++ b/Week 03/id_748/LeetCode_127_748_ladderLength.java @@ -0,0 +1,183 @@ +package com.code.week3; + + +import javafx.util.Pair; + +import java.util.*; + +/** + * 给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则: + * + * 每次转换只能改变一个字母。 + * 转换过程中的中间单词必须是字典中的单词。 + * 说明: + * + * 如果不存在这样的转换序列,返回 0。 + * 所有单词具有相同的长度。 + * 所有单词只由小写字母组成。 + * 字典中不存在重复的单词。 + * 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。 + * 示例 1: + * + * 输入: + * beginWord = "hit", + * endWord = "cog", + * wordList = ["hot","dot","dog","lot","log","cog"] + * + * 输出: 5 + * + * 解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", + * 返回它的长度 5。 + * 示例 2: + * + * 输入: + * beginWord = "hit" + * endWord = "cog" + * wordList = ["hot","dot","dog","lot","log"] + * + * 输出: 0 + * + * 解释: endWord "cog" 不在字典中,所以无法进行转换 + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/word-ladder + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_127_748_ladderLength { + + public int ladderLength(String beginWord, String endWord, List wordList) { + + + // 所有单词都有相同的长度 + int L = beginWord.length(); + + HashMap> allComboDict = new HashMap>(); + + wordList.forEach( + word -> { + for (int i = 0; i < L; i++) { + // key是通用式 + // value是所有类别 + String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L); + ArrayList transformations = allComboDict.getOrDefault(newWord, new ArrayList()); + transformations.add(word); + allComboDict.put(newWord, transformations); + } + }); + + // Queue for BFS + // pair:一对k/v map:多对k/v + Queue> Q = new LinkedList>(); + Q.add(new Pair(beginWord, 1)); + + // Visited to make sure we don't repeat processing same word. + HashMap visited = new HashMap(); + visited.put(beginWord, true); + + while (!Q.isEmpty()) { + + // 取出单词 + Pair node = Q.remove(); + String word = node.getKey(); + int level = node.getValue(); + + for (int i = 0; i < L; i++) { + + String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L); + + // 找到相同的通用的value进行比较 + for (String adjacentWord : allComboDict.getOrDefault(newWord, new ArrayList())) { + + if (adjacentWord.equals(endWord)) { + return level + 1; + } + + // 将不符合的所有没用过的value加入到List中 + if (!visited.containsKey(adjacentWord)) { + visited.put(adjacentWord, true); + Q.add(new Pair(adjacentWord, level + 1)); + } + + + } + } + } + + return 0; + + } + + + + + + public int ladderLength1(String beginWord, String endWord, List wordList ){ + + // 0. 定义变量 + int L = beginWord.length(); // 题目限制条件,所有单词字符串长度一致 + HashMap> commonWords = new HashMap>(); + + + // 1. 预处理给定的单词集合,将其统一处理为:<通用式,所有符合通用式单词的集合> + wordList.forEach( + word -> { + for(int i= 0 ; i originWordsList = commonWords.getOrDefault(commonWord, new ArrayList()); + originWordsList.add(word); + commonWords.put(commonWord,originWordsList); + } + } + ); + + + // 0. 定义存放每次需要处理的单词的队列 & 已使用过的单词 + Queue> queue = new LinkedList<>(); + List usedWords = new ArrayList<>(); + + // 初始化,将起始单词和level存入初始化变量 + queue.add(new Pair<>(beginWord, 1)); + + + // 2. 编写循环处理逻辑 + // 2.1 终止条件:队列为空 + while (!queue.isEmpty()){ + Pair pop = ((LinkedList>) queue).pop(); + String key = pop.getKey(); + Integer level = pop.getValue(); + + // 2.2 循环获取当前队列的单词的每一通用式 + for(int i=0; i< key.length(); i++){ + String commonKey = key.substring(0,i) + "*" + key.substring(i+1, key.length()); + + // 2.3 对每一个通用式,从预处理的通用式集合中找到所有的单词进行匹配 + for(String word : commonWords.getOrDefault(commonKey, new ArrayList<>())){ + // 2.4 匹配成功则返回level,没匹配上则将没使用过的单词加入队列中 + if(word.equals(endWord)){ + return level + 1; + } + if(!usedWords.contains(word)){ + usedWords.add(word); + queue.add(new Pair<>(word,level+1)); + } + } + } + } + return 0; + } + + + + public static void main(String[] args) { + LeetCode_127_748_ladderLength ladderLength = new LeetCode_127_748_ladderLength(); + ArrayList strings = new ArrayList<>(); + String[] ss = new String[]{"hot","dot","dog","lot","log","cog"}; + Collections.addAll(strings,ss); + + int i = ladderLength.ladderLength1("hit", "cog", strings); + System.out.println(i); + + } + + +} diff --git a/Week 03/id_748/LeetCode_200_748_numIslands.java b/Week 03/id_748/LeetCode_200_748_numIslands.java new file mode 100644 index 000000000..669606be7 --- /dev/null +++ b/Week 03/id_748/LeetCode_200_748_numIslands.java @@ -0,0 +1,133 @@ +package com.code.week3; + + +import java.util.LinkedList; +import java.util.Queue; + +/** + * 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。 + * + * 示例 1: + * + * 输入: + * 11110 + * 11010 + * 11000 + * 00000 + * + * 输出: 1 + * 示例 2: + * + * 输入: + * 11000 + * 11000 + * 00100 + * 00011 + * + * 输出: 3 + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/number-of-islands + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_200_748_numIslands { + + void my_def(char[][] grid, int i, int j){ + int nr = grid.length; + int nc = grid[0].length; + + // DFS:终止条件 + if (i<0 || j<0 || i>=nr || j >= nc || grid[i][j] =='0') { + return; + } + + grid[i][j] = '0'; + my_def(grid, i-1, j); // 左 + my_def(grid, i+1, j); // 右 + my_def(grid, i, j-1); // 上 + my_def(grid, i, j+1); // 下 + + } + + + /** + * 遍历每个元素,如果为1则启动DFS搜素 + * DFS搜索的终止条件:1.不能越界,2.搜索到为0的坐标(0代表海) + * 搜索的过程中,将经过的位置都置为 0 + * 启动DFS的次数就是岛屿的数量 + * @param grid + * @return + */ + public int numIslandsDFS(char[][] grid){ + if (grid == null || grid.length == 0){ + return 0; + } + + int nr = grid.length; + int nc = grid[0].length; + int num_island = 0; + + for (int i=0; i< nr; ++i){ + for (int j=0; j< nc; ++j){ + if(grid[i][j] == '1'){ + ++num_island; + my_def(grid,i,j); + } + } + } + + return num_island; + } + + + + + public int numIsLandsBFS(char[][] grid){ + if (grid == null || grid.length == 0) { + return 0; + } + + int nr = grid.length; + int nc = grid[0].length; + int num_islands = 0; + + for (int r = 0; r < nr; ++r) { + for (int c = 0; c < nc; ++c) { + if (grid[r][c] == '1') { + ++num_islands; + grid[r][c] = '0'; // mark as visited + Queue linju = new LinkedList<>(); + linju.add(r * nc + c); + while (!linju.isEmpty()) { + int id = linju.remove(); + int row = id / nc; + int col = id % nc; + if (row - 1 >= 0 && grid[row-1][col] == '1') { + linju.add((row-1) * nc + col); + grid[row-1][col] = '0'; + } + if (row + 1 < nr && grid[row+1][col] == '1') { + linju.add((row+1) * nc + col); + grid[row+1][col] = '0'; + } + if (col - 1 >= 0 && grid[row][col-1] == '1') { + linju.add(row * nc + col-1); + grid[row][col-1] = '0'; + } + if (col + 1 < nc && grid[row][col+1] == '1') { + linju.add(row * nc + col+1); + grid[row][col+1] = '0'; + } + } + } + } + } + + return num_islands; + + } + + + + +} diff --git a/Week 03/id_748/NOTE.md b/Week 03/id_748/NOTE.md index a6321d6e2..17a5176fc 100644 --- a/Week 03/id_748/NOTE.md +++ b/Week 03/id_748/NOTE.md @@ -1,4 +1,98 @@ -# NOTE +# 深度优先搜索(DFS) +>从根节点出发,递归下去再回溯上来 +>不撞南墙不回头,一条路走到黑 +## 实现方法 +1. 递归 +2. 栈 +## 模版代码 +### 递归写法 +``` +visited = set()  - +def dfs(node, visited): +if node in visited: # terminator + # already visited  + return  + + visited.add(node)  + + # process current node here.  + ... + for next_node in node.children():  + if not next_node in visited:  +``` +### dfs(next_node, visited) + +非递归写法 +``` +def DFS(self, tree):  + + if tree.root is None:  + return []  + + visited, stack = [], [tree.root] + + while stack:  + node = stack.pop()  + visited.add(node) + + process (node)  + nodes = generate_related_nodes(node)  + stack.push(nodes)  + + # other processing work  +``` +# ... + +广度优先搜索(BFS) +>从根节点出发,一层一层的遍历 +>地毯式搜索 +## 实现方法 +1. 数组 +2. 队列 +## 模版代码 +``` +def BFS(graph, start, end): + + queue = []  + queue.append([start])  + visited.add(start) + + while queue:  + node = queue.pop()  + visited.add(node) + + process(node)  + nodes = generate_related_nodes(node)  + queue.push(nodes) + + # other processing work  +``` + ... + + +# 贪心算法 +## 概念 +* 贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。 +# 动态规划 +## 概念 +* 动态规划的本质不在于是递推或是递归,也不需要纠结是不是内存换时间 +* 动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法 +* 动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的 +# 二分查找 +## 概念 +* 二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列 +## 模版代码 +``` +left, right = 0, len(array) - 1  +while left <= right:  +   mid = (left + right) / 2  +   if array[mid] == target:  +     # find the target!!  +     break or return result  +   elif array[mid] < target:  +     left = mid + 1  +   else:  +     right = mid - 1 +``` diff --git "a/Week 05/id_003/64.\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.js" "b/Week 05/id_003/64.\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.js" new file mode 100644 index 000000000..7876b6d56 --- /dev/null +++ "b/Week 05/id_003/64.\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.js" @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode.cn id=64 lang=javascript + * + * [64] 最小路径和 + */ + +// @lc code=start +/** + * @param {number[][]} grid + * @return {number} + */ +var minPathSum = function(grid) { + var n = grid.length; + var m = grid[0].length; + var dp = Array.from(new Array(n),() => new Array(m)); + for(var i = 0;i < n;i++){ + for(var j = 0;j < m;j++){ + if( i != 0 && j!= 0){ + dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j]; + }else if(i == 0 && j!=0){ + dp[i][j] = dp[i][j-1]+grid[i][j]; + }else if(i != 0 && j==0){ + dp[i][j] = dp[i-1][j]+grid[i][j]; + }else if(i == 0 && j==0){ + dp[i][j] = grid[i][j]; + } + } + } + return dp[n-1][m-1]; +}; +// @lc code=end + diff --git "a/Week 05/id_003/91.\350\247\243\347\240\201\346\226\271\346\263\225.js" "b/Week 05/id_003/91.\350\247\243\347\240\201\346\226\271\346\263\225.js" new file mode 100644 index 000000000..576c69a57 --- /dev/null +++ "b/Week 05/id_003/91.\350\247\243\347\240\201\346\226\271\346\263\225.js" @@ -0,0 +1,33 @@ +/* + * @lc app=leetcode.cn id=91 lang=javascript + * + * [91] 解码方法 + */ + +// @lc code=start +/** + * @param {string} s + * @return {number} + */var numDecodings = function(s) { + if(s[0] == "0") return 0; + let dp = [1, 1], len = s.length; + for(let i=1; i < len; ++i) { + if(s[i - 1] != "0") { + let num = (s[i - 1] + s[i] | 0); + if(num >= 1 && num <= 26) { + dp[i + 1] = s[i] != "0"? dp[i - 1] + dp[i]: dp[i - 1]; + } else if(s[i] != "0") { + dp[i + 1] = dp[i]; + } else { + return 0; + } + } else if(s[i] != "0") { + dp[i + 1] = dp[i]; + } else { + return 0; + } + } + return dp[len]; +}; +// @lc code=end + diff --git a/Week 05/id_003/NOTE.md b/Week 05/id_003/NOTE.md index a6321d6e2..548589cef 100644 --- a/Week 05/id_003/NOTE.md +++ b/Week 05/id_003/NOTE.md @@ -1,4 +1,75 @@ # NOTE +## 递归模板 +``` + Python 代码模板 + def recursion(level, param1, param2, ...): + # recursion terminator + if level > MAX_LEVEL: + process_result + return + + # process logic in current level + process(level, data...) + + # drill down + self.recursion(level + 1, p1, ...) + + # reverse the current level status if needed + + + Java 代码模板 + public void recur(int level, int param) { + + // terminator + if (level > MAX_LEVEL) { + // process result + return; + } + + // process current logic + process(level, param); + + // drill down + recur( level: level + 1, newParam); + + // restore current status + + } +``` +## 分治模板 +``` + def divide_conquer(problem, param1, param2, ...): + # recursion terminator + if problem is None: + print_result + return + + # prepare data + data = prepare_data(problem) + subproblems = split_problem(problem, data) + + # conquer subproblems + subresult1 = self.divide_conquer(subproblems[0], p1, ...) + subresult2 = self.divide_conquer(subproblems[1], p1, ...) + subresult3 = self.divide_conquer(subproblems[2], p1, ...) + … + + # process and generate the final result + result = process_result(subresult1, subresult2, subresult3, …) + + # revert the current level states +``` +### 感触 +1. 拒绝人肉递归 +2. 找最近最简单方法,将其拆解为可重复解决的问题 +3. 善用数学归纳法 + +本质: 寻找重复性 + +## dp == 动态递推 +1. 最优子结构 +2. 存储中间状态 +3. 递推公式 diff --git a/Week 05/id_008/LeetCode_1143_008.js b/Week 05/id_008/LeetCode_1143_008.js new file mode 100644 index 000000000..4b600181f --- /dev/null +++ b/Week 05/id_008/LeetCode_1143_008.js @@ -0,0 +1,27 @@ +/** + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ +var longestCommonSubsequence = function(text1, text2) { + var row = text1.length; + var col = text2.length; + var dp = new Array(row + 1).fill(0); + + for (var i = 0; i <= row; ++i){ + dp[i] = new Array(col + 1).fill(0); + } + + for (var i = 1; i < row + 1; ++i){ + for(var r = 1; r < col + 1; ++r){ + if (text1[i - 1] === text2[r - 1]){ + dp[i][r] = dp[i - 1][r - 1] + 1; + } + else { + dp[i][r] = (dp[i - 1][r] > dp[i][r - 1]) ? dp[i - 1][r] : dp[i][r - 1]; + } + } + } + + return dp[row][col]; +}; diff --git a/Week 05/id_008/LeetCode_120_008.js b/Week 05/id_008/LeetCode_120_008.js new file mode 100644 index 000000000..8f284f40e --- /dev/null +++ b/Week 05/id_008/LeetCode_120_008.js @@ -0,0 +1,14 @@ +/** + * @param {number[][]} triangle + * @return {number} + */ +var minimumTotal = function(triangle) { + var dp = triangle; + for (var i = dp.length - 2; i >= 0; --i){ + for (var j = 0; j < dp[i].length; ++j){ + dp[i][j] += dp[i + 1][j] < dp[i + 1][j + 1] ? dp[i + 1][j] : dp[i + 1][j + 1]; + } + } + + return dp[0][0]; +}; diff --git a/Week 05/id_008/LeetCode_121_008.js b/Week 05/id_008/LeetCode_121_008.js new file mode 100644 index 000000000..9be13f897 --- /dev/null +++ b/Week 05/id_008/LeetCode_121_008.js @@ -0,0 +1,20 @@ +/** + * @param {number[]} prices + * @return {number} + */ +var maxProfit = function(prices) { + var min = Infinity; + var max = 0; + var len = prices.length; + + for(var i = 0; i < prices.length; ++i){ + if (prices[i] <= min) { + min = min < prices[i] ? min : prices[i]; + } + else { + max = prices[i] - min > max ? prices[i] - min : max; + } + } + + return max; +}; diff --git a/Week 05/id_008/LeetCode_152_008.js b/Week 05/id_008/LeetCode_152_008.js new file mode 100644 index 000000000..c9dd92a72 --- /dev/null +++ b/Week 05/id_008/LeetCode_152_008.js @@ -0,0 +1,20 @@ +/** + * @param {number[]} nums + * @return {number} + */ +var maxProduct = function(nums) { + var dpMax = new Array(nums.length); + var dpMin = new Array(nums.length); + dpMax[0] = nums[0]; + dpMin[0] = nums[0]; + + var max = dpMax[0]; + + for(var i = 1; i < nums.length; ++i){ + dpMax[i] = Math.max(dpMax[i - 1] * nums[i], dpMin[i - 1] * nums[i], nums[i]); + dpMin[i] = Math.min(dpMax[i - 1] * nums[i], dpMin[i - 1] * nums[i], nums[i]); + dpMax[i] > max && (max = dpMax[i]); + } + + return max; +}; diff --git a/Week 05/id_008/LeetCode_198_008.js b/Week 05/id_008/LeetCode_198_008.js new file mode 100644 index 000000000..9ac8f0893 --- /dev/null +++ b/Week 05/id_008/LeetCode_198_008.js @@ -0,0 +1,20 @@ +/** + * @param {number[]} nums + * @return {number} + */ +var rob = function(nums) { + if(nums.length === 0) { + return 0; + } + + var len = nums.length; + var dp = new Array(len + 1); + dp[0] = 0; + dp[1] = nums[0]; + + for (var i = 2; i <= len; ++i) { + dp[i] = dp[i - 1] > dp[i - 2] + nums[i - 1] ? dp[i - 1] : dp[i - 2] + nums[i - 1]; + } + + return dp[len]; +}; diff --git a/Week 05/id_008/LeetCode_213_008.js b/Week 05/id_008/LeetCode_213_008.js new file mode 100644 index 000000000..ac858fb27 --- /dev/null +++ b/Week 05/id_008/LeetCode_213_008.js @@ -0,0 +1,30 @@ +/** + * @param {number[]} nums + * @return {number} + */ +var rob = function(nums) { + var n = nums.length; + + if (n === 1) { + return nums[0]; + } + else if (n === 0) { + return 0; + } + + function find(nums){ + var dp = new Array(n - 1); + dp[0] = 0; + dp[1] = nums[0]; + + for (var i = 2; i < n; ++i) { + dp[i] = dp[i - 1] > dp[i - 2] + nums[i - 1] ? dp[i - 1] : dp[i - 2] + nums[i - 1]; + } + + return dp[n - 1]; + } + + var num1 = find(nums.slice(1)); + var num2 = find(nums.slice(0, nums.length - 1)); + return Math.max(num1, num2); +}; diff --git a/Week 05/id_008/LeetCode_322_008.js b/Week 05/id_008/LeetCode_322_008.js new file mode 100644 index 000000000..64f19826d --- /dev/null +++ b/Week 05/id_008/LeetCode_322_008.js @@ -0,0 +1,33 @@ +/** + * @param {number[]} coins + * @param {number} amount + * @return {number} + */ +var coinChange = function(coins, amount) { + var arr = new Array(amount + 1).fill(-2); + return find(coins, amount, arr); + + function find(coins, amount, arr){ + if (amount <= 0) { + return 0; + } + + if (arr[amount] != -2) { + return arr[amount]; + } + + var min = Infinity; + + for (var i = 0; i < coins.length; ++i) { + if (amount >= coins[i]){ + var temp = find(coins, amount - coins[i], arr); + + if (temp != -1){ + temp + 1 < min && (min = temp + 1); + } + } + } + + return arr[amount] = Number.isFinite(min) ? min : -1; + } +}; diff --git a/Week 05/id_008/LeetCode_32_008.js b/Week 05/id_008/LeetCode_32_008.js new file mode 100644 index 000000000..9e7c3ffc2 --- /dev/null +++ b/Week 05/id_008/LeetCode_32_008.js @@ -0,0 +1,24 @@ +/** + * @param {string} s + * @return {number} + */ +var longestValidParentheses = function(s) { + var max = -Infinity; + var n = s.length; + var dp = new Array(n).fill(0); + + for (var i = 1; i < n; ++i){ + if (s[i] === ')') { + if (s[i - 1] === '(') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + } + else if (i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] === '(') { + dp[i] = dp[i - 1] + ((i - dp[i - 1] >= 2) ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + + dp[i] > max && (max = dp[i]); + } + } + + return max > -Infinity ? max : 0; +}; diff --git a/Week 05/id_008/LeetCode_53_008.js b/Week 05/id_008/LeetCode_53_008.js new file mode 100644 index 000000000..978f66050 --- /dev/null +++ b/Week 05/id_008/LeetCode_53_008.js @@ -0,0 +1,21 @@ +/** + * @param {number[]} nums + * @return {number} + */ +var maxSubArray = function(nums) { + var max = nums[0]; + var sum = 0; + + for(var i = 0; i < nums.length; ++i) { + if (sum > 0) { + sum += nums[i]; + } + else { + sum = nums[i]; + } + + sum > max && (max = sum); + } + + return max; +}; diff --git a/Week 05/id_008/LeetCode_62_008.js b/Week 05/id_008/LeetCode_62_008.js new file mode 100644 index 000000000..94da85a4b --- /dev/null +++ b/Week 05/id_008/LeetCode_62_008.js @@ -0,0 +1,23 @@ +/** + * @param {number} m + * @param {number} n + * @return {number} + */ +var uniquePaths = function(m, n) { + return C(m + n - 2, n - 1); + + function C(n, m){ + var k = n - m; + var r = 1; + + for (var i = n; i > k; --i) { + r *= i; + } + + for (var i = m; i > 1; --i) { + r /= i; + } + + return r; + } +}; diff --git a/Week 05/id_008/LeetCode_63_008.js b/Week 05/id_008/LeetCode_63_008.js new file mode 100644 index 000000000..976d60dc1 --- /dev/null +++ b/Week 05/id_008/LeetCode_63_008.js @@ -0,0 +1,25 @@ +/** + * @param {number[][]} obstacleGrid + * @return {number} + */ +var uniquePathsWithObstacles = function(obstacleGrid) { + if (obstacleGrid.length === 0) { + return 0; + } + + var total = new Array(obstacleGrid[0].length).fill(0); + total[0] = 1; + + for (var i = 0; i < obstacleGrid.length; ++i) { + for (var j = 0; j < obstacleGrid[0].length; ++j) { + if (obstacleGrid[i][j] === 1) { + total[j] = 0; + } + else if (j) { + total[j] += total[j - 1]; + } + } + } + + return total.pop(); +}; diff --git a/Week 05/id_008/LeetCode_64_008.js b/Week 05/id_008/LeetCode_64_008.js new file mode 100644 index 000000000..6581943b0 --- /dev/null +++ b/Week 05/id_008/LeetCode_64_008.js @@ -0,0 +1,26 @@ +/** + * @param {number[][]} grid + * @return {number} + */ +var minPathSum = function(grid) { + var dp = new Array(grid.length); + + for (var i = 0; i < grid.length; ++i){ + for (var j = 0; j < grid[0].length; ++j) { + if (i !== 0 && j !== 0) { + dp[j] = (dp[j - 1] < dp[j] ? dp[j - 1] : dp[j]) + grid[i][j]; + } + else if (i === 0 && j !== 0) { + dp[j] = dp[j - 1] + grid[i][j]; + } + else if (i !== 0 && j === 0) { + dp[j] = dp[j] + grid[i][j]; + } + else if (i === 0 && j === 0){ + dp[j] = grid[i][j]; + } + } + } + + return dp[grid[0].length - 1]; +}; diff --git a/Week 05/id_008/LeetCode_70_008.js b/Week 05/id_008/LeetCode_70_008.js new file mode 100644 index 000000000..7ba0e577b --- /dev/null +++ b/Week 05/id_008/LeetCode_70_008.js @@ -0,0 +1,93 @@ +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function(n) { + var t = Math.sqrt(5); + return Math.round(1/t * (Math.pow((1+t)/2, n+1) - Math.pow((1-t)/2, n+1))); +}; + +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function(n) { + if (n < 2) { + return 1; + } + + var a = 1; + var b = 1; + + for (var i = 1; i < n; ++i) { + b += a; + a = b - a; + } + + return b; +}; + +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function(n) { + switch(n){ + case 0: return 1; + case 1: return 1; + case 2: return 2; + case 3: return 3; + case 4: return 5; + case 5: return 8; + case 6: return 13; + case 7: return 21; + case 8: return 34; + case 9: return 55; + case 10: return 89; + case 11: return 144; + case 12: return 233; + case 13: return 377; + case 14: return 610; + case 15: return 987; + case 16: return 1597; + case 17: return 2584; + case 18: return 4181; + case 19: return 6765; + case 20: return 10946; + case 21: return 17711; + case 22: return 28657; + case 23: return 46368; + case 24: return 75025; + case 25: return 121393; + case 26: return 196418; + case 27: return 317811; + case 28: return 514229; + case 29: return 832040; + case 30: return 1346269; + case 31: return 2178309; + case 32: return 3524578; + case 33: return 5702887; + case 34: return 9227465; + case 35: return 14930352; + case 36: return 24157817; + case 37: return 39088169; + case 38: return 63245986; + case 39: return 102334155; + case 40: return 165580141; + case 41: return 267914296; + case 42: return 433494437; + case 43: return 701408733; + case 44: return 1134903170; + case 45: return 1836311903; + } + + return 1; +}; + +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function(n) { + return [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903][n]; +}; diff --git a/Week 05/id_013/LeetCode_02_013.py b/Week 05/id_013/LeetCode_02_013.py new file mode 100644 index 000000000..61be05cf0 --- /dev/null +++ b/Week 05/id_013/LeetCode_02_013.py @@ -0,0 +1,17 @@ +""" +第一题:64. 最小路径和 +""" + +class Solution: + def minPathSum(self, grid: List[List[int]]) -> int: + for i in range(len(grid)): + for j in range(len(grid[0])): + if i == j == 0: + continue + elif i == 0: + grid[i][j] = grid[i][j-1] + grid[i][j] + elif j == 0: + grid[i][j] = grid[i-1][j] + grid[i][j] + else: + grid[i][j] = min(grid[i][j-1],grid[i-1][j])+grid[i][j] + return grid[-1][-1] \ No newline at end of file diff --git a/Week 05/id_013/LeetCode_03_013.py b/Week 05/id_013/LeetCode_03_013.py new file mode 100644 index 000000000..57bd4daba --- /dev/null +++ b/Week 05/id_013/LeetCode_03_013.py @@ -0,0 +1,22 @@ +""" +第三题:72. 编辑距离 +""" +class Solution: + def minDistance(self, word1: str, word2: str) -> int: + n1 = len(word1) + n2 = len(word2) + dp = [[0] * (n2 + 1) for _ in range(n1 + 1)] + # 第一行 + for j in range(1, n2 + 1): + dp[0][j] = dp[0][j-1] + 1 + # 第一列 + for i in range(1, n1 + 1): + dp[i][0] = dp[i-1][0] + 1 + for i in range(1, n1 + 1): + for j in range(1, n2 + 1): + if word1[i-1] == word2[j-1]: + dp[i][j] = dp[i-1][j-1] + else: + dp[i][j] = min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1] ) + 1 + #print(dp) + return dp[-1][-1] \ No newline at end of file diff --git a/Week 05/id_013/NOTE.md b/Week 05/id_013/NOTE.md index a6321d6e2..45bf10511 100644 --- a/Week 05/id_013/NOTE.md +++ b/Week 05/id_013/NOTE.md @@ -1,4 +1,25 @@ -# NOTE - - - +# NOTE +动态规划 +1.概念 +动态规划算法是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。在学习动态规划之前需要明确掌握几个重要概念。 + +阶段:对于一个完整的问题过程,适当的切分为若干个相互联系的子问题,每次在求解一个子问题,则对应一个阶段,整个问题的求解转化为按照阶段次序去求解。 +状态:状态表示每个阶段开始时所处的客观条件,即在求解子问题时的已知条件。状态描述了研究的问题过程中的状况。 +决策:决策表示当求解过程处于某一阶段的某一状态时,可以根据当前条件作出不同的选择,从而确定下一个阶段的状态,这种选择称为决策。 +策略:由所有阶段的决策组成的决策序列称为全过程策略,简称策略。 +最优策略:在所有的策略中,找到代价最小,性能最优的策略,此策略称为最优策略。 +状态转移方程:状态转移方程是确定两个相邻阶段状态的演变过程,描述了状态之间是如何演变的。 +2.使用场景 +能采用动态规划求解的问题的一般要具有 3 个性质: + +最优化:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。子问题的局部最优将导致整个问题的全局最优。换句话说,就是问题的一个最优解中一定包含子问题的一个最优解。 +无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关,与其他阶段的状态无关,特别是与未发生的阶段的状态无关。 +重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势) +3. 算法流程 +划分阶段:按照问题的时间或者空间特征将问题划分为若干个阶段。 +确定状态以及状态变量:将问题的不同阶段时期的不同状态描述出来。 +确定决策并写出状态转移方程:根据相邻两个阶段的各个状态之间的关系确定决策。 +寻找边界条件:一般而言,状态转移方程是递推式,必须有一个递推的边界条件。 +设计程序,解决问题。 + + diff --git a/Week 05/id_013/decode-ways.java b/Week 05/id_013/decode-ways.java new file mode 100644 index 000000000..17db48b55 --- /dev/null +++ b/Week 05/id_013/decode-ways.java @@ -0,0 +1,24 @@ +public int numDecodings4(String s) { + int len = s.length(); + int[] dp = new int[3]; + dp[len % 3] = 1; + if (s.charAt(len - 1) != '0') { + dp[(len - 1) % 3] = 1; + } + for (int i = len - 2; i >= 0; i--) { + if (s.charAt(i) == '0') { + dp[i % 3] = 0; //因为空间复用了,不要忘记归零 + continue; + } + int ans1 = dp[(i + 1) % 3]; + int ans2 = 0; + int ten = (s.charAt(i) - '0') * 10; + int one = s.charAt(i + 1) - '0'; + if (ten + one <= 26) { + ans2 = dp[(i + 2) % 3]; + } + dp[i % 3] = ans1 + ans2; + + } + return dp[0]; +} diff --git a/Week 05/id_013/maximal-square.java b/Week 05/id_013/maximal-square.java new file mode 100644 index 000000000..70eaa80ab --- /dev/null +++ b/Week 05/id_013/maximal-square.java @@ -0,0 +1,16 @@ +public class Solution { + public int maximalSquare(char[][] matrix) { + int rows = matrix.length, cols = rows > 0 ? matrix[0].length : 0; + int[][] dp = new int[rows + 1][cols + 1]; + int maxsqlen = 0; + for (int i = 1; i <= rows; i++) { + for (int j = 1; j <= cols; j++) { + if (matrix[i-1][j-1] == '1'){ + dp[i][j] = Math.min(Math.min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; + maxsqlen = Math.max(maxsqlen, dp[i][j]); + } + } + } + return maxsqlen * maxsqlen; + } +} diff --git a/Week 05/id_023/leetCode_32_023.js b/Week 05/id_023/leetCode_32_023.js new file mode 100644 index 000000000..33cf68945 --- /dev/null +++ b/Week 05/id_023/leetCode_32_023.js @@ -0,0 +1,29 @@ +/** + * @param {string} s +* @return {number} +*/ +var longestValidParentheses = function (s) { + var max = 0; + function isValid(str) { + var stack = ['#']; + for (var i = 0; i < str.length; i++) { + if (str[i] == '(') { + stack.push('('); + } else { + var topEle = stack.pop(); + if (topEle != '(') { + return false; + } + } + } + return stack.length == 1; + } + for (var i = 0; i < s.length; i++) { + for (var j = i + 2; j <= s.length; j += 2) { + if (isValid(s.slice(i, j))) { + max = Math.max(max, j - i); + } + } + } + return max; +}; diff --git a/Week 05/id_023/leetCode_64_023.js b/Week 05/id_023/leetCode_64_023.js new file mode 100644 index 000000000..c574c1676 --- /dev/null +++ b/Week 05/id_023/leetCode_64_023.js @@ -0,0 +1,28 @@ +/** + * @param {number[][]} grid + * @return {number} + */ +var minPathSum = function (grid) { + let l = grid.length, h = grid[0].length; + let temp = []; + for (let i = 0; i < l; i++) { + temp[i] = []; + } + for (let i = 0; i < l; i++) { + for (let j = 0; j < h; j++) { + if (i == 0 && j == 0) { + temp[i][j] = grid[i][j] + } + else if (i == 0) { + temp[i][j] = temp[i][j - 1] + grid[i][j] + } + else if (j == 0) { + temp[i][j] = temp[i - 1][j] + grid[i][j] + } + else { + temp[i][j] = grid[i][j] + Math.min(temp[i][j - 1], temp[i - 1][j]) + } + } + } + return temp[l - 1][h - 1] +}; \ No newline at end of file diff --git a/Week 05/id_038/week-05-038/.gitignore b/Week 05/id_038/week-05-038/.gitignore new file mode 100644 index 000000000..a47486b2b --- /dev/null +++ b/Week 05/id_038/week-05-038/.gitignore @@ -0,0 +1,164 @@ +.vertx +*.ipr +# log files +*.log +*.log.2* +.log +out +output +dist +dist-3rd +*.txt +!agent-shell-banner.txt +!shell-banner.txt +!VERSION.txt + +# For Eclipse + +.metadata +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders +*.iml +.DS_Store + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + + +# For Java + +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# For Maven + +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# For JetBrains + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +#data module ignore +node_modules + +static +public + +BETHUNE_PRO_HOST_PID +BETHUNE_PRO_AGENT_PID +*PID +APP_PID +PID_* +alert-rules-*.yml +altmng-configs.yml +alertmanager.yml +app-info-*.properties +app-info.properties +inspectResult/ + +/file-uploads/ diff --git a/Week 05/id_038/week-05-038/pom.xml b/Week 05/id_038/week-05-038/pom.xml new file mode 100644 index 000000000..240ce46d7 --- /dev/null +++ b/Week 05/id_038/week-05-038/pom.xml @@ -0,0 +1,95 @@ + + 4.0.0 + com.github.kylefeng + week-05-038 + jar + 1.0-SNAPSHOT + week-05-038 + http://maven.apache.org + + UTF-8 + 1.8 + 4.12 + 5.0.0 + ${junit.version}.0 + 1.0.0 + + + + + + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + + + + maven-surefire-plugin + 2.19.1 + + + **/Test*.java + **/*Test.java + **/*Tests.java + **/*TestCase.java + + + + slow + + + + + + org.junit.platform + junit-platform-surefire-provider + ${junit.platform.version} + + + + + + + + + com.google.guava + guava + 28.1-jre + + + + org.assertj + assertj-core + 3.13.2 + test + + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test + + + + org.junit.platform + junit-platform-launcher + ${junit.platform.version} + test + + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + diff --git a/Week 05/id_038/week-05-038/src/main/java/com/github/kylefeng/LeetCode_32_038.java b/Week 05/id_038/week-05-038/src/main/java/com/github/kylefeng/LeetCode_32_038.java new file mode 100644 index 000000000..0145a57f9 --- /dev/null +++ b/Week 05/id_038/week-05-038/src/main/java/com/github/kylefeng/LeetCode_32_038.java @@ -0,0 +1,83 @@ +package com.github.kylefeng; + +import java.util.Deque; +import java.util.LinkedList; + +/** + * 32. 最长有效括号 + * + * @author kylefeng + * @time 2019/11/13 14:18 + */ +public class LeetCode_32_038 { + + public static boolean isValid(String s) { + Deque stack = new LinkedList<>(); + for (char c : s.toCharArray()) { + if (c == '(') { + stack.push(c); + } else if (!stack.isEmpty() && stack.peek() == '(') { + stack.pop(); + } else { + return false; + } + } + return stack.isEmpty(); + } + + /** + * 暴力算法: + * 双指针截取字串,通过 isValid 函数判断该子串是否合法。 + * 最近子问题,就是截取字串,利用栈判断合法性。 + * + * @param s + * @return + */ + public static int solution_brutal(String s) { + if (s == null || s.isEmpty()) { + return 0; + } + + int maxlen = 0; + int strLen = s.length(); + + for (int i = 0; i < strLen; i++) { + // 注意内层循环需要 <= 字符串长度,否则不会囊括字串最后一个字符 + for (int j = i + 2; j <= strLen; j += 2) { + if (isValid(s.substring(i, j))) { + maxlen = Math.max(maxlen, j - i); + } + } + } + return maxlen; + } + + /** + * 基于递推公式: + * 1. 若 s[i] == ')' 并且 s[i-1] = '(',即形如 "......()" 那么递推公式为 + * dp[i] = dp[i-2] + 2 + *

+ *

+ * 2. 若 s[i] == ')' 且 s[i-1] = ')',即形如 "......))" 当 s[i - dp[i-1] -1] == '(' + * dp [i] = dp[i - 1] + dp[i - dp[i - 1] - 2] + 2 + * + * @param s + * @return + */ + public static int solution_dp(String s) { + int maxans = 0; + int dp[] = new int[s.length()]; + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) == ')') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + + } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + maxans = Math.max(maxans, dp[i]); + } + return maxans; + } + + +} diff --git a/Week 05/id_038/week-05-038/src/main/java/com/github/kylefeng/LeetCode_64_038.java b/Week 05/id_038/week-05-038/src/main/java/com/github/kylefeng/LeetCode_64_038.java new file mode 100644 index 000000000..4dfe8a921 --- /dev/null +++ b/Week 05/id_038/week-05-038/src/main/java/com/github/kylefeng/LeetCode_64_038.java @@ -0,0 +1,85 @@ +/* + * Enmotech.com Inc. + * Copyright (c) 2019 All Rights Reserved. + */ + +package com.github.kylefeng; + +/** + * 64. 最小路径和 + *

+ * 给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 + *

+ * 说明:每次只能向下或者向右移动一步。 + *

+ * 示例: + *

+ * 输入: + *

+ * [
+ *   [1,3,1],
+ *   [1,5,1],
+ *   [4,2,1]
+ * ]
+ * 
+ * 输出: 7 + * 解释: 因为路径 1→3→1→1→1 的总和最小。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/minimum-path-sum + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author kylefeng + * @time 2019/11/15 13:12 + */ +public class LeetCode_64_038 { + + public static int solution_by_brutal(int[][] grid) { + return recurCal(grid, 0, 0); + } + + static int recurCal(int[][] grid, int i, int j) { + if (i == grid.length || j == grid[0].length) { + return Integer.MAX_VALUE; + } + + if (i == grid.length - 1 && j == grid[0].length - 1) { + return grid[i][j]; + } + + return grid[i][j] + Math.min(recurCal(grid, i + 1, j), recurCal(grid, i, j + 1)); + } + + public static int solution_by_dp(int[][] grid) { + if (grid == null) { + return 0; + } + + int m = grid.length; + int n = grid[0].length; + + int[][] dp = new int[m][n]; + + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + + if (i == m - 1 && j != n - 1) { + dp[i][j] = grid[i][j] + dp[i][j + 1]; + + } else if (j == n - 1 && i != m - 1) { + dp[i][j] = grid[i][j] + dp[i + 1][j]; + + } else if (j != n - 1 && i != m - 1) { + dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]); + + } else { + dp[i][j] = grid[i][j]; + } + + } + } + + return dp[0][0]; + } + +} diff --git a/Week 05/id_038/week-05-038/src/main/java/com/github/kylefeng/LeetCode_76_038.java b/Week 05/id_038/week-05-038/src/main/java/com/github/kylefeng/LeetCode_76_038.java new file mode 100644 index 000000000..f6b0c8aa3 --- /dev/null +++ b/Week 05/id_038/week-05-038/src/main/java/com/github/kylefeng/LeetCode_76_038.java @@ -0,0 +1,79 @@ +/* + * Enmotech.com Inc. + * Copyright (c) 2019 All Rights Reserved. + */ + +package com.github.kylefeng; + +import java.util.HashMap; +import java.util.Map; + +/** + * 76. 最小覆盖子串 + * https://leetcode-cn.com/problems/minimum-window-substring/ + * + * @author kylefeng + * @time 2019/11/15 12:42 + */ +public class LeetCode_76_038 { + + + public static String solution_by_moving_window(String s, String t) { + if (s == null || s.isEmpty() || t == null || t.isEmpty()) { + return ""; + } + + // count of all unique chars in t + Map dictT = new HashMap<>(); + for (int i = 0; i < t.length(); i++) { + int count = dictT.getOrDefault(t.charAt(i), 0); + dictT.put(t.charAt(i), count + 1); + } + + // number of unique chars in t, which need to be present in the desired window. + int required = dictT.size(); + + // moving window pointer + int l = 0, r = 0; + + int formed = 0; + + // 滑动窗口中,字符出现频率 + Map windowCounts = new HashMap<>(); + + // (window-length, left, right) + int[] ans = {-1, 0, 0}; + + while (r < s.length()) { + + char c = s.charAt(r); + int count = windowCounts.getOrDefault(c, 0); + windowCounts.put(c, count + 1); + + if (dictT.containsKey(c) && windowCounts.get(c).intValue() == dictT.get(c).intValue()) { + formed++; + } + + while (l <= r && formed == required) { + c = s.charAt(l); + if (ans[0] == -1 || r - l + 1 < ans[0]) { + ans[0] = r - 1 + 1; + ans[1] = l; + ans[2] = r; + } + + windowCounts.put(c, windowCounts.get(c) - 1); + if (dictT.containsKey(c) && windowCounts.get(c).intValue() < dictT.get(c).intValue()) { + formed--; + } + + l++; + } + + r++; + } + + return ans[0] == -1 ? "" : s.substring(ans[1], ans[2] + 1); + } + +} diff --git a/Week 05/id_038/week-05-038/src/main/java/com/github/kylefeng/LeetCode_91_038.java b/Week 05/id_038/week-05-038/src/main/java/com/github/kylefeng/LeetCode_91_038.java new file mode 100644 index 000000000..c691be226 --- /dev/null +++ b/Week 05/id_038/week-05-038/src/main/java/com/github/kylefeng/LeetCode_91_038.java @@ -0,0 +1,44 @@ +package com.github.kylefeng; + +/** + * 91. 解码方法 + * https://leetcode-cn.com/problems/decode-ways/ + * + * @author kylefeng + * @time 2019/11/13 17:37 + */ +public class LeetCode_91_038 { + + + /** + * @param s + * @return + */ + public static int solution(String s) { + if (s == null || s.isEmpty()) { + return 0; + } + + int strLen = s.length(); + char[] chars = s.toCharArray(); + int[] dp = new int[strLen]; + + + dp[0] = chars[0] == '0' ? 0 : 1; + + for (int i = 1; i < strLen; i++) { + int cur = chars[i] - '0'; + int pre = (chars[i - 1] - '0') * 10 + cur; + + if (cur != 0) { + dp[i] += dp[i - 1]; + } + + if (10 <= pre && pre <= 26) { + dp[i] += i >= 2 ? dp[i - 2] : 1; + } + } + + return dp[strLen - 1]; + } +} diff --git a/Week 05/id_038/week-05-038/src/test/java/com/github/kylefeng/LeetCode_32_038_Test.java b/Week 05/id_038/week-05-038/src/test/java/com/github/kylefeng/LeetCode_32_038_Test.java new file mode 100644 index 000000000..2073bf1d8 --- /dev/null +++ b/Week 05/id_038/week-05-038/src/test/java/com/github/kylefeng/LeetCode_32_038_Test.java @@ -0,0 +1,27 @@ +package com.github.kylefeng; + +import org.junit.jupiter.api.Test; + +import static com.github.kylefeng.LeetCode_32_038.solution_brutal; +import static com.github.kylefeng.LeetCode_32_038.solution_dp; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author kylefeng + * @time 2019/11/13 14:18 + */ +public class LeetCode_32_038_Test { + + @Test + void testCasesForBrutal() { + assertEquals(2, solution_brutal("(()")); + assertEquals(4, solution_brutal(")()())")); + } + + @Test + void testCasesForDp() { + assertEquals(2, solution_dp("(()")); + assertEquals(4, solution_dp(")()())")); + } + +} diff --git a/Week 05/id_038/week-05-038/src/test/java/com/github/kylefeng/LeetCode_64_038_Test.java b/Week 05/id_038/week-05-038/src/test/java/com/github/kylefeng/LeetCode_64_038_Test.java new file mode 100644 index 000000000..78a703bde --- /dev/null +++ b/Week 05/id_038/week-05-038/src/test/java/com/github/kylefeng/LeetCode_64_038_Test.java @@ -0,0 +1,38 @@ +/* + * Enmotech.com Inc. + * Copyright (c) 2019 All Rights Reserved. + */ + +package com.github.kylefeng; + +import org.junit.jupiter.api.Test; + +import static com.github.kylefeng.LeetCode_64_038.solution_by_brutal; +import static com.github.kylefeng.LeetCode_64_038.solution_by_dp; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author kylefeng + * @time 2019/11/15 13:14 + */ +public class LeetCode_64_038_Test { + + @Test + void brutal_solution() { + assertEquals(7, solution_by_brutal(new int[][]{ + {1, 3, 1}, + {1, 5, 1}, + {4, 2, 1} + })); + } + + @Test + void dp_solution() { + assertEquals(7, solution_by_dp(new int[][]{ + {1, 3, 1}, + {1, 5, 1}, + {4, 2, 1} + })); + } + +} diff --git a/Week 05/id_038/week-05-038/src/test/java/com/github/kylefeng/LeetCode_76_038_Test.java b/Week 05/id_038/week-05-038/src/test/java/com/github/kylefeng/LeetCode_76_038_Test.java new file mode 100644 index 000000000..0e9a88c98 --- /dev/null +++ b/Week 05/id_038/week-05-038/src/test/java/com/github/kylefeng/LeetCode_76_038_Test.java @@ -0,0 +1,23 @@ +/* + * Enmotech.com Inc. + * Copyright (c) 2019 All Rights Reserved. + */ + +package com.github.kylefeng; + +import org.junit.jupiter.api.Test; + +import static com.github.kylefeng.LeetCode_76_038.solution_by_moving_window; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author kylefeng + * @time 2019/11/15 13:09 + */ +public class LeetCode_76_038_Test { + + @Test + void testSolutionByMovingWindow() { + assertEquals("BANC", solution_by_moving_window("ADOBECODEBANC", "ABC")); + } +} diff --git a/Week 05/id_038/week-05-038/src/test/java/com/github/kylefeng/LeetCode_91_039_Test.java b/Week 05/id_038/week-05-038/src/test/java/com/github/kylefeng/LeetCode_91_039_Test.java new file mode 100644 index 000000000..f58627dc8 --- /dev/null +++ b/Week 05/id_038/week-05-038/src/test/java/com/github/kylefeng/LeetCode_91_039_Test.java @@ -0,0 +1,19 @@ +package com.github.kylefeng; + +import org.junit.jupiter.api.Test; + +import static com.github.kylefeng.LeetCode_91_038.solution; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author kylefeng + * @time 2019/11/13 17:39 + */ +public class LeetCode_91_039_Test { + @Test + void testSolution() { + assertEquals(2, solution("12")); + assertEquals(3, solution("226")); + } + +} diff --git a/Week 05/id_048/048_Week05.md b/Week 05/id_048/048_Week05.md new file mode 100644 index 000000000..3265c59ee --- /dev/null +++ b/Week 05/id_048/048_Week05.md @@ -0,0 +1,44 @@ +分治模板: +def divide_conquer(problem, param1, param2, ...): + # recursion terminator 结束条件 + if problem is None: + print_result + return + + # prepare data + data = prepare_data(problem) 分解子问题 + subproblems = split_problem(problem, data) + + # conquer subproblems 递归,drill down + subresult1 = self.divide_conquer(subproblems[0], p1, ...) + subresult2 = self.divide_conquer(subproblems[1], p1, ...) + subresult3 = self.divide_conquer(subproblems[2], p1, ...) + … + + # process and generate the final result 合并结果集 + result = process_result(subresult1, subresult2, subresult3, …) + +重要点: +找到最近最简方法,将其拆解成可重复解决的子问题。 +用数学归纳法思维。 +动态规划 = 分治 + 最优子结构 + +关键点; +动态规划与递归,分治没有本质区别,关键是看有无最优子结构。 +共性:找到重复子问题。 +差异性:动态规划找最优子结构,中途可以淘汰次优解。 + +DP分解步骤: +1.化繁为简,定义子问题。 +把当前的复杂的问题转换成简单的子问题。 +需要找出递推方程,合并子问题的解。 +2.定义状态空间。 +定义出状态空间,可以用记忆化搜索递归。 +3.动态规划的方程。 +递归和记忆化,或者把DP的状态表建立起来,自底向上进行递推。 + +DP优化步骤: +1. 进行DP分解。 +2. 画表格,用二维空间来减少时间复杂度。 +3. 把二维空间优化成一维空间。 +4. 把一维空间优化成常量,进行空间压缩。 diff --git a/Week 05/id_048/LeetCode_403_048.java b/Week 05/id_048/LeetCode_403_048.java new file mode 100644 index 000000000..0b797d95f --- /dev/null +++ b/Week 05/id_048/LeetCode_403_048.java @@ -0,0 +1,64 @@ +package com.leetcode.week05; + +import java.util.HashSet; +import java.util.Set; + +/** + * Created by tim on 2019/11/17. + * 一只青蛙想要过河。 假定河流被等分为 x 个单元格,并且在每一个单元格内都有可能放有一石子(也有可能没有)。 青蛙可以跳上石头,但是不可以跳入水中。 + + 给定石子的位置列表(用单元格序号升序表示), 请判定青蛙能否成功过河(即能否在最后一步跳至最后一个石子上)。 开始时, 青蛙默认已站在第一个石子上,并可以假定它第一步只能跳跃一个单位(即只能从单元格1跳至单元格2)。 + + 如果青蛙上一步跳跃了 k 个单位,那么它接下来的跳跃距离只能选择为 k - 1、k 或 k + 1个单位。 另请注意,青蛙只能向前方(终点的方向)跳跃。 + + 请注意: + + 石子的数量 ≥ 2 且 < 1100; + 每一个石子的位置序号都是一个非负整数,且其 < 231; + 第一个石子的位置永远是0。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/frog-jump + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_403_048 { + public boolean canCross(int[] stones) { + if(stones[1]>1){//第一跳只能跳一格,如果stones[1]不为1,肯定为false + return false; + } + Set set = new HashSet(); + for(int i = 0; i < stones.length; i++){ + if(i>3 && stones[i] >= 2*stones[i-1]){//如果下一个石头位置序号为当前序号的两倍,肯定不符合 + return false; + } + set.add(stones[i]); + } + return check(stones[stones.length-1], set, 1,1); + } + + private boolean check(int last, Set set, int index,int step){ + if(index == last){ + return true; + } + + if(set.contains(index + step + 1)){ + if(check(last, set, index + step + 1, step + 1)){ + return true; + } + } + if(set.contains(index + step)){ + if(check(last, set, index + step, step)){ + return true; + } + } + + if(step-1>0){ + if(set.contains(index + step - 1)){ + if(check(last, set, index + step - 1, step - 1)){ + return true; + } + } + } + return false; + } +} diff --git a/Week 05/id_048/LeetCode_64_048.java b/Week 05/id_048/LeetCode_64_048.java new file mode 100644 index 000000000..2b6ec551c --- /dev/null +++ b/Week 05/id_048/LeetCode_64_048.java @@ -0,0 +1,25 @@ +package com.leetcode.week05; + +/** + * Created by tim on 2019/11/17. + * * 最小路径和 + * https://leetcode-cn.com/problems/minimum-path-sum/ + * 给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 + 说明:每次只能向下或者向右移动一步。 + */ +public class LeetCode_64_048 { + public int minPathSum(int[][] grid) { + for (int i = grid.length - 1; i >= 0; i--) { + for (int j = grid[0].length - 1; j >= 0; j--) { + if(i == grid.length - 1 && j != grid[0].length - 1) + grid[i][j] = grid[i][j] + grid[i][j + 1]; + else if(j == grid[0].length - 1 && i != grid.length - 1) + grid[i][j] = grid[i][j] + grid[i + 1][j]; + else if(j != grid[0].length - 1 && i != grid.length - 1) + grid[i][j] = grid[i][j] + Math.min(grid[i + 1][j],grid[i][j + 1]); + } + } + return grid[0][0]; + } + +} diff --git a/Week 05/id_048/LeetCode_76_048.java b/Week 05/id_048/LeetCode_76_048.java new file mode 100644 index 000000000..b939fa554 --- /dev/null +++ b/Week 05/id_048/LeetCode_76_048.java @@ -0,0 +1,91 @@ +package com.leetcode.week05; + +import javafx.util.Pair; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by tim on 2019/11/17. + * 给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。 + + 示例: + + 输入: S = "ADOBECODEBANC", T = "ABC" + 输出: "BANC" + 说明: + + 如果 S 中不存这样的子串,则返回空字符串 ""。 + 如果 S 中存在这样的子串,我们保证它是唯一的答案 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/minimum-window-substring + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_76_048 { + public String minWindow(String s, String t) { + + if (s.length() == 0 || t.length() == 0) { + return ""; + } + + Map dictT = new HashMap(); + + for (int i = 0; i < t.length(); i++) { + int count = dictT.getOrDefault(t.charAt(i), 0); + dictT.put(t.charAt(i), count + 1); + } + + int required = dictT.size(); + + // Filter all the characters from s into a new list along with their index. + // The filtering criteria is that the character should be present in t. + List> filteredS = new ArrayList>(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (dictT.containsKey(c)) { + filteredS.add(new Pair(i, c)); + } + } + + int l = 0, r = 0, formed = 0; + Map windowCounts = new HashMap(); + int[] ans = {-1, 0, 0}; + + // Look for the characters only in the filtered list instead of entire s. + // This helps to reduce our search. + // Hence, we follow the sliding window approach on as small list. + while (r < filteredS.size()) { + char c = filteredS.get(r).getValue(); + int count = windowCounts.getOrDefault(c, 0); + windowCounts.put(c, count + 1); + + if (dictT.containsKey(c) && windowCounts.get(c).intValue() == dictT.get(c).intValue()) { + formed++; + } + + // Try and co***act the window till the point where it ceases to be 'desirable'. + while (l <= r && formed == required) { + c = filteredS.get(l).getValue(); + + // Save the smallest window until now. + int end = filteredS.get(r).getKey(); + int start = filteredS.get(l).getKey(); + if (ans[0] == -1 || end - start + 1 < ans[0]) { + ans[0] = end - start + 1; + ans[1] = start; + ans[2] = end; + } + + windowCounts.put(c, windowCounts.get(c) - 1); + if (dictT.containsKey(c) && windowCounts.get(c).intValue() < dictT.get(c).intValue()) { + formed--; + } + l++; + } + r++; + } + return ans[0] == -1 ? "" : s.substring(ans[1], ans[2] + 1); + } +} diff --git "a/Week 05/id_053/[120]\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" "b/Week 05/id_053/[120]\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" new file mode 100644 index 000000000..23cb3a19f --- /dev/null +++ "b/Week 05/id_053/[120]\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" @@ -0,0 +1,63 @@ +//给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。 +// +// 例如,给定三角形: +// +// [ +// [2], +// [3,4], +// [6,5,7], +// [4,1,8,3] +//] +// +// +// 自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。 +// +// 说明: +// +// 如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。 +// Related Topics 数组 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int minimumTotal(List> triangle) { + // + int[] result = new int[triangle.size()+1]; + + for (int i = triangle.size()-1;i >= 0;i--){ + for (int j = 0;j = 0; row--) { + //f[i,j] += Math.min(f[i+1,j],f[i+1,j+1]); + //result[i][j] += Math.min(result[i+1][j],result[i+1][j+1]); + for (int column = 0;column < result[row].length;column++){ + result[row][column] += Math.min(result[row + 1][column],result[row + 1][column + 1]); + } + } + return result[0][0];*/ + //代码已测过没问题,但是耗时太久,花在对二维数组做初始化了,java暂时好像没有更好的办法,舍弃 + /*Integer[][] result = new Integer[triangle.size()][]; + for (int row = 0;row < result.length;row++){ + result[row] = new Integer[triangle.get(row).size()]; + for (int column = 0;column = 0; row--) { + //f[i,j] += Math.min(f[i+1,j],f[i+1,j+1]); + //result[i][j] += Math.min(result[i+1][j],result[i+1][j+1]); + for (int column = 0;column < result[row].length;column++){ + result[row][column] += Math.min(result[row + 1][column],result[row + 1][column + 1]); + } + } + return result[0][0];*/ + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week 05/id_053/[198]\346\211\223\345\256\266\345\212\253\350\210\215.java" "b/Week 05/id_053/[198]\346\211\223\345\256\266\345\212\253\350\210\215.java" new file mode 100644 index 000000000..575775d28 --- /dev/null +++ "b/Week 05/id_053/[198]\346\211\223\345\256\266\345\212\253\350\210\215.java" @@ -0,0 +1,60 @@ +//你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 +// +// 给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。 +// +// 示例 1: +// +// 输入: [1,2,3,1] +//输出: 4 +//解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 +//  偷窃到的最高金额 = 1 + 3 = 4 。 +// +// 示例 2: +// +// 输入: [2,7,9,3,1] +//输出: 12 +//解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 +//  偷窃到的最高金额 = 2 + 9 + 1 = 12 。 +// +// Related Topics 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int rob(int[] nums) { + //动态规划三部曲 + //1、找重复子问题(分治) + //2、定义状态数组 + //3、dp方程 + //max_stolen[i] 表示从0偷盗i-1家的最大金额数,递推方程 f(i) = f(i-1)+nums[i-1];此时的最大问题是 + //你没法确定f(i-1)是否包含了nums[i-2]这家,如果包含,则后面不能加nums[i-1],如不包含则要加入 + //此时考虑升维,多出一维来记录这家是否要偷 + //max_stolen[i][0,1]表示从0偷盗第i家的最大金额数,0表示不偷,1表示偷 + //1、假设第i家被偷;f(i) = f(i-2)+nums[i-1] + //2、假设第i家不偷:f(i) = max(max_stolen[i-1][0],max_stolen[i-1][1]) + //第一版(时间复杂度O(n),空间复杂度O(2n)) + if(nums == null || nums.length == 0) return 0; + if(nums.length == 1) return nums[0]; + /*int[][] max_stolen = new int[nums.length][2]; + max_stolen[0][0] = 0; + max_stolen[0][1] = nums[0]; + for(int i = 1;i < nums.length;i++) { + max_stolen[i][1] = max_stolen[i-1][0]+nums[i]; + max_stolen[i][0] = Math.max(max_stolen[i-1][0],max_stolen[i-1][1]); + } + return Math.max(max_stolen[nums.length-1][0],max_stolen[nums.length-1][1]);*/ + //第二版空间复杂度改成O(n) 用max_stolen[i]来记录从0到i家偷到的最大金额,且第i家必偷 + //DP方程 max_stolen[i] = max(nums[i] + max_stolen[i-2],max_stolen[i-1]); + int[] max_stolen = new int[nums.length]; + max_stolen[0] = nums[0]; + max_stolen[1] = Math.max(nums[0],nums[1]); + int max = Math.max(max_stolen[0],max_stolen[1]); + for (int i = 2;i < nums.length;i++){ + max_stolen[i] = Math.max(nums[i] + max_stolen[i-2],max_stolen[i-1]); + max = Math.max(max,max_stolen[i]); + } + return max; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week 05/id_053/[322]\351\233\266\351\222\261\345\205\221\346\215\242.java" "b/Week 05/id_053/[322]\351\233\266\351\222\261\345\205\221\346\215\242.java" new file mode 100644 index 000000000..016b9a19b --- /dev/null +++ "b/Week 05/id_053/[322]\351\233\266\351\222\261\345\205\221\346\215\242.java" @@ -0,0 +1,41 @@ +//给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。 +// +// 示例 1: +// +// 输入: coins = [1, 2, 5], amount = 11 +//输出: 3 +//解释: 11 = 5 + 5 + 1 +// +// 示例 2: +// +// 输入: coins = [2], amount = 3 +//输出: -1 +// +// 说明: +//你可以认为每种硬币的数量是无限的。 +// Related Topics 动态规划 + + +import java.util.Arrays; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int coinChange(int[] coins, int amount) { + if (coins == null || coins.length == 0) return -1; + if(coins.length == 1 && (amount % coins[0] != 0)) return -1; + //f(n) = min(f(n - k) ) + 1; + //k值是变量,从coins里面取 + int[] dp = new int[amount + 1]; + Arrays.fill(dp,amount + 1); + dp[0] = 0; + for (int i = 1;i <= amount;i++) { + for (int j = 0;j < coins.length;j++){ + if(i >= coins[j]){ + dp[i] = Math.min(dp[i-coins[j]] + 1,dp[i]); + } + } + } + return dp[dp.length - 1] == amount + 1 ? -1 : dp[dp.length - 1]; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week 05/id_053/[53]\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214.java" "b/Week 05/id_053/[53]\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214.java" new file mode 100644 index 000000000..ae76afccf --- /dev/null +++ "b/Week 05/id_053/[53]\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214.java" @@ -0,0 +1,42 @@ +//给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 +// +// 示例: +// +// 输入: [-2,1,-3,4,-1,2,1,-5,4], +//输出: 6 +//解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 +// +// +// 进阶: +// +// 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 +// Related Topics 数组 分治算法 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int maxSubArray(int[] nums) { + + //MaxTotal(n)表示前n个数的最大子序列和,假设 MaxTotal(n-1)为负数,则MaxTotal(n) 就是它的第n个数 + //反过来,加入MaxTotal(n-1)大于零,则MaxTotal(n) = MaxTotal(n - 1) +n; + //递推公式 MaxTotal(n) = max(MaxTotal(n-1),MaxTotal(n - 1) +n); + if(nums == null || nums.length == 0) throw new RuntimeException("参数错误"); + if(nums.length == 1) return nums[0]; + int[] dp = new int[nums.length]; + dp[0] =nums[0];//这一步很重要,否则数组中都是负数时不可能得到准确答案,因为dp[0]会默认初始化为零 + int max = dp[0]; + //优化后 + for(int i = 1;i max) max = dp[i]; + } + //循环可以放在一起,优化前 + /*int max = Integer.MIN_VALUE; + for (int i = 0;i < dp.length;i++) { + if (dp[i] > max) max = dp[i]; + }*/ + return max; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_063/Leetcode_062_063.java b/Week 05/id_063/Leetcode_062_063.java new file mode 100644 index 000000000..0c0a1efc2 --- /dev/null +++ b/Week 05/id_063/Leetcode_062_063.java @@ -0,0 +1,30 @@ + +/* + +思路 +动态规划求解 +dp[i][j]表示从i, j位置走到右下角的方法数 +显然有 +dp[i][j] = sum ( dp[i, j+1], dp[i+1, j] ) + + */ + + +class Solution { + public int uniquePaths(int m, int n) { + int[] dp = new int[n]; + + // i = m-1时候先初始化种子 + for (int j = n - 1; j >= 0; j--) { + dp[j] = 1; // 最底下一层肯定都只有一种走法 + } + + for (int i = m - 2; i >= 0; i--) { + for (int j = n - 2; j >= 0; j--) { + dp[j] = dp[j] + dp[j+1]; + } + } + + return dp[0]; + } +} diff --git a/Week 05/id_063/Leetcode_063_063.java b/Week 05/id_063/Leetcode_063_063.java new file mode 100644 index 000000000..4b7a8c1c2 --- /dev/null +++ b/Week 05/id_063/Leetcode_063_063.java @@ -0,0 +1,36 @@ +/* +思路 +动态规划 + +还是下面的状态转移方式,如果i,j 位置是障碍物,这种状态走到右下角的方法数为0 +dp[i][j]表示从i, j位置走到右下角的方法数 +显然有 +dp[i][j] = sum ( dp[i, j+1], dp[i+1, j] ) + + + */ + + +class Solution { + public int uniquePathsWithObstacles(int[][] obstacleGrid) { + int m = obstacleGrid.length; + int n = obstacleGrid[0].length; + + int[] dp = new int[n]; + + // i = m-1时候先初始化种子 + dp[n-1] = (obstacleGrid[m-1][n-1] == 1) ? 0 : 1; + for (int j = n - 2; j >= 0; j--) { + dp[j] = (obstacleGrid[m-1][j] == 1) ? 0 : dp[j+1]; + } + + for (int i = m - 2; i >= 0; i--) { + dp[n-1] = (obstacleGrid[i][n-1] == 1) ? 0 : dp[n-1]; + for (int j = n - 2; j >= 0; j--) { + dp[j] = (obstacleGrid[i][j] == 1) ? 0 : dp[j] + dp[j+1]; + } + } + + return dp[0]; + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_1143_063.java b/Week 05/id_063/Leetcode_1143_063.java new file mode 100644 index 000000000..35650acaf --- /dev/null +++ b/Week 05/id_063/Leetcode_1143_063.java @@ -0,0 +1,44 @@ +/* +思路 +DP算法解决LCS问题 + +dp[i][j]表示text1前i个字符和text2前j个字符能构造出来的LCS长度 +显然有下面状态转移关系 +如果 text1[i] == text[j] + dp[i][j] = dp[i-1][j-1] + 1 +反之 + dp[i][j] = max(dp[i-1][j], dp[i][j-1]) +*/ + + +class Solution { + public int longestCommonSubsequence(String text1, String text2) { + int m = text1.length(); + int n = text2.length(); + + if ((m == 0) || (n == 0)) { + return 0; + } + + int[] dp = new int[n]; + // i 为 0 初始化种子 + dp[0] = (text1.charAt(0) == text2.charAt(0)) ? 1 : 0; + for (int j = 1; j < n; j++) { + dp[j] = (dp[j-1] == 1) ? 1 : ((text1.charAt(0) == text2.charAt(j)) ? 1: 0); + } + + for (int i = 1; i < m; i++) { + int dp_old = dp[0]; // 保存dp[i-1][j-1], 因为这里用一维数组代替二位数组进行空间复杂度优化,所以这个老值要提前保存 + int dp_tmp = 0; + + dp[0] = (text1.charAt(i) == text2.charAt(0)) ? 1 : dp[0]; + for (int j = 1; j < n; j++) { + dp_tmp = dp[j]; // 保存老状态dp[i-1][j] 下一轮迭代计算dp[i][j+1]要使用这个值 + dp[j] = (text1.charAt(i) == text2.charAt(j)) ? (dp_old + 1) : Math.max(dp[j-1], dp[j]); + dp_old = dp_tmp; + } + } + + return dp[n-1]; + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_120_063.java b/Week 05/id_063/Leetcode_120_063.java new file mode 100644 index 000000000..d11828207 --- /dev/null +++ b/Week 05/id_063/Leetcode_120_063.java @@ -0,0 +1,25 @@ +/* + DP算法解决 + 递推公式 f[i][n] = min(f[i+1][n], f[i+1, n+1]) + num[i][n] +*/ + + +import java.util.List; + +class Solution { + public int minimumTotal(List> triangle) { + int len = triangle.size(); + if (len == 0) { + return 0; + } + + int[] dp = new int[len]; + for (int i = len - 1; i >= 0; i--) { + for (int j = 0; j < i + 1; j++) { + dp[j] = (i == len - 1) ? triangle.get(i).get(j) : Math.min(dp[j], dp[j+1]) + triangle.get(i).get(j); + } + } + + return dp[0]; + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_121_063.java b/Week 05/id_063/Leetcode_121_063.java new file mode 100644 index 000000000..12edbf127 --- /dev/null +++ b/Week 05/id_063/Leetcode_121_063.java @@ -0,0 +1,54 @@ +/* +定义 +dp[i][0] 是第i天还没买股票最大利润 +dp[i][1] 是第i天持有股票最大利润 +dp[i][2] 是第i天已经把股票卖掉的最大利润 + +递推公式为 + +dp[i][0] = max ( + dp[i-1][0], // 上一天为止还没买,今天也不做任何操作 +) + +dp[i][1] = max ( + dp[i-1][1], // 上一天为止已近买了,今天什么都做不了 + dp[i-1][0] - price[i], // 上一天为止还没买,今天买股票 +) + +dp[i][2] = max { + dp[i-1][2], // 前一天为止股票已经卖了,今天什么都做不了 + dp[i-1][1] + price[i] // 前一天为止还持有股票,今天卖股票 +} + +最后答案是 max ( dp[n-1][0], dp[n-1][2] ) + +推导可以发现 dp[i][0] 一直都是0 + +所以其实最后答案是 dp[n-1][2] > 0 ? dp[n-1][2] : 0; + + */ + +class Solution { + public int maxProfit(int[] prices) { + if ((prices == null) || (prices.length == 0)) { + return 0; + } + + int dp1, dp2; + int max1, max2; + dp1 = -prices[0]; dp2 = Integer.MIN_VALUE; + + for (int i = 1; i < prices.length; i++) { + max1 = Math.max(dp1, 0 - prices[i]); + max2 = Math.max(dp2, dp1 + prices[i]); + + dp1 = max1; dp2 = max2; + } + + return Math.max(dp2, 0); + } + + public static void main(String[] args) { + System.out.println(new Solution().maxProfit(new int[] {7,1,5,3,6,4})); + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_122_063.java b/Week 05/id_063/Leetcode_122_063.java new file mode 100644 index 000000000..f70c72dea --- /dev/null +++ b/Week 05/id_063/Leetcode_122_063.java @@ -0,0 +1,43 @@ + +/* +思路 + +dp[i][0] 表示第i天不持有股票的最大利润 +dp[i][1] 表示第i天持有股票的最大利润 + +dp[i][0] = max ( + dp[i-1][1] + prices[i] // 昨天为止持有股票,今天卖股票 + dp[i-1][0] // 昨天为止没股票,今天什么都不干 +) + +dp[i][1] = max ( + dp[i-1][1], // 昨天为止有股票,今天什么都不干 + dp[i-1][0] - prices[i] // 昨天为止没股票,今天买股票 +) + +最后答案是dp[n-1][0] (因为最后一天肯定手上不持有股票才可能是利润最大的) + + */ + +class Solution { + public int maxProfit(int[] prices) { + if ((prices == null) || (prices.length == 0)) { + return 0; + } + + int dp0 = 0; + int dp1 = -prices[0]; + int max0, max1; + for (int i = 1; i < prices.length; i++) { + max0 = Math.max(dp1 + prices[i], dp0); + max1 = Math.max(dp1, dp0 - prices[i]); + dp0 = max0; dp1 = max1; + } + + return dp0; + } + + public static void main(String[] args) { + System.out.println(new Solution().maxProfit(new int[] {7,1,5,3,6,4})); + } +} diff --git a/Week 05/id_063/Leetcode_123_063.java b/Week 05/id_063/Leetcode_123_063.java new file mode 100644 index 000000000..a34e03fe7 --- /dev/null +++ b/Week 05/id_063/Leetcode_123_063.java @@ -0,0 +1,85 @@ +/* +动态规划递推思路 + +dp[i][j][0] 表示第i天已经完成j比交易手上没有持有股票状态下的最佳收益 +dp[i][j][1] 表示第i天已经完成j比交易手上持有股票状态下的最佳收益 + +有如下递推关系 + +dp[i][j][0] = max ( + dp[i-1][j][0], // 前一天为止也没持有股票,交易次数也没变,今天什么都不做 + dp[i-1][j-1][1] + prices[i] // 前一天为止有股票,交易次数少一次,今天卖股票 +) + +dp[i][j][1] = max ( + dp[i-1][j][1] // 前一天为止,持有股票,交易次数不变,今天什么都不做 + dp[i-1][j][0] - prices[i] // 前一天为止,没股票,交易次数不变,今天买股票 +) + +其中j 只可能是0, 1, 2 +最后答案是max(dp[n-1][0][0], dp[n-1][1][0], ..... dp[n-1][k][0]) +(k是最多交易次数,这个问题里面k=2) + +*/ + +import sun.jvm.hotspot.jdi.IntegerTypeImpl; + +class Solution { + class Node { + long dp0; // 没持有股票时候最大利润 + long dp1; // 持有股票时候最大利润 + } + + public int maxProfit(int[] prices) { + if ((prices == null) || (prices.length == 0)) { + return 0; + } + + final int MAX_SOLD_NUM = 2; + Node[] dp_old = new Node[MAX_SOLD_NUM + 1]; + for (int i = 0; i <= MAX_SOLD_NUM; i++) { + dp_old[i] = new Node(); + } + + Node[] dp_new = new Node[MAX_SOLD_NUM + 1]; + for (int i = 0; i <= MAX_SOLD_NUM ; i++) { + dp_new[i] = new Node(); + } + + // 第一天的数据进行特殊初始化, 第一天不可能完成交易,因此dp[i][1][0:1] dp[i][2][0:1] ... dp[i][k][0:1] + // 都是不存在的状态,利润设置为特殊值即可 + dp_old[0].dp0 = 0; dp_old[0].dp1 = -prices[0]; + for (int j = 1; j <= MAX_SOLD_NUM; j++) { + dp_old[j].dp0 = dp_old[j].dp1 = Integer.MIN_VALUE; + } + + for (int i = 1; i < prices.length; i++) { + dp_new[0].dp0 = dp_old[0].dp0; + dp_new[0].dp1 = Math.max(dp_old[0].dp1, dp_old[0].dp0 - prices[i]); + + //System.out.print("(" + dp_new[0].dp0 + ", " + dp_new[0].dp1 + ") ------"); + + for (int j = 1; j <= MAX_SOLD_NUM; j++) { + dp_new[j].dp0 = Math.max(dp_old[j].dp0, dp_old[j-1].dp1 + prices[i]); + dp_new[j].dp1 = Math.max(dp_old[j].dp1, dp_old[j].dp0 - prices[i]); + + //System.out.print("(" + dp_new[j].dp0 + ", " + dp_new[j].dp1 + ") ------"); + } + + //System.out.println(); + + Node[] tmp = dp_new; dp_new = dp_old; dp_old = tmp; + } + + int result = Integer.MIN_VALUE; + for (int j = 0; j <= MAX_SOLD_NUM; j++) { + result = (int)Math.max(result, dp_old[j].dp0); + } + + return result; + } + + public static void main(String[] args) { + System.out.println(new Solution().maxProfit(new int[] {2, 1, 4})); + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_152_063.java b/Week 05/id_063/Leetcode_152_063.java new file mode 100644 index 000000000..7e5000f84 --- /dev/null +++ b/Week 05/id_063/Leetcode_152_063.java @@ -0,0 +1,58 @@ +/* +思路 + +定义状态dp[i] 为以nums[i]作为结尾因子的最大乘积 + +可能出现两种情况 + 1. nums[i]单独作为一个序列 + 2. nums[i] 跟nums[i-1] 连成一片 + dp[i] 的值就依赖于前面0 到 i-1的数值全部乘起来的最大值和最小值,因为nums[i]本身有正有负, + nums[i] 为正 dp[i]就应该是前面序列乘积最大值和num[i]相乘,否则就应该是前面序列最小值 + 和num[i]相乘,所以一维的状态不够用 + + 定义 dp[i][0] 为 以 i 结尾的序列的乘积最小值 + dp[i][1] 为 以 i 结尾的序列的乘积最大值 + + i从 0 到 nums.length进行迭代,每轮迭代更新最大值和最小值 + 最后答案就是 max( dp[0][1], dp[1][1], ... dp[n-1][1] ) + + + */ + + +class Solution { + public int maxProduct(int[] nums) { + if (nums.length == 0) { + return 0; + } + + int dp0 = nums[0]; + int dp1 = nums[0]; + int max, min; + int result = nums[0]; + + for (int i = 1; i < nums.length; i++) { + if (nums[i] == 0) { + dp0 = dp1 = 0; + continue; + } + + max = min = nums[i]; + + if (nums[i] > 0) { + max = Math.max(max, dp1 * nums[i]); + min = Math.min(min, dp0 * nums[i]); + } else { + max = Math.max(max, dp0 * nums[i]); + min = Math.min(min, dp1 * nums[i]); + } + + dp0 = min; + dp1 = max; + + result = Math.max(result, dp1); + } + + return result; + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_188_063.java b/Week 05/id_063/Leetcode_188_063.java new file mode 100644 index 000000000..c3657188c --- /dev/null +++ b/Week 05/id_063/Leetcode_188_063.java @@ -0,0 +1,104 @@ +/* +动态规划递推思路 + +dp[i][j][0] 表示第i天已经完成j比交易手上没有持有股票状态下的最佳收益 +dp[i][j][1] 表示第i天已经完成j比交易手上持有股票状态下的最佳收益 + +有如下递推关系 + +dp[i][j][0] = max ( + dp[i-1][j][0], // 前一天为止也没持有股票,交易次数也没变,今天什么都不做 + dp[i-1][j-1][1] + prices[i] // 前一天为止有股票,交易次数少一次,今天卖股票 +) + +dp[i][j][1] = max ( + dp[i-1][j][1] // 前一天为止,持有股票,交易次数不变,今天什么都不做 + dp[i-1][j][0] - prices[i] // 前一天为止,没股票,交易次数不变,今天买股票 +) + +其中j 只可能是0, 1, 2 +最后答案是max(dp[n-1][0][0], dp[n-1][1][0], ..... dp[n-1][k][0]) +(k是最多交易次数,这个问题里面k=2) + +如果交易次数限制超过了天数一半,等于没有限制,可以转换成不限制交易次数的DP,提升效率 + +*/ + + +class Solution { + class Node { + long dp0; // 没持有股票时候最大利润 + long dp1; // 持有股票时候最大利润 + } + + // 不限制交易次数的计算方式 + public int maxProfitNoSoldLimit(int[] prices) { + if ((prices == null) || (prices.length == 0)) { + return 0; + } + + int dp0 = 0; + int dp1 = -prices[0]; + int max0, max1; + for (int i = 1; i < prices.length; i++) { + max0 = Math.max(dp1 + prices[i], dp0); + max1 = Math.max(dp1, dp0 - prices[i]); + dp0 = max0; dp1 = max1; + } + + return dp0; + } + + + public int maxProfit(int k, int[] prices) { + if ((prices == null) || (prices.length == 0)) { + return 0; + } + + if (k >= prices.length / 2) { + return maxProfitNoSoldLimit(prices); + } + + int MAX_SOLD_NUM = Math.min(prices.length/2, k); + + Node[] dp_old = new Node[MAX_SOLD_NUM + 1]; + for (int i = 0; i <= MAX_SOLD_NUM; i++) { + dp_old[i] = new Node(); + } + + Node[] dp_new = new Node[MAX_SOLD_NUM + 1]; + for (int i = 0; i <= MAX_SOLD_NUM ; i++) { + dp_new[i] = new Node(); + } + + // 第一天的数据进行特殊初始化, 第一天不可能完成交易,因此dp[i][1][0:1] dp[i][2][0:1] ... dp[i][k][0:1] + // 都是不存在的状态,利润设置为特殊值即可 + dp_old[0].dp0 = 0; dp_old[0].dp1 = -prices[0]; + for (int j = 1; j <= MAX_SOLD_NUM; j++) { + dp_old[j].dp0 = dp_old[j].dp1 = Integer.MIN_VALUE; + } + + for (int i = 1; i < prices.length; i++) { + dp_new[0].dp0 = dp_old[0].dp0; + dp_new[0].dp1 = Math.max(dp_old[0].dp1, dp_old[0].dp0 - prices[i]); + + for (int j = 1; j <= MAX_SOLD_NUM; j++) { + dp_new[j].dp0 = Math.max(dp_old[j].dp0, dp_old[j-1].dp1 + prices[i]); + dp_new[j].dp1 = Math.max(dp_old[j].dp1, dp_old[j].dp0 - prices[i]); + } + + Node[] tmp = dp_new; dp_new = dp_old; dp_old = tmp; + } + + int result = Integer.MIN_VALUE; + for (int j = 0; j <= MAX_SOLD_NUM; j++) { + result = (int)Math.max(result, dp_old[j].dp0); + } + + return result; + } + + public static void main(String[] args) { + System.out.println(new Solution().maxProfit(2, new int[] {3,2,6,5,0,3})); + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_198_063.java b/Week 05/id_063/Leetcode_198_063.java new file mode 100644 index 000000000..18570589a --- /dev/null +++ b/Week 05/id_063/Leetcode_198_063.java @@ -0,0 +1,34 @@ +/* +思路 +动态规划 + +dp[i]表示以i位置结尾的序列的最大和,考虑上一个位置,只可能是i-2 或者是 i-3 +所以 dp[i] = max(dp[i-2], dp[i-3]) + nums[i] + + */ + + +public class Solution { + public int rob(int[] nums) { + if (nums.length == 0) { + return 0; + } + + int maxVal = 0; + int[] dp = new int[nums.length]; + + for (int i = 0; i < nums.length; i++) { + if ((i == 0) || (i == 1)) { + dp[i] = nums[i]; + } else if (i == 2) { + dp[i] = dp[i-2] + nums[i]; + } else { + dp[i] = Math.max(dp[i-2], dp[i-3]) + nums[i]; + } + + maxVal = Math.max(dp[i], maxVal); + } + + return maxVal; + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_213_063.java b/Week 05/id_063/Leetcode_213_063.java new file mode 100644 index 000000000..77daf4d3c --- /dev/null +++ b/Week 05/id_063/Leetcode_213_063.java @@ -0,0 +1,95 @@ +/* +DP 算法求解 +dp[i][j] 表示以i位置 开始 j位置结束的最大和区间 +dp[i][j] 要么是左边界向左移拓展出来的,要么是右边界向右移拓展出来的 + +所以 dp[i][j] = max( max(dp[i+2][j], dp[i+3][j]) + nums[i], max(dp[i][j-2], dp[i][j-3]) + nums[j] ) +如果abs(j - i) == 1 或者是 abs (n - j + i) == 1 对应的状态是不存在的, 对应的dp[i][j]表示为-1 +最后求所有dp[i][j]的最大值 + + + */ + +class Solution { + public int rob(int[] nums) { + if (nums.length == 0) { + return 0; + } + + int n = nums.length; + + int[][] dp = new int[n][n]; + for (int i = n - 1; i >= 0; i--) { + // 最后三行数据特殊处理,其他行按照递推公式进行 + if (i == n - 1) { + for (int j = 0; j <= n-1; j++) { + dp[i][j] = (j == n-1) ? nums[n-1] : -1; + } + continue; + } + + if (i == n - 2) { + for (int j = 0; j <= n-1; j++) { + dp[i][j] = (j == n-2) ? nums[n-2] : -1; + } + continue; + } + + if (i == n - 3) { + for (int j = 0; j <= n-1; j++) { + if (j == i) { + dp[i][j] = nums[j]; + } else if ((j == i+2) && (Math.abs(n-j+i) != 1)) { + dp[i][j] = nums[j] + nums[i]; + } else { + dp[i][j] = -1; + } + } + continue; + } + + for (int j = 0; j < nums.length; j++) { + if (i == j) { + dp[i][j] = nums[i]; + continue; + } + + if ((j < i) || (Math.abs(j-i) == 1) || (Math.abs(n-j+i) == 1)) { + dp[i][j] = -1; + continue; + } + + dp[i][j] = -1; + + if ( (j-3 >= 0) && (dp[i][j-3] != -1)) { + dp[i][j] = Math.max(dp[i][j], dp[i][j-3] + nums[j]); + } + + if ( (j-2 >= 0) && (dp[i][j-2] != -1)) { + dp[i][j] = Math.max(dp[i][j], dp[i][j-2] + nums[j]); + } + + if (dp[i+2][j] != -1) { + dp[i][j] = Math.max(dp[i][j], dp[i+2][j] + nums[i]); + } + + if (dp[i+3][j] != -1) { + dp[i][j] = Math.max(dp[i][j], dp[i+3][j] + nums[i]); + } + } + } + + int maxVal = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + maxVal = Math.max(maxVal, dp[i][j]); + } + } + + return maxVal; + } + + public static void main(String[] args) { + System.out.println(new Solution().rob(new int[] {2,3,2})); + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_221_063.java b/Week 05/id_063/Leetcode_221_063.java new file mode 100644 index 000000000..ea41120ed --- /dev/null +++ b/Week 05/id_063/Leetcode_221_063.java @@ -0,0 +1,79 @@ +/* + +找以第i层为底的最大正方形,最后答案就是i从0到n-1中所有最大正方形中最大的一个 + +每一层计算的时候构建dp[j]数组,dp[j]表示当前作为底那一层中,第j列从位置上从第0行到当前行的累加和 +当前层的dp数组可以用上一层的dp数组的数值加上当前层的矩阵数值得出 +,这样计算每一层的最大正方形的时候就可以转换成一个单调栈问题 +整个时间复杂度O(N*N) + + */ + + +import java.util.Stack; + +class Solution { + public int maximalSquare(char[][] matrix) { + if ((matrix.length == 0) || (matrix[0].length == 0)) { + return 0; + } + + int colLen = matrix[0].length; + int rowLen = matrix.length; + int[] dp = new int[colLen]; + int maxLen = 0; + + // i = 0 时候进行初始化 + for (int j = 0; j < colLen; j++) { + if (matrix[0][j] == '1') { + maxLen = 1; dp[j] = 1; + } + } + + for (int i = 1; i < rowLen; i++) { + for (int j = 0; j < colLen; j++) { + dp[j] = (matrix[i][j] == '1') ? dp[j] + 1 : 0; + } + + Stack stack = new Stack<>(); + + int val, left, right, len; + for (int j = 0; j < colLen; j++) { + if (stack.size() ==0) { + stack.add(j); + continue; + } + + + while ((stack.size() > 0) && (dp[j] < dp[stack.peek()])) { + val = dp[stack.pop()]; + left = stack.isEmpty() ? -1 : stack.peek(); + len = j - left - 1; + + maxLen = Math.max(Math.min(val, len), maxLen); + } + + stack.add(j); + } + + // 清空栈 + while (!stack.isEmpty()) { + val = dp[stack.pop()]; + left = stack.isEmpty() ? -1 : stack.peek(); + len = colLen - left - 1; + maxLen = Math.max(Math.min(val, len), maxLen); + } + } + + return maxLen * maxLen; + } + + public static void main(String[] args) { + System.out.println(new Solution().maximalSquare(new char[][] { + {'1','0','1','0','0'}, + {'1','0','1','1','1'}, + {'1','1','1','1','1'}, + {'1','0','0','1','0'} + })); + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_279_063.java b/Week 05/id_063/Leetcode_279_063.java new file mode 100644 index 000000000..47a5e6110 --- /dev/null +++ b/Week 05/id_063/Leetcode_279_063.java @@ -0,0 +1,24 @@ +/* +Dp 算法解决 + +先找小于等于n 的 完全平方数k*k +dp[i] 表示凑成和为i需要的最少的数字个数 +dp[i] = min(dp[i-1*1], dp[i-2*2], dp[i-3*3] ..... dp[i-k*k]) + 1; +特殊的,有 dp[0] = 0; + */ + +class Solution { + public int numSquares(int n) { + int[] dp = new int[n + 1]; + + dp[0] = 0; + for (int i = 1; i <= n; i++) { + dp[i] = Integer.MAX_VALUE; + for (int k = 1; k*k <= i; k++) { + dp[i] = Math.min(dp[i], dp[i-k*k] + 1); + } + } + + return dp[n]; + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_309_063.java b/Week 05/id_063/Leetcode_309_063.java new file mode 100644 index 000000000..82fca0903 --- /dev/null +++ b/Week 05/id_063/Leetcode_309_063.java @@ -0,0 +1,54 @@ + +/* +思路 + +dp[i][0] 表示第i天不持有股票且当天没有卖股票的最大利润 +dp[i][1] 表示第i天持有股票的最大利润 +dp[i][2] 表示第i天不持有股票且当天卖了股票的最大利润 + + +dp[i][0] = max ( + dp[i-1][2] // 昨天卖了股票,今天什么都不干 + dp[i-1][0] // 昨天没股票,今天什么都不干 +) + +dp[i][1] = max ( + dp[i-1][1], // 昨天为止有股票,今天什么都不干 + dp[i-1][0] - prices[i] // 昨天为止没股票,今天买股票 + dp[i-1][2] - prices[i] // 昨天刚卖了股票,今天买股票 ************ 根据题目描述,这种状态不合法,代码里面去除掉即可 +) + +dp[i][2] = max ( + dp[i-1][1] + prices[i] // 昨天有股票,今天卖股票 +) + +最后答案是max (dp[n-1][0], dp[n-1][2]) (因为最后一天肯定手上不持有股票才可能是利润最大的) + + */ + +class Solution { + public int maxProfit(int[] prices) { + if ((prices == null) || (prices.length == 0)) { + return 0; + } + + long dp0 = 0; + long dp1 = -prices[0]; + long dp2 = Integer.MIN_VALUE; + + long max0, max1, max2; + for (int i = 1; i < prices.length; i++) { + max0 = Math.max(dp2, dp0); + max1 = Math.max(dp1, dp0 - prices[i]); + max2 = dp1 + prices[i]; + + dp0 = max0; dp1 = max1; dp2 = max2; + } + + return (int)Math.max(dp0, dp2); + } + + public static void main(String[] args) { + System.out.println(new Solution().maxProfit(new int[] {1,2,3,0,2})); + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_312_063.java b/Week 05/id_063/Leetcode_312_063.java new file mode 100644 index 000000000..e7e17a001 --- /dev/null +++ b/Week 05/id_063/Leetcode_312_063.java @@ -0,0 +1,64 @@ +/* +DP 求解 + +设dp(i, j) 是戳破下标从i到j的所有气球问题的最大收益 +这个最大收益对应的戳气球方案中最后一个戳的位置为k, 那 +k左边所有气球和k右边所有气球的戳法一定是最大收益的戳法 +才能使得当前的戳法是最优的,这个就是本题最优子结构 + +枚举i, j之间所有可能的k, 每一个k都能算出来一个候选的最优戳法,计算过程又可以 +利用已经计算出的子问题的结果进行加速,所有候选中收益最大的就是当前的最优解 +一路递推就可以推出全局最优解 + + +注意最后戳破k, 那么i-1 和 j+1位置的数会成为k位置数的最后的左右边界 + +dp(i, j) = max { + dp(i, k-1) + nums[k]*nums[i-1]*nums[j+1] + dp(k, j) (k = i,i+1,......j) +} + */ + + + +class Solution { + public int maxCoins(int[] nums) { + if (nums.length == 0) { + return 0; + } + + int len = nums.length; + int[][] dp = new int[len][len]; + + for (int i = len-1 ; i >=0; i--) { + for (int j = i; j < len; j++) { + if (j == i) { + dp[i][j] = nums[i]; + dp[i][j] *= (i-1 >= 0) ? nums[i-1] : 1; + dp[i][j] *= (i+1 < len) ? nums[i+1] : 1; + continue; + } + + int maxVal = Integer.MIN_VALUE; + int leftVal = (i-1 >= 0) ? nums[i-1] : 1; + int rightVal = (j+1 < len) ? nums[j+1] : 1; + for (int k = i; k <= j; k++) { + if (k == i) { + maxVal = Math.max(leftVal * nums[k] * rightVal + dp[k+1][j], maxVal); + } else if (k == j) { + maxVal = Math.max(dp[i][k-1] + leftVal * nums[k] * rightVal, maxVal); + } else { + maxVal = Math.max(dp[i][k - 1] + leftVal * nums[k] * rightVal + dp[k + 1][j], maxVal); + } + } + + dp[i][j] = maxVal; + } + } + + return dp[0][len-1]; + } + + public static void main(String[] args) { + System.out.println(new Solution().maxCoins(new int[]{3,1,5,8})); + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_322_063.java b/Week 05/id_063/Leetcode_322_063.java new file mode 100644 index 000000000..04d3e270f --- /dev/null +++ b/Week 05/id_063/Leetcode_322_063.java @@ -0,0 +1,36 @@ +/* +思路 + +动态规划 + +dp[i] 表示i块钱最少的硬币数 +最后一个硬币只可能是coin中n中硬币面值其中一种 +dp[i] = min ( dp[i-coin[0]], dp[i-coin[1]] ...... dp[i- coin[len-1]] ) + 1 + + */ + +class Solution { + public int coinChange(int[] coins, int amount) { + int[] dp = new int[amount + 1]; + + for (int i = 0; i <= amount; i++) { + if (i == 0) { + dp[i] = 0; + continue; + } + + int min = Integer.MAX_VALUE; + for (int j = 0; j < coins.length; j++) { + if (i >= coins[j]) { + if (dp[i - coins[j]] != -1) { + min = Math.min(dp[i - coins[j]], min); + } + } + } + + dp[i] = (min == Integer.MAX_VALUE) ? -1 : min + 1; + } + + return dp[amount]; + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_32_063.java b/Week 05/id_063/Leetcode_32_063.java new file mode 100644 index 000000000..1e0005845 --- /dev/null +++ b/Week 05/id_063/Leetcode_32_063.java @@ -0,0 +1,67 @@ +/* + +思路 +动态规划求解 +合法子串有一个性质,就是多个合法子串连续穿起来也是合法子串,且每一个子串都以右括号结尾 + +dp[i]表示以i结尾的合法子串最长长度 +看i和i前面字符,如果是()形式,i结尾的最长子字符串就是i-1结尾的最长子字符串加上后面两个字符 +如果是))形式,如果i结尾的合法子串存在,i就必须找一个左括号来匹配,这个匹配位置肯定在一个i-1位置 +结尾的合法子串的前面,但是如果这个i-i结尾的子串不是最长的,那它前面肯定还有一个合法子串紧跟在它前面 +所以它前面一个字符一定是右括号,所以要找这个匹配位置,只可能在i-i结尾的最长子串的前面才可能找到 + +所以可以总结递推关系如下 +最后两个字符是()形式 + dp[i] = dp[i-2] + 2 +最后两个字符是))形式 + i-1 - dp[i-1] 位置是左括号 + dp[i] = dp[i-1] + 2 + dp[i-2 - dp[i-1]]; + + +其他 + dp[i] = 0 + + + */ + +class Solution { + public int longestValidParentheses(String s) { + int len = s.length(); + if (len == 0 || len == 1) { + return 0; + } + + int[] dp = new int[s.length()]; + + dp[0] = 0; dp[1] = (s.charAt(0) == '(' && s.charAt(1) == ')') ? 2 : 0; + int max = Math.max(dp[0], dp[1]); + for (int i = 2; i < s.length(); i++) { + if (s.charAt(i) != ')') { + dp[i] = 0; + continue; + } + + if (s.charAt(i-1) == '(') { + dp[i] = dp[i-2] + 2; + } else { + if (i-1-dp[i-1] >= 0 && s.charAt(i-1-dp[i-1]) == '(') { + dp[i] = dp[i-1] + 2; + if (i-2-dp[i-1] >= 0) { + // 还要加上前面的一串 + dp[i] += dp[i-2-dp[i-1]]; + } + } else { + dp[i] = 0; + } + } + + max = Math.max(dp[i], max); + } + + return max; + } + + public static void main(String[] args) { + System.out.println(new Solution().longestValidParentheses("()(())")); + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_363_063.java b/Week 05/id_063/Leetcode_363_063.java new file mode 100644 index 000000000..48e2f4b8e --- /dev/null +++ b/Week 05/id_063/Leetcode_363_063.java @@ -0,0 +1,65 @@ +/* + +思路 +每次迭代求以第i层为底,第j层为顶的矩形中和不超过k的最大和 +所有i j组合计算出来的最大值就是最后答案 + +对于一组i j的计算过程可以抽象成求取一维数组中和不超过K的连续子数组的 +和的最大值 + + + */ + + +class Solution { + + + private int maxSubSum(int[] nums, int k) { + int maxVal = Integer.MIN_VALUE; + for (int i = 0; i < nums.length; i++) { + int sum = 0; + for (int j = i; j < nums.length; j++) { + sum += nums[j]; + if (sum < k) { + maxVal = Math.max(maxVal, sum); + } else { + if (sum == k) { + return k; + } + } + } + } + + return maxVal; + } + + public int maxSumSubmatrix(int[][] matrix, int k) { + if ((matrix.length == 0) || (matrix[0].length == 0)) { + return 0; + } + + int rowLen = matrix.length; + int colLen = matrix[0].length; + int[] nums = new int[colLen]; + int maxVal = Integer.MIN_VALUE; + + for (int top_i = 0; top_i < rowLen; top_i++) { + for (int jj = 0; jj < colLen; jj++) { + nums[jj] = 0; + } + + for (int bottom_i = top_i; bottom_i < rowLen; bottom_i++) { + for (int jj = 0; jj < colLen; jj++) { + nums[jj] += matrix[bottom_i][jj]; + } + + maxVal = Math.max(maxVal, maxSubSum(nums, k)); + if (maxVal == k) { + break; + } + } + } + + return maxVal; + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_403_063_solution1.java b/Week 05/id_063/Leetcode_403_063_solution1.java new file mode 100644 index 000000000..77821b78a --- /dev/null +++ b/Week 05/id_063/Leetcode_403_063_solution1.java @@ -0,0 +1,103 @@ +package problem403.Solution1; + + +/* +树形DP求解 + +设dp(i, j) 表示从某位置跳跃j步到达位置为i的位置是否可能 + +dp(i, j) 对于一系列其他的dp状态呈树形依赖关系 +dp(i, j) = dp(i-j, j-1) || dp(i-j, j+1) || dp(i-j, j) + +如果i是不存在的位置,dp(i, j)为false + +basecase 如下 +i是第一个位置情况下,dp(i, 0) = true , 对于其余j dp(i, j) = false +i位置是第二个石头位置,j只能为1 + +递归进行树型的递推,basecase是回溯的方式 +速度稍慢,有迭代式的DP解法 + + */ + + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +class Solution { + + private Map cache; + private Set stonePos; + private int firstStone; + private int secondStone; + + private long getKey(int i, int j) { + return (((long)(i)) << 32) + j; + } + + private boolean isValid(int i, int j) { + //System.out.println(i + ", " + j); + if (!stonePos.contains(i)) { + return false; + } + + if (i == firstStone) { + return j == 0; + } + + if (i == secondStone) { + return j == 1; + } + + if (j <= 0) { + return false; + } + + long key = getKey(i, j); + if (cache.containsKey(key)) { + return cache.get(key); + } + + boolean result = isValid(i - j, j - 1) || isValid(i - j, j) || isValid(i - j, j + 1); + cache.put(key, result); + + return result; + } + + public boolean canCross(int[] stones) { + if (stones.length == 1) { + return true; + } + + if (!((stones[0] == 0) && (stones[1] == 1))) { + return false; + } + + cache = new HashMap<>(); + stonePos = new HashSet<>(); + + firstStone = stones[0]; + secondStone = stones[1]; + + for (int pos : stones) { + stonePos.add(pos); + } + + int lastPos = stones[stones.length - 1]; + for (int pos : stones) { + if (isValid(lastPos, lastPos - pos)) { + return true; + } + } + + return false; + } + + public static void main(String[] args) { + System.out.println(new Solution().canCross(new int[]{0,1,3,4,5,7,9,10,12})); + } +} + + diff --git a/Week 05/id_063/Leetcode_403_063_solution2.java b/Week 05/id_063/Leetcode_403_063_solution2.java new file mode 100644 index 000000000..f8ae4487e --- /dev/null +++ b/Week 05/id_063/Leetcode_403_063_solution2.java @@ -0,0 +1,56 @@ +/* +DP算法解决 + dp[i] 表示所有能够到下标为i的位置的可能上一跳的跳数的集合 + dp[i] 依赖于dp[i-1], dp[i-2] .... 只要j位置有可能跳到i, 计算出dp[j]的时候 + dp[i] 的值就可以被更新,因此如果从左到右进行迭代,当前如果迭代位置是i, 所有 + dp[i-1], dp[i-2]..... 等计算时候,dp[i]的值就已经被更新完了,不需要计算,但是 + dp[i]需要更新dp[i+1], dp[i+2] ..... 的数值 + 到最后,只需要看dp[stones.length - 1] 是不是空集就行了 +*/ + + +import java.util.*; + +public class Solution { + public boolean canCross(int[] stones) { + if (stones.length == 1) { + return true; + } + + if (!((stones[0] == 0) && (stones[1] == 1))) { + return false; + } + + List[] dp = new List[stones.length]; + Map pos2idx = new HashMap<>(); + for (int i = 0; i < stones.length; i++) { + dp[i] = new ArrayList(); + } + + Set stonePos = new HashSet<>(); + for (int i = 0; i < stones.length; i++) { + stonePos.add(stones[i]); + pos2idx.put(stones[i], i); + } + + dp[1].add(1); + for (int i = 0; i < stones.length - 1; i++) { + Set steps = new HashSet<>(); + for (int step : dp[i]) { + if (step - 1 > 0) { + steps.add(step - 1); + } + steps.add(step + 1); + steps.add(step); + } + + for (int step : steps) { + if (stonePos.contains(stones[i] + step)) { + dp[pos2idx.get(stones[i] + step)].add(step); + } + } + } + + return !(dp[stones.length-1].isEmpty()); + } +} diff --git a/Week 05/id_063/Leetcode_410_063.java b/Week 05/id_063/Leetcode_410_063.java new file mode 100644 index 000000000..a4aa4ec34 --- /dev/null +++ b/Week 05/id_063/Leetcode_410_063.java @@ -0,0 +1,53 @@ +/* + +Dp算法求解 + +dp(i, j) 表示下标为i或者i之前的所有数字分成j组,所有分法中每组数据总和的最大值的下界 +dp(i, j) = min( max( dp(i-1, j-1), nums[j] ) + max( dp(i-2, j-1), nums[j] + nums[j-1] ) + max( dp(i-3, j-1), nums[j] + nums[j-1] + nums[j-2] ) + ...... + ) + +注意只有j<=i+1时候dp(i, j) 才有意义 + */ + +public class Solution { + public int splitArray(int[] nums, int m) { + if (nums.length < m || nums.length == 0) { + return 0; + } + + int[] dp = new int[nums.length]; + + // j = 1 时候初始化第一列, 数组只分一组 + int sum = 0; + for (int i = 0; i < nums.length; i++) { + sum += nums[i]; + dp[i] = sum; + } + + for (int j = 2; j <= m; j++) { + for (int i = nums.length-1; i >= 0; i--) { + if (j > (i+1)) { + dp[i] = -1; // 无意义数值 + continue; + } + + sum = 0; + int minVal = Integer.MAX_VALUE; + for (int k = 1; j-1 <= i-k+1; k++) { + sum += nums[i-k+1]; + minVal = Math.min(minVal, Math.max(dp[i-k], sum)); + } + dp[i] = minVal; + } + } + + return dp[nums.length - 1]; + } + + public static void main(String[] args) { + System.out.println(new Solution().splitArray(new int[] {1,4,4}, 3)); + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_45_063.java b/Week 05/id_063/Leetcode_45_063.java new file mode 100644 index 000000000..de6ff6191 --- /dev/null +++ b/Week 05/id_063/Leetcode_45_063.java @@ -0,0 +1,35 @@ +/* + +思路 +先用dp算法计算每一个位置之前所有节点中能够到达后续节点最远的位置,得到maxLen数组 +题目说了最后一定可以到终点 +所以每跳一步,都让可达的范围尽量远,最后跳的步数就是最少的 + +*/ + +class Solution { + public int jump(int[] nums) { + int[] maxLen = new int[nums.length]; // maxLen[i]表示前i个元素能跳到最远的位置 + + maxLen[0] = nums[0]; + for (int i = 1; i < nums.length; i++) { + maxLen[i] = Math.max(maxLen[i-1], i + nums[i]); + } + + int step = 0; // 当前能跳到的最远的位置的最少步数 + int i = 0; + + while (true) { + if (i >= nums.length-1) { + return step; + } + + i = maxLen[i]; + step++; + } + } + + public static void main(String[] args) { + new Solution().jump(new int[] {1,1,1,1}); + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_518_063.java b/Week 05/id_063/Leetcode_518_063.java new file mode 100644 index 000000000..d5e3c7bf6 --- /dev/null +++ b/Week 05/id_063/Leetcode_518_063.java @@ -0,0 +1,42 @@ +/* +求和为coins的组合数 +设dp[i][j]为前i种数字能凑出的和为j的组合数量 + +dp[i][j] = dp[i-1][j] + dp[i-1][j-coins[i]] + dp[i-1][j-coins[i]*2] ........ + +dp[i][j]在计算时候可以利用dp[i][j-coins[i]]进行加速 + */ + + +class Solution { + public int change(int amount, int[] coins) { + if (coins.length == 0) { + return amount == 0 ? 1 : 0; + } + + int[] dp_new = new int[amount + 1]; + int[] dp_old = new int[amount + 1]; + + //i = 0初始化 + for (int j = 0; j <= amount; j++) { + dp_old[j] = (j % coins[0] == 0) ? 1 : 0; + } + + for (int i = 1; i < coins.length; i++) { + for (int j = 0; j <= amount; j++) { + dp_new[j] = dp_old[j]; + if (j >= coins[i]) { + dp_new[j] += dp_new[j - coins[i]]; + } + } + + int[] tmp = dp_old; dp_old = dp_new; dp_new = tmp; + } + + return dp_old[amount]; + } + + public static void main(String[] args) { + System.out.println(new Solution().change(5, new int[] {1,2,5})); + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_53_063.java b/Week 05/id_063/Leetcode_53_063.java new file mode 100644 index 000000000..7b48616aa --- /dev/null +++ b/Week 05/id_063/Leetcode_53_063.java @@ -0,0 +1,31 @@ +/* +思路 +DP 算法解决 +dp[i]表示以i位置结束的最大和子数组的和的数值 +显然有: + +dp[i-1]负数, + dp[i] = nums[i], i位置自己作为一个子数组 +dp[i-1]非负 + dp[i] = nums[i] + dp[i-1], i位置根前面连一起组成子数组 + +最后找所有dp[i]中最大值 + + */ + +class Solution { + public int maxSubArray(int[] nums) { + if (nums.length == 0) { + return 0; + } + + int dp = nums[0]; + int maxVal = dp; + for (int i = 1; i < nums.length; i++) { + dp = (dp >= 0) ? dp + nums[i] : nums[i]; + maxVal = Math.max(maxVal, dp); + } + + return maxVal; + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_552_063.java b/Week 05/id_063/Leetcode_552_063.java new file mode 100644 index 000000000..bc1b207b5 --- /dev/null +++ b/Week 05/id_063/Leetcode_552_063.java @@ -0,0 +1,53 @@ +/* + +Dp算法解决 + +定义dp(i, j, k)为长度为i, 是否包含A(j=1包含,j=0不包含), 是否以L结尾(k=0不是以L结尾,k=1是以L结尾)的合法字符串个数 + +最后结果是dp(n,0,0) + dp(n,0,1) + dp(n,1,0) + dp(n,1,1) + +考虑最后结尾的字符的不同情况,可以推导下面的递推公式 + + +dp(i, 0, 0) = dp(i-1, 0, 0或1) 前i-1个合法,第个是P +dp(i, 1, 0) = dp(i-1, 0, 0或1) 前i-1个合法,前i-1个不包含A,第i个是A + + dp(i-1, 1, 0或1) 前i-1个合法,第i个是P +dp(i, 0, 1) = dp(i-2, 0, 0) 前i-2个合法且没有A, i-1 和 i两个字符是LL + + dp(i-2, 0, 0或者1) 前i-2个合法且没有A, i-1 和 i两个字符是PL + +dp(i, 1, 1) = dp(i-2, 1, 0) 前i-2个合法有一个A且不能L结尾,最后两个字符是LL + + dp(i-2, 1, 0或1) 前i-2个有一个A,最后两个字符是PL + + dp(i-2,0, 0或1) 前i-2个没有A,最后两个字符是AL + + */ + + + +class Solution { + public int checkRecord(int n) { + long[][][] dp = new long[n+1][2][2]; + dp[0][0][0] = 1; + dp[0][1][0] = 0; + dp[0][0][1] = 0; + dp[0][1][1] = 0; + + dp[1][0][0] = 1; + dp[1][1][0] = 1; + dp[1][0][1] = 1; + dp[1][1][1] = 0; + + for (int i = 2; i <= n; i++) { + dp[i][0][0] = (dp[i-1][0][0] + dp[i-1][0][1]) % 1000000007; + dp[i][1][0] = (dp[i-1][0][0] + dp[i-1][0][1] + dp[i-1][1][0] + dp[i-1][1][1]) % 1000000007; + dp[i][0][1] = (dp[i-2][0][0] + dp[i-2][0][0] + dp[i-2][0][1]) % 1000000007; + dp[i][1][1] = (dp[i-2][1][0] + dp[i-2][1][0] + dp[i-2][1][1] + + dp[i-2][0][0] + dp[i-2][0][1]) % 1000000007; + } + + return (int)((dp[n][0][0] + dp[n][1][0] + dp[n][0][1] + dp[n][1][1]) % 1000000007); + } + + public static void main(String[] args) { + System.out.println(new Solution().checkRecord(2)); + } +} diff --git a/Week 05/id_063/Leetcode_55_063.java b/Week 05/id_063/Leetcode_55_063.java new file mode 100644 index 000000000..cfd6ec68a --- /dev/null +++ b/Week 05/id_063/Leetcode_55_063.java @@ -0,0 +1,35 @@ +/* +DP算法 + +dp[i]表示前i个位置都经过之后能够覆盖到的最远距离 +dp[i] = max (dp[i-1], i + nums[i]) + + + */ + +class Solution { + public boolean canJump(int[] nums) { + if (nums.length == 0) { + return true; + } + + int dp = nums[0]; + for (int i = 0; i < nums.length; i++) { + if (dp >= nums.length - 1) { + return true; + } + + if (i > dp) { + return false; + } + + dp = Math.max(dp, i + nums[i]); + } + + return false; + } + + public static void main(String[] args) { + System.out.println(new Solution().canJump(new int[] {2,3,1,1,4})); + } +} diff --git a/Week 05/id_063/Leetcode_621_063.java b/Week 05/id_063/Leetcode_621_063.java new file mode 100644 index 000000000..4b9b60802 --- /dev/null +++ b/Week 05/id_063/Leetcode_621_063.java @@ -0,0 +1,92 @@ +/* + +思路 +把每n+1次选择想象成一轮,当前一轮选择肯定是每种任务选一个,如果任务种类数不够,就 +用气泡填充,直到有一轮选择中途导致所有任务选择完,这一轮就是结束的一轮,且步数是最少 +的,所以每次应该选剩余量最多的任务,够n种就选n个,否则填气泡,除了最后一轮外, +每一轮产生n+1步数开销是不可能节省的 + + + */ + +import java.util.*; + +class Solution { + public int leastInterval(char[] tasks, int n) { + TreeMap cnt = new TreeMap<>(new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + }); // key 为剩余数量,value为剩余该数量的任务种数 + + int[] cntArr = new int[26]; + + for (char ch : tasks) { + cntArr[ch-'A']++; + } + + for (int i : cntArr) { + if (i != 0) { + cnt.putIfAbsent(i, 0); + cnt.put(i, cnt.get(i) + 1); + } + } + + int step = 0; + while (true) { + Map tmp = new HashMap<>(); + // 循环完成一轮选择 + int chooseCnt = n+1; + for (int taskNum : cnt.keySet()) { + if (cnt.get(taskNum) >= chooseCnt) { + cnt.put(taskNum, cnt.get(taskNum) - chooseCnt); + if (taskNum > 1) { + tmp.put(taskNum - 1, chooseCnt); + } + chooseCnt = 0; + } else { + chooseCnt -= cnt.get(taskNum); + if (taskNum > 1) { + tmp.put(taskNum - 1, cnt.get(taskNum)); + } + + cnt.put(taskNum, 0); + } + + if (chooseCnt == 0) { + break; + } + } + + for (int taskNum : tmp.keySet()) { + cnt.putIfAbsent(taskNum, 0); + cnt.put(taskNum, cnt.get(taskNum) + tmp.get(taskNum)); + } + + List zeroList = new ArrayList<>(); + for (int taskNum : cnt.keySet()) { + if (cnt.get(taskNum) == 0) { + zeroList.add(taskNum); + } + } + + for (int taskNum : zeroList) { + cnt.remove(taskNum); + } + + if (!cnt.isEmpty()) { + step += (n+1); + } else { + step += ((n+1) - chooseCnt); + break; + } + } + + return step; + } + + public static void main(String[] args) { + System.out.println(new Solution().leastInterval(new char[] {'A','A','A','B','B','B'}, 0)); + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_647_063.java b/Week 05/id_063/Leetcode_647_063.java new file mode 100644 index 000000000..1aab82519 --- /dev/null +++ b/Week 05/id_063/Leetcode_647_063.java @@ -0,0 +1,63 @@ +/* + +思路 +动态规划递推 + +dp[i][j] 表示i开始j结尾的字符串是不是回文 + +递推关系如下 + +j == i+1: + dp[i][j] = s[i] == s[j] ? true : false + +j < i + dp[i][j] = false + +j > i+1 : + dp[i][j] = s[i] == s[j] ? dp[i+1][j-1] : false + + + */ + + + +class Solution { + public int countSubstrings(String s) { + if (s.length() == 0) { + return 0; + } + + int len = s.length(); + + boolean[] dp_old = new boolean[len]; + boolean[] dp_new = new boolean[len]; + + //i = s.length() - 1 时候初始化dp数值 + dp_old[len-1] = true; + + int cnt = 1; + for (int i = s.length() - 2; i >= 0; i--) { + for (int j = i ; j < len; j++) { + if (j == i) { + dp_new[j] = true; + } else if (j == i+1) { + dp_new[j] = (s.charAt(i) == s.charAt(j)); + } else { + dp_new[j] = (s.charAt(i) == s.charAt(j)) && dp_old[j-1]; + } + + if (dp_new[j]) { + cnt++; + } + } + + boolean[] tmp = dp_old; dp_old = dp_new; dp_new = tmp; + } + + return cnt; + } + + public static void main(String[] args) { + System.out.println(new Solution().countSubstrings("aaa")); + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_64_063.java b/Week 05/id_063/Leetcode_64_063.java new file mode 100644 index 000000000..c1d99be31 --- /dev/null +++ b/Week 05/id_063/Leetcode_64_063.java @@ -0,0 +1,39 @@ + +/* + +动态规划 + +dp[i][j]表示从0,0位置到i,j 位置的最小路径和 + +dp[i][j] = min(dp[i][j-1], dp[i-1][j]) + gird[i][j] + */ + + +public class Solution { + public int minPathSum(int[][] grid) { + if (grid.length == 0) { + return 0; + } + + int len = grid[0].length; + int[] old_dp = new int[len]; + int[] new_dp = new int[len]; + + // 初始化第0行数据 + old_dp[0] = grid[0][0]; + for (int j = 1; j < len; j++) { + old_dp[j] = old_dp[j-1] + grid[0][j]; + } + + for (int i = 1; i < grid.length; i++) { + new_dp[0] = old_dp[0] + grid[i][0]; + for (int j = 1; j < len; j++) { + new_dp[j] = Math.min(old_dp[j], new_dp[j-1]) + grid[i][j]; + } + + int[] tmp = old_dp; old_dp = new_dp; new_dp = tmp; + } + + return old_dp[len-1]; + } +} diff --git a/Week 05/id_063/Leetcode_70_063.java b/Week 05/id_063/Leetcode_70_063.java new file mode 100644 index 000000000..e97e885e6 --- /dev/null +++ b/Week 05/id_063/Leetcode_70_063.java @@ -0,0 +1,30 @@ +/* + 思路 DP算法解决 + + f(n) 表示n个台阶的爬法,可以通过前面的f(n-1) 和 f(n-2)推导出来 + 最后一步要么走两步要么走一步,所以有简单递推公式如下 + f(n) = f(n-1) + f(n-2) +*/ + +public class Solution { + public int climbStairs(int n) { + int fn, fn_1, fn_2; + + if (n == 1) { + return 1; + } else if (n == 2) { + return 2; + } + + fn_1 = 2; + fn_2 = 1; + fn = 0; + for (int i = 3; i <= n; i++) { + fn = fn_1 + fn_2; + fn_2 = fn_1; + fn_1 = fn; + } + + return fn; + } +} diff --git a/Week 05/id_063/Leetcode_714_063.java b/Week 05/id_063/Leetcode_714_063.java new file mode 100644 index 000000000..418e69795 --- /dev/null +++ b/Week 05/id_063/Leetcode_714_063.java @@ -0,0 +1,42 @@ +/* +思路 + +dp[i][0] 表示第i天不持有股票的最大利润 +dp[i][1] 表示第i天持有股票的最大利润 + +dp[i][0] = max ( + dp[i-1][1] + prices[i] -fee // 昨天为止持有股票,今天卖股票 + dp[i-1][0] // 昨天为止没股票,今天什么都不干 +) + +dp[i][1] = max ( + dp[i-1][1], // 昨天为止有股票,今天什么都不干 + dp[i-1][0] - prices[i] // 昨天为止没股票,今天买股票 +) + +最后答案是dp[n-1][0] (因为最后一天肯定手上不持有股票才可能是利润最大的) + + */ + +class Solution { + public int maxProfit(int[] prices, int fee) { + if ((prices == null) || (prices.length == 0)) { + return 0; + } + + int dp0 = 0; + int dp1 = -prices[0]; + int max0, max1; + for (int i = 1; i < prices.length; i++) { + max0 = Math.max(dp1 + prices[i] - fee, dp0); + max1 = Math.max(dp1, dp0 - prices[i]); + dp0 = max0; dp1 = max1; + } + + return dp0; + } + + public static void main(String[] args) { + System.out.println(new Solution().maxProfit(new int[] {1,3,2,8,4,9}, 2)); + } +} diff --git a/Week 05/id_063/Leetcode_72_063.java b/Week 05/id_063/Leetcode_72_063.java new file mode 100644 index 000000000..1476e6908 --- /dev/null +++ b/Week 05/id_063/Leetcode_72_063.java @@ -0,0 +1,63 @@ +/* +思路 + +将状态dp[i][j]定义为word1前0-i下标字符组成的子串转换成 +word2 0-j下标字符组成的子串需要的最少步骤 + +如果i和j位置上字符是相同的,那么dp[i][j] = dp[i-1][j-1] +如果i j 字符不同 + 有几种操作: + 把 i 字符换成 j 字符,word1[0:i-1] 再转换成 word2[0:j-1] 最少操作 1 + dp[i-1][j-1]次 + 把 i 字符删掉 word1[0:i-1] 再转换成 word2[0:j] 最少操作 1 + dp[i-1][j] 次 + 把 i 位置后面加一个j字符,word1[0:i] 再转换成 word2[0:j-1], 最少操作 1 + dp[i][j-1]次 + 三种里面取最少的一个,进行递推 + */ + +class Solution { + public int minDistance(String word1, String word2) { + if ((word1.length() == 0) || (word2.length() == 0)) { + return Math.max(word1.length(), word2.length()); + } + + int[] dp_old = new int[word2.length()]; + int[] dp_new = new int[word2.length()]; + + for (int i = 0; i < word1.length(); i++) { + if (i == 0) { + boolean flag = false; // 标志word1.charAt(0)在word2出现没有 + for (int j = 0; j < word2.length(); j++) { + if (word2.charAt(j) == word1.charAt(0)) { + flag = true; + } + + dp_new[j] = (flag) ? j : j+1; + } + } else { + boolean flag = false; + for (int ii = 0; ii <= i; ii++) { + if (word1.charAt(ii) == word2.charAt(0)) { + flag = true; + break; + } + } + dp_new[0] = (flag) ? i : i+1; + + for (int j = 1; j < word2.length(); j++) { + if (word1.charAt(i) == word2.charAt(j)) { + dp_new[j] = dp_old[j-1]; + } else { + dp_new[j] = Math.min(dp_old[j-1], dp_old[j]); + dp_new[j] = Math.min(dp_new[j-1], dp_new[j]); + dp_new[j] = dp_new[j] + 1; + } + } + } + + int[] dp_tmp = dp_old; + dp_old = dp_new; + dp_new = dp_tmp; + } + + return dp_old[word2.length()-1]; + } +} diff --git a/Week 05/id_063/Leetcode_76_063.java b/Week 05/id_063/Leetcode_76_063.java new file mode 100644 index 000000000..05e5bb813 --- /dev/null +++ b/Week 05/id_063/Leetcode_76_063.java @@ -0,0 +1,119 @@ +/* + +思路 +用i j 两个位置枚举区间的上界和下界,只有刚好满足字符计数要求的 +区间才有可能是最短的,所以在能够满足计数要求情况下,尽量将左边界 +右移,移动不了时候,扩展右边界,如果扩展右边界能够增加当前左边界位置 +的字符的数量,左边界就有机会右移,这样两个边界一直更新,直到左右边界都 +不能动为止,中间所有出现的符合计数要求的区间中长度最小值就是最终 +的答案,时间复杂度是线性的,比起枚举所有的子区间要快很多,因为 +计算过程中利用了当前区间的字符计数信息,避免了大量的重复统计计算 + + */ + + +import java.util.HashMap; +import java.util.Map; + +class Solution { + + // 判定当前持有的字符数量是否充足 + private boolean isCharEnough(Map a, Map b) { + for (char ch : b.keySet()) { + if (!a.containsKey(ch)) { + return false; + } + + if (a.get(ch) < b.get(ch)) { + return false; + } + } + + return true; + } + + public String minWindow(String s, String t) { + if (s.length() < t.length()) { + return ""; + } + + Map chrCnt = new HashMap<>(); + for (int i = 0; i < t.length(); i++) { + chrCnt.putIfAbsent(t.charAt(i), 0); + chrCnt.put(t.charAt(i), chrCnt.get(t.charAt(i)) + 1); + } + + Map state = new HashMap<>(); + int rightEnd = -1; + for (int i = 0; i < s.length(); i++) { + if (!chrCnt.containsKey(s.charAt(i))) { + continue; + } + + state.putIfAbsent(s.charAt(i), 0); + state.put(s.charAt(i), state.get(s.charAt(i)) + 1); + + if (isCharEnough(state, chrCnt)) { + rightEnd = i; + break; + } + } + + if (rightEnd == -1) { + return ""; + } + + int i = 0, j = rightEnd; + int minLen = (j-i+1); + int result_i = i, result_j = j; + while (true) { + // 移动左边界 + while (true) { + char ch = s.charAt(i); + if (!chrCnt.containsKey(ch)) { + i++; + } else if (state.get(ch) > chrCnt.get(ch)) { + i++; + state.put(ch, state.get(ch) - 1); + } else { + break; + } + } + + if ((j - i + 1) < minLen) { + result_i = i; result_j = j; minLen = (j - i +1); + } + + // 移动右边界 + while(true) { + if (j+1 == s.length()) { + j = s.length(); + break; + } + + char ch = s.charAt(j+1); + if (!chrCnt.containsKey(ch)) { + j++; + continue; + } else { + j++; + state.put(ch, state.get(ch) + 1); + if (ch == s.charAt(i)) { + // 只有增加了左边界所在的字符的数量,左边界才可能右移 + break; + } + } + } + + if (j == s.length()) { + break; + } + } + + return s.substring(result_i, result_j + 1); + } + + public static void main(String[] args) { + System.out.println(new Solution().minWindow("ADOBECODEBANC", "ABC")); + } +} diff --git a/Week 05/id_063/Leetcode_91_063.java b/Week 05/id_063/Leetcode_91_063.java new file mode 100644 index 000000000..9601c444b --- /dev/null +++ b/Week 05/id_063/Leetcode_91_063.java @@ -0,0 +1,45 @@ +/* + +DP 算法求解 +dp[i]表示i位置结尾的字符串的编码方式个数 +显然最后一个字符i要么最为一个两位数的最后一位,要么自己作为一个一位数 +num = (s[i-1]-'0')*10 + (i-'0') 在1到26之间,就可以组成两位数 + dp[i] = dp[i-2] + dp[i-1] +其他 + dp[i] = dp[i-1] + + + */ + +class Solution { + private int char2num(char ch1, char ch2) { + return (ch1 - '0') * 10 + (ch2 - '0'); + } + + public int numDecodings(String s) { + if (s.length() == 0) { + return 0; + } + + int[] dp = new int[s.length()]; + + dp[0] = (s.charAt(0) >='1' && s.charAt(0) <='9') ? 1 : 0; + for (int i = 1; i < s.length(); i++) { + dp[i] = (s.charAt(i) >= '1' && s.charAt(i) <= '9') ? dp[i-1] : 0; + + if (s.charAt(i-1) != '0') { + int num = char2num(s.charAt(i - 1), s.charAt(i)); + if ((num >= 1) && (num <= 26)) { + if (i >= 2) { + dp[i] += dp[i - 2]; + } else { + dp[i] += 1; + } + } + } + + } + + return dp[s.length()-1]; + } +} \ No newline at end of file diff --git a/Week 05/id_063/Leetcode_980_063.java b/Week 05/id_063/Leetcode_980_063.java new file mode 100644 index 000000000..a8b18f574 --- /dev/null +++ b/Week 05/id_063/Leetcode_980_063.java @@ -0,0 +1,137 @@ +/* + +dp(i,j,k)表示从开始位置,剩余未走的空格集合为全集 转变到 i,j 位置,剩余未走的空格集合为k的状态 的路径总数 +目标就是求dp(end_i, end_j, 空集) + + +dp(i, j, k) = dp (i-1, j, (k 并 (i,j))) + + dp (i+1, j, (k 并 (i,j))) + + dp (i, j-1, (k 并 (i,j))) + + dp (i, j+1, (k 并 (i,j))) + +如果(i, j) 就在k 集合中,这样的状态不存在,dp(i,j,k) = 0; +同样(i, j) 位置如果是障碍物,这样的状态也不存在,dp(i, j, k) = 0; +(i, j)位置不合法,状态也不存在,同样dp(i, j, k) = 0 + +因为第三维k 的递推没有规律,所以只能用记忆化方式进行递归的DP + +从终点往起点推状态,冗余计算比较多,速度慢 + + */ + +import java.util.HashMap; +import java.util.Map; + +class Solution { + + private int rowLen; + private int colLen; + int[][] gridData; + private Map[][] dp; + int start_i; + int start_j; + int emptyNum; // 空白的数量 + + // 坐标转换成整型数中的一位, 用于集合的表示 + private int pos2bit(int i, int j) { + return (1 << (i * colLen + j)); + } + + // 判断位置是否在集合中 + private boolean isInSet(int i, int j, int setVal) { + return ((setVal & pos2bit(i, j)) != 0); + } + + // 位置是否有效 + private boolean isPosValid(int i, int j) { + return (i >= 0) && (i < rowLen) && (j >= 0) && (j < colLen); + } + + int getPathNum(int i, int j, int setVal) { + //System.out.println(i + ", " + j + ", " + Integer.toBinaryString(setVal)); + + if (!isPosValid(i, j)) { + return 0; + } + + if (isInSet(i, j, setVal)) { + return 0; + } + + if (gridData[i][j] == -1) { + return 0; + } + + if ((i == start_i) && (j == start_j)) { + int cnt = 0; + while (setVal != 0) { + cnt++; + setVal &= (setVal - 1); + } + return cnt == emptyNum+1 ? 1 : 0; + } + + if ((dp[i][j] != null) && (dp[i][j].containsKey(setVal))) { + return dp[i][j].get(setVal); + } + + if (dp[i][j] == null) { + dp[i][j] = new HashMap<>(); + } + + int bitVal = pos2bit(i, j); + int sum = 0; + + if (!isInSet(i-1, j, setVal)) { + sum += getPathNum(i-1, j, setVal | bitVal); + } + + if (!isInSet(i+1, j, setVal)) { + sum += getPathNum(i+1, j, setVal | bitVal); + } + + if (!isInSet(i, j-1, setVal)) { + sum += getPathNum(i, j-1, setVal | bitVal); + } + + if (!isInSet(i, j+1, setVal)) { + sum += getPathNum(i, j+1, setVal | bitVal); + } + + dp[i][j].put(setVal, sum); + return sum; + } + + public int uniquePathsIII(int[][] grid) { + rowLen = grid.length; + colLen = grid[0].length; + gridData = grid; + dp = new Map[rowLen][colLen]; + + int end_i = 0, end_j = 0; + emptyNum = 0; + for (int i = 0; i < rowLen; i++) { + for (int j = 0; j < colLen; j++) { + if (grid[i][j] == 1) { + start_i = i; + start_j = j; + } else if (grid[i][j] == 2) { + end_i = i; + end_j = j; + } else if (grid[i][j] == 0) { + emptyNum++; + } + } + } + + return getPathNum(end_i, end_j, 0); + } + + public static void main(String[] args) { + System.out.println(new Solution().uniquePathsIII(new int[][] { + {1,0,0,0}, + {0,0,0,0}, + {0,0,2,-1} + })); + } +} diff --git a/Week 05/id_068/coin_change.cpp b/Week 05/id_068/coin_change.cpp new file mode 100644 index 000000000..b6bcffd60 --- /dev/null +++ b/Week 05/id_068/coin_change.cpp @@ -0,0 +1,18 @@ +class Solution { +public: + int coinChange(vector& coins, int amount) { + int dp[amount+1]; + for(int i = 0 ;i <= amount; i++){ + dp[i] = amount+1; + } + dp[0] = 0; + for(int i = 1 ; i <= amount; ++i) { + for(int j = 0; j < coins.size(); ++j) { + if(coins[j] <= i){ + dp[i] = min(dp[i], dp[i-coins[j]] + 1); + } + } + } + return dp[amount] > amount ? -1 : dp[amount]; + } +}; diff --git a/Week 05/id_068/lcs.cpp b/Week 05/id_068/lcs.cpp new file mode 100644 index 000000000..07e806ddc --- /dev/null +++ b/Week 05/id_068/lcs.cpp @@ -0,0 +1,20 @@ +class Solution { +public: + int longestCommonSubsequence(string text1, string text2) { + int m = text1.length(); + int n = text2.length(); + int dp[m+1][n+1] = {0}; + for(int i = 0; i <= m; ++i){ + for(int j = 0; j <= n; ++j) + dp[i][j] = 0; + } + for(int i = 1; i <= m; ++i){ + for(int j = 1; j <= n; ++j){ + if(text1[i] == text2[j]) + dp[i][j] = dp[i-1][j-1] +1; + else dp[i][j] = max(dp[i-1][j], dp[i][j]); + } + } + return dp[m][n] + } +}; diff --git a/Week 05/id_078/LeetCode_32_078.java b/Week 05/id_078/LeetCode_32_078.java new file mode 100644 index 000000000..4d5619d70 --- /dev/null +++ b/Week 05/id_078/LeetCode_32_078.java @@ -0,0 +1,38 @@ +//һֻ '(' ')' ַҳİЧŵӴijȡ +// +// ʾ 1: +// +// : "(()" +//: 2 +//: ЧӴΪ "()" +// +// +// ʾ 2: +// +// : ")()())" +//: 4 +//: ЧӴΪ "()()" +// +// Related Topics ַ ̬滮 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int longestValidParentheses(String s) { + int answer = 0; + int dp[] = new int[s.length()]; + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) == ')') { + if (s.charAt(i - 1) == '(') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + answer = Math.max(answer, dp[i]); + } + } + return answer; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_078/LeetCode_64_078.java b/Week 05/id_078/LeetCode_64_078.java new file mode 100644 index 000000000..b296e242e --- /dev/null +++ b/Week 05/id_078/LeetCode_64_078.java @@ -0,0 +1,39 @@ +//һǸ m x n ҳһϽǵ½ǵ·ʹ·ϵܺΪС +// +// ˵ÿֻ»ƶһ +// +// ʾ: +// +// : +//[ +//? [1,3,1], +// [1,5,1], +// [4,2,1] +//] +//: 7 +//: Ϊ· 13111 ܺС +// +// Related Topics ̬滮 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int minPathSum(int[][] grid) { + int[] dp = new int[grid[0].length]; + for (int i = grid.length - 1; i >= 0; i--) { + for (int j = grid[0].length - 1; j >= 0; j--) { + if(i == grid.length - 1 && j != grid[0].length - 1) + dp[j] = grid[i][j] + dp[j + 1]; + else if(j == grid[0].length - 1 && i != grid.length - 1) + dp[j] = grid[i][j] + dp[j]; + else if(j != grid[0].length - 1 && i != grid.length - 1) + dp[j] = grid[i][j] + Math.min(dp[j], dp[j + 1]); + else + dp[j] = grid[i][j]; + } + } + return dp[0]; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_083/Leetcode-221.java b/Week 05/id_083/Leetcode-221.java new file mode 100644 index 000000000..fd248f405 --- /dev/null +++ b/Week 05/id_083/Leetcode-221.java @@ -0,0 +1,72 @@ +/* + * @lc app=leetcode id=221 lang=java + * + * [221] Maximal Square + * + * https://leetcode.com/problems/maximal-square/description/ + * + * algorithms + * Medium (34.45%) + * Likes: 1782 + * Dislikes: 42 + * Total Accepted: 163.3K + * Total Submissions: 472.7K + * Testcase Example: '[["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]' + * + * Given a 2D binary matrix filled with 0's and 1's, find the largest square + * containing only 1's and return its area. + * + * Example: + * + * + * Input: + * + * 1 0 1 0 0 + * 1 0 1 1 1 + * 1 1 1 1 1 + * 1 0 0 1 0 + * + * Output: 4 + * + */ + +// @lc code=start +class Solution { + public int maximalSquare(char[][] matrix) { + // //一维动态规划 + // int rows = matrix.length, cols = rows > 0 ? matrix[0].length : 0; + // int[] dp = new int[cols + 1]; + // int maxsqlen = 0, prev = 0; + // for (int i = 1; i <= rows; i++) { + // for (int j = 1; j <= cols; j++) { + // int temp = dp[j]; + // if (matrix[i - 1][j - 1] == '1') { + // dp[j] = Math.min(Math.min(dp[j - 1], prev), dp[j]) + 1; + // maxsqlen = Math.max(maxsqlen, dp[j]); + // } else { + // dp[j] = 0; + // } + // prev = temp; + // } + // } + + + //二维动态规划 + int rows = matrix.length,cols=rows>0?matrix[0].length:0; + int [][]dp = new int[rows+1][cols+1]; + int maxsqlen =0; + for(int i=1;i<=rows;i++){ + for(int j=1;j<=cols;j++){ + if(matrix[i-1][j-1] == '1'){ + dp[i][j] = Math.min(Math.min(dp[i][j-1], dp[i-1][j]), dp[i-1][j-1]) +1; + maxsqlen = Math.max(maxsqlen, dp[i][j]); + } + } + } + + + return maxsqlen * maxsqlen; + } +} +// @lc code=end + diff --git a/Week 05/id_083/Leetcode-32.java b/Week 05/id_083/Leetcode-32.java new file mode 100644 index 000000000..96719ea64 --- /dev/null +++ b/Week 05/id_083/Leetcode-32.java @@ -0,0 +1,57 @@ +/* + * @lc app=leetcode id=32 lang=java + * + * [32] Longest Valid Parentheses + * + * https://leetcode.com/problems/longest-valid-parentheses/description/ + * + * algorithms + * Hard (26.59%) + * Likes: 2479 + * Dislikes: 109 + * Total Accepted: 228.8K + * Total Submissions: 856.5K + * Testcase Example: '"(()"' + * + * Given a string containing just the characters '(' and ')', find the length + * of the longest valid (well-formed) parentheses substring. + * + * Example 1: + * + * + * Input: "(()" + * Output: 2 + * Explanation: The longest valid parentheses substring is "()" + * + * + * Example 2: + * + * + * Input: ")()())" + * Output: 4 + * Explanation: The longest valid parentheses substring is "()()" + * + * + */ + +// @lc code=start +class Solution { + public int longestValidParentheses(String s) { + //使用动态规划的方法 + int maxans = 0; + int dp[] = new int[s.length()]; + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) == ')') { + if (s.charAt(i - 1) == '(') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + maxans = Math.max(maxans, dp[i]); + } + } + return maxans; + } +} +// @lc code=end + diff --git a/Week 05/id_083/Leetcode-363.java b/Week 05/id_083/Leetcode-363.java new file mode 100644 index 000000000..bef3dd2f1 --- /dev/null +++ b/Week 05/id_083/Leetcode-363.java @@ -0,0 +1,88 @@ +import java.util.TreeSet; + +/* + * @lc app=leetcode id=363 lang=java + * + * [363] Max Sum of Rectangle No Larger Than K + * + * https://leetcode.com/problems/max-sum-of-rectangle-no-larger-than-k/description/ + * + * algorithms + * Hard (35.73%) + * Likes: 515 + * Dislikes: 37 + * Total Accepted: 31.5K + * Total Submissions: 88K + * Testcase Example: '[[1,0,1],[0,-2,3]]\n2' + * + * Given a non-empty 2D matrix matrix and an integer k, find the max sum of a + * rectangle in the matrix such that its sum is no larger than k. + * + * Example: + * + * + * Input: matrix = [[1,0,1],[0,-2,3]], k = 2 + * Output: 2 + * Explanation: Because the sum of rectangle [[0, 1], [-2, 3]] is + * 2, + * and 2 is the max number no larger than k (k = 2). + * + * Note: + * + * + * The rectangle inside the matrix must have an area > 0. + * What if the number of rows is much larger than the number of columns? + * + */ + +// @lc code=start +class Solution { +/* first consider the situation matrix is 1D + we can save every sum of 0~i(0<=irow; + int res = Integer.MIN_VALUE; + for(int i = 0;i=0;j--){ + int val = 0; + TreeSet set = new TreeSet(); + set.add(0); + + //traverse every column/row and sum up + for(int k = 0;k> map = new HashMap<>(); + for(int i=0;i()); + } + map.get(0).add(0); + + for(int i=0;i0 && map.containsKey(stones[i] + step)){ + map.get(stones[i] + step).add(step); + } + } + } + } + return map.get(stones[stones.length-1]).size()>0; + } +} +// @lc code=end + diff --git a/Week 05/id_083/Leetcode-410.java b/Week 05/id_083/Leetcode-410.java new file mode 100644 index 000000000..0c2b0cf63 --- /dev/null +++ b/Week 05/id_083/Leetcode-410.java @@ -0,0 +1,85 @@ +/* + * @lc app=leetcode id=410 lang=java + * + * [410] Split Array Largest Sum + * + * https://leetcode.com/problems/split-array-largest-sum/description/ + * + * algorithms + * Hard (43.30%) + * Likes: 1139 + * Dislikes: 63 + * Total Accepted: 63.2K + * Total Submissions: 145.9K + * Testcase Example: '[7,2,5,10,8]\n2' + * + * Given an array which consists of non-negative integers and an integer m, you + * can split the array into m non-empty continuous subarrays. Write an + * algorithm to minimize the largest sum among these m subarrays. + * + * + * Note: + * If n is the length of array, assume the following constraints are + * satisfied: + * + * 1 ≤ n ≤ 1000 + * 1 ≤ m ≤ min(50, n) + * + * + * + * Examples: + * + * Input: + * nums = [7,2,5,10,8] + * m = 2 + * + * Output: + * 18 + * + * Explanation: + * There are four ways to split nums into two subarrays. + * The best way is to split it into [7,2,5] and [10,8], + * where the largest sum among the two subarrays is only 18. + * + * + */ + +// @lc code=start +class Solution { + public int splitArray(int[] nums, int m) { + //2019.11.15 二分搜索+贪心算法 + long left = 0,right = 0; + int n = nums.length; + for(int i=0;i> 1; + long sum = 0; + int cnt =1; + for(int i=0;i mid){ + cnt++; + sum = nums[i]; + }else{ + sum += nums[i]; + } + } + if(cnt <= m){ + ans = Math.min(ans, mid); + right = mid-1; + }else{ + left = mid + 1; + } + } + return (int)ans; + } +} +// @lc code=end + diff --git a/Week 05/id_083/Leetcode-552.java b/Week 05/id_083/Leetcode-552.java new file mode 100644 index 000000000..ac2647337 --- /dev/null +++ b/Week 05/id_083/Leetcode-552.java @@ -0,0 +1,105 @@ +/* + * @lc app=leetcode id=552 lang=java + * + * [552] Student Attendance Record II + * + * https://leetcode.com/problems/student-attendance-record-ii/description/ + * + * algorithms + * Hard (34.63%) + * Likes: 379 + * Dislikes: 74 + * Total Accepted: 16.3K + * Total Submissions: 47.1K + * Testcase Example: '1' + * + * Given a positive integer n, return the number of all possible attendance + * records with length n, which will be regarded as rewardable. The answer may + * be very large, return it after mod 10^9 + 7. + * + * A student attendance record is a string that only contains the following + * three characters: + * + * + * + * 'A' : Absent. + * 'L' : Late. + * ⁠'P' : Present. + * + * + * + * + * A record is regarded as rewardable if it doesn't contain more than one 'A' + * (absent) or more than two continuous 'L' (late). + * + * Example 1: + * + * Input: n = 2 + * Output: 8 + * Explanation: + * There are 8 records with length 2 will be regarded as rewardable: + * "PP" , "AP", "PA", "LP", "PL", "AL", "LA", "LL" + * Only "AA" won't be regarded as rewardable owing to more than one absent + * times. + * + * + * + * Note: + * The value of n won't exceed 100,000. + * + * + * + * + */ + +// @lc code=start + + //2019.11.15 recursion 时间复杂度O(2^n) + //f[n]=2f[n−1]−f[n−4] + // class Solution { + // int M=1000000007; + // public int checkRecord(int n) { + // int[] f =new int[n+1]; + // f[0]=1; + // for(int i=1;i<=n;i++) + // f[i]=func(i); + // int sum=func(n); + // for(int i=1;i<=n;i++){ + // sum+=(f[i-1]*f[n-i])%M; + // } + // return sum%M; + // } + // public int func(int n) + // { + // if(n==0) + // return 1; + // if(n==1) + // return 2; + // if(n==2) + // return 4; + // if(n==3) + // return 7; + // return (2*func(n-1) - func(n-4))%M; + // } + // } + + //DP 时间复杂度O(n) + class Solution { + long M = 1000000007; + public int checkRecord(int n) { + long[] f = new long[n <= 5 ? 6 : n + 1]; + f[0] = 1; + f[1] = 2; + f[2] = 4; + f[3] = 7; + for (int i = 4; i <= n; i++) + f[i] = ((2 * f[i - 1]) % M + (M - f[i - 4])) % M; + long sum = f[n]; + for (int i = 1; i <= n; i++) { + sum += (f[i - 1] * f[n - i]) % M; + } + return (int)(sum % M); + } + } +// @lc code=end + diff --git a/Week 05/id_083/Leetcode-621.java b/Week 05/id_083/Leetcode-621.java new file mode 100644 index 000000000..da593bb19 --- /dev/null +++ b/Week 05/id_083/Leetcode-621.java @@ -0,0 +1,103 @@ +import java.util.Arrays; + +/* + * @lc app=leetcode id=621 lang=java + * + * [621] Task Scheduler + * + * https://leetcode.com/problems/task-scheduler/description/ + * + * algorithms + * Medium (46.93%) + * Likes: 2121 + * Dislikes: 402 + * Total Accepted: 115.8K + * Total Submissions: 246.7K + * Testcase Example: '["A","A","A","B","B","B"]\n2' + * + * Given a char array representing tasks CPU need to do. It contains capital + * letters A to Z where different letters represent different tasks. Tasks + * could be done without original order. Each task could be done in one + * interval. For each interval, CPU could finish one task or just be idle. + * + * However, there is a non-negative cooling interval n that means between two + * same tasks, there must be at least n intervals that CPU are doing different + * tasks or just be idle. + * + * You need to return the least number of intervals the CPU will take to finish + * all the given tasks. + * + * + * + * Example: + * + * + * Input: tasks = ["A","A","A","B","B","B"], n = 2 + * Output: 8 + * Explanation: A -> B -> idle -> A -> B -> idle -> A -> B. + * + * + * + * + * Note: + * + * + * The number of tasks is in the range [1, 10000]. + * The integer n is in the range [0, 100]. + * + * + */ + +// 由于相同的任务之间必须有 n 的冷却时间,所以我们可以想到按照任务的数量来安排它们, +// 即一种任务的出现次数越多,我们就越早地安排。 +// 例如有 5 种任务 A, B, C, D, E,且它们分别有 6, 1, 1, 1, 1 个时, +// 假设冷却时间 n = 2, +// 那么我们首先安排任务 A, +// 随后在 2 单位的冷却时间里,我们安排任务 B, C, +// 随后继续安排任务 A, +// 再安排任务 D, E,以此类推。 + + +// @lc code=start +class Solution { + public int leastInterval(char[] tasks, int n) { + // //使用排序方法,时间复杂度O(time) + // int []map = new int[26]; + // for(char c:tasks){ + // map[c - 'A']++; + // } + // Arrays.sort(map); + // int time =0; + // while(map[25]>0){ + // int i=0; + // while(i<=n){ + // if(map[25] == 0){ + // break; + // } + // if(i<26 && map[25-i]>0){ + // map[25-i] -- ; + // } + // time ++; + // i ++; + // } + // Arrays.sort(map); + // } + // return time; + + + //时间复杂度O(M),M为任务的总数 + int []map = new int [26]; + for(char c:tasks){ + map[c - 'A'] ++; + } + Arrays.sort(map); + int max_val = map[25]-1,idle_slots = max_val*n; + for(int i=24;i>=0;i--){ + idle_slots -= Math.min(map[i], max_val); + } + + return idle_slots>0 ? idle_slots+tasks.length : tasks.length; + } +} +// @lc code=end + diff --git a/Week 05/id_083/Leetcode-64.java b/Week 05/id_083/Leetcode-64.java new file mode 100644 index 000000000..2f5b63ce0 --- /dev/null +++ b/Week 05/id_083/Leetcode-64.java @@ -0,0 +1,67 @@ +/* + * @lc app=leetcode id=64 lang=java + * + * [64] Minimum Path Sum + * + * https://leetcode.com/problems/minimum-path-sum/description/ + * + * algorithms + * Medium (49.21%) + * Likes: 1770 + * Dislikes: 45 + * Total Accepted: 277.9K + * Total Submissions: 562.1K + * Testcase Example: '[[1,3,1],[1,5,1],[4,2,1]]' + * + * Given a m x n grid filled with non-negative numbers, find a path from top + * left to bottom right which minimizes the sum of all numbers along its path. + * + * Note: You can only move either down or right at any point in time. + * + * Example: + * + * + * Input: + * [ + * [1,3,1], + * ⁠ [1,5,1], + * ⁠ [4,2,1] + * ] + * Output: 7 + * Explanation: Because the path 1→3→1→1→1 minimizes the sum. + * + * + */ + +// @lc code=start + +// 方法 :一维动态规划 +// 在上个解法中,我们可以用一个一维数组来代替二维数组,dp 数组的大小和行大小相同。 +//这是因为对于某个固定状态,只需要考虑下方和右侧的节点。 +//首先初始化 dpdp 数组最后一个元素是右下角的元素值,然后我们向左移更新每个 dp(j)dp(j) 为: + +// dp(j)=grid(i,j)+min(dp(j),dp(j+1)) + +// 我们对于每一行都重复这个过程,然后向上一行移动,计算完成后 dp(0) 就是最后的结果。 + + +class Solution { + public int minPathSum(int[][] grid) { + int[] dp = new int[grid[0].length]; + for (int i = grid.length - 1; i >= 0; i--) { + for (int j = grid[0].length - 1; j >= 0; j--) { + if(i == grid.length - 1 && j != grid[0].length - 1) + dp[j] = grid[i][j] + dp[j + 1]; + else if(j == grid[0].length - 1 && i != grid.length - 1) + dp[j] = grid[i][j] + dp[j]; + else if(j != grid[0].length - 1 && i != grid.length - 1) + dp[j] = grid[i][j] + Math.min(dp[j], dp[j + 1]); + else + dp[j] = grid[i][j]; + } + } + return dp[0]; + } +} +// @lc code=end + diff --git a/Week 05/id_083/Leetcode-647.java b/Week 05/id_083/Leetcode-647.java new file mode 100644 index 000000000..f77b8efd7 --- /dev/null +++ b/Week 05/id_083/Leetcode-647.java @@ -0,0 +1,88 @@ +/* + * @lc app=leetcode id=647 lang=java + * + * [647] Palindromic Substrings + * + * https://leetcode.com/problems/palindromic-substrings/description/ + * + * algorithms + * Medium (58.45%) + * Likes: 1763 + * Dislikes: 89 + * Total Accepted: 131K + * Total Submissions: 224.2K + * Testcase Example: '"abc"' + * + * Given a string, your task is to count how many palindromic substrings in + * this string. + * + * The substrings with different start indexes or end indexes are counted as + * different substrings even they consist of same characters. + * + * Example 1: + * + * + * Input: "abc" + * Output: 3 + * Explanation: Three palindromic strings: "a", "b", "c". + * + * + * + * + * Example 2: + * + * + * Input: "aaa" + * Output: 6 + * Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa". + * + * + * + * + * Note: + * + * + * The input string length won't exceed 1000. + * + * + * + */ + +// @lc code=start +class Solution { + public int countSubstrings(String s) { + //2019.11.15 DP实现 + if (s == null || s.length() == 0) { + return 0; + } + int result = 0; + boolean[][] dp = buildDPForCountSubstrings(s); + for (int j = 0; j < dp.length; j++) { + for (int i = 0; i <= j; i++) { + if (dp[i][j]) { + result++; + } + } + } + return result; + } + + + private boolean[][] buildDPForCountSubstrings(String s) { + int n = s.length(); + boolean[][] dp = new boolean[n][n]; + //注意i 和j 的边界,只计算上半部分,j - i <= 1是为了处理边界,dp[i + 1][j - 1]是dp[i][j]砍头去尾后的是否是回文 + for (int j = 0; j < n; j++) { + for (int i = 0; i <= j; i++) { + if (i == j) { + dp[i][j] = true; + } else { + dp[i][j] = s.charAt(i) == s.charAt(j) && (j - i <= 1 || dp[i + 1][j - 1]); + } + } + } + return dp; + } +} +// @lc code=end + diff --git a/Week 05/id_083/Leetcode-72.java b/Week 05/id_083/Leetcode-72.java new file mode 100644 index 000000000..dc6daf488 --- /dev/null +++ b/Week 05/id_083/Leetcode-72.java @@ -0,0 +1,91 @@ +/* + * @lc app=leetcode id=72 lang=java + * + * [72] Edit Distance + * + * https://leetcode.com/problems/edit-distance/description/ + * + * algorithms + * Hard (39.94%) + * Likes: 2678 + * Dislikes: 41 + * Total Accepted: 207.3K + * Total Submissions: 516.3K + * Testcase Example: '"horse"\n"ros"' + * + * Given two words word1 and word2, find the minimum number of operations + * required to convert word1 to word2. + * + * You have the following 3 operations permitted on a word: + * + * + * Insert a character + * Delete a character + * Replace a character + * + * + * Example 1: + * + * + * Input: word1 = "horse", word2 = "ros" + * Output: 3 + * Explanation: + * horse -> rorse (replace 'h' with 'r') + * rorse -> rose (remove 'r') + * rose -> ros (remove 'e') + * + * + * Example 2: + * + * + * Input: word1 = "intention", word2 = "execution" + * Output: 5 + * Explanation: + * intention -> inention (remove 't') + * inention -> enention (replace 'i' with 'e') + * enention -> exention (replace 'n' with 'x') + * exention -> exection (replace 'n' with 'c') + * exection -> execution (insert 'u') + * + * + */ + +// @lc code=start +class Solution { + public int minDistance(String word1, String word2) { + //使用动态规划的方法 + int n = word1.length(); + int m = word2.length(); + + // if one of the strings is empty + if (n * m == 0) + return n + m; + + // array to store the convertion history + int [][] d = new int[n + 1][m + 1]; + + // init boundaries + for (int i = 0; i < n + 1; i++) { + d[i][0] = i; + } + for (int j = 0; j < m + 1; j++) { + d[0][j] = j; + } + + // DP compute + for (int i = 1; i < n + 1; i++) { + for (int j = 1; j < m + 1; j++) { + int left = d[i - 1][j] + 1; + int down = d[i][j - 1] + 1; + int left_down = d[i - 1][j - 1]; + if (word1.charAt(i - 1) != word2.charAt(j - 1)) + left_down += 1; + d[i][j] = Math.min(left, Math.min(down, left_down)); + + } + } + return d[n][m]; + } +} +// @lc code=end + diff --git a/Week 05/id_083/Leetcode-91.java b/Week 05/id_083/Leetcode-91.java new file mode 100644 index 000000000..d01aea803 --- /dev/null +++ b/Week 05/id_083/Leetcode-91.java @@ -0,0 +1,72 @@ +/* + * @lc app=leetcode id=91 lang=java + * + * [91] Decode Ways + * + * https://leetcode.com/problems/decode-ways/description/ + * + * algorithms + * Medium (23.10%) + * Likes: 1842 + * Dislikes: 2084 + * Total Accepted: 313.2K + * Total Submissions: 1.4M + * Testcase Example: '"12"' + * + * A message containing letters from A-Z is being encoded to numbers using the + * following mapping: + * + * + * 'A' -> 1 + * 'B' -> 2 + * ... + * 'Z' -> 26 + * + * + * Given a non-empty string containing only digits, determine the total number + * of ways to decode it. + * + * Example 1: + * + * + * Input: "12" + * Output: 2 + * Explanation: It could be decoded as "AB" (1 2) or "L" (12). + * + * + * Example 2: + * + * + * Input: "226" + * Output: 3 + * Explanation: It could be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 + * 6). + * + */ + +// @lc code=start +class Solution { + public int numDecodings(String s) { + //DP solution + if(s == null || s.length() == 0) { + return 0; + } + int n = s.length(); + int[] dp = new int[n+1]; + dp[0] = 1; + dp[1] = s.charAt(0) != '0' ? 1 : 0; + for(int i = 2; i <= n; i++) { + int first = Integer.valueOf(s.substring(i-1, i)); + int second = Integer.valueOf(s.substring(i-2, i)); + if(first >= 1 && first <= 9) { + dp[i] += dp[i-1]; + } + if(second >= 10 && second <= 26) { + dp[i] += dp[i-2]; + } + } + return dp[n]; + } +} +// @lc code=end + diff --git a/Week 05/id_098/LeetCode_32_098.java b/Week 05/id_098/LeetCode_32_098.java new file mode 100644 index 000000000..560f2737b --- /dev/null +++ b/Week 05/id_098/LeetCode_32_098.java @@ -0,0 +1,19 @@ +/*动态方程推导 +longest[] 存储每一位最长有效字符串长度 +DP方程 longest[i] = longest[i - 1] + 2 + longest[i - longest[i - 1] - 2] +*/ +class Solution { + public int longestValidParentheses(String s) { + s = ")" + s; + int[] longest = new int[s.length() + 1]; + + for (int i = 1; i < s.length(); i++){ + if (s.charAt(i) == ')' && s.charAt(i - longest[i - 1] - 1) == '(') { + longest[i] = longest[i - 1] + 2 + longest[i - longest[i - 1] - 2]; + longest[s.length()] = Math.max(longest[i], longest[s.length()]); + } + } + + return longest[s.length()]; + } +} \ No newline at end of file diff --git a/Week 05/id_098/LeetCode_72_098.java b/Week 05/id_098/LeetCode_72_098.java new file mode 100644 index 000000000..7f5a65811 --- /dev/null +++ b/Week 05/id_098/LeetCode_72_098.java @@ -0,0 +1,33 @@ +//利用二维数组 M * N 进行DP方程求解 + +class Solution { + public int minDistance(String word1, String word2) { + int n = word1.length(); + int m = word2.length(); + + if (n * m == 0) + return n + m; + + int [][] d = new int[n + 1][m + 1]; + + for (int i = 0; i < n + 1; i++) { + d[i][0] = i; + } + for (int j = 0; j < m + 1; j++) { + d[0][j] = j; + } + + for (int i = 1; i < n + 1; i++) { + for (int j = 1; j < m + 1; j++) { + int left = d[i - 1][j] + 1; + int down = d[i][j - 1] + 1; + int left_down = d[i - 1][j - 1]; + if (word1.charAt(i - 1) != word2.charAt(j - 1)) + left_down += 1; + d[i][j] = Math.min(left, Math.min(down, left_down)); + + } + } + return d[n][m]; + } +} diff --git a/Week 05/id_103/LeeCode_410_103.java b/Week 05/id_103/LeeCode_410_103.java new file mode 100644 index 000000000..c7e60ce7d --- /dev/null +++ b/Week 05/id_103/LeeCode_410_103.java @@ -0,0 +1,41 @@ +/* + * @lc app=leetcode.cn id=410 lang=java + * + * [410] 分割数组的最大值 + */ + +// @lc code=start +class Solution { + public int splitArray(int[] nums, int m) { + long ans = 0; + long L = 0,R = 1; + for(int i = 0;i < nums.length;i++) + R += nums[i]; + while(L < R) { + long mid = (L + R) / 2; + if(guess(mid,nums,m)) { + ans = mid; + R = mid; + } + else + L = mid + 1; + } + return (int)ans; + } + public boolean guess(long mid,int[] nums,long m) { + long sum = 0; + for(int i = 0;i < nums.length;i++) { + if(sum + nums[i] > mid) { + --m; + sum = nums[i]; + if(sum > mid) + return false; + } + else + sum += nums[i]; + } + return m > 0; + } +} +// @lc code=end + diff --git a/Week 05/id_103/LeeCode_621_103.java b/Week 05/id_103/LeeCode_621_103.java new file mode 100644 index 000000000..8394a9ab6 --- /dev/null +++ b/Week 05/id_103/LeeCode_621_103.java @@ -0,0 +1,34 @@ +/* + * @lc app=leetcode.cn id=621 lang=java + * + * [621] 任务调度器 + */ + +// @lc code=start +class Solution { + public int leastInterval(char[] tasks, int n) { + int[] count = new int[26]; + for (int i = 0; i < tasks.length; i++) { + count[tasks[i] - 'A']++; + } + Arrays.sort(count); + int result = 0; + while (count[25] > 0) { + int i = 0; + while (i <= n) { + if (count[25] == 0) { + break; + } + if (i < 26 && count[25 - i] > 0) { + count[25 - i]--; + } + result++; + i++; + } + Arrays.sort(count); + } + return result; + } +} +// @lc code=end + diff --git a/Week 05/id_103/LeeCode_76_103.java b/Week 05/id_103/LeeCode_76_103.java new file mode 100644 index 000000000..9dbbae4f9 --- /dev/null +++ b/Week 05/id_103/LeeCode_76_103.java @@ -0,0 +1,47 @@ +/* + * @lc app=leetcode.cn id=76 lang=java + * + * [76] 最小覆盖子串 + */ + +// @lc code=start +class Solution { + public String minWindow(String s, String t) { + Map lookup = new HashMap<>(); + for (char c : s.toCharArray()) { + lookup.put(c, 0); + } + for (char c : t.toCharArray()) { + if (lookup.containsKey(c)) { + lookup.put(c, lookup.get(c) + 1); + } + else return ""; + } + int start = 0; + int end = 0; + int min_len = Integer.MAX_VALUE; + int counter = t.length(); + String res = ""; + while (end < s.length()) { + char c1 = s.charAt(end); + if (lookup.get(c1) > 0) { + counter--; + } + lookup.put(c1, lookup.get(c1) - 1); + end++; + while (counter == 0) { + if (min_len > end - start) { + min_len = end - start; + res = s.substring(start, end); + } + char c2 = s.charAt(start); + if (lookup.get(c2) == 0) counter++; + lookup.put(c2, lookup.get(c2) + 1); + start++; + } + } + return res; + } +} +// @lc code=end + diff --git a/Week 05/id_108/LeeCode_072_108.java b/Week 05/id_108/LeeCode_072_108.java new file mode 100644 index 000000000..f6289ac3f --- /dev/null +++ b/Week 05/id_108/LeeCode_072_108.java @@ -0,0 +1,33 @@ +package study; + +/** + 参考:powcai解法 + 难点:难点还是在于找出状态转移方程 +*/ +public class LeeCode_072_108 { + + public int minDistance(String word1, String word2) { + int n1 = word1.length(); + int n2 = word2.length(); + int[][] dp = new int[n1 + 1][n2 + 1]; + // 第一行 + for (int j = 1; j <= n2; j++) dp[0][j] = dp[0][j - 1] + 1; + // 第一列 + for (int i = 1; i <= n1; i++) dp[i][0] = dp[i - 1][0] + 1; + + for (int i = 1; i <= n1; i++) { + for (int j = 1; j <= n2; j++) { + if (word1.charAt(i - 1) == word2.charAt(j - 1)) dp[i][j] = dp[i - 1][j - 1]; + else dp[i][j] = Math.min(Math.min(dp[i - 1][j - 1], dp[i][j - 1]), dp[i - 1][j]) + 1; + } + } + return dp[n1][n2]; + } + + public static void main(String[] args) { + String word1 = "horse"; + String word2 = "ros"; + System.out.println(new LeeCode_072_108().minDistance(word1, word2)); + } + +} diff --git a/Week 05/id_108/LeeCode_091_108.java b/Week 05/id_108/LeeCode_091_108.java new file mode 100644 index 000000000..e6d10b4ea --- /dev/null +++ b/Week 05/id_108/LeeCode_091_108.java @@ -0,0 +1,22 @@ +package study; + +public class LeeCode_091_108 { + + public int numDecodings(String s) { + if (s.isEmpty() || s.charAt(0) == '0') return 0; + int[] dp = new int[s.length() + 1]; + dp[0] = 1; + for (int i = 1; i < dp.length; ++i) { + dp[i] = (s.charAt(i - 1) == '0') ? 0 : dp[i - 1]; + if (i > 1 && (s.charAt(i - 2) == '1' || (s.charAt(i - 2) == '2' && s.charAt(i - 1) <= '6'))) { + dp[i] += dp[i - 2]; + } + } + return dp[s.length()]; + } + + public static void main(String[] args) { + System.out.println(new LeeCode_091_108().numDecodings("12")); + } + +} diff --git a/Week 05/id_113/LeetCode64.py b/Week 05/id_113/LeetCode64.py new file mode 100644 index 000000000..1085b3a9f --- /dev/null +++ b/Week 05/id_113/LeetCode64.py @@ -0,0 +1,9 @@ +class Solution: + def minPathSum(self, grid: List[List[int]]) -> int: + for i in range(len(grid)): + for j in range(len(grid[0])): + if i == j == 0: continue + elif i == 0: grid[i][j] = grid[i][j - 1] + grid[i][j] + elif j == 0: grid[i][j] = grid[i - 1][j] + grid[i][j] + else: grid[i][j] = min(grid[i - 1][j], grid[i][j - 1]) + grid[i][j] + return grid[-1][-1] diff --git a/Week 05/id_113/LeetCode647.py b/Week 05/id_113/LeetCode647.py new file mode 100644 index 000000000..140f760c7 --- /dev/null +++ b/Week 05/id_113/LeetCode647.py @@ -0,0 +1,12 @@ +class Solution: + def countSubstrings(self, s: str) -> int: + if not s: + return 0 + res = len(s) + dp = [[i,i+1] for i in range(len(s))] + for i in range(1, len(s)): + for j in dp[i-1]: + if j-1 >= 0 and s[j-1] == s[i]: + res += 1 + dp[i].append(j-1) + return res diff --git a/Week 05/id_113/LeetCode91.py b/Week 05/id_113/LeetCode91.py new file mode 100644 index 000000000..897a804fb --- /dev/null +++ b/Week 05/id_113/LeetCode91.py @@ -0,0 +1,39 @@ +class Solution: + def numDecodings(self, s: str) -> int: + if not s or s[0] == '0': # 基本情况,直接返回 0 + return 0 + dp = [None] * len(s) # 构建 dp 数组 + dp[0] = 1 # 只有一个数时肯定为 1 + if len(s) > 1: # 为 dp[1] 填充值 + if s[1] == '0': # s[i] 为 ‘0’ 时 + if int(s[0:2]) <= 26: # 截取前两数,判断是否小于或等于 26 + dp[1] = 1 # 因为 s[i] 为 ‘0’ 所以 dp[1] 只有 1 种可能 + else: + return 0 # 比如 60 , 此时该序列无法翻译 + else: # s[i] 不为 ‘0’ 时 + if int(s[0:2]) <= 26: + dp[1] = 2 # 比如 16,有两种翻译结果 + else: + dp[1] = 1 # 比如 27,只有一种结果 + else: # 只有一个数 + return 1 + + for i in range(2, len(s)): # 从 2 开始 + if s[i] == '0': # s[i] 为 ‘0’ 时 + if s[i-1] == '0': # 前一个为 ‘0’ + return 0 # 无解 + else: # 前一个不为 ‘0’ + if int(s[i-1:i+1]) <= 26: # s[i-1] 和 s[i] 组成的数 <= 26 + dp[i] = dp[i-2] + else: + return 0 + else: # s[i] 不为 ‘0’ + if s[i-1] == '0': # 前一个为 ‘0’ + dp[i] = dp[i-1] + else: # 前一个不为 ‘0’ + if int(s[i-1:i+1]) <= 26: # s[i-1] 和 s[i] 组成的数 <= 26 + dp[i] = dp[i-1] + dp[i-2] + else: # s[i-1] 和 s[i] 组成的数 > 26 + dp[i] = dp[i-1] + + return dp[len(s) - 1] diff --git a/Week 05/id_118/LeetCode_647_118.py b/Week 05/id_118/LeetCode_647_118.py new file mode 100644 index 000000000..64cfbdf9f --- /dev/null +++ b/Week 05/id_118/LeetCode_647_118.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Created by Isaac Zhou at 11/17/2019 + +class Solution: + def countSubstrings(self, s: str) -> int: + count = 0 + cache = [[False for _ in range(len(s))] for _ in range(len(s))] + + for i in range(len(s)): + for j in range(i): + if i - j <= 2: + cache[j][i] = (s[j] == s[i]) + + else: + cache[j][i] = (s[j] == s[i] and cache[j + 1][i - 1]) + + if cache[j][i]: + count += 1 + cache[i][i] = True + count += 1 + + return count diff --git a/Week 05/id_118/LeetCode_64_118.py b/Week 05/id_118/LeetCode_64_118.py new file mode 100644 index 000000000..681b39e8b --- /dev/null +++ b/Week 05/id_118/LeetCode_64_118.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Created by Isaac Zhou at 11/17/2019 +from typing import List + + +class Solution: + def minPathSum(self, grid: List[List[int]]) -> int: + m, n = len(grid), len(grid[0]) + + for i in range(1, n): + grid[0][i] += grid[0][i-1] + + for i in range(1, m): + grid[i][0] += grid[i-1][0] + + for i in range(1, m): + for j in range(1, n): + grid[i][j] += min(grid[i-1][j], grid[i][j-1]) + + return grid[-1][-1] + diff --git a/Week 05/id_123/LeetCode_53_123.c b/Week 05/id_123/LeetCode_53_123.c new file mode 100644 index 000000000..bb8624c17 --- /dev/null +++ b/Week 05/id_123/LeetCode_53_123.c @@ -0,0 +1,22 @@ +//̬滮dp[i],iΪβ +//ƹʽΪ +// if(dp[i-1]>0) +// dp[i]=nums[i]+dp[i+1]; +// else +// dp[i]=nums[i] +int maxSubArray(int* nums, int numsSize){ + int max = nums[0]; + int* dp = (int*)malloc(sizeof(int)*numsSize); + dp[0] = nums[0]; + for(int i=1; i=0){ + dp[i]=nums[i]+dp[i-1]; + } + else + dp[i]=nums[i]; + if(max=0 ? dp[i-1][j]:0; +// int two = j-1>=0 ? dp[i][j-1]:0; +// dp[i][j] = one + two; +// if(i==0&&j==0){ +// dp[0][0] = 1; +// } +// printf("%d",dp[i][j]); +// } +// } +// return dp[m-1][n-1]; + + +// } + +//公式法,从n个元素中取出m个元素的循环排列数:C(n,m)=n!/[m!(n-m)!]=n*(n-1)*...*(n-m+1)/m! +//本题中相当于从m-n-2中取得n-1的随机组合方式 +// int uniquePaths(int m, int n){ +// int M=n-1; +// int min=m-1=3nN*뱾f(1)=1,f(2)=2,1 + int f=(1/sqrt(5))*(pow(((1+sqrt(5))/2),(n+1))-pow(((1-sqrt(5))/2),(n+1))); + return f; + +} \ No newline at end of file diff --git a/Week 05/id_128/LeetCode_1143_128.cs b/Week 05/id_128/LeetCode_1143_128.cs new file mode 100644 index 000000000..ba3beca2b --- /dev/null +++ b/Week 05/id_128/LeetCode_1143_128.cs @@ -0,0 +1,52 @@ +public class Solution +{ + public int LongestCommonSubsequence(string text1, string text2) + { + if (String.IsNullOrEmpty(text1) || string.IsNullOrEmpty(text2)) + return 0; + var m = text1.Length + 1; + var n = text2.Length + 1; + + var opt = new int[m, n]; + for (int i = 1; i < m; i++) + { + for (int j = 1; j < n; j++) + { + if (text1[i - 1] == text2[j - 1]) + opt[i, j] = 1 + opt[i - 1, j - 1]; + else + opt[i, j] = Math.Max(opt[i - 1, j], opt[i, j - 1]); + } + } + return opt[m - 1, n - 1]; + } +} + + +//space optimzied +public class Solution +{ + public int LongestCommonSubsequence(string text1, string text2) + { + if (String.IsNullOrEmpty(text1) || string.IsNullOrEmpty(text2)) + return 0; + var m = text1.Length + 1; + var n = text2.Length + 1; + + var opt = new int[n]; + for (int j = 1; j < m; j++) + { + int tmp = 0; + for (int i = 1; i < n; i++) + { + int prev = tmp; + tmp = opt[i]; + if (text1[j - 1] == text2[i - 1]) + opt[i] = 1 + prev; + else + opt[i] = Math.Max(opt[i - 1], opt[i]); + } + } + return opt[n - 1]; + } +} \ No newline at end of file diff --git a/Week 05/id_128/LeetCode_120_128.cs b/Week 05/id_128/LeetCode_120_128.cs new file mode 100644 index 000000000..e778d639e --- /dev/null +++ b/Week 05/id_128/LeetCode_120_128.cs @@ -0,0 +1,106 @@ + +//dp bottom up. reuse input array +public class Solution +{ + public int MinimumTotal(IList> triangle) + { + if (triangle is null) return 0; + if (triangle.Count() <= 0) return 0; + if (triangle[0].Count() <= 0) return 0; + + for (int row = triangle.Count() - 2; row >= 0; row--) + { + for (int col = 0; col < triangle[row].Count(); col++) + { + triangle[row][col] += Math.Min(triangle[row + 1][col], triangle[row + 1][col + 1]); + } + } + return triangle[0][0]; + } +} + +//dp bottom up. reduce dimension +public class Solution +{ + public int MinimumTotal(IList> triangle) + { + if (triangle is null) return 0; + if (triangle.Count() <= 0) return 0; + if (triangle[0].Count() <= 0) return 0; + + var dp = triangle[triangle.Count() - 1]; + for (int row = triangle.Count() - 2; row >= 0; row--) + { + for (int col = 0; col < triangle[row].Count(); col++) + { + dp[col] = triangle[row][col] + Math.Min(dp[col], dp[col + 1]); + } + } + return dp[0]; + } +} + +//dp top down +public class Solution +{ + public int MinimumTotal(IList> triangle) + { + if (triangle is null) return 0; + if (triangle.Count() <= 0) return 0; + if (triangle[0].Count() <= 0) return 0; + + for (int row = 1; row < triangle.Count(); row++) + { + for (int col = 0; col < triangle[row].Count(); col++) + { + if (col == 0) + triangle[row][col] += triangle[row - 1][col]; + else if (col == triangle[row].Count() - 1) + triangle[row][col] += triangle[row - 1][col - 1]; + else + triangle[row][col] += Math.Min(triangle[row - 1][col], triangle[row - 1][col - 1]); + } + } + + var res = Int32.MaxValue; + foreach (var i in triangle[triangle.Count() - 1]) + { + res = i < res ? i : res; + } + return res; + } +} + +//recursive with memo +public class Solution +{ + public int MinimumTotal(IList> triangle) + { + if (triangle is null) return 0; + if (triangle.Count() <= 0) return 0; + if (triangle[0].Count() <= 0) return 0; + + var memo = new bool[triangle.Count()][]; + for (int i = 0; i < triangle.Count(); i++) + { + memo[i] = new bool[triangle[i].Count()]; + } + return _MinimumTotal(triangle, 0, 0, memo); + } + + private int _MinimumTotal(IList> triangle, int row, int col, bool[][] memo) + { + if (row >= triangle.Count() - 1) + { + return triangle[row][col]; + } + + if (!memo[row][col]) + { + memo[row][col] = true; + triangle[row][col] += Math.Min(_MinimumTotal(triangle, row + 1, col), + _MinimumTotal(triangle, row + 1, col + 1)); + } + return triangle[row][col]; + } +} \ No newline at end of file diff --git a/Week 05/id_128/LeetCode_121_128.cs b/Week 05/id_128/LeetCode_121_128.cs new file mode 100644 index 000000000..befc33f47 --- /dev/null +++ b/Week 05/id_128/LeetCode_121_128.cs @@ -0,0 +1,36 @@ + +//double pointer or optimized brute force +public class Solution +{ + public int MaxProfit(int[] prices) + { + if (prices.Length <= 1) return 0; + + var minPrice = prices[0]; + var maxProfit = 0; + for (int i = 1; i < prices.Length; i++) + { + minPrice = Math.Min(minPrice, prices[i]); + maxProfit = Math.Max(maxProfit, prices[i] - minPrice); + } + return maxProfit; + } +} + +//dp +public class Solution +{ + public int MaxProfit(int[] prices) + { + if (prices.Length <= 1) return 0; + + var dp = new int[prices.Length]; + dp[0] = 0; + for (int i = 1; i < prices.Length; i++) + { + dp[i] = dp[i-1] + + Math.Max(prices[i] - prices[i-1], 0); + } + return dp[prices.Length -1]; + } +} diff --git a/Week 05/id_128/LeetCode_152_128.cs b/Week 05/id_128/LeetCode_152_128.cs new file mode 100644 index 000000000..514747862 --- /dev/null +++ b/Week 05/id_128/LeetCode_152_128.cs @@ -0,0 +1,49 @@ +//dp +public class Solution +{ + public int MaxProduct(int[] nums) + { + if (nums.Length <= 0) return 0; + if (nums.Length == 1) return nums[0]; + + var max = new int[nums.Length]; + var min = new int[nums.Length]; + min[0] = nums[0]; + max[0] = nums[0]; + for (int i = 1; i < nums.Length; i++) + { + min[i] = Math.Min(max[i - 1] * nums[i], Math.Min(min[i - 1] * nums[i], nums[i])); + max[i] = Math.Max(max[i - 1] * nums[i], Math.Max(min[i - 1] * nums[i], nums[i])); + } + return max.Max(); + } +} + +//dp reduce space. note the runMin/runMax swap +public class Solution +{ + public int MaxProduct(int[] nums) + { + if (nums.Length <= 0) return 0; + if (nums.Length == 1) return nums[0]; + + var runMin = nums[0]; + var runMax = nums[0]; + var max = runMax; + for (int i = 1; i < nums.Length; i++) + { + runMin = nums[i] * Math.Min(runMin, 1); + runMax = nums[i] * Math.Max(runMax, 1); + if (nums[i] < 0) + { + var temp = runMin; + runMin = runMax; + runMax = temp; + } + max = Math.Max(max, runMax); + } + return max; + } +} + +//bidirectional search \ No newline at end of file diff --git a/Week 05/id_128/LeetCode_322_128.cs b/Week 05/id_128/LeetCode_322_128.cs new file mode 100644 index 000000000..41be95def --- /dev/null +++ b/Week 05/id_128/LeetCode_322_128.cs @@ -0,0 +1,126 @@ + +//dfs with trimming branch. time out. +public class Solution +{ + public int CoinChange(int[] coins, int amount) + { + if (amount <= 0) return 0; + if (coins.Length <= 0) return -1; + Helper(coins, amount, 0, 0); + if (result >= Int32.MaxValue) + result = -1; + return result; + } + private int result = Int32.MaxValue; + private void Helper(int[] coins, int amount, int currentAmount, int coinNumbers) + { + if (coinNumbers >= result) + return; + if (currentAmount > amount) + return; + if (currentAmount == amount) + result = Math.Min(result, coinNumbers); + + foreach (var coin in coins) + { + Helper(coins, amount, currentAmount + coin, coinNumbers + 1); + } + } +} + +//bfs with branch trimming. if not trimming branch, it would out of memory. +public class Solution +{ + public int CoinChange(int[] coins, int amount) + { + if (amount <= 0) return 0; + if (coins.Length <= 0) return -1; + + var queue = new Queue(); + var amountVisited = new HashSet(); + queue.Enqueue(0); + int level = 0; + while (queue.Count() > 0) + { + var count = queue.Count(); + while (count-- > 0) + { + var prevAmount = queue.Dequeue(); + foreach (var coin in coins) + { + int curAmount = coin + prevAmount; + if (curAmount == amount) + return level + 1; + if (curAmount < amount && !amountVisited.Contains(curAmount)) + { + queue.Enqueue(curAmount); + amountVisited.Add(curAmount); + } + } + } + level++; + } + return -1; + } +} + + + +//dp + +public class Solution +{ + public int CoinChange(int[] coins, int amount) + { + if (amount <= 0) return 0; + if (coins.Length <= 0) return -1; + + var dp = new int[amount + 1]; + dp[0] = 0; + + for (int i = 1; i <= amount; i++) + { + dp[i] = Int32.MaxValue; + foreach (var coin in coins) + { + if (coin <= i) + dp[i] = Math.Min(dp[i], dp[i - coin]); + } + dp[i] = dp[i] + 1; + } + if (dp[amount] < 0 || dp[amout] >= Int32.MaxValue) + return -1; + else + return dp[amount]; + } +} + + +//dp loop coins first +public class Solution +{ + public int CoinChange(int[] coins, int amount) + { + if (amount <= 0) return 0; + if (coins.Length <= 0) return -1; + + var dp = new int[amount + 1]; + dp[0] = 0; + for (int i = 1; i <= amount; i++) + { + dp[i] = amount + 1; + } + foreach (var coin in coins) + { + for (int i = 1; i <= amount; i++) + { + if (coin <= i) + dp[i] = Math.Min(dp[i], dp[i - coin] + 1); + } + } + if (dp[amount] >= amount + 1) + return -1; + else + return dp[amount]; + } +} \ No newline at end of file diff --git a/Week 05/id_128/LeetCode_32_128.cs b/Week 05/id_128/LeetCode_32_128.cs new file mode 100644 index 000000000..e192ca1c8 --- /dev/null +++ b/Week 05/id_128/LeetCode_32_128.cs @@ -0,0 +1,31 @@ +public class Solution +{ + public int LongestValidParentheses(string s) + { + if (s.Length < 2) return 0; + var dp = new int[s.Length]; + if (s[1] == ')' && s[0] == '(') + dp[1] = 2; + + for (int i = 2; i < s.Length; i++) + { + if (s[i] == ')') + { + if (s[i - 1] == '(') + { + dp[i] = dp[i - 2] + 2; + } + else + { + var left = i - 1 - dp[i - 1]; + if (left >= 0 && s[left] == '(') + { + dp[i] = (left >= 1 ? dp[left - 1] : 0) + dp[i - 1] + 2; + } + + } + } + } + return dp.Max(); + } +} \ No newline at end of file diff --git a/Week 05/id_128/LeetCode_53_128.cs b/Week 05/id_128/LeetCode_53_128.cs new file mode 100644 index 000000000..a163ebb29 --- /dev/null +++ b/Week 05/id_128/LeetCode_53_128.cs @@ -0,0 +1,44 @@ +/* +Apparently, this is a optimization problem, which can be usually solved by DP. So when it comes to DP, the first thing for us to figure out is the format of the sub problem(or the state of each sub problem). The format of the sub problem can be helpful when we are trying to come up with the recursive relation. + +At first, I think the sub problem should look like: maxSubArray(int A[], int i, int j), which means the maxSubArray for A[i: j]. In this way, our goal is to figure out what maxSubArray(A, 0, A.length - 1) is. However, if we define the format of the sub problem in this way, it's hard to find the connection from the sub problem to the original problem(at least for me). In other words, I can't find a way to divided the original problem into the sub problems and use the solutions of the sub problems to somehow create the solution of the original one. + +So I change the format of the sub problem into something like: maxSubArray(int A[], int i), which means the maxSubArray for A[0:i ] which must has A[i] as the end element. Note that now the sub problem's format is less flexible and less powerful than the previous one because there's a limitation that A[i] should be contained in that sequence and we have to keep track of each solution of the sub problem to update the global optimal value. However, now the connect between the sub problem & the original one becomes clearer +*/ + +//dp +public class Solution +{ + public int MaxSubArray(int[] nums) + { + if (nums.Length <= 0) return 0; + if (nums.Length == 1) return nums[0]; + + var dp = new int[nums.Length]; + dp[0] = nums[0]; + for (int i = 1; i < nums.Length; i++) + { + dp[i] = nums[i] + Math.Max(0, dp[i - 1]); + } + return dp.Max(); + } +} + +// dp reduced space +public class Solution +{ + public int MaxSubArray(int[] nums) + { + if (nums.Length <= 0) return 0; + if (nums.Length == 1) return nums[0]; + + int subAns = nums[0]; + int opt = subAns; + for (int i = 1; i < nums.Length; i++) + { + subAns = nums[i] + Math.Max(0, subAns); + opt = Math.Max(subAns, opt); + } + return opt; + } +} \ No newline at end of file diff --git a/Week 05/id_128/LeetCode_62_128.cs b/Week 05/id_128/LeetCode_62_128.cs new file mode 100644 index 000000000..9aa62cc9d --- /dev/null +++ b/Week 05/id_128/LeetCode_62_128.cs @@ -0,0 +1,70 @@ +//recursion with memo +public class Solution +{ + public int UniquePaths(int m, int n) + { + if (m <= 0 || n <= 0) return 0; + if (m == 1) return n; + if (n == 1) return m; + int memo = new int[m][n]; + _UniquePaths(0, 0, m, n, memo); + } + + private int _UniquePaths(int i, int j, int m, int n, int[][] memo) + { + if (i >= m || j >= n) return 0; + if (memo[i][j] != 0) + return memo[i][j]; + return _UniquePaths(i + 1, j) + _UniquePaths(i, j + 1); + } +} + +//dp +public class Solution +{ + public int UniquePaths(int m, int n) + { + if (m <= 0 || n <= 0) return 0; + if (m == 1 || n == 1) return 1; + var opt = new int[m, n]; + + for (int j = 0; j < n; j++) + opt[0, j] = 1; + for (int i = 0; i < m; i++) + opt[i, 0] = 1; + + for (int i = 1; i < m; i++) + { + for (int j = 1; j < n; j++) + { + opt[i, j] = opt[i - 1, j] + opt[i, j - 1]; + } + } + return opt[m - 1, n - 1]; + } + +} + +//dp space optimized +public class Solution +{ + public int UniquePaths(int m, int n) + { + if (m <= 0 || n <= 0) return 0; + if (m == 1 || n == 1) return 1; + var opt = new int[m]; + + for (int i = 0; i < m; i++) + opt[i] = 1; + + for (int j = 1; j < n; j++) + { + for (int i = 1; i < m; i++) + { + opt[i] = opt[i - 1] + opt[i]; + } + } + return opt[m - 1]; + } + +} \ No newline at end of file diff --git a/Week 05/id_128/LeetCode_63_128.cs b/Week 05/id_128/LeetCode_63_128.cs new file mode 100644 index 000000000..0ae93ab75 --- /dev/null +++ b/Week 05/id_128/LeetCode_63_128.cs @@ -0,0 +1,31 @@ +public class Solution +{ + public int UniquePathsWithObstacles(int[][] obstacleGrid) + { + int m = obstacleGrid.Length; + if (m <= 0) return 0; + int n = obstacleGrid[0].Length; + if (n <= 0) return 0; + + var opt = new int[m]; + if (obstacleGrid[0][0] == 1) + return 0; + opt[0] = 1; + + for (int j = 0; j < n; j++) + { + for (int i = 0; i < m; i++) + { + if (obstacleGrid[i][j] == 1) + { + opt[i] = 0; + } + else if (i > 0) + { + opt[i] += opt[i - 1]; + } + } + } + return opt[m - 1]; + } +} \ No newline at end of file diff --git a/Week 05/id_128/LeetCode_64_128.cs b/Week 05/id_128/LeetCode_64_128.cs new file mode 100644 index 000000000..483ad55db --- /dev/null +++ b/Week 05/id_128/LeetCode_64_128.cs @@ -0,0 +1,51 @@ +//dp +public class Solution +{ + public int MinPathSum(int[][] grid) + { + var m = grid.Length; + var n = grid[0].Length; + if (m <= 0) return 0; + if (n <= 0) return 0; + + for (int i = 0; i < m; i++) + { + for (int j = 0; j < n; j++) + { + if (i == 0 && j == 0) continue; + else if (i == 0) grid[i][j] = grid[i][j - 1] + grid[i][j]; + else if (j == 0) grid[i][j] = grid[i - 1][j] + grid[i][j]; + else grid[i][j] = Math.Min(grid[i - 1][j], grid[i][j - 1]) + grid[i][j]; + } + } + return grid[m - 1][n - 1]; + } +} + +//dp reduce dimension +public class Solution +{ + public int MinPathSum(int[][] grid) + { + var m = grid.Length; + var n = grid[0].Length; + if (m <= 0) return 0; + if (n <= 0) return 0; + + var dp = grid[0]; + + for (int i = 1; i < n; i++) + { + dp[i] += dp[i - 1]; + } + for (int i = 1; i < m; i++) + { + for (int j = 0; j < n; j++) + { + if (j == 0) dp[j] += grid[i][j]; + else dp[j] = Math.Min(dp[j - 1], dp[j]) + grid[i][j]; + } + } + return dp[n - 1]; + } +} \ No newline at end of file diff --git a/Week 05/id_128/LeetCode_70_128.cs b/Week 05/id_128/LeetCode_70_128.cs new file mode 100644 index 000000000..8e7eaa162 --- /dev/null +++ b/Week 05/id_128/LeetCode_70_128.cs @@ -0,0 +1,35 @@ + +//fib +public class Solution +{ + public int ClimbStairs(int n) + { + if (n < 3) return n; + int first = 1; + int second = 2; + for (int i = 3; i <= n; i++) + { + int third = first + second; + first = second; + second = third; + } + return second; + } + +} + +//dp +public int ClimbStairs(int n) +{ + if (n <= 2) return n; + var dp = new int[n + 1]; + dp[0] = 0; + dp[1] = 1; + dp[2] = 2; + + for (int i = 3; i <= n; i++) + { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; +} \ No newline at end of file diff --git a/Week 05/id_128/NOTE.md b/Week 05/id_128/NOTE.md index a6321d6e2..7ec510376 100644 --- a/Week 05/id_128/NOTE.md +++ b/Week 05/id_128/NOTE.md @@ -1,4 +1,40 @@ # NOTE - +public class Solution +{ + public int ClimbStairs(int n) + { + if(n==1 || n==2) + return 1; + if(n==3) + return 3; + + var dp = new int[n+1, 4]; + dp[1][1] = 1; + dp[2][2] = 1; + dp[3][1] = 1; + dp[3][2] = 1; + dp[3][3] = 1; + for(int i=4; i<=n; ++i) { + dp[i][1] = dp[i-1][2]+dp[i-1][3]; + dp[i][2] = dp[i-2][1]+dp[i-2][3]; + dp[i][3] = dp[i-3][1]+dp[i-3][2]; + } + + return dp[n][1] + dp[n][2] + dp[n][3]; + } +} + + +DP 的一些技巧 +1. top down 遇到困难的话, 可以尝试bottom up +2. 字符串变化问题 通常需要二维dp +3. 没有思路写dp状态转移方程时, brute force 可以帮助打开思路 +4. 当找到子问题和状态但子问题无法合并为原始问题时,考虑增加一个状态或定义更严格的子问题并从所有子问题的解中找寻最佳结果作为原始问题的结论 + +DP 的思考过程 +1. 尝试分解子问题 +2. 找到涉及到的【状态】 +3. 找到【选择】 +4. 写出不同的【选择】导致的【状态】变化的方程 diff --git a/Week 05/id_133/leetcode_120_133.java b/Week 05/id_133/leetcode_120_133.java new file mode 100644 index 000000000..21ae6a3fc --- /dev/null +++ b/Week 05/id_133/leetcode_120_133.java @@ -0,0 +1,19 @@ + +/** + * https://leetcode-cn.com/problems/triangle/description/ + * 题号:120 + * 题目:三角形最小路径和 + */ + +class Solution { + public int minimumTotal(List> triangle) { + int row = triangle.size(); + int[] minlen = new int[row+1]; + for (int level = row-1;level>=0;level--){ + for (int i = 0;i<=level;i++){ + minlen[i] = Math.min(minlen[i], minlen[i+1]) + triangle.get(level).get(i); + } + } + return minlen[0]; + } +} \ No newline at end of file diff --git a/Week 05/id_133/leetcode_322_133.java b/Week 05/id_133/leetcode_322_133.java new file mode 100644 index 000000000..e9ebd8a84 --- /dev/null +++ b/Week 05/id_133/leetcode_322_133.java @@ -0,0 +1,23 @@ + +/** + * https://leetcode-cn.com/problems/coin-change/ + * 题号:322 + * 题目:零钱兑换 + */ + +public class Solution { + public int coinChange(int[] coins, int amount) { + int max = amount + 1; + int[] dp = new int[amount + 1]; + Arrays.fill(dp, max); + dp[0] = 0; + for (int i = 1; i <= amount; i++) { + for (int j = 0; j < coins.length; j++) { + if (coins[j] <= i) { + dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1); + } + } + } + return dp[amount] > amount ? -1 : dp[amount]; + } +} \ No newline at end of file diff --git a/Week 05/id_138/LeetCode_188_138.java b/Week 05/id_138/LeetCode_188_138.java new file mode 100644 index 000000000..afa6e57f1 --- /dev/null +++ b/Week 05/id_138/LeetCode_188_138.java @@ -0,0 +1,83 @@ +/** + * 买卖股票的最佳时机 + * https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/ + * @author L + * + */ +public class LeetCode_188_138 { + /** + * 动态规划 : + * for 状态1 in 状态1所有取值: + * for 状态2 in 状态2所有取值: + * for 状态3 in 状态3所有取值: + * dp[状态1][状态2][状态3] = 择优(选择1,选择2,.... 选择n) + * + * //dp[n][k][s] 第一个是天数,第二个是允许交易的最大次数(1....k),第三个是当前的持有状态(0=没有持有股票,1=持有股票) + * @param prices + * @return + */ + public int maxProfit(int k, int[] prices) { + if(prices == null || prices.length == 0) + return 0; + if(k == 0) + return 0; + int n = prices.length; + if (k > n / 2) + return maxProfit_k_infi(prices); + int[][][] dp = new int[n][k+1][2]; + for(int i=0; i=1;kk--) { + if(i == 0) {//处理初始状态 + dp[i][kk][0] = 0; + dp[i][kk][1] = -prices[i]; + continue; + } + dp[i][kk][0] = Math.max(dp[i-1][kk][0], dp[i-1][kk][1]+prices[i]);//第i天未持有股票的最大获利 + dp[i][kk][1] = Math.max(dp[i-1][kk][1], dp[i-1][kk-1][0]-prices[i]);//第i天持有股票的最大获利 + } + } + return dp[n-1][k][0]; + } + + /** + * k 无穷大 + * @param k + * @param prices + * @return + */ + public int maxProfit_k_infi(int[] prices) { + if(prices == null || prices.length == 0) + return 0; + int n = prices.length; + int dp_i_0 = 0, dp_i_1 = Integer.MIN_VALUE; + for (int i = 0; i < n; i++) { + int temp = dp_i_0; + dp_i_0 = Math.max(dp_i_0, dp_i_1 + prices[i]); + dp_i_1 = Math.max(dp_i_1, temp - prices[i]); + } + return dp_i_0; + } + + /** + * k=1 每天只允许1次交易 + * @param prices + * @return + */ + public int maxProfit_k_1(int[] prices) { + if(prices == null || prices.length == 0) + return 0; + int n = prices.length; + int[][] dp = new int[n][2]; + for(int i=0;i stack = new Stack(); + for(int i=0;i=2?dp[i-2]:0)+2;//()必须是一对才有效,所以判断从i>=2开始 + }else if(i-dp[i-1] >0 && s.charAt(i-dp[i-1]-1) == '(') {//判断前一个最大子串的左侧位置是否是'(' + dp[i] = dp[i-1] + ( (i-dp[i-1])>=2 ? dp[i-dp[i-1]-2]:0 ) + 2; + } + max = Math.max(max, dp[i]); + } + } + return max; + } + +} diff --git a/Week 05/id_138/LeetCode_62_138.java b/Week 05/id_138/LeetCode_62_138.java new file mode 100644 index 000000000..929a67f01 --- /dev/null +++ b/Week 05/id_138/LeetCode_62_138.java @@ -0,0 +1,56 @@ +import java.util.Arrays; + +/** + * 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 + +机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 + +问总共有多少条不同的路径? + * @author L + * + */ +public class LeetCode_62_138 { + /** + * 标准的dp解法,使用二维数组存放结果 + * @param m + * @param n + * @return + */ + public int uniquePaths(int m, int n) { + if(m<=1 || n<=1) + return 1; + int[][] dp = new int[m][n];//定义dp状态,数组存放走到dp[i][j]位置的路径总数,0<=i<=m-1;0<=j<=n-1 + for(int i=0;i=0;i--) { + for(int j=col-1;j>=0;j--) {//从右下角开始遍历处理 + if(i == row-1 && j != col-1) {//最后一行但不是最后一列,只能向右走 + dp[i][j] = grid[i][j] + dp[i][j+1];//当前的位置值+右侧最小值 + } + else if(i != row-1 && j == col-1) {//最后一列但不是最后一行,只能向下走 + dp[i][j] = grid[i][j] + dp[i+1][j];//当前位置值+下方最小值 + }else if(i != row-1 && j != col-1){//不是最后一行也不是最后一列,既可以向右也可以向下 + dp[i][j] = grid[i][j]+ Math.min(dp[i+1][j], dp[i][j+1]); + }else {//右下角的位置 + dp[i][j] = grid[i][j]; + } + } + } + return dp[0][0]; + } +} diff --git a/Week 05/id_138/LeetCode_72_138.java b/Week 05/id_138/LeetCode_72_138.java new file mode 100644 index 000000000..3ff9d5c5b --- /dev/null +++ b/Week 05/id_138/LeetCode_72_138.java @@ -0,0 +1,42 @@ +/** + * 编辑距离 + * 给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。 + +你可以对一个单词进行如下三种操作: + +插入一个字符 +删除一个字符 +替换一个字符 +思路: +编辑距离算法被数据科学家广泛应用,是用作机器翻译和语音识别评价标准的基本算法。 +最简单的方法是检查所有可能的编辑序列,从中找出最短的一条。但这个序列总数可能达到指数级,但完全不需要这么多,因为我们只要找到距离最短的那条而不是所有可能的序列。 +写的很好理解的方法: +https://leetcode-cn.com/problems/edit-distance/solution/bian-ji-ju-chi-mian-shi-ti-xiang-jie-by-labuladong/ + * @author L + * + */ +public class LeetCode_72_138 { + public int minDistance(String word1, String word2) { + int m = word1.length(); + int n = word2.length(); + int[][] dp = new int[m+1][n+1];//# 返回 s1[0..i] 和 s2[0..j] 的最小编辑距离; dp 函数的 base case是 i,j 等于 -1,而数组索引至少是 0,所以 dp 数组会偏移一位。 + //dp[i-1][j-1] # 存储 s1[0..i] 和 s2[0..j] 的最小编辑距离 + for (int i=1;i<=m;i++) { + dp[i][0] = i;//word2为空,则操作为i + } + for(int j=1;j<=n;j++) { + dp[0][j] = j;//word1为空 则操作为j + } + //自底向上求解 + for(int i=1;i<=m;i++) { + for(int j=1;j<=n;j++) { + if(word1.charAt(i-1) == word2.charAt(j-1)) {//当前下标i j位置字符相同 + dp[i][j] = dp[i-1][j-1]; + }else {//当前下标i j 位置字符不相同 + dp[i][j] = Math.min(Math.min(dp[i-1][j]+1, dp[i][j-1]+1), dp[i-1][j-1]+1); + } + } + } + return dp[m][n]; + } +} diff --git a/Week 05/id_138/LeetCode_91_138.java b/Week 05/id_138/LeetCode_91_138.java new file mode 100644 index 000000000..8d0fadfc2 --- /dev/null +++ b/Week 05/id_138/LeetCode_91_138.java @@ -0,0 +1,47 @@ +/** + * 解码方法 一条包含字母 A-Z 的消息通过以下方式进行了编码: + * + * 'A' -> 1 'B' -> 2 ... 'Z' -> 26 给定一个只包含数字的非空字符串,请计算解码方法的总数。 + * 输入: "12" 输出: 2 + * 解释: 它可以解码为 "AB"(1 2)或者 "L"(12) + * + * @author L + * + */ +public class LeetCode_91_138 { + /** + * 类似爬楼梯 + * 动态规划思路 1)重复子问题 + * 2)dp状态定义dp[i] = s(i....N)的解码方法数 + * 3)dp方程 dp[i] = dp[i-1] || dp[i-1]+dp[i-2](条件) + * @param s + * @return + */ + public int numDecodings(String s) { + if(s==null || s.length() == 0 || s.startsWith("0")) + return 0; + int len = s.length(); + int[] dp = new int[len+1]; + dp[len] = 1; + if(s.charAt(len-1) == '0') { + dp[len-1] = 0;//0的解码个数为0 + }else { + dp[len-1] = 1;//非0的解码个数为1 + } + for(int i=len-2;i>=0;i--) { + if(s.charAt(i) == '0') { + dp[i] = 0; + continue; + } + + if((s.charAt(i) -'0')*10 +(s.charAt(i+1)-'0') <= 26) {//当前i位置与i+1位置组合的数字是否<=26 + dp[i] = dp[i+1] + dp[i+2]; + }else { + dp[i] = dp[i+1]; + } + } + + return dp[0]; + } + +} diff --git a/Week 05/id_138/NOTE.md b/Week 05/id_138/NOTE.md index a6321d6e2..3c7932ae6 100644 --- a/Week 05/id_138/NOTE.md +++ b/Week 05/id_138/NOTE.md @@ -1,4 +1,72 @@ # NOTE +Week05学习总结 +1.了解动态规划的实现以及关键点 + 1)复习递归代码的模板(方法调用方法自身) + + public void recurise(int level, int param) { + + //1 recurisor terminator == 递归终结条件 + if(level> MAX_LEVEL) + //process result == 处理递归终止的返回结果 + return; + + //2 process current level logic == 当前层逻辑处理 + process(level, param); + + //3 drill down == 调用自身,递归方法 + recurise(level:level+1,newParam) + + //4 restore current status == 按需要重置状态 + + + } + + 2)复习分治代码模板(将复杂问题分解为重复子问题,然后合并执行结果) + + def divide_conquer(problem, param1, param2, ...): + # recursion terminator + if problem is None: + print_result + return + + # prepare data + data = prepare_data(problem) + subproblems = split_problem(problem, data) + + # conquer subproblems + subresult1 = self.divide_conquer(subproblems[0], p1, ...) + subresult2 = self.divide_conquer(subproblems[1], p1, ...) + subresult3 = self.divide_conquer(subproblems[2], p1, ...) + … + + # process and generate the final result + result = process_result(subresult1, subresult2, subresult3, …) + + # revert the current level states + + 3)动态规划定义 + 通过将复杂的问题分解为递归的更简单的子问题来简化处理,思路是将问题分解为子问题然后递归地找到子问题的最优解来最佳地解决问题,即寻找最优子结构。 + + 4)动态规划的步骤 + i) 寻找重复子问题 + ii) 定义动态规划状态 + iii) 推导动态规划方程 + + 2 代表性练习题 + 股票的买卖时机,该习题有一系列的衍生扩展,但是都可以套用相同模板: + 动态规划 : + 定义dp状态:dp(n,k,s) + n是天数,k是允许交易的最大次数(1....k),s是当前的持有状态(0=没有持有股票,1=持有股票) + + for 状态1 in 状态1所有取值: + for 状态2 in 状态2所有取值: + for 状态3 in 状态3所有取值: + //dp[状态1][状态2][状态3] = 择优(选择1,选择2,.... 选择n) + + dp[i][kk][0] = Math.max(dp[i-1][kk][0], dp[i-1][kk][1]+prices[i]);//第i天未持有股票的最大获利 + dp[i][kk][1] = Math.max(dp[i-1][kk][1], dp[i-1][kk-1][0]-prices[i]);//第i天持有股票的最大获利 + + \ No newline at end of file diff --git a/Week 05/id_143/LeetCode_43_143.java b/Week 05/id_143/LeetCode_43_143.java new file mode 100644 index 000000000..b41295a73 --- /dev/null +++ b/Week 05/id_143/LeetCode_43_143.java @@ -0,0 +1,31 @@ +import java.util.Stack; + +/* + * @lc app=leetcode.cn id=32 lang=java + * + * [32] 最长有效括号 + */ +//利用栈来实现,栈里记录字符数组的下标。 +// @lc code=start +class Solution { + public int longestValidParentheses(String s) { + int maxN = 0; + Stack a = new Stack(); + a.push(-1); + for(int i = 0; i < s.length(); i++){ + if(s.charAt(i) == '('){ + a.push(i); + }else{ + a.pop(); + if(a.isEmpty()){ + a.push(i); + }else{ + maxN = Math.max(maxN, (i - a.peek())); + } + } + } + return maxN; + } +} +// @lc code=end + diff --git a/Week 05/id_143/LeetCode_64_143.java b/Week 05/id_143/LeetCode_64_143.java new file mode 100644 index 000000000..e9dcd97dd --- /dev/null +++ b/Week 05/id_143/LeetCode_64_143.java @@ -0,0 +1,30 @@ +/* + * @lc app=leetcode.cn id=64 lang=java + * + * [64] 最小路径和 + */ + +// @lc code=start +//DP 从左上角到右下角 +class Solution { + public int minPathSum(int[][] grid) { + int rows = grid.length -1; + int clos = grid[0].length -1; + for(int i = 0 ;i<= rows;i++){ + for(int j =0 ;j <= clos;j++){ + if(i == 0 && j == 0){ + grid[0][0] = grid[0][0]; + }else if(i == 0 && j > 0){ + grid[0][j] = grid[0][j] + grid[0][j-1]; + }else if(j == 0 && i > 0 ){ + grid[i][0] = grid[i][0] + grid[i-1][0]; + }else{ + grid[i][j] = Math.min(grid[i-1][j], grid[i][j-1])+grid[i][j]; + } + } + } + return grid[rows][clos]; + } +} +// @lc code=end + diff --git a/Week 05/id_143/LeetCode_72_143.java b/Week 05/id_143/LeetCode_72_143.java new file mode 100644 index 000000000..3058f2053 --- /dev/null +++ b/Week 05/id_143/LeetCode_72_143.java @@ -0,0 +1,48 @@ +/* + * @lc app=leetcode.cn id=72 lang=java + * + * [72] 编辑距离 + */ + +// @lc code=start +class Solution { + public int minDistance(String word1, String word2) { + + //1、分治 求minDistance(String word1, String word2)的最大子串 + // 当,最后一位相等时==1+minDistance(String word1.substring(n-1), String word2.substring(n-1)),不等时,即求其少一位的子串 + //2、定义二维状态数组a[i][j]。从空串开始。记录到两者距离(即需要操作的次数)。 + //3、状态方程 当s[i]==s[j]时,a[i][j] =a[i-1][j-1],因为不需要操作 + // 当s[i]!=s[j]时,a[i][j] = 1 + Min(a[i-1][j-1],a[i-1][j],a[i][j-1]),因为分别对应替换,插入,和删除 + int m = word1.length(); + int n = word2.length(); + int dp[][] = new int[m+1][n+1]; + for(int i = 0;i<= m ;i++){ + for(int j = 0; j<= n;j++){ + if(i ==0 && j==0){ + dp[i][j] = 0; + continue; + } + if(i == 0 && j>0){ + dp[i][j] = dp[i][j-1] +1; + continue; + } + if(j == 0 && i>0){ + dp[i][j] = dp[i-1][j] +1; + continue; + } + + //普适情况,因为数组前面多了一个空串,所以字串数组都减1 + if(word1.charAt(i-1) == word2.charAt(j-1)){ + dp[i][j] = dp[i-1][j-1]; + }else{ + dp[i][j] = 1 + Math.min(Math.min(dp[i-1][j-1], dp[i-1][j]),dp[i][j-1]); + } + + } + } + return dp[m][n]; + } + +} +// @lc code=end + diff --git a/Week 05/id_143/LeetCode_91_143.java b/Week 05/id_143/LeetCode_91_143.java new file mode 100644 index 000000000..339609b99 --- /dev/null +++ b/Week 05/id_143/LeetCode_91_143.java @@ -0,0 +1,39 @@ +/* + * @lc app=leetcode.cn id=91 lang=java + * + * [91] 解码方法 + */ + +// @lc code=start +class Solution { + public int numDecodings(String s) { + //分治s(1~n) = s(1~n-1)+s(1-n-2) + //状态数组dp[i],代表 s(i~n)的解法 + //状态方程dp[i] = dp[i+1] + dp[i+2] + + int dp[] = new int[s.length()+1]; + dp[s.length()] = 1; + if( s.charAt(s.length()-1) !='0'){ + dp[s.length()-1] = 1; + }else{ + dp[s.length()-1] = 0; + } + for(int i = s.length()-2 ;i>=0;i--){ + if(s.charAt(i) == '0'){ + continue; + } + int ten = (s.charAt(i) - '0') * 10; + int one = s.charAt(i + 1) - '0'; + if (ten + one <= 26) { + dp[i] = dp[i+1]+dp[i + 2]; + }else{ + dp[i] = dp[i+1]; + } + } + + return dp[0]; + + } +} +// @lc code=end + diff --git a/Week 05/id_143/NOTE.md b/Week 05/id_143/NOTE.md index a6321d6e2..6273c1935 100644 --- a/Week 05/id_143/NOTE.md +++ b/Week 05/id_143/NOTE.md @@ -1,4 +1,26 @@ -# NOTE +# 算法训练营学习 +# 第五周 +## 第十二课 +### 动态归划 +1. 动态规划与分治及递归没有本质的区别,共性:都是找重复子问题。 +2. 动态规划与分治及递归的本别在于DP,有最优子结构,中途可以淘汰次优解。 +3. 动态规则模板 + - 分治(子问题) + - 定义状态组数 + - 状态方程 +``` +关于爬递问题的变种:可以分三步走,以及相邻两次不能一样的解法:需要升维了 +1.分治;f(n)=f(n-1)[1] + f(n-2)[2] + f(n-3)[3],但往前递归就带有条件了。 +2.状态数组 a[i][j],i为阶数,j为到达这阶最后的跳阶数目为,1,2,3. +3.状态方程有三个: +f(n)[1] = f(n-2)[2] + f(n-3)[3] +f(n)[2] = f(n-1)[1] + f(n-3)[3] +f(n)[3] = f(n-1)[1] + f(n-2)[2] - +``` + +## 个人感悟 + - **课程学习计划问题**: 考试周过于轻松,把时间调了一部分在工作上了。对学习来讲起到了反效果。本周得尽快找回感觉。 + - **练习问题**: 一周的题目没有过完三次。不知道效果打了多少折扣,心有愧欠,又想说结束训练营后再多练,但我感觉到时更找不出抽时间的理由。 + - 团队问题: 定期"**叫醒**"服务。团队方面,有8个人弃考,基本上属于从第一次开始就没怎么看视频和作业的。余下的人,更需要相互坚持。现在更需要鼓作气,冲到最后! diff --git a/Week 05/id_153/LeetCode_32_153.js b/Week 05/id_153/LeetCode_32_153.js new file mode 100644 index 000000000..c3f1d4231 --- /dev/null +++ b/Week 05/id_153/LeetCode_32_153.js @@ -0,0 +1,17 @@ +var longestValidParentheses = function(s) { + let maxans = 0; + let stackArr = [-1]; + for (let i = 0; i < s.length; i++) { + if (s[i] === "(") { + stackArr.push(i); + } else { + stackArr.pop(); + if (stackArr.length === 0) { + stackArr.push(i); + } else { + maxans = Math.max(maxans, i - stackArr[stackArr.length - 1]); + } + } + } + return maxans; +}; diff --git a/Week 05/id_153/LeetCode_64_153.js b/Week 05/id_153/LeetCode_64_153.js new file mode 100644 index 000000000..3c465e461 --- /dev/null +++ b/Week 05/id_153/LeetCode_64_153.js @@ -0,0 +1,23 @@ +/** + * @param {number[][]} grid + * @return {number} + */ +var minPathSum = function(grid) { + var n = grid.length; + var m = grid[0].length; + var dp = Array.from(new Array(n), () => new Array(m)); + for (var i = 0; i < n; i++) { + for (var j = 0; j < m; j++) { + if (i !== 0 && j !== 0) { + dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]; + } else if (i === 0 && j !== 0) { + dp[i][j] = dp[i][j - 1] + grid[i][j]; + } else if (i !== 0 && j === 0) { + dp[i][j] = dp[i - 1][j] + grid[i][j]; + } else if (i === 0 && j === 0) { + dp[i][j] = grid[i][j]; + } + } + } + return dp[n - 1][m - 1]; +}; diff --git a/Week 05/id_153/NOTE.md b/Week 05/id_153/NOTE.md index a6321d6e2..47897e184 100644 --- a/Week 05/id_153/NOTE.md +++ b/Week 05/id_153/NOTE.md @@ -1,4 +1,58 @@ -# NOTE +# 总结 - +## 动态规划 +动态递推,将复杂问题分解成简单子问题。本质上是递归问题、分治问题。 +动态规划和递归或者分治没有根本上的区别(关键看有无最优子结构) +共性:找到重复子问题 +差异性:最优子结构、中途可以淘汰次优解 +## 感触 +1. 人肉递归低效、很累 +2. 找到最近最简方法,将其拆解成可重复解决的问题 +3. 数学归纳法思维(抵制人肉递归的诱惑) +4. 寻找重复性 + +## 递归代码模板 +```java +public void recur(int level, int param) { + + // terminator 递归终止条件 + if (level > MAX_LEVEL) { + // process result + return; + } + + // process current logic 处理当前层逻辑 + process(level, param); + + // drill down 递归 + recur( level: level + 1, newParam); + + // restore current status 清除当前层状态 + +} +``` + +## 分治代码模板 +```python +def divide_conquer(problem, param1, param2, ...): + # recursion terminator + if problem is None: + print_result + return + + # prepare data + data = prepare_data(problem) + subproblems = split_problem(problem, data) + + # conquer subproblems + subresult1 = self.divide_conquer(subproblems[0], p1, ...) + subresult2 = self.divide_conquer(subproblems[1], p1, ...) + subresult3 = self.divide_conquer(subproblems[2], p1, ...) + … + + # process and generate the final result + result = process_result(subresult1, subresult2, subresult3, …) + + # revert the current level states +``` \ No newline at end of file diff --git "a/Week 05/id_158/32.\346\234\200\351\225\277\346\234\211\346\225\210\346\213\254\345\217\267.cs" "b/Week 05/id_158/32.\346\234\200\351\225\277\346\234\211\346\225\210\346\213\254\345\217\267.cs" new file mode 100644 index 000000000..d49a3287a --- /dev/null +++ "b/Week 05/id_158/32.\346\234\200\351\225\277\346\234\211\346\225\210\346\213\254\345\217\267.cs" @@ -0,0 +1,50 @@ +/* + * @lc app=leetcode.cn id=32 lang=csharp + * + * [32] 最长有效括号 + */ + +// @lc code=start +using System; +using System.Collections.Generic; + +public class Solution { + // 栈 + public int LongestValidParentheses(string s) { + int max = 0; + Stack stack = new Stack(); + stack.Push(-1); + for (int i = 0; i < s.Length; i++) { + if (s[i] == '(') { + stack.Push(i); + } else { + stack.Pop(); + if (stack.Count==0) { + stack.Push(i); + } else { + max = Math.Max(max, i - stack.Peek()); + } + } + } + return max; + } + + // DP + public int longestValidParentheses(string s) { + int maxans = 0; + int[] dp = new int[s.Length]; + for (int i = 1; i < s.Length; i++) { + if (s[i] == ')') { + if (s[i - 1] == '(') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + } else if (i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '(') { + dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + maxans = Math.Max(maxans, dp[i]); + } + } + return maxans; + } +} +// @lc code=end + diff --git "a/Week 05/id_158/322.\351\233\266\351\222\261\345\205\221\346\215\242.cs" "b/Week 05/id_158/322.\351\233\266\351\222\261\345\205\221\346\215\242.cs" new file mode 100644 index 000000000..499c39f8f --- /dev/null +++ "b/Week 05/id_158/322.\351\233\266\351\222\261\345\205\221\346\215\242.cs" @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode.cn id=322 lang=csharp + * + * [322] 零钱兑换 + */ + +// @lc code=start +using System; + +public class Solution { + public int CoinChange(int[] coins, int amount) { + int max = amount+1; + int[] dp = new int[amount + 1]; + Array.Fill(dp, max); + dp[0] = 0; + for (int i = 1; i <= amount; i++) { + for (int j = 0; j < coins.Length; j++) { + if (coins[j] <= i) { + dp[i] = Math.Min(dp[i], dp[i - coins[j]] + 1); + } + } + } + return dp[amount] > amount ? -1 : dp[amount]; + } +} +// @lc code=end + diff --git "a/Week 05/id_158/53.\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214.cs" "b/Week 05/id_158/53.\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214.cs" new file mode 100644 index 000000000..82172eb06 --- /dev/null +++ "b/Week 05/id_158/53.\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214.cs" @@ -0,0 +1,22 @@ +/* + * @lc app=leetcode.cn id=53 lang=csharp + * + * [53] 最大子序和 + */ + +// @lc code=start +using System; +using System.Linq; + +public class Solution { + public int MaxSubArray(int[] nums) { + int[] dp =nums; + for (int i = 1; i < nums.Length; i++) + { + dp[i] = Math.Max(nums[i],nums[i]+dp[i-1]); + } + return dp.Max(); + } +} +// @lc code=end + diff --git "a/Week 05/id_158/64.\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.cs" "b/Week 05/id_158/64.\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.cs" new file mode 100644 index 000000000..a14401a8e --- /dev/null +++ "b/Week 05/id_158/64.\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.cs" @@ -0,0 +1,30 @@ +/* + * @lc app=leetcode.cn id=64 lang=csharp + * + * [64] 最小路径和 + */ + +// @lc code=start +using System; + +public class Solution { + public int MinPathSum(int[][] grid) { + int[,] dp = new int[grid.Length,grid[0].Length]; + for (int i = grid.Length - 1; i >= 0; i--) { + for (int j = grid[0].Length - 1; j >= 0; j--) { + if(i == grid.Length - 1 && j != grid[0].Length - 1) + dp[i,j] = grid[i][j] + dp[i,j + 1]; + else if(j == grid[0].Length - 1 && i != grid.Length - 1) + dp[i,j] = grid[i][j] + dp[i + 1,j]; + else if(j != grid[0].Length - 1 && i != grid.Length - 1) + dp[i,j] = grid[i][j] + Math.Min(dp[i + 1,j], dp[i,j + 1]); + else + dp[i,j] = grid[i][j]; + } + } + return dp[0,0]; + } + +} +// @lc code=end + diff --git "a/Week 05/id_158/70.\347\210\254\346\245\274\346\242\257.cs" "b/Week 05/id_158/70.\347\210\254\346\245\274\346\242\257.cs" new file mode 100644 index 000000000..c08be9344 --- /dev/null +++ "b/Week 05/id_158/70.\347\210\254\346\245\274\346\242\257.cs" @@ -0,0 +1,40 @@ +/* + * @lc app=leetcode.cn id=70 lang=csharp + * + * [70] 爬楼梯 + */ + +// @lc code=start +using System.Collections.Generic; + +public class Solution5 { + + // 递归 + // private Dictionary dict = new Dictionary(); + // public int ClimbStairs(int n) { + // if(n==1) return 1; + // if(n==2) return 2; + // if(dict.ContainsKey(n)){ + // return dict[n]; + // } + // var fn= ClimbStairs(n-1)+ClimbStairs(n-2); + // dict.Add(n,fn); + // return fn; + // } + + // DP + public int climbStairs(int n) { + if (n == 1) { + return 1; + } + int[] dp = new int[n + 1]; + dp[1] = 1; + dp[2] = 2; + for (int i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; + } +} +// @lc code=end + diff --git a/Week 05/id_173/LeetCode_64_173.cpp b/Week 05/id_173/LeetCode_64_173.cpp new file mode 100644 index 000000000..424e79276 --- /dev/null +++ b/Week 05/id_173/LeetCode_64_173.cpp @@ -0,0 +1,29 @@ +/* + * 64. 最小路径和 + */ + +class Solution { +public: + int minPathSum(vector>& grid) { + if(grid.empty()) + return 0; + + int rows = grid.size(); + int cols = grid[0].size(); + + for(int i=0; i dp(s.size()+1, 1); + + for(int i=1; i= '1' && s[i] <= '6')) + dp[i+1] = dp[i] + dp[i-1]; + else + dp[i+1] = dp[i]; + } + + return dp[s.size()]; + } +}; \ No newline at end of file diff --git a/Week 05/id_173/NOTE.md b/Week 05/id_173/NOTE.md index a6321d6e2..c9cbb9b64 100644 --- a/Week 05/id_173/NOTE.md +++ b/Week 05/id_173/NOTE.md @@ -1,4 +1,7 @@ -# NOTE - - - +## 动态规划 +#### 1. 概述 +  动态规划与递归、分治并没有根本上的区别,都是寻找重复子问题,但差异在于它存在最优子结构,一般用来解决最大、最小、最多或最少的问题,在解题的过程中需要不断地去除次优解,只保留最优解即可。
+#### 2. 解题三部曲 +> (1)寻找最优子结构
+> (2)定义状态数组
+> (3)写出状态转移方程
diff --git a/Week 05/id_183/Leetcode_32_183.cpp b/Week 05/id_183/Leetcode_32_183.cpp new file mode 100644 index 000000000..073969a01 --- /dev/null +++ b/Week 05/id_183/Leetcode_32_183.cpp @@ -0,0 +1,40 @@ +/* + * @lc app=leetcode id=32 lang=cpp + * + * [32] Longest Valid Parentheses + */ + +// @lc code=start +class Solution { +public: + int longestValidParentheses(string s) { + //if string is empty ,return 0 + if(s.empty()) + { + return 0; + } + int size = s.size(); + int res = 0; + vector dp(size); + for(int i =1;i= 0 && s[pre] == '(') + { + dp[i] = dp[i-1] + 2; + if(pre > 0) + { + dp[i] += dp[pre-1]; + } + res = max(res,dp[i]); + } + } + + } + return res; + } +}; +// @lc code=end + diff --git a/Week 05/id_183/Leetcode_64_183.cpp b/Week 05/id_183/Leetcode_64_183.cpp new file mode 100644 index 000000000..c062d893a --- /dev/null +++ b/Week 05/id_183/Leetcode_64_183.cpp @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode id=64 lang=cpp + * + * [64] Minimum Path Sum + */ + +// @lc code=start +class Solution { +public: + int minPathSum(vector>& grid) { + int row = grid.size(); + int column = grid[0].size(); + vector> dp(row , vector(column)); + for(int i = grid.size() - 1;i>=0;i--) + { + for(int j = grid[0].size()-1;j>=0;j--) + { + if(i==grid.size() -1 && j !=grid[0].size()-1) + dp[i][j] = grid[i][j] + dp[i][j+1]; + else if(j == grid[0].size()-1 && i != grid.size()-1) + dp[i][j] = grid[i][j] + dp[i+1][j]; + else if(j != grid[0].size() - 1 && i != grid.size() - 1) + dp[i][j] = grid[i][j] + min(dp[i + 1][j], dp[i][j + 1]); + else + dp[i][j] = grid[i][j]; + } + } + return dp[0][0]; + } +}; +// @lc code=end + diff --git a/Week 05/id_183/Leetcode_72_183.cpp b/Week 05/id_183/Leetcode_72_183.cpp new file mode 100644 index 000000000..33c249a3b --- /dev/null +++ b/Week 05/id_183/Leetcode_72_183.cpp @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode id=72 lang=cpp + * + * [72] Edit Distance + */ + +// @lc code=start +class Solution { +public: + int min3(int x, int y, int z) { + return min(min(x, y), z); + } + int minDistance(string word1, string word2) { + int R = word1.size(); + int C = word2.size(); + int dp[R + 1][C + 1] = {0}; + for (int i = 0; i <= R; ++i) dp[i][0] = i; + for (int i = 0; i <= C; ++i) dp[0][i] = i; + for (int i = 1; i <= R; ++i) { + for (int j = 1; j <= C; ++j) { + dp[i][j] = min3( + dp[i - 1][j] + 1, + dp[i][j - 1] + 1, + dp[i - 1][j - 1] + int(word1[i - 1] != word2[j - 1])); + } + } + return dp[R][C]; + } +}; + +// @lc code=end + diff --git a/Week 05/id_188/leetcode_33_188.go b/Week 05/id_188/leetcode_33_188.go new file mode 100644 index 000000000..26fc50f1a --- /dev/null +++ b/Week 05/id_188/leetcode_33_188.go @@ -0,0 +1,62 @@ +/* + * @lc app=leetcode.cn id=33 lang=golang + * + * [33] 搜索旋转排序数组 + * + * https://leetcode-cn.com/problems/search-in-rotated-sorted-array/description/ + * + * algorithms + * Medium (36.08%) + * Likes: 408 + * Dislikes: 0 + * Total Accepted: 54.2K + * Total Submissions: 149.7K + * Testcase Example: '[4,5,6,7,0,1,2]\n0' + * + * 假设按照升序排序的数组在预先未知的某个点上进行了旋转。 + * + * ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 + * + * 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。 + * + * 你可以假设数组中不存在重复的元素。 + * + * 你的算法时间复杂度必须是 O(log n) 级别。 + * + * 示例 1: + * + * 输入: nums = [4,5,6,7,0,1,2], target = 0 + * 输出: 4 + * + * + * 示例 2: + * + * 输入: nums = [4,5,6,7,0,1,2], target = 3 + * 输出: -1 + * + */ + +// @lc code=start +func search(nums []int, target int) int { + if nums == nil || len(nums) == 0 { + return -1 + } + left, right := 0, len(nums)-1 + for left < right { + mind := (left + right) / 2 + if nums[0] <= nums[mind] && (target > nums[mind] || target < nums[0]) { + left = mind + 1 + } else if nums[0] > nums[mind] && target < nums[0] && target > nums[mind] { + left = mind + 1 + } else { + right = mind + } + } + if left == right && nums[left] == target { + return left + } else { + return -1 + } +} + +// @lc code=end diff --git a/Week 05/id_188/leetcode_62_188.go b/Week 05/id_188/leetcode_62_188.go new file mode 100644 index 000000000..abeff93cd --- /dev/null +++ b/Week 05/id_188/leetcode_62_188.go @@ -0,0 +1,77 @@ +/* + * @lc app=leetcode.cn id=62 lang=golang + * + * [62] 不同路径 + * + * https://leetcode-cn.com/problems/unique-paths/description/ + * + * algorithms + * Medium (56.91%) + * Likes: 351 + * Dislikes: 0 + * Total Accepted: 51.2K + * Total Submissions: 89.5K + * Testcase Example: '3\n2' + * + * 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 + * + * 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 + * + * 问总共有多少条不同的路径? + * + * + * + * 例如,上图是一个7 x 3 的网格。有多少可能的路径? + * + * 说明:m 和 n 的值均不超过 100。 + * + * 示例 1: + * + * 输入: m = 3, n = 2 + * 输出: 3 + * 解释: + * 从左上角开始,总共有 3 条路径可以到达右下角。 + * 1. 向右 -> 向右 -> 向下 + * 2. 向右 -> 向下 -> 向右 + * 3. 向下 -> 向右 -> 向右 + * + * + * 示例 2: + * + * 输入: m = 7, n = 3 + * 输出: 28 + * + */ + +// @lc code=start +func uniquePaths(m int, n int) int { + // 二维数组 + // griph := [][]int{} + // for i := 0; i < m; i++ { + // griph = append(griph, make([]int, n, n)) + // griph[i][0] = 1 + // } + // for i := 0; i < n; i++ { + // griph[0][i] = 1 + // } + // for i := 1; i < m; i++ { + // for j := 1; j < n; j++ { + // griph[i][j] = griph[i-1][j] + griph[i][j-1] + // } + // } + // return griph[m-1][n-1] + + // 一维数组 + cache := make([]int, n, n) + for index := 0; index < n; index++ { + cache[index] = 1 + } + for i := 1; i < m; i++ { + for j := 1; j < n; j++ { + cache[j] += cache[j-1] + } + } + return cache[n-1] +} + +// @lc code=end diff --git a/Week 05/id_188/leetcode_63_188.go b/Week 05/id_188/leetcode_63_188.go new file mode 100644 index 000000000..77ddff711 --- /dev/null +++ b/Week 05/id_188/leetcode_63_188.go @@ -0,0 +1,81 @@ +/* + * @lc app=leetcode.cn id=63 lang=golang + * + * [63] 不同路径 II + * + * https://leetcode-cn.com/problems/unique-paths-ii/description/ + * + * algorithms + * Medium (31.78%) + * Likes: 179 + * Dislikes: 0 + * Total Accepted: 28.9K + * Total Submissions: 90.8K + * Testcase Example: '[[0,0,0],[0,1,0],[0,0,0]]' + * + * 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 + * + * 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 + * + * 现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径? + * + * + * + * 网格中的障碍物和空位置分别用 1 和 0 来表示。 + * + * 说明:m 和 n 的值均不超过 100。 + * + * 示例 1: + * + * 输入: + * [ + * [0,0,0], + * [0,1,0], + * [0,0,0] + * ] + * 输出: 2 + * 解释: + * 3x3 网格的正中间有一个障碍物。 + * 从左上角到右下角一共有 2 条不同的路径: + * 1. 向右 -> 向右 -> 向下 -> 向下 + * 2. 向下 -> 向下 -> 向右 -> 向右 + * + * + */ + +// @lc code=start +func uniquePathsWithObstacles(obstacleGrid [][]int) int { + if obstacleGrid == nil || len(obstacleGrid) == 0 { + return 0 + } + r, c := len(obstacleGrid), len(obstacleGrid[0]) + if obstacleGrid[r-1][c-1] == 1 { + return 0 + } + cache := [][]int{} + for i := 0; i < r; i++ { + cache = append(cache, make([]int, c, c)) + } + cache[r-1][c-1] = 1 + for i := r - 2; i >= 0; i++ { + if obstacleGrid[i][c-1] != 1 { + cache[i][c-1] = cache[i+1][c-1] + } + } + for i := c - 2; i >= 0; i++ { + if obstacleGrid[r-1][i] != 1 { + cache[r-1][i] = cache[r-1][i+1] + } + } + for i := r - 2; i >= 0; i-- { + for j := c - 2; j >= 0; j-- { + if obstacleGrid[i][j] != 1 { + cache[i][j] = cache[i+1][j] + cache[i][j+1] + } + } + } + + return cache[0][0] +} + +// @lc code=end diff --git a/Week 05/id_198/LeetCode_104_198.go b/Week 05/id_198/LeetCode_104_198.go new file mode 100644 index 000000000..622f2ad27 --- /dev/null +++ b/Week 05/id_198/LeetCode_104_198.go @@ -0,0 +1,43 @@ +package leetcode + +import "math" + +/* + * + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func maxDepth(root *TreeNode) int { + + if root == nil { + + return 0 + } + + if root.Left == nil && root.Right == nil { + + return 1 + } + + if root.Left == nil { + + return maxDepth(root.Right) + 1 + } + + if root.Right == nil { + + return maxDepth(root.Left) + 1 + } + + return int(math.Max(float64(maxDepth(root.Left)), float64(maxDepth(root.Right)))) + 1 +} diff --git a/Week 05/id_198/LeetCode_104_198_test.go b/Week 05/id_198/LeetCode_104_198_test.go new file mode 100644 index 000000000..7d40202fe --- /dev/null +++ b/Week 05/id_198/LeetCode_104_198_test.go @@ -0,0 +1,23 @@ +package leetcode + +import "testing" + +func Test_maxDepth(t *testing.T) { + type args struct { + root *TreeNode + } + tests := []struct { + name string + args args + want int + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := maxDepth(tt.args.root); got != tt.want { + t.Errorf("maxDepth() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/Week 05/id_198/LeetCode_312_198.go b/Week 05/id_198/LeetCode_312_198.go new file mode 100644 index 000000000..128319ac0 --- /dev/null +++ b/Week 05/id_198/LeetCode_312_198.go @@ -0,0 +1,33 @@ +package leetcode + +func maxCoins(nums []int) int { + + max := func(a, b int) int { + + if a > b { + + return a + } + return b + } + + v := []int{1} + v = append(v, nums...) + v = append(v, 1) + n := len(nums) + dp := make([][]int, n+2) + for i := 0; i < n+2; i++ { + + dp[i] = make([]int, n+2) + } + for s := 1; s <= n; s++ { + for i := 1; i+s-1 <= n; i++ { + j := i + s - 1 + for k := i; k <= j; k++ { + + dp[i][j] = max(dp[i][j], v[i-1]*v[k]*v[j+1]+dp[i][k-1]+dp[k+1][j]) + } + } + } + return dp[1][n] +} diff --git a/Week 05/id_198/LeetCode_312_198_test.go b/Week 05/id_198/LeetCode_312_198_test.go new file mode 100644 index 000000000..cc95f84b6 --- /dev/null +++ b/Week 05/id_198/LeetCode_312_198_test.go @@ -0,0 +1,23 @@ +package leetcode + +import "testing" + +func Test_maxCoins(t *testing.T) { + type args struct { + nums []int + } + tests := []struct { + name string + args args + want int + }{ + {name: "t1", args: args{nums: []int{3, 1, 5, 8}}, want: 167}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := maxCoins(tt.args.nums); got != tt.want { + t.Errorf("maxCoins() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/Week 05/id_198/LeetCode_32_198.go b/Week 05/id_198/LeetCode_32_198.go new file mode 100644 index 000000000..ba4a74003 --- /dev/null +++ b/Week 05/id_198/LeetCode_32_198.go @@ -0,0 +1,41 @@ +package leetcode + +// +func longestValidParentheses(s string) int { + + max := func(i, j int) int { + + if i > j { + + return i + } + + return j + } + rt := 0 + t := []rune(s) + dp := make([]int, len(t)) + for i := 1; i < len(t); i++ { + if t[i] == ')' { + if t[i-1] == '(' { + if i >= 2 { + + dp[i] += dp[i-2] + 2 + } else { + dp[i] += 2 + } + } else if i-dp[i-1] > 0 && s[i-dp[i-1]-1] == '(' { + + if i-dp[i-1] >= 2 { + + dp[i] = dp[i-1] + dp[i-dp[i-1]-2] + 2 + } else { + + dp[i] = dp[i-1] + 2 + } + } + rt = max(rt, dp[i]) + } + } + return rt +} diff --git a/Week 05/id_198/LeetCode_32_198_test.go b/Week 05/id_198/LeetCode_32_198_test.go new file mode 100644 index 000000000..650d1a983 --- /dev/null +++ b/Week 05/id_198/LeetCode_32_198_test.go @@ -0,0 +1,25 @@ +package leetcode + +import "testing" + +func Test_longestValidParentheses(t *testing.T) { + type args struct { + s string + } + tests := []struct { + name string + args args + want int + }{ + {name: "t1", args: args{s: ")()())"}, want: 4}, + {name: "t2", args: args{s: "()"}, want: 2}, + {name: "t3", args: args{s: "(()"}, want: 2}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := longestValidParentheses(tt.args.s); got != tt.want { + t.Errorf("longestValidParentheses() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/Week 05/id_198/LeetCode_42_198.go b/Week 05/id_198/LeetCode_42_198.go new file mode 100644 index 000000000..c2ef6ec58 --- /dev/null +++ b/Week 05/id_198/LeetCode_42_198.go @@ -0,0 +1,42 @@ +package leetcode + +//https://leetcode-cn.com/problems/trapping-rain-water +//接雨水: 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 +func trap(height []int) int { + + if len(height) < 1 { + + return 0 + } + + left := 0 + right := len(height) - 1 + ans := 0 + + leftMax := 0 + rightMax := 0 + + for left < right { + + if height[left] < height[right] { + + if height[left] >= leftMax { + + leftMax = height[left] + } else { + ans += (leftMax - height[left]) + } + left++ + } else { + + if height[right] > rightMax { + + rightMax = height[right] + } else { + ans += (rightMax - height[right]) + } + right-- + } + } + return ans +} diff --git a/Week 05/id_198/LeetCode_42_198_test.go b/Week 05/id_198/LeetCode_42_198_test.go new file mode 100644 index 000000000..a21a4ae9d --- /dev/null +++ b/Week 05/id_198/LeetCode_42_198_test.go @@ -0,0 +1,23 @@ +package leetcode + +import "testing" + +func Test_trap(t *testing.T) { + type args struct { + height []int + } + tests := []struct { + name string + args args + want int + }{ + {name: "sample", args: args{height: []int{0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1}}, want: 6}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := trap(tt.args.height); got != tt.want { + t.Errorf("trap() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/Week 05/id_198/LeetCode_4_198.go b/Week 05/id_198/LeetCode_4_198.go new file mode 100644 index 000000000..b578c3fac --- /dev/null +++ b/Week 05/id_198/LeetCode_4_198.go @@ -0,0 +1,83 @@ +package leetcode + +import "fmt" + +func findMedianSortedArrays(nums1 []int, nums2 []int) float64 { + + l1 := len(nums1) + l2 := len(nums2) + + // end 1 + e1 := (l1 + l2) / 2 + // end 2 + e2 := e1 + if (l1+l2)%2 == 0 { + + e1 = e1 - 1 + e2 = e1 + 1 + } + + v1 := 0 + v2 := 0 + + target := &nums1 + if len(nums1) == 0 { + + target = &nums2 + } + curr := 0 + + s1 := 0 + s2 := 0 + fmt.Printf("%d,%d\n", e1, e2) + for { + + if s1+s2 == e1 { + + v1 = (*target)[curr] + } + if s1+s2 == e2 { + + v2 = (*target)[curr] + break + } + + c1 := 0 + c2 := 0 + if s1 < l1 { + + c1 = nums1[s1] + if s2 < l2 { + + c2 = nums2[s2] + if c2 > c1 { + + target = &nums2 + curr = s2 + + } else { + + target = &nums1 + curr = s1 + } + } else { + + c2 = nums1[s1] + target = &nums1 + curr = s1 + + } + s1++ + continue + } + + if s2 < l2 { + + target = &nums2 + s2++ + curr = s2 + } + } + + return float64(v1+v2) / 2 +} diff --git a/Week 05/id_198/LeetCode_4_198_test.go b/Week 05/id_198/LeetCode_4_198_test.go new file mode 100644 index 000000000..7f1f90d03 --- /dev/null +++ b/Week 05/id_198/LeetCode_4_198_test.go @@ -0,0 +1,29 @@ +package leetcode + +import "testing" + +func Test_findMedianSortedArrays(t *testing.T) { + type args struct { + nums1 []int + nums2 []int + } + tests := []struct { + name string + args args + want float64 + }{ + {name: "sample", args: args{nums1: []int{1, 3}, nums2: []int{2}}, want: 2.0}, + {name: "sample-2", args: args{nums1: []int{1}, nums2: []int{2, 3}}, want: 2.0}, + {name: "sample-3", args: args{nums1: []int{}, nums2: []int{2, 3}}, want: 2.5}, + {name: "sample-4", args: args{nums1: []int{1, 3}, nums2: []int{2, 3}}, want: 2.5}, + {name: "sample-5", args: args{nums1: []int{1}, nums2: []int{2}}, want: 1.5}, + {name: "sample-6", args: args{nums1: []int{1, 2}, nums2: []int{3, 4}}, want: 2.5}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := findMedianSortedArrays(tt.args.nums1, tt.args.nums2); got != tt.want { + t.Errorf("findMedianSortedArrays() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/Week 05/id_198/NOTE.md b/Week 05/id_198/NOTE.md index a6321d6e2..520f96865 100644 --- a/Week 05/id_198/NOTE.md +++ b/Week 05/id_198/NOTE.md @@ -1,4 +1,52 @@ # NOTE - +- [ ] 什么样的问题适合DP? +## 回归 + +分治=>回溯=>递归=>动态规划 + +递归go代码模板: +```go +func recursion(level, param1, param2, ...): + //recursion terminator + if level > MAX_LEVEL{ + //process_result + return + } + // process logic in current level + process(level, data...) + + // drill down + recursion(level + 1, p1, ...) + + // reverse the current level status if needed +``` + +分治go代码模板 +```go +func divideConquer(problem, param1, param2, ...): + // recursion terminator + if problem == nil: + // print_result + return + + // prepare data + data = prepareData(problem) + subproblems = splitProblem(problem, data) + + // conquer subproblems + subresult1 = self.divide_conquer(subproblems[0], p1, ...) + subresult2 = self.divide_conquer(subproblems[1], p1, ...) + subresult3 = self.divide_conquer(subproblems[2], p1, ...) + … + + // process and generate the final result + result = process_result(subresult1, subresult2, subresult3, …) + + //revert the current level states +``` + +* [动态规划](https://en.wikipedia.org/wiki/Dynamic_programming) +* [MIT 动态规划课程最短路径算法](https://www.bilibili.com/video/av53233912?from=search&seid=2847395688604491997) +* [一个方法团灭 6 道股票问题](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/yi-ge-fang-fa-tuan-mie-6-dao-gu-piao-wen-ti-by-l-3/) \ No newline at end of file diff --git a/Week 05/id_203/LeetCode_221_203.go b/Week 05/id_203/LeetCode_221_203.go new file mode 100644 index 000000000..dba3216ca --- /dev/null +++ b/Week 05/id_203/LeetCode_221_203.go @@ -0,0 +1,49 @@ +package week05 + +/** +在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。 + +示例: + +输入: + +1 0 1 0 0 +1 0 1 1 1 +1 1 1 1 1 +1 0 0 1 0 + +输出: 4 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/maximal-square +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +func maximalSquare(matrix [][]byte) int { + var maxSide byte + for i := range matrix{ + for j := range matrix[i]{ + if i != 0 && j != 0 { + if matrix[i][j] == '1'{ + matrix[i][j] = 1 + min(matrix[i-1][j], matrix[i][j-1], matrix[i-1][j-1]) + } + } + + if matrix[i][j] - '0' > maxSide{ + maxSide = matrix[i][j] - '0' + } + } + } + + return int(maxSide) * int(maxSide) +} + +func min(a ...byte) byte { + min := a[0] + for i := 0; i < len(a); i++ { + if a[i] < min { + min = a[i] + } + } + return min +} \ No newline at end of file diff --git a/Week 05/id_203/LeetCode_32_203.go b/Week 05/id_203/LeetCode_32_203.go new file mode 100644 index 000000000..f63330ada --- /dev/null +++ b/Week 05/id_203/LeetCode_32_203.go @@ -0,0 +1,59 @@ +package week05 + +//我的解法 +//func longestValidParentheses(s string) int { +// maxans, n := 0, len(s) +// dp := make([]int, n) +// +// for i := 1; i < n; i++ { +// if s[i] == ')' { +// if s[i] == '(' { +// if i >= 2 { +// dp[i] = dp[i - 2] + 2 +// } else { +// dp[i] = 2 +// } +// } else if i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '(' { +// dp[i] = dp[i - 1] + 2 +// if (i - dp[i - 1]) >= 2 { +// dp[i] = dp[i - dp[i - 1] - 2] +// } +// } +// maxans = Max(maxans, dp[i]) +// } +// +// } +// +// return maxans +//} + +// 国际站的解法 +func longestValidParentheses(s string) int { + if len(s) == 0 { + return 0 + } + n, left, max := len(s), 0, 0 + dp := make([]int, n) + + for i := 0; i < n; i++ { + if s[i] == '(' { + left++ + } else if left > 0 { + dp[i] = dp[i-1] + 2 + if i-dp[i] > 0 { + dp[i] += dp[i-dp[i]] + } + max = Max(max, dp[i]) + left-- + } + } + + return max +} + +func Max(a, b int) int { + if a > b { + return a + } + return b +} \ No newline at end of file diff --git a/Week 05/id_213/MaxSubArray.java b/Week 05/id_213/MaxSubArray.java new file mode 100644 index 000000000..d8b4b19c3 --- /dev/null +++ b/Week 05/id_213/MaxSubArray.java @@ -0,0 +1,21 @@ +class Solution { +//思路 :建立一个等长数组,遍历数组求和,依次比较某下标与前一个下标求到的和, +//较大的和存在遍历到的下标位置。然后遍历数组,获取存取和的数组中的最大值。 + public int maxSubArray(int[] nums) { + int len = nums.length; + if (leng == 0) return 0; + int[] dp = new int[len]; + dp[0] = nums[0]; + for (int i = 1; i < len; i++) { + if (dp[i - 1] >= 0) { + dp[i] = dp[i - 1] +nums[i]; + } else { + dp[i] = nums[i]; + } + } + int res = dp[0]; + for (int i = 1; i < len; i++ ) + res = Math.max(res, dp[i]); + return res; + } +} \ No newline at end of file diff --git a/Week 05/id_213/MinimumTotal.java b/Week 05/id_213/MinimumTotal.java new file mode 100644 index 000000000..04db1219d --- /dev/null +++ b/Week 05/id_213/MinimumTotal.java @@ -0,0 +1,14 @@ +class Solution { +//思路 :通过从上至下递归,通过比较得到最小值。 +//思路 :由下至上DP,通过level层与level-1层的两者之和取小值存于level层的相应为值。 + public int minimumTotal(List triangle) { + int row = triangle.size(); + int[] minlen = new int[row + 1]; + for (int level = row - 1; level >= 0; level--) { + for (int i = 0; i <= level; i++) { + minlen[i] = Math.min(minlen[i], minlen[i + 1]) + triangle.get(level).get(i); + } + } + return minlen[0]; + } +} \ No newline at end of file diff --git a/Week 05/id_213/Rob.java b/Week 05/id_213/Rob.java new file mode 100644 index 000000000..37a92fb61 --- /dev/null +++ b/Week 05/id_213/Rob.java @@ -0,0 +1,15 @@ +class Solution { + public int rob(int[] nums) { + if (nums == null || nums.length == 0) return 0; + int[][] dp = new int[nums.length][2]; + dp[0][0] = 0; + dp[0][1] = nums[0]; + int ans = Math.max(dp[0][0], dp[0][1]); + for (int i = 1; i < nums.length; i++) { + dp[i][0] = Math.max(dp[i - 1][0] + dp[i - 1][1]); + dp[i][1] = dp[i - 1][0] + nums[i]; + ans = Max.max(Math.max(dp[i][0],dp[i][1]), ans); + } + return ans; + } +} \ No newline at end of file diff --git a/Week 05/id_213/UniquePath.java b/Week 05/id_213/UniquePath.java new file mode 100644 index 000000000..d379cc182 --- /dev/null +++ b/Week 05/id_213/UniquePath.java @@ -0,0 +1,16 @@ +class Solution{ +//思路:采用自底向上的思路,最后一步可由两步完成。有该位置的上方位和左方位完成。 +//某位置的到达方式可以有该位置的右位置和下位置的求和得到。 +//采用自底向上的方式时默认最后一行,最后一列只有一种方式。 + public int uniquePath(int m, int n) { + int[] cur = new int[n]; + Arrays.fill(cur,1); + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + cur[j] += cur[j-1]; + } + } + return cur[n-1]; + } + +} \ No newline at end of file diff --git a/Week 05/id_213/UniquePath2.java b/Week 05/id_213/UniquePath2.java new file mode 100644 index 000000000..c122b439c --- /dev/null +++ b/Week 05/id_213/UniquePath2.java @@ -0,0 +1,18 @@ +class Solution { +//该题思路与不同路径1类似:自底向上遍历,当有障碍时,该位置没有可行走的方式数量。 +//故其左位置或者上位置的方法数量加0即可。 + public int uniquePathsWithObstacles(int[][] obstacleGrid) { + int width = obstacleGrid[0].length; + int[] dp = new int[width]; + dp[0] = 1; + for(int[] rows : obstacleGrid) { + for (int j = 0; j < width; j++) { + if(rows[j] == 1) + dp[j] = 0; + else if (j > 0) + dp[j] += dp[j-1]; + } + } + return dp[width - 1]; + } +} \ No newline at end of file diff --git a/Week 05/id_218/LeetCode_53_219.java b/Week 05/id_218/LeetCode_53_219.java new file mode 100644 index 000000000..13e3416dd --- /dev/null +++ b/Week 05/id_218/LeetCode_53_219.java @@ -0,0 +1,22 @@ +package leetcode.week5; + +/** + * https://leetcode-cn.com/problems/maximum-subarray/ + * + * @author eason.feng at 2019/11/17/0017 17:21 + **/ +public class LeetCode_53_219 { + + public int maxSubArray(int[] nums) { + if (nums.length == 1) { + return nums[0]; + } + int max = nums[0]; + for (int i = 1; i < nums.length; i++) { + nums[i] = Math.max(0, nums[i - 1]) + nums[i]; + max = Math.max(max, nums[i]); + } + return max; + } + +} diff --git a/Week 05/id_218/LeetCode_62_218.java b/Week 05/id_218/LeetCode_62_218.java new file mode 100644 index 000000000..b1f98546b --- /dev/null +++ b/Week 05/id_218/LeetCode_62_218.java @@ -0,0 +1,35 @@ +package leetcode.week5; + +/** + * n 行, m 列 + * https://leetcode-cn.com/problems/unique-paths/ + * @author eason.feng at 2019/11/17/0017 14:35 + **/ +public class LeetCode_62_218 { + + public int uniquePaths(int m, int n) { + if (m == n && m == 1) { + return 1; + } + int[][] result = new int[n][m]; + for (int i = n - 1; i >= 0; i--) { + for (int j = m - 1; j >= 0; j--) { + if ((i == n - 1) && (j == m - 1)) { + result[i][j] = 0; + continue; + } + if ((i == n - 1) && (j != m - 1)) { + result[i][j] = 1; + continue; + } + if ((j == m - 1) && (i != n - 1)) { + result[i][j] = 1; + continue; + } + result[i][j] = ((i + 1) > n ? 0 : result[i + 1][j]) + ((j + 1) > m ? 0 : result[i][j + 1]); + } + } + return result[0][0]; + } + +} diff --git a/Week 05/id_218/LeetCode_63_218.java b/Week 05/id_218/LeetCode_63_218.java new file mode 100644 index 000000000..76d00c25c --- /dev/null +++ b/Week 05/id_218/LeetCode_63_218.java @@ -0,0 +1,52 @@ +package leetcode.week5; + +/** + * @author eason.feng at 2019/11/17/0017 15:01 + **/ +public class LeetCode_63_218 { + + public int uniquePathsWithObstacles(int[][] obstacleGrid) { + int n = obstacleGrid.length; + int m = obstacleGrid[0].length; + if (obstacleGrid[n - 1][m - 1] == 1) { + return 0; + } + if ((m == n && m == 1)) { + return 1; + } + int[][] result = new int[n][m]; + for (int i = n - 1; i >= 0; i--) { + for (int j = m - 1; j >= 0; j--) { + if (obstacleGrid[i][j] == 1) { + result[i][j] = 0; + continue; + } + if ((i == n - 1) && (j == m - 1)) { + result[i][j] = 0; + continue; + } + if ((i == n - 1) && (j != m - 1)) { + if (j + 1 == m - 1) { + result[i][j] = 1; + } + else { + result[i][j] = result[i][j + 1]; + } + continue; + } + if ((j == m - 1) && (i != n - 1)) { + if (i + 1 == n - 1) { + result[i][j] = 1; + } + else { + result[i][j] = result[i + 1][j]; + } + continue; + } + result[i][j] = ((i + 1) > n ? 0 : result[i + 1][j]) + ((j + 1) > m ? 0 : result[i][j + 1]); + } + } + return result[0][0]; + } + +} diff --git a/Week 05/id_218/NOTE.md b/Week 05/id_218/NOTE.md index a6321d6e2..c71de2445 100644 --- a/Week 05/id_218/NOTE.md +++ b/Week 05/id_218/NOTE.md @@ -1,4 +1,16 @@ -# NOTE +#### 动态规划(Dynamic Programming) - +##### 分治 回溯 递归 动态规划 +##### 动态特点: +* 动态规划和递归或者分治没有根本上的区别(关键看有无最优的子结构) +* 共性:找到重复的子问题 +* 差异性:最优子结构、中途可以淘汰次优解。 + +#### 动态规划关键点: + +* 最优子结构 opt[n] = best_of(opt[n-1], opt[n-2]) +* 存储中间状态:opt[i] +* 递推公式(状态转移方程或者DP方程) + * Fib:opt[n] = opt[n-1] + opt[n-2] + * 二维路径: opt[i, j] = opt[i+1][j] = opt[i][j+1](且判断a[i,j]是否为空地) diff --git a/Week 05/id_223/LeetCode_32_223.java b/Week 05/id_223/LeetCode_32_223.java new file mode 100644 index 000000000..2d29a7c10 --- /dev/null +++ b/Week 05/id_223/LeetCode_32_223.java @@ -0,0 +1,17 @@ +class Solution { + public int longestValidParentheses(String s) { + int dp[] = new int[s.length()]; + int res = 0; + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) == ')') { + if (s.charAt(i-1) == '(') { + dp[i] = (i>=2 ? dp[i-2] : 0) + 2; + }else if (i-dp[i-1]-1 >= 0 && s.charAt(i-dp[i-1]-1) == '(') { + dp[i] = dp[i-1] + (i-dp[i-1]-1 >=2 ? dp[i-dp[i-1]-2] : 0) + 2; + } + } + res = Math.max(dp[i], res); + } + return res; + } +} \ No newline at end of file diff --git a/Week 05/id_223/LeetCode_64_223.java b/Week 05/id_223/LeetCode_64_223.java new file mode 100644 index 000000000..cde06395d --- /dev/null +++ b/Week 05/id_223/LeetCode_64_223.java @@ -0,0 +1,13 @@ +class Solution { + public int minPathSum(int[][] grid) { + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[i].length; j++) { + if (i > 0 && j > 0) grid[i][j] = Math.min(grid[i-1][j],grid[i][j-1]) + grid[i][j]; + else if (i == 0 && j > 0) grid[i][j] = grid[i][j-1] + grid[i][j]; + else if (j == 0 && i > 0) grid[i][j] = grid[i-1][j] + grid[i][j]; + else grid[i][j] = grid[i][j]; + } + } + return grid[grid.length-1][grid[0].length-1]; + } +} \ No newline at end of file diff --git a/Week 05/id_223/NOTE.md b/Week 05/id_223/NOTE.md index a6321d6e2..94069805d 100644 --- a/Week 05/id_223/NOTE.md +++ b/Week 05/id_223/NOTE.md @@ -1,4 +1,18 @@ # NOTE +- 动态规划和递归或分治没有本质的区别(关键看有无最优的子结构) +- 共性: 找到重复子问题 +- 差异性:最优子结构、中途可以淘汰次优解 +> 自底向上 递推 + +##### 数学归纳法 + +###### 动态规划关键点 +1. 最优子结构(找重复性) +2. 储存中间状态(状态空间) +3. 递推公式:状态转移方程或DP方程 + +###### 最长公共子序列 +dp方程 if([s1-1]!=[s2-1]) max(dp[s1-1][s2],dp[s1][s2-1])else lcs([s1-1],[s2-1]) + 1; diff --git a/Week 05/id_243/DPSolveShoppingCar.java b/Week 05/id_243/DPSolveShoppingCar.java new file mode 100644 index 000000000..da4a0bfbb --- /dev/null +++ b/Week 05/id_243/DPSolveShoppingCar.java @@ -0,0 +1,75 @@ +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author eazonshaw + * @date 2019/11/13 11:30 + * + * 用动态规划解决女朋友的双十一购物车问题 + * 题目场景:假设购物的规则是满200减20,要从购物车中选择n个产品,让他们大于且尽量接近于200。 + */ +public class DPSolveShoppingCar { + + //所选择的商品列表 + private static List shoppingList = new ArrayList<>(); + + //假设items[]为商品数组,n为商品数量,w表示满减条件(即题设中的200) + public static void double11advance(int[] items,int n,int w){ + + //这里我们将商品的上线阈值定在w的3倍,建立动态规划的状态表 + int m = 3; + boolean[][] states = new boolean[n][m*w+1]; + //第一行数据特殊出力 + states[0][0] = true; + if(items[0]<= m*w){ + states[0][items[0]] = true; + } + //动态规划 + for(int i = 1;i=1;i--){ + //该商品有列入清单 + if(j >= items[i] && states[i-1][j-items[i]]){ + shoppingList.add(i); + j = j-items[i]; + } + } + if(j != 0) shoppingList.add(0); + } + + public static void main(String[] args) { + int[] items = new int[]{30,75,80,70,65,35,45,99}; + int n = items.length; + int m = 200; + double11advance(items,n,m); + shoppingList.stream().sorted().collect(Collectors.toList()).forEach(i -> System.out.println(items[i]));// 75,80,45 + } + + + +} diff --git a/Week 05/id_243/LeetCode_120_243.java b/Week 05/id_243/LeetCode_120_243.java new file mode 100644 index 000000000..d5f3911f1 --- /dev/null +++ b/Week 05/id_243/LeetCode_120_243.java @@ -0,0 +1,62 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * @author eazonshaw + * @date 2019/11/15 15:35 + * + * 题目:三角形最小路径和 + * + */ +public class LeetCode_120_243 { + + //动态规划,利用状态转移表,建立二维数组(自顶向下) + public int minimumTotal1(List> triangle) { + int n = triangle.size(); + if(n == 0){ + return 0; + } + //状态转移表 + int[][] states = new int[n][n]; + //记录初始坐标 + states[0][0] = triangle.get(0).get(0); + //动态规划 + for(int i = 1;i < n;i++){ + List list = triangle.get(i); + for(int j = 0;j < list.size();j++){ + if(j == 0){ + states[i][0] = states[i-1][0] + list.get(j); + } + else if(j == list.size()-1){ + states[0][j] = states[0][j-1] + list.get(j); + } + else{ + states[i-j][j] = Math.min(states[i-j-1][j],states[i-j][j-1]) + list.get(j); + } + } + } + //比较计算最小路径值 + int rs = Integer.MAX_VALUE; + for(int i = 0;i < n;i++){ + rs = Math.min(states[n-1-i][i],rs); + } + return rs; + } + + //动态规划,自底向上,利用一位数组 + public int minimumTotal2(List> triangle) { + int n = triangle.size(); + if(n == 0){ + return 0; + } + int[] states = new int[n+1]; + for(int i = n-1;i >= 0;i--){ + for(int j = 0;j <= i;j++) { + states[j] = Math.min(states[j],states[j+1]) + triangle.get(i).get(j); + } + } + return states[0]; + } + + +} diff --git a/Week 05/id_243/LeetCode_221_243.java b/Week 05/id_243/LeetCode_221_243.java new file mode 100644 index 000000000..f3af383ff --- /dev/null +++ b/Week 05/id_243/LeetCode_221_243.java @@ -0,0 +1,41 @@ +/** + * @author eazonshaw + * @date 2019/11/17 21:35 + * + * 题目:221.最小正方形 + */ +public class LeetCode_221_243 { + + public int maximalSquare(char[][] matrix) { + if(matrix.length == 0){ + return 0; + } + int m = matrix.length; + int n = matrix[0].length; + int[][] states = new int[m][n]; + //处理边界值 + states[0][0] = matrix[0][0] == '0'? 0 : 1; + //定义最长边长 + int max_side = states[0][0]; + for(int row = 1;row < m;row++){ + states[row][0] = matrix[row][0] == '0'? 0 : 1; + max_side = Math.max(max_side,states[row][0]); + } + for(int col = 1;col < n;col++){ + states[0][col] = matrix[0][col] == '0'? 0 : 1; + max_side = Math.max(max_side,states[0][col]); + } + for(int row = 1;row < m;row++){ + for(int col = 1;col < n;col++){ + states[row][col] = matrix[row][col] == '0'? + 0 : Math.min(Math.min(states[row-1][col],states[row-1][col-1]),states[row][col-1]) + 1; + max_side = Math.max(max_side,states[row][col]); + } + } + return max_side*max_side; + } + +} + +//动态规划,子问题,f(i,j) = matrix(i,j) == 0 ? 0 : min(f(i-1,j),f(i-1,j-1),f(i,j-1))+1 +//再设置一个变量记录最大边长值 max_side diff --git a/Week 05/id_243/LeetCode_53_243.java b/Week 05/id_243/LeetCode_53_243.java new file mode 100644 index 000000000..cfd8b05b7 --- /dev/null +++ b/Week 05/id_243/LeetCode_53_243.java @@ -0,0 +1,34 @@ +/** + * @author eazonshaw + * @date 2019/11/15 16:52 + * + * 题目:53. 最大子序和 + * 描述:给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 + */ +public class LeetCode_53_243 { + + //暴力法,冒泡双循环,找到最大值 + public int maxSubArray1(int[] nums) { + int max = Integer.MIN_VALUE; + for(int i = 0;i < nums.length;i++){ + int currSum = 0; + for(int j = i;j < nums.length;j++){ + currSum = currSum + nums[j]; + max = Math.max(max,currSum); + } + } + return max; + } + + //动态规划 + public int maxSubArray2(int[] nums) { + int currSum = nums[0]; + int max = currSum; + for(int i = 1;i < nums.length;i++){ + currSum = Math.max(currSum + nums[i],nums[i]); + max = Math.max(currSum,max); + } + return max; + } + +} diff --git a/Week 05/id_243/LeetCode_64_243.java b/Week 05/id_243/LeetCode_64_243.java new file mode 100644 index 000000000..2c5f09c30 --- /dev/null +++ b/Week 05/id_243/LeetCode_64_243.java @@ -0,0 +1,52 @@ +/** + * @author eazonshaw + * @date 2019/11/17 20:48 + * + * 题目:64.最小路径和 + */ +public class LeetCode_64_243 { + + //从原坐标出发,当前节点的值,等于上面的值与左边的值中的较小值加上自己本身,其中要记得处理边界问题,即n==0和m==0 + //dp方程:dp(i,j) = Math.min(dp(i-1,j),dp(i,j-1))+grid[i][j] + public int minPathSum1(int[][] grid) { + int m = grid.length; + int n = grid[0].length; + int[][] states = new int[m][n]; + //处理边界问题 + states[0][0] = grid[0][0]; + for(int i = 1;i < m;i++){ + states[i][0] = states[i-1][0] + grid[i][0]; + } + for(int j = 1;j < n;j++){ + states[0][j] = states[0][j-1] + grid[0][j]; + } + //动态转移方程 + for(int i = 1;i < m;i++){ + for(int j = 1;j < n;j++){ + states[i][j] = Math.min(states[i-1][j],states[i][j-1]) + grid[i][j]; + } + } + return states[m-1][n-1]; + } + + //将状态数组转为一维数组 + //dp方程为:dp(i) = Math.min(dp(i-1),dp(i))+grid[j][i] + public int minPathSum2(int[][] grid) { + int m = grid.length; + int n = grid[0].length; + int[] states = new int[n]; + //初始化 + states[0] = grid[0][0]; + for(int col = 1;col < n;col++){ + states[col] = states[col-1] + grid[0][col]; + } + //dp + for(int row = 1;row < m;row++){ + for(int col = 0;col < n;col++){ + states[col] = col == 0?states[col] + grid[row][col]:Math.min(states[col-1],states[col])+grid[row][col]; + } + } + return states[n-1]; + } +} + diff --git a/Week 05/id_243/LeetCode_70_243.java b/Week 05/id_243/LeetCode_70_243.java new file mode 100644 index 000000000..4dfd4b00b --- /dev/null +++ b/Week 05/id_243/LeetCode_70_243.java @@ -0,0 +1,23 @@ +/** + * @author eazonshaw + * @date 2019/11/15 15:12 + */ +public class LeetCode_70_243 { + + public int climbStairs(int n) { + //状态数组 + int[] states = new int[n+1]; + //当n为1时特殊处理 + if(n == 1){ + return 1; + } + states[0] = 1; + states[1] = 1; + + for(int i = 2;i 把问题分解为多个阶段,每个阶段对应一个决策,记录每个阶段可达的状态集合(去重),通过当前阶段的状态集合,来推导下一阶段的状态集合,动态地往前推进。 +### 动态规划问题特征 +> 一个模型,三个特征 +#### 一个模型 +**多阶段决策最优解模型** +#### 三个特征 +1. 最优子结构 +2. 无后效性(只能前进,不能后退) +3. 重复子问题(利用回溯法,画出递归树,其中有重复的状态) + +### 动态规划问题核心思维 +#### 状态转移表法 +> 回溯算法实现 - 定义状态 - 画递归树 - 找重复子问题 - 画状态转移表 - 根据递推关系填表 - 将填表过程翻译成代码 +#### 状态转移方程(关键) +> 找最优子结构 - 写状态转移方程 - 将状态转移方程翻译成代码 + +### 动态规划问题关键点 +1. 动态规划和递归/分治没有根本上的区别(关键看有没有最优子结构) +2. 共性:找到重复子问题 +3. 差异性:最优子结构,中途可以淘汰次优解 + +### 工业例子 +#### 如何简单实现搜索引擎的拼写纠错功能? +> 关键点:如何量化两个字符串的相似度? + +**KEY:编辑距离** +* 莱温斯坦距离(描述差异化,可以进行增、删、改操作) +* 最长公共子串长度(描述相似性,可以进行增、删操作) + +---- + +### 动态规划算法问题解题思路 +1. 重复性(将问题分解为子问题) +2. 储存中间状态 +3. 递归公式(状态转移方程/DP方程) + +### 思维小结 +1. 打破思维惯性,形成及其思维 +2. 理解复杂逻辑的关键 +3. 也是职业进步的药店要领 + +### LeetCode +* [70.爬楼梯](https://leetcode-cn.com/problems/climbing-stairs/description/) +* [120.三角形最小路径和](https://leetcode-cn.com/problems/triangle/description/) +* [53.最大子序和](https://leetcode-cn.com/problems/maximum-subarray/) +* [64.最小路径和](https://leetcode-cn.com/problems/minimum-path-sum/) +* [221.最小正方形](https://leetcode-cn.com/problems/maximal-square/submissions/) diff --git a/Week 05/id_253/LeetCode_1143_253.java b/Week 05/id_253/LeetCode_1143_253.java new file mode 100644 index 000000000..c4ef997d1 --- /dev/null +++ b/Week 05/id_253/LeetCode_1143_253.java @@ -0,0 +1,14 @@ +class Solution { + public int longestCommonSubsequence(String text1, String text2) { + int n1 = text1.length(); + int n2 = text2.length(); + int[][] dp = new int[n1+1][n2+1]; + for(int i =1 ;i < n1+1 ; i++){ + for(int j =1 ; j< n2+1 ; j++){ + if(text1.charAt(i - 1) == text2.charAt(j - 1)) dp[i][j]=dp[i-1][j-1] + 1 ; + else dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]) ; + } + } + return dp[n1][n2]; + } +} diff --git a/Week 05/id_253/LeetCode_120_253.java b/Week 05/id_253/LeetCode_120_253.java new file mode 100644 index 000000000..62288c14b --- /dev/null +++ b/Week 05/id_253/LeetCode_120_253.java @@ -0,0 +1,27 @@ +class Solution { + public int minimumTotal(List> triangle) { + int[] dp = new int[triangle.size()+1]; + for(int i =triangle.size()-1 ; i>=0 ; i--){ + for(int j=0 ; j> triangle) { +// row = triangle.size(); +// memo = new Integer[row][row]; +// return helper(0,0,triangle); +// } +// public int helper(int level , int c , List> triangle){ +// if(memo[level][c] != null) return memo[level][c]; +// if(level == row -1) return memo[level][c]=triangle.get(level).get(c); +// int left = helper(level+1,c,triangle); +// int right = helper(level+1,c+1,triangle); +// return memo[level][c] = Math.min(left,right)+triangle.get(level).get(c); +// } +// } diff --git a/Week 05/id_253/LeetCode_152_253.java b/Week 05/id_253/LeetCode_152_253.java new file mode 100644 index 000000000..19ac2c017 --- /dev/null +++ b/Week 05/id_253/LeetCode_152_253.java @@ -0,0 +1,17 @@ +class Solution { + public int maxProduct(int[] nums) { + int max = Integer.MIN_VALUE; + int imax = 1 ; int imin = 1; + for(int num : nums){ + if(num < 0 ) { + int temp = imax; + imax = imin; + imin = temp; + } + imax = Math.max(imax * num,num); + imin = Math.min(imin * num,num); + max = Math.max(imax,max); + } + return max; + } +} diff --git a/Week 05/id_253/LeetCode_198_253.java b/Week 05/id_253/LeetCode_198_253.java new file mode 100644 index 000000000..4fb9bd771 --- /dev/null +++ b/Week 05/id_253/LeetCode_198_253.java @@ -0,0 +1,14 @@ +class Solution {//0是不偷,1是偷 + public int rob(int[] nums) { + int n = nums.length; + if(n == 0) return 0; + int[][] dp = new int[n+1][3]; + dp[0][0] = 0; + dp[0][1] = nums[0]; + for(int i=1 ;i < n ; i++){ + dp[i][0] = Math.max(dp[i-1][0] , dp[i-1][1]); + dp[i][1] = dp[i-1][0] + nums[i]; + } + return Math.max(dp[n-1][0],dp[n-1][1]); + } +} diff --git a/Week 05/id_253/LeetCode_221_253.java b/Week 05/id_253/LeetCode_221_253.java new file mode 100644 index 000000000..7cbe00d46 --- /dev/null +++ b/Week 05/id_253/LeetCode_221_253.java @@ -0,0 +1,17 @@ +class Solution { + public int maximalSquare(char[][] matrix) { + if(matrix.length == 0) return 0 ; + int m = matrix.length;int n = matrix[0].length; + int[][] dp = new int[m+1][n+1]; + int res=0 ; + for(int i =1 ; i <= m ; i++){ + for(int j=1 ; j<=n ; j++){ + if(matrix[i-1][j-1]=='1') { + dp[i][j] = Math.min(Math.min(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1]) + 1; + res = Math.max(dp[i][j],res); + } + } + } + return res*res; + } +} diff --git a/Week 05/id_253/LeetCode_53_253.java b/Week 05/id_253/LeetCode_53_253.java new file mode 100644 index 000000000..5b47809fe --- /dev/null +++ b/Week 05/id_253/LeetCode_53_253.java @@ -0,0 +1,12 @@ +class Solution { + public int maxSubArray(int[] nums) { + int ans = nums[0]; + int sum =0; + for(int num : nums){ + if(sum>0) sum+=num; + else sum=num; + ans = Math.max(sum,ans); + } + return ans; + } +} diff --git a/Week 05/id_253/LeetCode_621_253.java b/Week 05/id_253/LeetCode_621_253.java new file mode 100644 index 000000000..58f341776 --- /dev/null +++ b/Week 05/id_253/LeetCode_621_253.java @@ -0,0 +1,35 @@ +// class Solution { +// public int leastInterval(char[] tasks, int n) { +// int[] map = new int[26]; +// for(char ch : tasks){ +// map[ch - 'A']++; +// } +// int time=0; +// Arrays.sort(map); +// while(map[25]>0){ +// int i =0 ; +// while(i<=n){ +// if(map[25] == 0) break; +// if(i<26 && map[25-i]>0) map[25-i]--; +// time++; +// i++; +// } +// Arrays.sort(map); +// } +// return time; +// } +// } +public class Solution { + public int leastInterval(char[] tasks, int n) { + int[] map = new int[26]; + for(char c : tasks){ + map[c - 'A']++; + } + Arrays.sort(map); + int prespace = map[25] - 1; int idlespace = prespace*n; + for(int i =24 ; i >= 0 && map[i] > 0 ; i--){ + idlespace -= Math.min(prespace,map[i]); + } + return (idlespace> 0)? idlespace + tasks.length : tasks.length; + } +} diff --git a/Week 05/id_253/LeetCode_62_253.java b/Week 05/id_253/LeetCode_62_253.java new file mode 100644 index 000000000..e131683e8 --- /dev/null +++ b/Week 05/id_253/LeetCode_62_253.java @@ -0,0 +1,18 @@ +class Solution { + public int uniquePaths(int m, int n) { + int[][] path = new int[m][n]; + for(int i =0 ; i < m ; i++){ + path[i][0] = 1; + } + for(int i =0 ; i < n ; i++){ + path[0][i] = 1; + } + for(int i =1 ; i < m ; i++){ + for(int j=1 ; j < n ; j++){ + path[i][j] = path[i-1][j] + path[i][j-1]; + } + } + return path[m-1][n-1]; + } +} + diff --git a/Week 05/id_253/LeetCode_63_253.java b/Week 05/id_253/LeetCode_63_253.java new file mode 100644 index 000000000..438e9d4db --- /dev/null +++ b/Week 05/id_253/LeetCode_63_253.java @@ -0,0 +1,29 @@ +class Solution { + public int uniquePathsWithObstacles(int[][] obstacleGrid) { + int m = obstacleGrid.length;int n = obstacleGrid[0].length; + if((m == 1 || n == 1)) { + if(obstacleGrid[m-1][n-1]==1) return 0; + if(obstacleGrid[m-1][n-1]==0 && obstacleGrid[0][0]==0) return 1; + if(obstacleGrid[0][0]==1) return 0; + } + int[][] path = new int[m][n]; + path[0][0] = obstacleGrid[0][0] == 1 ? 0 : 1; + for (int i = 1; i < m; i++) { + path[i][0] = ((obstacleGrid[i][0] == 0 && path[i - 1][0] == 1) ? 1 : 0); + } + for (int j = 1; j < n; j++) { + path[0][j] = ((obstacleGrid[0][j] == 0 && path[0][j - 1] == 1) ? 1 : 0); + } + for(int i=1;i= 0 ; i--){ + for(int j = i ; j < n ; j++){ + dp[i][j] = (s.charAt(i) == s.charAt(j)) && (j-i<3||dp[i+1][j-1]); + if(dp[i][j]) res++; + } + } + return res; + } +} diff --git a/Week 05/id_253/LeetCode_64_253.java b/Week 05/id_253/LeetCode_64_253.java new file mode 100644 index 000000000..e81ca1ef9 --- /dev/null +++ b/Week 05/id_253/LeetCode_64_253.java @@ -0,0 +1,25 @@ +class Solution { + public int minPathSum(int[][] grid) { + if(grid.length == 0) return 0; + int m = grid.length ; int n = grid[0].length; + int[][] dp = new int[m][n]; + dp[0][0] = grid[0][0]; + int sum=dp[0][0]; + for(int i =1 ;i < m ;i++){ + sum+=grid[i][0]; + dp[i][0] = sum; + } + sum = dp[0][0]; + for(int i =1 ; i< n ; i++){ + sum+=grid[0][i]; + dp[0][i] = sum;; + } + int res = 0; + for(int i =1 ; i < m ;i ++){ + for(int j =1 ;j=1 && first<=9) dp[i] += dp[i-1]; + if(second>=10 && second<=26) dp[i] += dp[i-2]; + } + return dp[n]; + } +} diff --git a/Week 05/id_258/LeetCode_120_258.js b/Week 05/id_258/LeetCode_120_258.js new file mode 100644 index 000000000..8c7a5a3a4 --- /dev/null +++ b/Week 05/id_258/LeetCode_120_258.js @@ -0,0 +1,23 @@ +/* + * @lc app=leetcode.cn id=120 lang=javascript + * + * [120] 三角形最小路径和 + */ + +// @lc code=start +/** + * @param {number[][]} triangle + * @return {number} + */ +var minimumTotal = function(triangle) { + + for(let i = triangle.length - 2; i >= 0; i--) { + for(let j = 0; j < triangle[i].length; j++) { + triangle[i][j] += Math.min(triangle[i+1][j], triangle[i+1][j+1]); + } + } + + return triangle[0][0]; +}; +// @lc code=end + diff --git a/Week 05/id_258/LeetCode_198_258.js b/Week 05/id_258/LeetCode_198_258.js new file mode 100644 index 000000000..7ebae7682 --- /dev/null +++ b/Week 05/id_258/LeetCode_198_258.js @@ -0,0 +1,33 @@ +/* + * @lc app=leetcode.cn id=198 lang=javascript + * + * [198] 打家劫舍 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var rob = function (nums) { + if (!nums || nums.length === 0) return false; + + let n = nums.length; + + let a = [ + [], + [] + ]; + + a[0][0] = 0; + a[1][0] = nums[0]; + + for (let i = 1; i < nums.length; i++) { + a[0][i] = Math.max(a[1][i - 1], a[0][i - 1]); + a[1][i] = a[0][i - 1] + nums[i]; + } + + return Math.max(a[0][n - 1], a[1][n - 1]); + +}; +// @lc code=end \ No newline at end of file diff --git a/Week 05/id_258/LeetCode_53_258.js b/Week 05/id_258/LeetCode_53_258.js new file mode 100644 index 000000000..7af6f5b96 --- /dev/null +++ b/Week 05/id_258/LeetCode_53_258.js @@ -0,0 +1,21 @@ +/* + * @lc app=leetcode.cn id=53 lang=javascript + * + * [53] 最大子序和 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var maxSubArray = function(nums) { + let dp = nums; + + for(let i = 1; i < nums.length; i++) { + dp[i] = Math.max(dp[i-1], 0) + dp[i]; + } + return Math.max.apply(null,dp); +}; +// @lc code=end + diff --git a/Week 05/id_258/LeetCode_64_258.js b/Week 05/id_258/LeetCode_64_258.js new file mode 100644 index 000000000..6d312f9be --- /dev/null +++ b/Week 05/id_258/LeetCode_64_258.js @@ -0,0 +1,29 @@ +/* + * @lc app=leetcode.cn id=64 lang=javascript + * + * [64] 最小路径和 + */ + +// @lc code=start +/** + * @param {number[][]} grid + * @return {number} + */ +var minPathSum = function(grid) { + if(!grid || grid.length === 0) return false; + + let dp = []; + for(let i = 0; i < grid.length; i++) { + + } + + for(let i = grid.length - 1; i >= 1; i--) { + for(let j = grid[i].length -1 ; j >= 1; j--) { + dp[i][j] = Math.min(grid[i-1][j], grid[i][j-1]) + grid[i][j]; + } + } + + return dp[0][0]; +}; +// @lc code=end + diff --git a/Week 05/id_258/LeetCode_70_258.js b/Week 05/id_258/LeetCode_70_258.js new file mode 100644 index 000000000..459c6c215 --- /dev/null +++ b/Week 05/id_258/LeetCode_70_258.js @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode.cn id=70 lang=javascript + * + * [70] 爬楼梯 + */ + +// @lc code=start +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function (n) { + // 记忆化搜索 + var memo = [] + var fib = function(n) { + if(n===1 || n===2) return n; + + if(!memo[n]) { + memo[n] = fib(n - 1) + fib(n - 2) + } + return memo[n] + } + return fib(n) +}; + +climbStairs(4) +// @lc code=end \ No newline at end of file diff --git a/Week 05/id_273/LeetCode_1143_273.java b/Week 05/id_273/LeetCode_1143_273.java new file mode 100644 index 000000000..6ca7973d7 --- /dev/null +++ b/Week 05/id_273/LeetCode_1143_273.java @@ -0,0 +1,29 @@ +//1143. 最长公共子序列 + +//解法1:动态规划 执行用时:7ms +//思路:对于两个字符串的公共子序列, 存在以下几种情况 +// 1. S1 = "" S2 = 任意字符串 此时不存在公共子序列 +// 2. S1 = "A" S2 = 任意字符串 此时需要判断S2中是否存在S1, 只要存在公共子序列长度为1 +// 3. S1 = "...A" S2 = "........A" S1,S2长度不同, 但最后一位都是同一个字符串, 那么公共子序列长度至少为1 +// 那么第三种情况就可以确定子问题为:S1, S2的子序列长度 = 1 + (S1, S2除去公共子序列"A"后剩余部分的公共子序列的长度) +// 那么状态方程为: +// 若S1, S2最后一位字符相同:dp[i][j] = 1 + dp[i - 1][j - 1] +// 若S1, S2最后一位不相同:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) +// 明确了边界条件与状态方程, 程序就很好写了 +//时间复杂度:O(m*n) +//空间复杂度:O(m*n) +//总结:通过比较了不同写法, 把字符串用数组缓存然后对数组操作比起直接操作字符串的效率要高出不少 +// +public int longestCommonSubsequence(String text1, String text2) { + if(text1.equals(text2)) return text1.length(); + char[] t1 = text1.toCharArray(); + char[] t2 = text2.toCharArray(); + int[][] dp = new int[t1.length + 1][t2.length + 1]; + for (int i = 0; i < t1.length; i++) { + for (int j = 0; j < t2.length; j++) { + if (t1[i] == t2[j]) dp[i + 1][j + 1] = dp[i][j] + 1; + else dp[i + 1][j + 1] = Math.max(dp[i][j + 1], dp[i + 1][j]); + } + } + return dp[dp.length - 1][dp[0].length - 1]; +} \ No newline at end of file diff --git a/Week 05/id_273/LeetCode_120_273.java b/Week 05/id_273/LeetCode_120_273.java new file mode 100644 index 000000000..6df4ece66 --- /dev/null +++ b/Week 05/id_273/LeetCode_120_273.java @@ -0,0 +1,77 @@ +//120. 三角形的最小路径和 + +//解法1:DFS 超时 +//思路:我们要获取最顶层节点的最小路径和, 那么就需要通过比较下层相邻的两个节点的最小路径和, 然后选取一个最小值 +// 因此可以通过深度优先递归计算上述重复子问题 +//时间复杂度:O(2^N) +//空间复杂度:O(2^N) +public int minimumTotal(List> triangle) { + int size = triangle.size(); + return recur(triangle, size, 0, 0); +} + +private int recur(List> triangle, int size, int row, int col) { + if (row == size - 1) return triangle.get(row).get(col); + int left = recur(triangle, size, row + 1, col); + int right = recur(triangle, size, row + 1, col + 1); + return triangle.get(row).get(col) + Math.min(left, right); +} + +//解法2:记忆化递归 执行用时:1ms +//思路:基于DFS递归的基础上, 添加一个缓存用于存放每次计算的结果, 避免了重复的计算 +//时间复杂度:O(m*n) m=row, n=col +//空间复杂度:O(m*n) +public int minimumTotal(List> triangle) { + int size = triangle.size(); + int[][] cache = new int[size][size]; + return recur(triangle, size, 0, 0, cache); +} + +private int recur(List> triangle, int size, int row, int col, int[][] cache) { + if (cache[row][col] != 0) return cache[row][col]; + else if (row == size - 1) return cache[row][col] = triangle.get(row).get(col); + int left = recur(triangle, size, row + 1, col, cache); + int right = recur(triangle, size, row + 1, col + 1, cache); + return cache[row][col] = Math.min(left, right) + triangle.get(row).get(col); +} + +//解法3:动态规划(二维数组版) 执行时间:3ms +//思路:重复子问题:当前节点的最小路径和 = 下一层中相邻两个节点路径长度较小值 + 当前节点的路径长度 +// 那么就可以推导出状态方程dp[i][j] = min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j); +// 因此可以通过一个二维数组存储每一个节点的最小路径和, 最后返回最顶层节点的最小路径和 +//时间复杂度:O(m*n) m=row, n=col +//空间复杂度:O(m*n) +public int minimumTotal(List> triangle) { + int row = triangle.size(); + int col = triangle.get(row - 1).size(); + int[][] dp = new int[row][col]; + for (int i = 0; i < col; i++) { + dp[row - 1][i] = triangle.get(row - 1).get(i); + } + for (int i = row - 2; i >= 0; i--) { + for (int j = 0; j <= i); j++) { + dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j); + } + } + return dp[0][0]; +} + +//解法3.1 动态规划(一维数组版) 执行用时:3ms +//思路:基于解法3, 可以发现每次获取当前节点的最小路径和, 只需要通过下一层的两个相邻节点的最小路径和进行计算 +// 那么我们就可以选择只用一个一维数组存储每一层所有节点的最小路径和, 当计算上一层的节点时再根据当前数组中暂存的值进行计算即可 +//时间复杂度:O(m*n) +//空间复杂度:O(n) +public int minimumTotal(List> triangle) { + int row = triangle.size(); + int col = triangle.get(row - 1).size(); + int[] dp = new int[col]; + for (int i = 0; i < col; i++) { + dp[i] = triangle.get(row - 1).get(i); + } + for (int i = row - 2; i >= 0; i--) { + for (int j = 0; j <= i; j++) { + dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j); + } + } + return dp[0]; +} \ No newline at end of file diff --git a/Week 05/id_273/LeetCode_121_273.java b/Week 05/id_273/LeetCode_121_273.java new file mode 100644 index 000000000..83fe84fb4 --- /dev/null +++ b/Week 05/id_273/LeetCode_121_273.java @@ -0,0 +1,126 @@ +//121. 买卖股票的最佳时机 + + + +//解法1:暴力枚举 执行用时:280ms +//思路:获取所有买入卖出的组合, 计算其中的利润最大值返回 +//时间复杂度:O(n^2) +//空间复杂度:O(1) +public int maxProfit(int[] prices) { + int sum = 0; + for (int i = 0; i < prices.length - 1; i++) { + for (int j = i + 1; j < prices.length; j++) { + sum = Math.max(sum, prices[j] - prices[i]); + } + } + return sum; +} + +//解法1.1:暴力枚举降维 执行用时:2ms +//思路:我们在枚举所有买入卖出的组合时, 只需要通过枚举 "当天价格 - 之前某天的最小价格" 的所有结果, 然后返回其中的最大值即是最大利润 +//时间复杂度:O(n) +//空间复杂度:O(1) +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int sum = 0; + int minVal = prices[0]; + for (int i = 1; i < prices.length; i++) { + sum = Math.max(sum, prices[i] - minVal); + minVal = Math.min(minVal, prices[i]); + } + return sum; +} + +//股票问题动态规划统一分析: +//1. 穷举所有的"状态" +// 每天都可以有三种选择分别是:买入buy, 卖出sell, 无操作rest +// 买入必须在卖出之后, 因为题目限制只能完成一次交易后才能开始第二次交易 +// 卖出必须在买入之后, 因为卖出的前提是要持有股票 +// 无操作可以在买入后继续保持持有股票, 也可以在卖出后继续保持不持有股票 + +// 那么可以通过一个三维数组存放这几种状态的全部组合:DP[i][k][0 or 1] +// 语义为:当前为第i天,进行第k次交易,当前未持有/持有股票 +// 最后要求的结果则是:DP[n - 1][k - 1][0] +// 即:最后一天, 不能再进行交易, 且手上没持有股票 + +//2. 状态转移方程: +// 那么统一的状态转移方程就可以确定如下: +// 1. DP[i][k][0] = max(DP[i - 1][k][0], DP[i - 1][k][1] + prices[i]) +// 即:第i天未持有股票, 那么可以从中择优:i - 1天也未持有股票, 第i天继续保持未持有; i - 1天持有股票, 第i天抛售股票完成一次交易 +// 2. DP[i][k][1] = max(DP[i - 1][k][1], DP[i - 1][k - 1][0] - prices[i]) +// 即:第i天持有股票, 那么可以从中择优:i - 1天持有股票, 第i天继续保持持有; i - 1天未持有股票, 在第k - 1次交易的利润额基础上再买入一支股票 +// BaseCase: +// DP[i][0][0] = 0 :k从1开始进行交易, k = 0, 不允许交易 + +//3. 当前问题分析 +// 对于当前题目的要求, K = 1, 也就是这几天内只能完成一笔交易, 则状态方程如下: +// 1. DP[i][1][0] = max(DP[i - 1][1][0], DP[i - 1][1][1] + prices[i]) +// 2. DP[i][1][1] = max(DP[i - 1][1][1], (DP[i - 1][0][0] == 0) - prices[i]) +// 可以发现第2个转移方程中出现的"DP[i - 1][0][0]", 对应BaseCase可以省略不写 +// 而其他情况中k都为1, 即k对状态转移不产生影响, 因此状态K也可以省略不写 +// 到最后, 这个问题的状态转移方程如下: +// 1. DP[i][0] = max(DP[i - 1][0], DP[i - 1][1] + prices[i]) +// 2. DP[i][1] = max(DP[i - 1][1], -prices[i]) + +//解法2:动态规划(三维数组) 执行用时:7ms +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int[][][] dp = new int[prices.length][2][2]; + for (int i = 0; i < prices.length; i++) { + for (int k = 1; k >= 1; k--) { + if (i - 1 == -1) { + dp[i][k][0] = 0; + dp[i][k][1] = -prices[i]; + continue; + } + //当天不持股 = max(前一天也不持股, 前一天持股卖出) + dp[i][k][0] = Math.max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]); + //当天持股 = max(持有前一天的股, 前一天不持股买入) + dp[i][k][1] = Math.max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i]); + } + } + return dp[prices.length - 1][1][0]; +} + +//解法2.1:动态规划(二维数组版) 执行用时:3ms +//思路:可以将当天的状态分为股票未持有和持有, 当天未持有或持有能获取的最大利润额度分别用dp[i][0], dp[i][1]表示 +// 首先我们需要明确问题, 整个流程只能进行一次买卖操作!! +// 我们将买入股票设为-prices[i], 卖出股票设置为+prices[i] +// 若当天选择不持有股票, 那么有两种情况: +// 1. 前一天未持有股票:那么当天选择不持有股票的利润额就跟前一天不持有股票的利润额相同 dp[i][0] = dp[i - 1][0] +// 2. 前一天持有股票:那么当天选择不持有股票(抛售股票)的利润额为前一天持有的利润额加上当天卖出的价钱 dp[i][0] = dp[i - 1][1] + prices[i] +// 因此当天不持有股票的最大利润额 dp[i][0] = max([i - 1][0], dp[i - 1][1] + prices[i]) +// 若当天选择持有股票, 也分为两种情况: +// 1. 前一天若未持有股票, 那么当天可以选择买入股票 dp[i][1] = -prices[i]; +// 2. 前一天若持有股票, 那么当天可以选择继续持有原有的股票dp[i][1] = dp[i - 1][1] +// 因此当天持有股票情况的最大利润额 dp[i][1] = max(-prices[i], dp[i - 1][1]) +//时间复杂度:O(n) +//空间复杂度:O(n) +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int[][] dp = new int[prices.length][2]; + dp[0][0] = 0; + dp[0][1] = -prices[0]; + for (int i = 1; i < prices.length; i++) { + dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]); + dp[i][1] = Math.max(-prices[i], dp[i - 1][1]); + } + return dp[prices.length - 1][0]; +} + +//解法2.1:动态规划(状态压缩) 执行用时:2ms +//思路:我们发现解法2在计算过程中, 计算当天利润额只用到了前一天持有股票和未持有股票的利润额 +// 那么可以压缩数组长度为2, 仅仅用于保存当天和前一天的利润额 +//时间复杂度:O(n) +//空间复杂度:O(1) +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int[] dp = new int[2]; + dp[0] = 0; + dp[1] = -prices[0]; + for (int i = 1; i < prices.length; i++) { + dp[0] = Math.max(dp[0], dp[1] + prices[i]); + dp[1] = Math.max(dp[1], -prices[i]); + } + return dp[0]; +} diff --git a/Week 05/id_273/LeetCode_122_273.java b/Week 05/id_273/LeetCode_122_273.java new file mode 100644 index 000000000..0999d93a6 --- /dev/null +++ b/Week 05/id_273/LeetCode_122_273.java @@ -0,0 +1,59 @@ +//122. 买卖股票的最佳时机II + +//解法1:贪心思想 执行用时:1ms +//思路:若当天价格大于前一天的价格, 就进行买卖操作, 也就是获取差值总额 +//时间复杂度O(n) +//空间复杂度O(1) +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int sum = 0; + for (int i = 0; i < prices.length - 1; i++) { + if (prices[i] < prices[i + 1]) sum += prices[i + 1] - prices[i]; + } + return sum; +} + +//当前问题分析 +// 对于当前题目的要求, K = +infinite, 也就是这几天内可以进行多笔交易, 则状态方程如下: +// 1. DP[i][k][0] = max(DP[i - 1][k][0], DP[i - 1][k][1] + prices[i]) +// 2. DP[i][k][1] = max(DP[i - 1][k][1], DP[i - 1][k - 1][0] - prices[i]) +// 可以发现第2个转移方程中出现的"DP[i - 1][k - 1][0]", 由于k为正无穷, 因此k - 1 = k +// 而所有k对状态转移不产生影响, 因此状态K可以省略不写 +// 到最后, 这个问题的状态转移方程如下: +// 1. DP[i][0] = max(DP[i - 1][0], DP[i - 1][1] + prices[i]) +// 2. DP[i][1] = max(DP[i - 1][1], DP[i - 1][0] - prices[i]) + +//解法2:动态规划 执行用时:3ms +//思路:和121题基本一致, 通过一个二维数组存储每一天持有股票和未持有股票的利润最大值 +// 不同点在于:现在我们可以进行多笔交易。因此, 若当天选择持有股票, 情况就稍有变化: +// 当天可以选择继续持有前一天的股票以及买入股票。若选择买入股票, 那么就需要加上当天抛售股票获得的利润额 +// dp[i][1] = max(dp[i - 1][1], dp[i][0] - prices[i]) +//时间复杂度:O(n) +//空间复杂度:O(n) +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int[][] dp = new int[prices.length][2]; + dp[0][1] = -prices[0]; + dp[0][0] = 0; + for (int i = 1; i < prices.length; 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][0] - prices[i]); + } + return dp[dp.length - 1][0]; +} + +//解法2.1:动态规划-空间压缩 执行用时:2ms +//思路:参考121动态规划的空间压缩 +//时间复杂度:O(n) +//空间复杂度:O(1) +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int[] dp = new int[2]; + dp[0] = 0; + dp[1] = -prices[0]; + for (int i = 1; i < prices.length; i++) { + dp[0] = Math.max(dp[0], dp[1] + prices[i]); + dp[1] = Math.max(dp[1], dp[0] - prices[i]); + } + return dp[0]; +} \ No newline at end of file diff --git a/Week 05/id_273/LeetCode_123_273.java b/Week 05/id_273/LeetCode_123_273.java new file mode 100644 index 000000000..6a05185f7 --- /dev/null +++ b/Week 05/id_273/LeetCode_123_273.java @@ -0,0 +1,57 @@ +//123. 买卖股票的最佳时机III + +//当前问题分析 +// 对于当前题目的要求, K = 2, 也就是这几天内只能进行2笔交易, 而由于K = 2, 此时无法消除k的影响, 所以必须对交易次数K也进行穷举: +// for (int k = 2; k >= 1; k--) { +// DP[i][k][0] = max(DP[i - 1][k][0], DP[i - 1][k][1] + prices[i]) +// DP[i][k][1] = max(DP[i - 1][k][1], DP[i - 1][k - 1][0] - prices[i]) +// } + +//解法1:三维DP 执行用时:6ms +public int maxProfit(int[] prices) { + int[][][] dp = new int[prices.length][3][2]; + for (int i = 0; i < prices.length; i++) { + for (int k = 2; k >= 1; k--) { + if (i - 1 == -1) { + dp[i][k][0] = 0; + dp[i][k][1] = -prices[i]; + continue; + } + dp[i][k][0] = Math.max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]); + dp[i][k][1] = Math.max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i]); + } + } + return dp[prices.length - 1][2][0]; +} + +//解法2:状态压缩 执行用时:5ms +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int[][] dp = new int[3][2]; + for (int i = 0; i < prices.length; i++) { + for (int k = 2; k >= 1; k--) { + if (i == 0) { + dp[k][0] = 0; + dp[k][1] = -prices[i]; + continue; + } + dp[k][0] = Math.max(dp[k][0], dp[k][1] + prices[i]); + dp[k][1] = Math.max(dp[k][1], dp[k - 1][0] - prices[i]); + } + } + return dp[2][0]; +} + +//解法3:一维 执行用时:3ms +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int dp_10 = 0, dp_11 = -prices[0]; + int dp_20 = 0, dp_21 = -prices[0]; + for (int i = 1; i < prices.length; i++) { + dp_20 = Math.max(dp_20, dp_21 + prices[i]); + dp_21 = Math.max(dp_21, dp_10 - prices[i]); + dp_10 = Math.max(dp_10, dp_11 + prices[i]); + dp_11 = Math.max(dp_11, -prices[i]); + } + return dp_20; +} \ No newline at end of file diff --git a/Week 05/id_273/LeetCode_152_273.java b/Week 05/id_273/LeetCode_152_273.java new file mode 100644 index 000000000..7cb6b1ec7 --- /dev/null +++ b/Week 05/id_273/LeetCode_152_273.java @@ -0,0 +1,44 @@ +//152. 乘积的最大子序列 + +//解法1:动态规划(二维数组版) +//思路:因为是两数相乘, 所以会出现负负得正这种情况, 因此在53题的基础上, 还需要保存当前元素的能与之前的元素组成的最小序列 +// 在判断乘积最大子序列时, 需要先观察当前元素是否为负数: +// 1. 若是负数, 则交换dp[i - 1][0]与[i - 1][1], 以保证nums[i]能够乘一个最小值从而获得一个正数 +// 2. 若是正数, 则与dp[i - 1][0]相乘, 观察结果是否大于result +//时间复杂度:O(n) +//空间复杂度:O(2n) +public int maxProduct(int[] nums) { + int[][] dp = new int[nums.length][2]; + dp[0][0] = nums[0]; + dp[0][1] = nums[0]; + int result = dp[0][0]; + for (int i = 1; i < nums.length; i++) { + if (nums[i] < 0) { + int temp = dp[i - 1][0]; dp[i - 1][0] = dp[i - 1][1]; dp[i - 1][1] = temp; + } + dp[i][0] = Math.max(dp[i - 1][0] * nums[i], nums[i]); + dp[i][1] = Math.min(dp[i - 1][1] * nums[i], nums[i]); + result = Math.max(result, dp[i][0]); + } + return result; +} + +//解法1.1:动态规划(变量暂存版) 执行用时:2ms +//思路:发现上一个解法中推导当前元素的最大乘积子序列所用到的变量就只有当前元素和上一个元素的最大最小值 +// 因此可以用2个变量imax, imin替代 +//时间复杂度:O(n) +//空间复杂度:O(1) +public int maxProduct(int[] nums) { + int imax = 1; + int imin = 1; + int result = Integer.MIN_VALUE; + for (int i = 0; i < nums.length; i++) { + if (nums[i] < 0) { + int temp = imax; imax = imin; imin = temp; + } + imax = Math.max(nums[i], imax * nums[i]); + imin = Math.min(nums[i], imin * nums[i]); + result = Math.max(imax, result); + } + return result; +} \ No newline at end of file diff --git a/Week 05/id_273/LeetCode_188_273.java b/Week 05/id_273/LeetCode_188_273.java new file mode 100644 index 000000000..d9563299c --- /dev/null +++ b/Week 05/id_273/LeetCode_188_273.java @@ -0,0 +1,65 @@ +//188. 买卖股票的最佳时机IV + +//当前问题分析 +// 一次完整的交易由一次买入和卖出构成, 至少需要两天, 那么限制k次交易也就意味着只有当price.length = 2*k才是有效的 +// 对于k > price.length/2的情况, 我们可以认为k = +无穷, 也就可以转换为股票问题II +// + + +//解法1:三维DP + 贪心 执行用时:9ms +//时间复杂度:O(n*k) +//空间复杂度:O(n*k*2) +public int maxProfit(int k, int[] prices) { + if (prices.length == 0 || prices == null) return 0; + if (k > prices.length >> 1) { + return maxProfitWithoutLimit(prices); + } + int[][][] dp = new int[prices.length][k + 1][2]; + for (int i = 0; i < prices.length; i++) { + for (int j = k; j >= 1; j--) { + if (i - 1 == -1) { + dp[i][j][0] = 0; + dp[i][j][1] = -prices[i]; + continue; + } + dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]); + dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]); + } + } + return dp[prices.length - 1][k][0]; +} + +private int maxProfitWithoutLimit(int[] prices) { + int sum = 0; + for (int i = 0; i < prices.length - 1; i++) { + if (prices[i] < prices[i + 1]) { + sum+= prices[i + 1] - prices[i]; + } + } + return sum; +} + +//解法2:降维 + 贪心 执行用时:7ms +//时间复杂度:O(n*k) +//空间复杂度:O(2 * K) +public int maxProfit(int k, int[] prices) { + if (prices.length == 0 || prices == null) return 0; + if (k > prices.length >> 1) { + return maxProfitWithoutLimit(prices); + } + int[][] dp = new int[k + 1][2]; + for (int i = 0; i < prices.length; i++) { + for (int j = k; j >= 1; j--) { + if (i == 0) { + dp[j][0] = 0; + dp[j][1] = -prices[i]; + continue; + } + dp[j][0] = Math.max(dp[j][0], dp[j][1] + prices[i]); + dp[j][1] = Math.max(dp[j][1], dp[j - 1][0] - prices[i]); + } + } + return dp[k][0]; +} + +//贪心.... diff --git a/Week 05/id_273/LeetCode_198_273.java b/Week 05/id_273/LeetCode_198_273.java new file mode 100644 index 000000000..134c0d2f8 --- /dev/null +++ b/Week 05/id_273/LeetCode_198_273.java @@ -0,0 +1,62 @@ +//198. 打劫家舍 + +//解法1:动态规划(二维数组版) 执行用时:0ms +//思路: +// 重复子问题:对于每一件屋子, 求出它偷与不偷能获取的最大收益 +// 状态定义: 对于当前屋子, 我们可以选择偷也可以选择不偷: +// 如果选择偷的话上一个屋子就一定不能偷 +// 如果选择不偷的话上一个屋子可以选择偷, 也可以选择不偷 +// DP方程: 我们可以将每间屋子选择偷与不偷的最大收益通过二维数组dp存储 +// 对于第N间屋子, 选择偷的最大收益:dp[n][1] = nums[n] + dp[n - 1][0] +// 选择不偷的最大收益:dp[n][0] = max : dp[n - 1][0] and dp[n - 1][1] +// 那么只要获取第N间屋子偷与不偷的收益最大值, 就是当前能偷到的最大收益 +//时间复杂度:O(n) +//空间复杂度:O(n) +public int rob(int[] nums) { + if (nums.length == 0 || nums == null) return 0; + int[][] dp = new int[nums.length][2]; + dp[0][1] = nums[0]; + dp[0][0] = 0; + for (int i = 1; i < nums.length; i++) { + dp[i][1] = dp[i - 1][0] + nums[i]; + dp[i][0] = Math.max(dp[i - 1][1], dp[i - 1][0]); + } + return Math.max(dp[nums.length - 1][0], dp[nums.length - 1][1]); +} + +//解法1.1:动态规划(一维数组版) 执行用时:0ms +//思路:基于解法1, 对于第N间房子的最大收益:要么选择偷当前房子 + 偷上上一间房子能获取的最大收益; 要么选择偷上一间房子能获取的最大收益 +//时间复杂度:O(n) +//空间复杂度:O(n) +public int rob(int[] nums) { + if (nums == null || nums.length == 0) return 0; + if (nums.length == 1) return nums[0]; + int[] dp = new int[nums.length]; + dp[0] = nums[0]; + dp[1] = Math.max(dp[0], nums[1]);//第二件房子的最大收益:要么偷第一间房子, 要么偷第二间房子 + for (int i = 2; i < nums.length; i++) { + dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]); + } + return dp[nums.length - 1]; +} + +//解法1.2:动态规划(变量暂存) 执行用时:0ms +//思路:基于解法1.2, 我们发现在每次计算最大收益时, 只需要比较"当前屋子的最大收益"与"上一间屋子的最大收益"与"上上一间屋子的最大收益"进行计算 +// 那么就可以设置3个参数, prev代表上上间屋子的最大收益, temp代表上一间屋子的最大收益, curr代表当前屋子的最大收益 +//时间复杂度:O(n) +//空间复杂度:O(1) +public int rob(int[] nums) { + int prev = 0, curr = 0, temp = 0; + for (int i : nums) { + temp = curr; + curr = Math.max(prev + i, temp); + prev = temp; + } + return curr; +} + + + + + + diff --git a/Week 05/id_273/LeetCode_213_273.java b/Week 05/id_273/LeetCode_213_273.java new file mode 100644 index 000000000..e99f8c372 --- /dev/null +++ b/Week 05/id_273/LeetCode_213_273.java @@ -0,0 +1,49 @@ +//213. 打家劫舍II + +//解法1:动态规划(一维数组版) 执行用时:0ms +//思路:总体上与198题没太大区别, 二者都是求出每一间屋子偷与不偷获得收益的最大值 +// 主要区别在于该题中, 第一间屋子与最后一间屋子相邻, 这就意味着我们需要分别求出: +// 1. 偷第一间屋子, 不偷最后一间屋子获取的最大收益 +// 2. 偷最后一间屋子, 不偷第一间屋子获取的最大收益 +// 只要取二者中较大值即是答案 +//时间复杂度:O(n) +//空间复杂度:O(2n) +public int rob(int[] nums) { + int length = nums.length; + if (length == 0) return 0; + if (length == 1) return nums[0]; + if (length == 2) return Math.max(nums[0], nums[1]); + int[] dp1 = new int[nums.length - 1];//1 ~ n - 1 + dp1[0] = nums[0]; + dp1[1] = Math.max(dp1[0], nums[1]); + for (int i = 2; i < nums.length - 1; i++) { + dp1[i] = Math.max(nums[i] + dp1[i - 2], dp1[i - 1]); + } + int[] dp2 = new int[nums.length - 1];//2 ~ n + dp2[0] = nums[1]; + dp2[1] = Math.max(dp2[0], nums[2]); + for (int i = 3; i < nums.length; i++) { + dp2[i - 1] = Math.max(dp2[i - 2], nums[i] + dp2[i - 3]); + } + return Math.max(dp1[nums.length - 2], dp2[nums.length - 2]); +} + +//解法1.2:动态规划(变量暂存) 执行用时:0ms +//思路:基于解法1, 申请三个变量分别存储偷n - 2, n - 1, n个房子能获取的最大收益 +//时间复杂度:O(n) +//空间复杂度:O(1) +public int rob(int[] nums) { + if (nums.length == 0 || nums == null) return 0; + if (nums.length == 1) return nums[0]; + return Math.max(myRob(Arrays.copyOfRange(nums, 0, nums.length - 1)), myRob(Arrays.copyOfRange(nums, 1, nums.length))); +} + +private int myRob(int[] nums) { + int prev = 0, temp = 0, curr = 0; + for (int i : nums) { + temp = curr; + curr = Math.max(i + prev, temp); + prev = temp; + } + return curr; +} diff --git a/Week 05/id_273/LeetCode_309_273.java b/Week 05/id_273/LeetCode_309_273.java new file mode 100644 index 000000000..31574f4b2 --- /dev/null +++ b/Week 05/id_273/LeetCode_309_273.java @@ -0,0 +1,39 @@ +//309. 最佳买卖股票时机含冷冻期 + +//当前问题分析: +// 每当完成一次完整的交易后, 需要间隔一天再继续下一次交易 +// 因此, 状态方程为: +// 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 - 2][0] - prices[i]); + +//解法1:二维DP 执行用时:2ms +//时间复杂度:O(n) +//空间复杂度:O(2 * n) +public int maxProfit(int[] prices) { + if (prices.length <= 1 || prices == null) return 0; + int[][] dp = new int[prices.length][2]; + dp[0][0] = 0; + dp[0][1] = -prices[0]; + dp[1][0] = Math.max(dp[0][0], dp[0][1] + prices[1]); + dp[1][1] = Math.max(dp[0][1], -prices[1]); + for (int i = 2; i < prices.length; 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 - 2][0] - prices[i]); + } + return dp[prices.length - 1][0]; +} + +//解法2:DP降维 执行用时:1ms +//时间复杂度:O(n) +//空间复杂度:O(1) +public int maxProfit(int[] prices) { + if (prices.length <= 1 || prices == null) return 0; + int dp_0 = 0, dp_1 = -prices[0], dp_pre_0 = 0; + for (int i = 1; i < prices.length; i++) { + int temp = dp_0; + dp_0 = Math.max(dp_0, dp_1 + prices[i]); + dp_1 = Math.max(dp_1, dp_pre_0 - prices[i]); + dp_pre_0 = temp; + } + return dp_0; +} \ No newline at end of file diff --git a/Week 05/id_273/LeetCode_322_273.java b/Week 05/id_273/LeetCode_322_273.java new file mode 100644 index 000000000..1bbb0f6f0 --- /dev/null +++ b/Week 05/id_273/LeetCode_322_273.java @@ -0,0 +1,88 @@ +//322. 零钱兑换 + +//解法1:暴力递归 提交超时 +//思路:假设硬币有1, 2, 5三种面额, 总金额 = 11 +// 那么兑换总金额11所需要的最小硬币数F(11)就是 " 拿了1块钱:F(10)、拿了2块钱:F(9)、拿了5块钱:F(6) " 三者中的最小值 +// 那么就可以明确子问题为 F(n) = min(n - coins[0~k]) +// 只要明确了重复做的事, 那么首先可以考虑交给递归完成 +//时间复杂度:O(k * n^k) +//空间复杂度:O(1) +public int coinChange(int[] coins, int amount) { + if (amount == 0) return 0; + int ans = Integer.MAX_VALUE; + for (int coin : coins) { + if (amount - coin < 0) continue; + int subProblem = coinChange(coins, amount - coin); + if (subProblem == -1) continue; + ans = Math.min(subProblem + 1, ans); + } + return ans == Integer.MAX_VALUE ? -1 : ans; +} + + +//解法1.1:记忆化递归 执行用时:21ms +//思路:通过缓存数组避免重复计算 +//时间复杂度:O(k * n) +//空间复杂度:O(n) +public int coinChange(int[] coins, int amount, int[] cache) { + int[] cache = new int[amount + 1]; + return recur(coins, amount, cache); +} + +private int recur(int[] coins, int amount, int[] cache) { + if (cache[amount] != 0) return cache[amount]; + if (amount == 0) return 0; + int result = Integer.MAX_VALUE; + for (int coin : coins) { + if (amount - coin < 0) continue; + int sub_problem = recur(coins, amount - coin, cache); + if (sub_problem == -1) continue; + result = Math.min(result, sub_problem + 1); + } + return cache[amount] = (result == Integer.MAX_VALUE ? -1 : result); +} + +//解法3: 动态规划 执行用时:12ms +//思路:通过解法1我们能够明确子问题, 因此状态方程为dp[i] = min(dp[i - coins[0~k]]) +// 明确了状态方程, 剩下要做的就是一些边界条件的判断了 +//时间复杂度:O(N*K) +//空间复杂度:O(N) +public int coinChange(int[] coins, int amount) { + int[] dp = new int[amount + 1]; + for (int i = 1; i <= amount; i++) { + int result = -1; + for (int coin : coins) { + if (i - coin >= 0 && dp[i - coin] != -1) { + int sub_problem = dp[i - coin] + 1; + result = result == -1 ? sub_problem : Math.min(result, sub_problem); + } + } + dp[i] = result; + } + return dp[amount]; +} + +//解法4:BFS 执行用时:87ms +//思路:通过广度优先遍历F(N)的所有子问题, 若发现子问题F(N - coin[0~K]) == 0, 说明在当前层发现零钱兑换完毕, 兑换的数量就是当前的层数 +//总结:一开始只通过Queue写BFS解法, 数据量一大就超时, 后来看别人的题解受到启发用了Set去重, 改写后现在勉强能够运行.. +public int coinChange(int[] coins, int amount) { + Queue queue = new LinkedList<>(); + HashSet set = new HashSet<>(); + queue.offer(amount); + int level = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + int temp = queue.poll(); + if (temp == 0) return level; + for (int coin : coins) { + if (temp >= coin && !set.contains(temp - coin)) { + set.add(temp - coin); + queue.offer(temp - coin); + } + } + } + level++; + } + return -1; +} \ No newline at end of file diff --git a/Week 05/id_273/LeetCode_32_273.java b/Week 05/id_273/LeetCode_32_273.java new file mode 100644 index 000000000..6f1da1543 --- /dev/null +++ b/Week 05/id_273/LeetCode_32_273.java @@ -0,0 +1,30 @@ +//32. 最长的有效括号 + + +//解法1:栈 +//思路:首先要明确一组字符串, 它的有效括号与无效括号概念, 例如:")()(()" 就只存在一组有效括号"()", 长度为2, "(()" 缺少右括号组成一组有效括号 +// 可以通过栈维护未组成有效括号的字符的index, 若当前获取到一个能与栈顶元素对应的字符组成有效括号的字符, 那么更新最大有效长度 +//代码流:s = "")()(())" index = 0 stack [ -1, 0 ] count = 0; +// index = 1 stack [ -1, 0, 1 ] count = 0; +// index = 2 stack [ -1, 0, ] count = 2 - 0; +// index = 3 stack [ -1, 0, 3 ] count = 2; +// index = 4 stack [ -1, 0, 3, 4 ] count = 2; +// index = 5 stack [ -1, 0, 3 ] count = 5 - 3 = 2; +// index = 6 stack [ -1, 0 ] count = 6 - 0 = 6; +//时间复杂度:O(n) +//空间复杂度:O(n) +public int longestValidParentheses(String s) { + Stack stack = new Stack<>(); + int left = -1; + int count = 0; + stack.push(left); + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == ')' && stack.size() > 1 && s.charAt(stack.peek()) == '(') { + stack.pop(); + count = Math.max(count, i - stack.peek()); + } else { + stack.push(i); + } + } + return count; +} \ No newline at end of file diff --git a/Week 05/id_273/LeetCode_53_273.java b/Week 05/id_273/LeetCode_53_273.java new file mode 100644 index 000000000..4b74a41bf --- /dev/null +++ b/Week 05/id_273/LeetCode_53_273.java @@ -0,0 +1,41 @@ +//53. 最大子序和 + +//解法1:暴力解法 执行用时:105ms +//思路:遍历所有元素, 获取当前元素能够与后面的所有元素组成的子序和中的最大值, 然后返回所有元素子序和最大值中的最大值 +//时间复杂度:O(n^2) +//空间复杂度:O(1) +public int maxSubArray(int[] nums) { + int result = Integer.MIN_VALUE; + for (int i = 0; i < nums.length; i++) { + int sum = 0; + for (int j = i; j < nums.length; j++) { + sum += nums[j]; + result = Math.max(result, sum); + } + } + return result; +} + +//解法2:动态规划 执行用时:1ms +//思路:遍历所有元素, 获取当前元素能够与之前的所有元素组成的子序和最大值, 也就是用之前计算过的最大子序和来推导当前最大子序和 +// 例如:[-2,1,-3,4,-1,2,1,-5,4] +// dp[0] = -2, 前面已经没有元素与其组成子序和 +// dp[1] = dp[0] > 0 ? 1 + dp[0] : 1, 如果dp[0]小于等于0, 与dp[0]组成子序和反而让结果变得更小了, 还不如就以自身作为子序和的起始位置 +// dp[2] = dp[1] > 0 ? -3 + dp[1] : - 3, 如果dp[1]大于0, 那么与dp[1]组成的子序和要比自身作为子序和的结果更大 +// dp[3] = 4, 同理.. +// dp[4] = 3 +// dp[5] = 5 +// .... +// 每一步都获取当前元素能够组成的最大子序和, 然后下一个元素观察上一个元素的子序和能否对它产生增益或减益的影响, 如果增益的话, 就与之前的元素组成新的子序和; 减益就以自身作为新的子序和 +// 由此可见, 状态方程就是:dp[i] = (dp[i - 1] > 0 ? dp[i - 1] + nums[i] : nums[i]) +// 不断迭代后, 返回所有子序和中的最大值即可 +public int maxSubArray(int[] nums) { + int[] dp = new int[nums.length]; + dp[0] = nums[0]; + int max = dp[0]; + for (int i = 1; i < nums.length; i++) { + dp[i] = (dp[i - 1] > 0 ? dp[i - 1] + nums[i] : nums[i]); + max = Math.max(max, dp[i]); + } + return max; +} \ No newline at end of file diff --git a/Week 05/id_273/LeetCode_62_273.java b/Week 05/id_273/LeetCode_62_273.java new file mode 100644 index 000000000..1e32f94b2 --- /dev/null +++ b/Week 05/id_273/LeetCode_62_273.java @@ -0,0 +1,60 @@ +//62. 不同路径 + +//解法1:暴力递归 执行超时 +//思路:自顶向下解决问题, 例如3x3的矩阵中, F(3, 3) = F(2, 3) + F(3, 2) +// 由于只能选择向右或向下, 那么终点的横、竖列都只存在一种走法, 所以在m or n = 1时return 1 +//时间复杂度:O(2^n) +//空间复杂度:O(1) +public int uniquePaths1(int m, int n) { + if (m == 1 || n == 1) return 1; + return uniquePaths1(m, n - 1) + uniquePaths1(m - 1, n); +} + +//解法2:记忆化递归 执行用时:1ms +//思路:添加一个二维数组用于存储每一步递归计算的值 +//时间复杂度:O(m*n) +//空间复杂度:O(m*n) +public int uniquePaths(int m, int n) { + int[][] cache = new int[m][n]; + return recur(m, n, cache); +} + +private int recur(int m, int n, int[][] cache) { + if (cache[m - 1][n - 1] != 0) return cache[m - 1][n - 1]; + if (m == 1 || n == 1) return cache[m - 1][n - 1] = 1; + return cache[m - 1][n - 1] = recur(m - 1, n, cache) + recur(m, n - 1, cache); +} + +//解法3:动态规划(二维数组版) 执行用时:1ms +//思路:我们可以用一个二维数组存储途径终点的所有格子所对应的路径数 +// 不难发现, 终点的横、竖方向都只能存在一种走法, 因为它们只能选择向右或向左 +// 且对于中间任意一个位子的格子, 它的路径 = 下方格子的路径 + 右方格子的路径 +// 由此, 以终点坐标为dp[0][0], 状态方程就推导为:dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +//时间复杂度:O(m*n) +//空间复杂度:O(m*n) +public int uniquePaths(int m, int n) { + int[][] dp = new int[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (i == 0 || j == 0) dp[i][j] = 1; + else dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + return dp[m - 1][n - 1]; +} + +//解法1.1:动态规划(一维数组版) 执行用时:1ms +//思路:基于动态规划二维数组解, 我们每次获取dp[i][j]只需要通过当前格子的右方和下方的格子进行计算, 这代表我们只需要一个一维数组暂存当前列的路径数 +// 在移动列时, 也就是i++的时候, 一维数组中暂存的列信息中, 计算dp[j]时所依赖的右方和下方的格子正是数组中的dp[j]和dp[j - 1] +//时间复杂度:O(m*n) +//空间复杂度:O(n) +public int uniquePaths(int m, int n) { + int[] dp = new int[m]; + Arrays.fill(dp, 1); + for (int i = 1; i < n; i++) { + for (int j = 0; j < m; j++) { + if (j > 0) dp[j] += dp[j - 1]; + } + } + return dp[m - 1]; +} diff --git a/Week 05/id_273/LeetCode_63_273.java b/Week 05/id_273/LeetCode_63_273.java new file mode 100644 index 000000000..3d85a0862 --- /dev/null +++ b/Week 05/id_273/LeetCode_63_273.java @@ -0,0 +1,48 @@ +//63. 不同路径II + +//解法1:动态规划(二维数组版) 执行用时:1ms +//思路:在62. 不同路径的基础上增加了障碍物, 也就是机器人在向右或者向下走的过程中, 如果碰到了障碍物, 只能选择另一个方向 +// 那么如果是终点的横、竖列中某个格子存在障碍物的话, 那么该格子至终点所连的区域都认为不可达 +// 可以通过一个二维数组存储每一个格子当前的路径数, 只需要先判断终点的横竖列中所有方块的路径树, +// 再根据状态方程:dp[i][j] = dp[i - 1][j] + dp[i][j - 1] 计算其余部分即可 +//时间复杂度O(m*n) +//空间复杂度O(m*n) +public int uniquePathsWithObstacles(int[][] obstacleGrid) { + if (obstacleGrid == null || obstacleGrid.length == 0) return 0; + int m = obstacleGrid.length; + int n = obstacleGrid[0].length; + int[][] dp = new int[m][n]; + for (int i = 0; i < m; i++) { + if (obstacleGrid[i][0] == 1) break; + else dp[i][0] = 1; + } + for (int i = 0; i < n; i++) { + if (obstacleGrid[0][i] == 1) break; + else dp[0][i] = 1; + } + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + if (obstacleGrid[i][j] == 1) dp[i][j] = 0; + else dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + return dp[m - 1][n - 1]; +} + +//解法1.1:动态规划(一维数组版) 执行用时:0ms +//思路:以62题同样的方式, 我们也可以对1.1版本的代码进行降维 +//时间复杂度:O(m*n) +//空间复杂度:O(n) +public int uniquePathsWithObstacles(int[][] obstacleGrid) { + if (obstacleGrid == null || obstacleGrid.length == 0) return 0; + int width = obstacleGrid[0].length; + int[] dp = new int[width]; + dp[0] = 1; + for (int[] temp : obstacleGrid) { + for (int i = 0; i < width; i++) { + if (temp[i] == 1) dp[i] = 0; + else if (i > 0) dp[i] += dp[i - 1]; + } + } + return dp[width - 1]; +} \ No newline at end of file diff --git a/Week 05/id_273/LeetCode_70_283.java b/Week 05/id_273/LeetCode_70_283.java new file mode 100644 index 000000000..38b272a10 --- /dev/null +++ b/Week 05/id_273/LeetCode_70_283.java @@ -0,0 +1,64 @@ +//70. 爬楼梯 + +//通过规律可知该题的结果为斐波那契数列 +//假设n = 1 ,只能选择跨一步,F(1) = 1 +// n = 2 ,则只可能从台阶1跨1步,或者直接跨2步 F(2) = 2 +// n = 3 ,则只可能从台阶1跨2步,或者从台阶2跨1步 F(3) = F(2) + F(1) +// n = 4 ,则只可能从台阶2跨2步,或者从台阶3跨1步 F(4) = F(3) + F(2) +// .... + +//解法1:斐波那契数列递归 提交超时 +//思路:... +//时间复杂度O(2^n) +public int climbStairs(int n) { + if (n <= 2) return n; + return climbStairs(n-1) + climbStairs(n-2); +} + +//解法2:记忆化递归 执行用时:0ms +//思路:创建一个缓存数组用于存储每一次递归的结果, 避免了不必要的重复计算 +//时间复杂度O(n) +//空间复杂度O(n) +public int climbStairs(int n) { + int[] cache = new int[n + 1]; + return recur(cache, n); +} + +private int recur(int[] cache, int n) { + if (cache[n] != 0) return cache[n]; + if (n <= 2) return cache[n] = n; + return cache[n] = recur(cache, n - 1) + recur(cache, n - 2); +} + +//解法3:动态规划(一维数组版) 执行用时:0ms +//思路:可以根据重复子问题能够推导出动态规划的状态方程 dp[n] = dp[n - 1] + dp[n - 2], 有了状态方程, 动态规划就会非常简单 +// 只需要将每一级台阶要跨的步数采用数组存储, 下一次计算时从数组中获取即可 +//时间复杂度O(n) +//空间复杂度O(n) +public int climbStairs(int n) { + if (n <= 2) return n; + int[] dp = new int[n]; + dp[0] = 1; + dp[1] = 2; + for (int i = 2; i < n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n - 1]; +} + +//解法2.1 动态规划(变量暂存版) 执行用时:0ms +//思路:由于动态规划方法每次都只需要操作三个变量, 因此将数组赋值操作简化为变量之间的赋值操作 +//时间复杂度O(n) +//空间复杂度O(1) +public int climbStairs(int n) { + if (n <= 2) return n; + int prev = 1, temp = 2, curr = 0; + for (int i = 3; i <= n; i++) { + curr = temp + prev; + prev = temp; + temp = curr; + } + return curr; +} + + diff --git a/Week 05/id_273/LeetCode_714_273.java b/Week 05/id_273/LeetCode_714_273.java new file mode 100644 index 000000000..6ae687fda --- /dev/null +++ b/Week 05/id_273/LeetCode_714_273.java @@ -0,0 +1,36 @@ +//714. 买卖股票的最佳时机含手续费 + +//当前问题分析: +// 只需要在原DP方程的基础上, 每次完成交易的时候减去手续费即可 +// + +//解法1:DP 执行用时:22ms +//时间复杂度:O(N) +//空间复杂度:O(N*2) +public int maxProfit(int[] prices, int fee) { + int[][] dp = new int[prices.length][2]; + for (int i = 0; i < prices.length; i++) { + if (i - 1 == -1) { + dp[i][0] = 0; + dp[i][1] = -prices[i]; + continue; + } + dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee); + dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]); + } + return Math.max(dp[prices.length - 1][0], dp[prices.length - 1][1]); +} + +//解法2:DP降维 执行用时:8ms +//时间复杂度:O(N) +//空间复杂度:O(1) +public int maxProfit(int[] prices, int fee) { + int[] dp = new int[2]; + dp[0] = 0; + dp[1] = -prices[0]; + for (int i = 1; i < prices.length; i++) { + dp[0] = Math.max(dp[0], dp[1] + prices[i] - fee); + dp[1] = Math.max(dp[1], dp[0] - prices[i]); + } + return dp[0]; +} \ No newline at end of file diff --git a/Week 05/id_273/NOTE.md b/Week 05/id_273/NOTE.md index a6321d6e2..2b28d2ed5 100644 --- a/Week 05/id_273/NOTE.md +++ b/Week 05/id_273/NOTE.md @@ -1,4 +1,11 @@ # NOTE - +### 第十二课 动态规划 +1. 动态规划解法一般出现在求最优解的题目中, 一般动态规划的题解通常靠硬想是想不出来的, 这也导致了很多动态规划的题解让人感觉是从天上掉下来的。我个人刚开始接触换零钱这道题, 由于当时递归还用得不是很熟, 看不懂递归题解直接看动态规划, 动态规划题解理解起来很简单, 但是却感觉遥不可及。 +2. 经过一些题目的练习, 我发现动态规划的题解通常都源自于暴力递归, 这是因为动态规划与暴力递归的本质都是寻找重复子问题。而动态规划在寻找重复子问题的同时, 还推导了最优子结构, 也就是每一步保存的都是当前的最优解, 最后能够以当前的最优解一直推导到全局的最优解。 +3. 那么总结动态规划和递归/分治根本上的区别就是 “ 有无最优子结构 ” +#### 动态规划关键点 +1. 寻找最优子结构 +2. 存储中间状态 +3. 递归公式(状态转移方程) \ No newline at end of file diff --git a/Week 05/id_278/Leetcode_64_278.js b/Week 05/id_278/Leetcode_64_278.js new file mode 100644 index 000000000..a644afd37 --- /dev/null +++ b/Week 05/id_278/Leetcode_64_278.js @@ -0,0 +1,24 @@ +/** + * 64. Minimum Path Sum + * @param {number[][]} grid + * @return {number} + */ +var minPathSum = function(grid) { + let sum = [grid[0][0]]; + + for (let i = 1; i < grid[0].length; ++ i) { + sum[i] = grid[0][i] + sum[i - 1]; + } + + for (let j = 1; j < grid.length; ++ j) { + for (let i = 0; i < grid[j].length; ++ i) { + if (i === 0) { + sum[i] += grid[j][i]; + continue; + } + sum[i] = grid[j][i] + Math.min(sum[i - 1], sum[i]); + } + } + + return sum[sum.length - 1]; +}; diff --git a/Week 05/id_278/Leetcode_72_278.js b/Week 05/id_278/Leetcode_72_278.js new file mode 100644 index 000000000..6434d79fc --- /dev/null +++ b/Week 05/id_278/Leetcode_72_278.js @@ -0,0 +1,36 @@ +/** + * 72. Edit Distance + * @param {string} word1 + * @param {string} word2 + * @return {number} + */ +var minDistance = function(word1, word2) { + let distances = []; + + for (let row = 0; row < word1.length + 1; ++ row) { + distances.push(new Array(word2.length + 1)); + } + + for (let row = 0; row < word1.length + 1; ++ row) { + distances[row][0] = row; + } + + for (let column = 0; column < word2.length + 1; ++ column) { + distances[0][column] = column; + } + + for (let row = 1; row < word1.length + 1; ++ row) { + for (let column = 1; column < word2.length + 1; ++ column) { + if (word1[row - 1] === word2[column - 1]) { + distances[row][column] = distances[row - 1][column - 1]; + } else { + distances[row][column] = 1 + Math.min( + distances[row - 1][column], + distances[row][column - 1], + distances[row - 1][column - 1]); + } + } + } + + return distances[word1.length][word2.length]; +}; diff --git a/Week 05/id_283/Leetcode_221_283.java b/Week 05/id_283/Leetcode_221_283.java new file mode 100644 index 000000000..433d7a94b --- /dev/null +++ b/Week 05/id_283/Leetcode_221_283.java @@ -0,0 +1,29 @@ +/* + * @lc app=leetcode id=221 lang=java + * + * [221] Maximal Square + */ + +// @lc code=start +class Solution { + public int maximalSquare(char[][] matrix) { + int row = matrix.length; + if(row == 0) + return 0; + int col = matrix[0].length; + int[][] dp = new int[row+1][col+1]; + int max = 0; + for(int i = 1; i <= row; i++){ + for(int j = 1; j <= col; j++){ + if(matrix[i-1][j-1] == '1'){ + int min = Math.min(dp[i-1][j], dp[i][j-1]); + dp[i][j] = Math.min(min, dp[i-1][j-1])+1; + max = Math.max(dp[i][j], max); + } + } + } + return max * max; + } +} +// @lc code=end + diff --git a/Week 05/id_283/Leetcode_647_283.java b/Week 05/id_283/Leetcode_647_283.java new file mode 100644 index 000000000..6d12096d0 --- /dev/null +++ b/Week 05/id_283/Leetcode_647_283.java @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode id=647 lang=java + * + * [647] Palindromic Substrings + */ + +// @lc code=start +class Solution { + public int countSubstrings(String s) { + int count = 0; + boolean[][] dp = new boolean[s.length()][s.length()]; + for (int i = s.length()-1; i >=0 ; i--) { + for (int j = i; j < s.length(); j++) { + if (i==j) + dp[i][j] = true; + else + dp[i][j] = s.charAt(i) == s.charAt(j) && (j <= i+1 || dp[i+1][j-1]); + + if (dp[i][j]) + count++; + } + } + return count; + } +} +// @lc code=end + diff --git a/Week 05/id_298/edit_distance.py b/Week 05/id_298/edit_distance.py new file mode 100644 index 000000000..cde385c35 --- /dev/null +++ b/Week 05/id_298/edit_distance.py @@ -0,0 +1,50 @@ +class Solution: + def minDistance(self, word1: str, word2: str) -> int: + n = len(word1) + m = len(word2) + + dp = [[0] * (m + 1) for _ in range(n + 1)] + + # 初始化边界 + for i in range(1, n + 1): + dp[i][0] = dp[i -1][0] + 1 + for j in range(1, m + 1): + dp[0][j] = dp[0][j - 1] + 1 + + # 动态递推: 自底向上 + for i in range(1, n + 1): + for j in range(1, m + 1): + if word1[i - 1] == word2[j - 1]: + dp[i][j] = dp[i - 1][j - 1] + else: + dp[i][j] = min(dp[i - 1][j] , dp[i][j - 1] , dp[i -1][j - 1]) + 1 + return dp[-1][-1] + +""" + r o s + [[0, 1, 2, 3], + h [1, 1, 2, 3], + o [2, 2, 2, 3], + r [3, 3, 3, 3], + s [4, 4, 4, 4], + e [5, 5, 0, 0]] + dp[i-1][j] : 删除操作 + dp[i][j-1] : 插入操作 + dp[i-1][j-1] --> d: 替换操作 + 当前 i-1,j i,j-1 i-1,j-1 + i = 1, j = 1 h r dp[1][1] = min(dp[0][1], dp[1][0], dp[0][0]) + 1 = min(1, 1, 0) + 1 = 1 + i = 1, j = 2 h o dp[1][2] = min(dp[0][2], dp[1][1], dp[0][1]) + 1 = min(2, 1, 1) + 1 = 2 + i = 1, j = 3 h s dp[1][3] = min(dp[0][3], dp[1][2], dp[0][2]) + 1 = min(3, 2, 2) + 1 = 3 + i = 2, j = 1 o r dp[2][1] = min(dp[1][1], dp[2][0], dp[1][0]) + 1 = min(1, 2, 1) + 1 = 2 + i = 2, j = 2 o o dp[2][2] = min(dp[1][2], dp[2][1], dp[1][1]) + 1 = min(2, 2, 1) + 1 = 2 + i = 2, j = 3 o s dp[2][3] = min(dp[1][3], dp[2][2], dp[1][2]) + 1 = min(3, 2, 2) + 1 = 3 + i = 3, j = 1 r r dp[3][1] = min(dp[2][1], dp[3][0], dp[2][0]) + 1 = min(2, 3, 2) + 1 = 3 + i = 3, j = 2 r o dp[3][2] = min(dp[2][2], dp[3][1], dp[2][1]) + 1 = min(2, 3, 2) + 1 = 3 + i = 3, j = 3 r s dp[3][3] = min(dp[2][3], dp[3][2], dp[2][2]) + 1 = min(3, 3, 2) + 1 = 3 + i = 4, j = 1 s r dp[4][1] = min(dp[3][1], dp[4][0], dp[3][0]) + 1 = min(3, 4, 3) + 1 = 4 + i = 4, j = 2 s o dp[4][2] = min(dp[3][2], dp[4][1], dp[3][1]) + 1 = min(3, 4, 3) + 1 = 4 + i = 4, j = 3 s s dp[4][3] = min(dp[3][3], dp[4][2], dp[3][2]) + 1 = min(3, 4, 3) + 1 = 4 + i = 5, j = 1 e r dp[5][1] = min(dp[4][1], dp[5][0], dp[4][0]) + 1 = min(4, 5, 4) + 1 = 5 + i = 5, j = 2 e o dp[5][2] = min(dp[4][2], dp[5][1], dp[4][1]) + 1 = min(4, 5, 4) + 1 = 5 + i = 5, j = 3, e s dp[5][3] = min(dp[4][3], dp[5][2], dp[4][2]) + 1 = min(4, 5, 4) + 1 = 5 +""" diff --git a/Week 05/id_298/minimum-path-sum.py b/Week 05/id_298/minimum-path-sum.py new file mode 100644 index 000000000..37381ebb4 --- /dev/null +++ b/Week 05/id_298/minimum-path-sum.py @@ -0,0 +1,30 @@ +class Solution: + def minPathSum(self, grid: List[List[int]]) -> int: + for i in range(len(grid)): + for j in range(len(grid[0])): + if i == j == 0: + continue + elif i == 0: + grid[i][j] = grid[i][j - 1] + grid[i][j] + elif j == 0: + grid[i][j] = grid[i - 1][j] + grid[i][j] + else: + grid[i][j] = min(grid[i - 1][j], grid[i][j - 1]) + grid[i][j] + return grid[-1][-1] + +""" +[ + [1, 3, 1], + [1, 5, 1], + [4, 2, 1], +] +每次只能向下或者向右移动一步 +最小路径和 +原地DP: +i == j == 0: 处理第一个元素 +i == 0时,处理列元组,也就是向右走,当前元素等于上一列加上当前元素 +j == 0时,处理行元素,也就是向下走,党员元素等于前一行加上当前元素 +除此之外的情况: +递推方程 f(i, j) = min(f(i-1, j), f(i, (j-1)) + f(x, y) +从左边和上面两个元素中选一个最小的,加上当前元素 +""" diff --git a/Week 05/id_308/LeedCode_120.js b/Week 05/id_308/LeedCode_120.js new file mode 100644 index 000000000..1e0b128c6 --- /dev/null +++ b/Week 05/id_308/LeedCode_120.js @@ -0,0 +1,26 @@ +/** + * 题目: 三角形最小路径和 + * 语言: JavaScript + * 执行结果: 打败了66.38%的用户 + * 方法:动态规划 + * */ + +/** + * @param {number[][]} triangle + * @return {number} + */ +var minimumTotal = function(triangle) { + const len = triangle.length; + + if(len === 0) return 0; + if(len === 1) return Math.min(...triangle[0]); + + const dp = triangle; + for(let j=len-2;j>=0;j--) { + for(let i=0;i 0) { + sum += value; + }else{ + sum = value; + } + curr = Math.max(curr,sum); + } + + return curr; +}; diff --git a/Week 05/id_308/LeedCode_70.js b/Week 05/id_308/LeedCode_70.js new file mode 100644 index 000000000..eb16e6c9f --- /dev/null +++ b/Week 05/id_308/LeedCode_70.js @@ -0,0 +1,22 @@ +/** + * 题目: 爬楼梯 + * 语言: JavaScript + * 执行结果: 打败了95.64%的用户 + * 方法:动态规划 + * */ + +/** + * @param {number} n + * @return {number} + */ +const climbStairs = function(n) { + const result = [1,1,2]; + let i = 3; + + while(i <= n) { + result[i] = result[i-1] + result[i-2]; + i++; + } + + return result[n]; +}; diff --git a/Week 05/id_308/LeedCode_714.js b/Week 05/id_308/LeedCode_714.js new file mode 100644 index 000000000..44db061b7 --- /dev/null +++ b/Week 05/id_308/LeedCode_714.js @@ -0,0 +1,29 @@ +/** + * 题目: 买卖股票的最佳时机含手续费 + * 语言: JavaScript + * 执行结果: 打败了7.35%的用户 + * 方法:动态规划 + * */ + + + +/** + * @param {number[]} prices + * @param {number} fee + * @return {number} + */ +var maxProfit = function(prices, fee) { + let len = prices.length; + if(len < 2) return 0; + + prices[0] = [0,-prices[0]-fee]; + + for(let i=1;i 1 { + dp[i] = dp[i-2] + 2 + } else { + dp[i] = 2 + } + } else { + if i-dp[i-1]-1 >= 0 && bytes[i-dp[i-1]-1] == left { + dp[i] = dp[i-1] + 2 + if i-dp[i-1] >= 2 { + dp[i] += dp[i-dp[i-1]-2] + } + } else { + dp[i] = 0 + } + } + } + if max < dp[i] { + max = dp[i] + } + } + return max +} diff --git a/Week 05/id_313/LeetCode_64_313.go b/Week 05/id_313/LeetCode_64_313.go new file mode 100644 index 000000000..efaa547ea --- /dev/null +++ b/Week 05/id_313/LeetCode_64_313.go @@ -0,0 +1,26 @@ +package main + +func minPathSum(grid [][]int) int { + m := len(grid) + n := len(grid[0]) + + for i := 1; i < m; i++ { + grid[i][0] += grid[i-1][0] + } + + for j := 1; j < n; j++ { + grid[0][j] += grid[0][j-1] + } + + for i := 1; i < m; i++ { + for j := 1; j < n; j++ { + if grid[i-1][j] < grid[i][j-1] { + grid[i][j] += grid[i-1][j] + } else { + grid[i][j] += grid[i][j-1] + } + + } + } + return grid[m-1][n-1] +} diff --git a/Week 05/id_313/LeetCode_72_313.go b/Week 05/id_313/LeetCode_72_313.go new file mode 100644 index 000000000..fddc417ca --- /dev/null +++ b/Week 05/id_313/LeetCode_72_313.go @@ -0,0 +1,34 @@ +package main + +func minDistance(word1 string, word2 string) int { + dp := make([][]int, len(word1)+1) + for i := 0; i < len(word1)+1; i++ { + dp[i] = make([]int, len(word2)+1) + } + for i := 0; i < len(word1)+1; i++ { + dp[i][0] = i + } + for i := 0; i < len(word2)+1; i++ { + dp[0][i] = i + } + for i := 1; i < len(word1)+1; i++ { + for j := 1; j < len(word2)+1; j++ { + if word1[i-1] == word2[j-1] { + dp[i][j] = dp[i-1][j-1] + } else { + dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1 + } + } + } + return dp[len(word1)][len(word2)] +} + +func min(a int, b int, c int) int { + if a > b { + a = b + } + if a > c { + a = c + } + return a +} diff --git a/Week 05/id_318/LeetCode_32_318.py b/Week 05/id_318/LeetCode_32_318.py new file mode 100644 index 000000000..460fa487a --- /dev/null +++ b/Week 05/id_318/LeetCode_32_318.py @@ -0,0 +1,27 @@ +# +# @lc app=leetcode id=32 lang=python3 +# +# [32] Longest Valid Parentheses +# +# Test case: ")())((())())(()())" +# Expected Answer: 14 +# +# @lc code=start +class Solution: + def longestValidParentheses(self, s: str) -> int: + if len(s) > 0: + dp = [0] * len(s) + else: + return 0 + res = 0 + for i in range(1, len(s)): + if s[i] == ")": + if s[i - 1] == "(": + dp[i] = dp[i - 2] + 2 + elif s[i - 1] == ")" and s[i - dp[i - 1] - 1] == "(": + dp[i] = dp[i - 1] + 2 + dp[i - dp[i - 1] - 2] + if dp[i] > res: + res = dp[i] + return res +# @lc code=end + diff --git a/Week 05/id_318/LeetCode_64_318.java b/Week 05/id_318/LeetCode_64_318.java new file mode 100644 index 000000000..ac0355d55 --- /dev/null +++ b/Week 05/id_318/LeetCode_64_318.java @@ -0,0 +1,22 @@ +/* + * @lc app=leetcode id=64 lang=java + * + * [64] Minimum Path Sum + */ + +// @lc code=start +class Solution { + public int minPathSum(int[][] grid) { + for(int i = 0; i < grid.length; i++) { + for(int j = 0; j < grid[0].length; j++) { + if(i == 0 && j == 0) continue; + else if(i == 0) grid[i][j] += grid[i][j-1]; + else if(j == 0) grid[i][j] += grid[i-1][j]; + else grid[i][j] += Math.min(grid[i][j-1], grid[i-1][j]); + } + } + return grid[grid.length-1][grid[0].length-1]; + } +} +// @lc code=end + diff --git a/Week 05/id_328/LongestValidParentheses.java b/Week 05/id_328/LongestValidParentheses.java new file mode 100644 index 000000000..b0c961666 --- /dev/null +++ b/Week 05/id_328/LongestValidParentheses.java @@ -0,0 +1,17 @@ +class LongestValidParentheses { + public int longestValidParentheses(String s) { + int ans = 0; + int[] dp = new int[s.length()]; + for(int i = 1 ;i < s.length(); i++){ + if(s.charAt(i) == ')'){ + if(s.charAt(i-1) == '('){ + dp[i] = (i > 1? dp[i-2]:0 )+ 2; + }else if( i - dp[i-1] > 0 && s.charAt(i - dp[i-1]- 1 ) == '(') { + dp[i] = dp[i-1] + ( (i-dp[i-1]) > 1? dp [ i-dp[i-1] -2] : 0 ) + 2; + } + } + ans = Math.max(dp[i],ans); + } + return ans; + } +} \ No newline at end of file diff --git a/Week 05/id_328/MinimumPathSum.java b/Week 05/id_328/MinimumPathSum.java new file mode 100644 index 000000000..bdcbf2c76 --- /dev/null +++ b/Week 05/id_328/MinimumPathSum.java @@ -0,0 +1,20 @@ +class MinimumPathSum { + public int minPathSum(int[][] grid) { + int lenX = grid.length; + int lenY = grid[0].length; + for(int i = 1 ; i < lenX;i++) { + grid[i][0] += grid[i-1][0]; + } + for(int j = 1 ; j < lenY;j++) { + grid[0][j] += grid[0][j-1]; + } + + for(int k = 1 ; k < lenX ; k ++) { + for(int l = 1; l < lenY; l++) { + grid[k][l] += Math.min(grid[k-1][l],grid[k][l-1]); + } + } + + return grid[lenX-1][lenY-1]; + } +} \ No newline at end of file diff --git a/Week 05/id_338/LeetCode_120_338.java b/Week 05/id_338/LeetCode_120_338.java new file mode 100644 index 000000000..9b5c9940d --- /dev/null +++ b/Week 05/id_338/LeetCode_120_338.java @@ -0,0 +1,19 @@ +import java.util.List; + +/** + * @author Leesen + * @date 2019/11/17 23:09 + * @Link https://leetcode-cn.com/problems/triangle/solution/di-gui-ji-yi-hua-sou-suo-zai-dao-dp-by-crsm/ + */ +public class Triangle_120 { + public int minimumTotal(List> triangle) { + int row = triangle.size(); + int[] minLen = new int[row+1]; //第i行有i+1个值 + for (int level=row-1; level>=0; level--) { + for (int i=0; i<=level; i++) { + minLen[i] = Math.min(minLen[i], minLen[i+1]) + triangle.get(level).get(i); + } + } + return minLen[0]; + } +} diff --git a/Week 05/id_338/LeetCode_53_338.java b/Week 05/id_338/LeetCode_53_338.java new file mode 100644 index 000000000..3db8a04e9 --- /dev/null +++ b/Week 05/id_338/LeetCode_53_338.java @@ -0,0 +1,25 @@ +/** + * @author Leesen + * @date 2019/11/17 23:28 + * @Link https://leetcode-cn.com/problems/maximum-subarray/solution/dong-tai-gui-hua-fen-zhi-fa-python-dai-ma-java-dai/ + */ +public class MaxSubArray_53 { + public int maxSubArray(int[] nums) { + int len = nums.length; + if (len == 0) { + return 0; + } + + int[] dp = new int[len]; + dp[0] = nums[0]; + for (int i=1; i 0) //j>0之前没想到 + dp[j] += dp[j - 1]; + } + } + return dp[width - 1]; + } + + + public static void main(String[] args) { + int[][] obstacleGrid = new int[2][2]; + obstacleGrid[0][0] = 0; + obstacleGrid[0][1] = 1; + obstacleGrid[1][0] = 0; + obstacleGrid[1][1] = 0; + uniquePathsWithObstacles(obstacleGrid); + } + + + //状态方程思路 + //左上角到右下角走法,等同于右下角到左上角走法 + //需要在初始化以及每层递归过程中剔除障碍物 +} diff --git a/Week 05/id_343/LeetCode_621.go b/Week 05/id_343/LeetCode_621.go new file mode 100644 index 000000000..6c3976d06 --- /dev/null +++ b/Week 05/id_343/LeetCode_621.go @@ -0,0 +1,18 @@ +func leastInterval(tasks []byte, n int) int { + charSlice := [26]int{} + max := 0 + count := 0 + for i:=0;i>& grid) { + int dp[grid[0].size()]; + for (int i = grid.size() - 1; i >= 0; i--) { + for (int j = grid[0].size() - 1; j >= 0; j--) { + if (i == grid.size() - 1 && j != grid[0].size() - 1) + dp[j] = grid[i][j] + dp[j + 1]; + else if (j == grid[0].size() - 1 && i != grid.size() - 1) + dp[j] = grid[i][j] + dp[j]; + else if (j != grid[0].size() - 1 && i != grid.size() - 1) + dp[j] = grid[i][j] + min(dp[j], dp[j + 1]); + else + dp[j] = grid[i][j]; + } + } + return dp[0]; + } +}; +// @lc code=end + diff --git a/Week 05/id_353/Leetcode_91_353.cpp b/Week 05/id_353/Leetcode_91_353.cpp new file mode 100644 index 000000000..9876c9a03 --- /dev/null +++ b/Week 05/id_353/Leetcode_91_353.cpp @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode.cn id=91 lang=cpp + * + * [91] 解码方法 + */ + +// @lc code=start +class Solution { +public: + int numDecodings(string s) { + if (s[0] == '0') return 0; + int pre = 1, curr = 1; + for (int i = 1; i < s.size(); i++) { + int tmp = curr; + if (s[i] == '0') { + if (s[i - 1] == '1' || s[i - 1] == '2') { + curr = pre; + } + else { + return 0; + } + } + else if (s[i - 1] == '1' || (s[i - 1] == '2' && s[i] >= '1' && s[i] <= '6')) { + curr += pre; + } + pre = tmp; + } + return curr; + } +}; +// @lc code=end + diff --git a/Week 05/id_358/NOTE.md b/Week 05/id_358/NOTE.md index a6321d6e2..b9cd131e5 100644 --- a/Week 05/id_358/NOTE.md +++ b/Week 05/id_358/NOTE.md @@ -1,4 +1,156 @@ -# NOTE +# [358-Week 05] 学习总结 - +## 第五周 第十二课 +本周学习了动态规划的解题思路和实战题解析。 + +首先,通过回顾了之前学习的递归和分治的思路,发现动态规划有相似的地方。 + +相似处: + +* 1 人脑递归很累,避免人肉递归 +* 找到最近最简fangf-->重复解决 +* 利用数学桂腊飞 + +### 动态规划的特点: + +定义:接近分治,递归 + +比较: + +1. 与递归,分治没有本质的区别 + +2. 共性都是找问题的重复子问题 +3. 差异:动态规划是找到问题的最优子结构,中途可以淘汰次优解。 + +## 实战题解析 + +### 1. Fibonacci数列 + +1. 递归 O(2^n) + +2. 利用缓存,缓存已经计算过的结果 O(n) + +3. 动态规划 bottom up + + ```js + a[0] = 0, a[1] = 1; + for(let i = 2; i< n; i++) { + a[n] = a[i-1] + a[i-2] + } + return a[n] + ``` + + > 一般通过递推的方式,归纳一般规律;竞赛中选手为了求快,会直接写for循环递推。 + +### 2. count the paths计算路径 + +1. paths(start, end) = + + paths(A, end) + paths(B, end) = + + paths(D, end) + paths(C,end) + .... + + 分治 + 递归 + +2. 递推 Bottom up + + 用二维数组存储到大(i,j)的路径数量 + + 递推得出状态转移方程(DP方程):opt[i,j] = opt[i+1, j] + opt[i, j+1] + + 完整解法: + + ```js + if(a[i, j] === '空地') { + opt[i, j] = opt[i+1, j] + opt[i, j+1] + } else { + opt[i, j] = 0 + } + ``` + + + +> DP 关键点: +> +> 1. 求最优子结构的过程: opt[n] = best of opt[n-1], opt[n-2] .....,借助于数组 +> 2. 储存中间状态opt[i] +> 3. 归纳递推公式 + + + +### 3. 最长公共子序列问题 + +1. 暴力法: + + 从text1 构造所有可能的子字符串与text2一一对比 + +2. 动态规划:利用二维数组递推 + +DP方程: + +如果两个字符串s1,s2最后一个字符不相等,最长公共子序列LCS 为Max(LCS[s1-1, s2], LCS[s1,s2-1]) + +如果s1,s2最后一个字符串想到,则LCS[s1,s2] = LCS[s1-1, s2-2] + 1 + + + +> 5 easy steps to DP +> +> 1. Define subproblems +> 2. Guess part of solutions +> 3. Relate subproblems' solutions +> 4. Recurse & memoize or build DP table (bottom -up) +> 5. Solve original problem + +### 4. 三角形最小路径和问题 + +1. 分治法。会超时 O(2^n) + +2. DP O(n^2) + + 由一般性递推DP方程。此问题类比之前的棋盘上的路径问题。从起点到终点的路径,之前的问题求路径数量,此问题求最小路径和。同样需用从底向上,借助于数组存储中间状态。 + + 可以看出:使用二维数组sub[i,j],记录本层i,j位置到终点最小路径和,可以得到: + + sub[i,j] = min(sub[i+1, i], sub[i+1], j+1) + a[i,j] + + 最终sub[0,0] 是最终解。 + + 此题要求不使用额外空间,可以在原数组的基础上更新值。 + +### 5. 打家劫舍问题 + +回顾下DP求解问题的步骤: + +1. 分析子问题 +2. 状态定义 +3. DP方程 + +使用数组,a[i]表示 0....i 天 能偷到的最大金额。问题结果a[n-1] + +首先比较容易想到,需要增加一维数组表示i天偷还是不偷 + +```[0 +a[i][0,1] : 0 表示不偷,1 偷 +a[i][0] = Max(a[i-1][0], a[i-1][1]) +a[i][0] = a[i-1][0] + nums[i] +``` + +后又可以进一步改进,使用一维数组就足够,改变下状态定义,思路一致 + +a[i] : 0 ... i 天且num[i] 必偷的最大值 + +a[i] = Max(a[i-1], a[i-2] + nums[i]) + + + +### 6 股票买卖问题 + +一个通用DP解决一系列问题 + +[参考链接](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/yi-ge-fang-fa-tuan-mie-6-dao-gu-piao-wen-ti-by-l-3/) + + + + \ No newline at end of file diff --git a/Week 05/id_358/longest-valid-parentheses.js b/Week 05/id_358/longest-valid-parentheses.js new file mode 100644 index 000000000..9a7615d16 --- /dev/null +++ b/Week 05/id_358/longest-valid-parentheses.js @@ -0,0 +1,46 @@ +/** + * 设数组dp,初始化每个位置为0;下标为i处更新值为:以字符串下标i元素结尾的最长有效子字符串的长度 + * 有以下情况:形如 ....() 的字符串,dp[i] = dp[i-2] + 2 + * 形如 .....)) 的字符串, 下标i-1为有效字符串的结尾,下标i为更大有效字符串结尾,设下标为i-1元素所在有效子字符串为sub, + * sub的长度为dp[i-1], sub之前的有效字符串长度为 dp[i- dp[i-1] - 2]; 求和得 dp[i] = dp[i-1] + 2 + dp[i - dp[i-1] -2] + * @param {string} s + * @return {number} + */ +// var longestValidParentheses = function(s) { +// let max = 0; +// let dp = new Array(s.length).fill(0) +// for(let i = 1; i =2 ? dp[i-2]: 0) + 2 +// } +// else if(i-dp[i-1] > 0 && s[i-dp[i-1] -1] === '(') { +// dp[i] = dp[i-1] + ((i-dp[i-1] >=2) ? dp[i-dp[i-1] -2] : 0) +2 +// } + +// max = Math.max(dp[i], max) +// } + +// } +// return max; +// }; + +var longestValidParentheses = function(s) { + var max = 0; + var n = s.length; + var dp = new Array(n).fill(0); + for(var i = 1;i < n;i++){ + if(s[i] == ')'){ + // 右括号前边是左括号 + if(s[i-1] == '('){ + dp[i] = ( i >= 2 ? dp[i-2] : 0) + 2; + } + // 当前右括号前边是右括号,并且前一个合法子序列的前边是左括号和当前右括号组成一对,则最长子序列个数加2 + else if(i - dp[i-1] > 0 && s[i - dp[i-1] - 1] == '('){ + dp[i] = dp[i-1] + ( (i - dp[i-1] >= 2) ? dp[i - dp[i-1] - 2] : 0 ) + 2; + } + max = Math.max(max,dp[i]); + } + } + return max; +}; \ No newline at end of file diff --git a/Week 05/id_358/minimum-path-sum.js b/Week 05/id_358/minimum-path-sum.js new file mode 100644 index 000000000..15d6701ee --- /dev/null +++ b/Week 05/id_358/minimum-path-sum.js @@ -0,0 +1,21 @@ +/** 直接在原数组上遍历不需要额外的空间 + * gri[i,j] 表示从左上角到右下角的最小路径值 + * grid[i][j] = min(grid[i+1][j], grid[i][j+1]) + grid[i][j] + * @param {number[][]} grid + * @return {number} + */ + var minPathSum = function(grid) { + for(let i = grid.length-1; i >=0; i--) { + for(let j = grid[0].length-1; j >=0; j--) { + if(i !== grid.length -1 && j != grid[0].length - 1) { + grid[i][j] = Math.min(grid[i+1][j], grid[i][j+1]) + grid[i][j] + } else if(i === grid.length - 1 && j !== grid[0].length - 1) { + grid[i][j] = grid[i][j+1]+ grid[i][j] + } else if(i !== grid.length - 1 && j === grid[0].length - 1) { + grid[i][j] = grid[i+1][j] + grid[i][j] + } + + } + } + return grid[0][0] +}; \ No newline at end of file diff --git a/Week 05/id_363/LeetCode_221_363.java b/Week 05/id_363/LeetCode_221_363.java new file mode 100644 index 000000000..2a1b68b81 --- /dev/null +++ b/Week 05/id_363/LeetCode_221_363.java @@ -0,0 +1,58 @@ +package com.test.leetcode.week05; + +import org.junit.Test; + + +public class SolutionMaximalSquare221 { + + + @Test + public void test1() { + char[][] matrix = { + {'1','0','1','0','0'}, + {'1','0','1','1','1'}, + {'1','1','1','1','1'}, + {'1','0','0','1','0'}, + }; + char[][] matrix2 = { + {'1'} + }; + char[][] matrix3 = { + {'0'} + }; + System.out.println(maximalSquare(matrix)); + System.out.println(maximalSquare(matrix2)); + System.out.println(maximalSquare(matrix3)); + } + + /** + * dp: + * 1.子问题:二维数组变小 + * 状态数组 dp[r][c] 存储当前最大正方形的边长 + * dp方程 + * + * dp[r][c] = Math.min(...) + 1 + * @param matrix + * @return + */ + public int maximalSquare(char[][] matrix) { + if (matrix == null || matrix.length < 1) { + return 0; + } + int row = matrix.length, col = matrix[0].length; + int[][] dp = new int[row + 1][col + 1]; + int maxLen = 0; + for (int r = 0; r < row; r ++) { + for (int c = 0; c < col; c ++) { + if (matrix[r][c] == '1') { + dp[r + 1][c + 1] = Math.min(dp[r+ 1][c], Math.min(dp[r][c], dp[r][c + 1])) + 1; + maxLen = Math.max(maxLen, dp[r + 1][c + 1]); + } + } + } + return maxLen * maxLen; + } + + + +} diff --git a/Week 05/id_363/LeetCode_64_363.java b/Week 05/id_363/LeetCode_64_363.java new file mode 100644 index 000000000..bc3f05b9d --- /dev/null +++ b/Week 05/id_363/LeetCode_64_363.java @@ -0,0 +1,72 @@ +package com.test.leetcode.week05; + +import org.junit.Test; + + +public class SolutionMinPathSum64 { + + + @Test + public void test1() { + int[][] grid = { + {1,3,1}, + {1,5,1}, + {4,2,1} + }; + System.out.println(minPathSum(grid)); + System.out.println(minPathSum2(grid)); + } + + /** + * DP: + * 子问题:数组变小 + * 状态数组 存储当前点到右下角的路径的最小和 + * dp方程: dp[i][j] = Math.max(dp[i][j + 1] + dp[i + 1][j]) + * @param grid + * @return + */ + public int minPathSum(int[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + int row = grid.length, col = grid[0].length; + int[][] dp = new int[row][col]; + dp[row - 1][col - 1] = grid[row - 1][col - 1]; + for (int c = col - 2; c >= 0; c --) { + dp[row - 1][c] = dp[row - 1][c + 1] + grid[row - 1][c]; + } + for (int r = row - 2; r >= 0; r --) { + dp[r][col -1] = dp[r + 1][col - 1] + grid[r][col - 1]; + } + for (int r = row - 2; r >= 0; r --) { + for (int c = col - 2; c >= 0; c --) { + dp[r][c] = Math.min(dp[r][c + 1], dp[r + 1][c]) + grid[r][c]; + } + } + return dp[0][0]; + } + + public int minPathSum2(int[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + int row = grid.length, col = grid[0].length; + int[] dp = new int[col]; + dp[col - 1]= grid[row - 1][col - 1]; + for (int c = col - 2; c >= 0; c --) { + dp[c] = dp[c + 1] + grid[row - 1][c]; + } + for (int r = row - 2; r >= 0; r --) { + for (int c = col - 1; c >= 0; c --) { + if (c == col - 1) { + dp[c] = grid[r][c] + dp[c]; + } else { + dp[c] = Math.min(dp[c + 1], dp[c]) + grid[r][c]; + } + } + } + return dp[0]; + } + + +} diff --git a/Week 05/id_363/LeetCode_91_363.java b/Week 05/id_363/LeetCode_91_363.java new file mode 100644 index 000000000..e1fb22694 --- /dev/null +++ b/Week 05/id_363/LeetCode_91_363.java @@ -0,0 +1,59 @@ +package com.test.leetcode.week05; + +import org.junit.Test; + + +public class SolutionNumsEncoding91 { + + + @Test + public void test1() { + System.out.println(numDecodings("220")); + System.out.println(numDecodings("12")); + System.out.println(numDecodings("0")); + System.out.println(numDecodings("226")); + } + + /** + * 给定字符串,计算解码方法的总数 + * + * 数学归纳法 + * + * dp: + * 子问题:将字符串边短 + * 状态数组:存储从0到i处字符串的解码方法总数 + * dp方程:dp[i] = dp[i - 1] + if (s.substr(i-1,i) <= 26) : dp[i - 2] + * + * + * s[i] == 0, s[i - 1] == 1/2 dp[i]=dp[i-2] + * s[i-1] == 1 || s[i-1]== 2 && s[i]<= 6 dp[i] = dp[i-1]+dp[i-2] + * else dp[i] = dp[i - 1] + * 注意:0 和 26 + * @param s + * @return + */ + public int numDecodings(String s) { + char[] chars = ("#" + s).toCharArray(); + if (chars[1] == '0') { + return 0; + } + int[] dp = new int[chars.length]; + dp[0] = 1; dp[1]= 1; + for (int i = 2; i < chars.length; i ++) { + if (chars[i] == '0') { + if (chars[i - 1] == '1' || chars[i - 1] == '2') { + dp[i] = dp[i - 2]; + } else { + return 0; + } + } else if (chars[i - 1] == '1' || (chars[i - 1] == '2' && chars[i] <= '6')) { + dp[i] = dp[i - 1] + dp[i - 2]; + } else { + dp[i] = dp[i - 1]; + } + } + return dp[dp.length - 1]; + } + + +} diff --git a/Week 05/id_363/NOTE.md b/Week 05/id_363/NOTE.md index a6321d6e2..27ca829db 100644 --- a/Week 05/id_363/NOTE.md +++ b/Week 05/id_363/NOTE.md @@ -1,4 +1,21 @@ # NOTE +#### 字符串题目相关的经验: +- 1.从最后一个字符看起 +- 2.找重复子问题 +- 3.2个字符串的变化问题往往最后要做成一个二维数组,二维数组的行和列分别是题目中给出的2个不同的字符串。 +#### 动态规划小结: +- 1.打破自己的思维惯性,形成机器思维[找重复子问题] +- 2.理解复杂逻辑关键 +- 3.也是职业进阶的要领: 不要人肉递归,要做的是分治 + 合并 + +#### 动态规划求解模板【背会】: +- 1.暴力:时间复杂度一般是n^2 +- 2.动态规划: + 1. 分治[子问题] + 2.状态数组(数组中要存储什么内容,数组长度) + 3.dp方程 + +练习动态规划的时候 自顶向下和自下向上 都要练习 diff --git a/Week 05/id_363/practice/LeetCode_198_363.java b/Week 05/id_363/practice/LeetCode_198_363.java new file mode 100644 index 000000000..eb52ba90b --- /dev/null +++ b/Week 05/id_363/practice/LeetCode_198_363.java @@ -0,0 +1,80 @@ +package com.test.leetcode.week05; + +import org.junit.Test; + + +public class SolutionHouseRob198 { + + + @Test + public void test1() { + System.out.println(rob(new int[]{1,2,3,1})); + System.out.println(rob(new int[]{2,7,9,3,1})); + + System.out.println(rob2(new int[]{1,2,3,1})); + System.out.println(rob2(new int[]{2,7,9,3,1})); + + System.out.println(rob3(new int[]{1,2,3,1})); + System.out.println(rob3(new int[]{2,7,9,3,1})); + } + + + /** + * 求和的两个数值不能相邻 + * nums= [1,2,3,1] + * 当发现当前层的一个值不能计算计算出来下一层的值,那么可能需要一个二维数组 + * 状态 0 表示偷当前层的最大值 1 表示不偷当前层的最大值 + * + * 1.暴力 + * 2.DP + * 子问题:数组变小 + * 状态数组:二维状态数组 行表示当前层最大的金额 列表示当前层偷还是不偷 + * dp方程:偷: dp[i][0] = dp[i - 1][1] + nums[i] dp[i][1] = Math.max(dp[i-1][0], dp[i-1][1]); + * @param nums + * @return + */ + public int rob(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int len = nums.length; + int[][] dp = new int[len][2]; + dp[0][0] = nums[0]; dp[0][1]=0; + for (int i = 1; i < len;i++) { + dp[i][0] = dp[i-1][1] + nums[i]; + dp[i][1] = Math.max(dp[i - 1][0] , dp[i -1][1]); + } + return Math.max(dp[len-1][0], dp[len - 1][1]); + } + + public int rob2(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int preTou = nums[0], preNotTou = 0; + for (int i = 1; i < nums.length; i ++) { + int tou = preNotTou + nums[i]; + int notTou = Math.max(preNotTou, preTou); + preTou = tou; + preNotTou = notTou; + } + return Math.max(preTou, preNotTou); + } + + + /** + * dp方程:dp[i] = Math.max(dp[i-1], dp[i -2] + nums[i]) + * 状态数组:数组中存储从0到i一定偷当前层所能得到的最大金额 + * 子问题:数组变小 + * * @param nums + * @return + */ + public int rob3(int[] nums) { + int[] dp = new int[nums.length + 1]; + dp[0] = 0; dp[1] = nums[0]; + for (int i = 1; i < nums.length; i ++) { + dp[i + 1] = Math.max(dp[i], dp[i -1] + nums[i]); + } + return dp[nums.length]; + } +} diff --git a/Week 05/id_363/practice/LeetCode_213_363.java b/Week 05/id_363/practice/LeetCode_213_363.java new file mode 100644 index 000000000..37a0e19fa --- /dev/null +++ b/Week 05/id_363/practice/LeetCode_213_363.java @@ -0,0 +1,62 @@ +package com.test.leetcode.week05; + +import org.junit.Test; + + +public class SolutionHouseRobii213 { + + + @Test + public void test1() { + System.out.println(rob(new int[]{2,3,2})); + System.out.println(rob(new int[]{1,2,3,1})); + } + + + /** + * 所有的房屋都围成一圈,意味着如果选择第一个,那么不能选择最后一个 + * + * 子问题: max(rob(0, len -2), rob(1, len - 1)); + * 状态数组 : 记录到当前状态偷和不偷的最大金额 + * dp方程 : 0 表示偷 1 表示不偷 + * dp[i][0] = dp[i-1][1] + nums[i] dp[i][1] = Math.max(dp[i-1][0], dp[i-1][1]) + * + * @param nums + * @return + */ + public int rob(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + if (nums.length == 1) { + return nums[0]; + } + return Math.max(helper(nums, 0, nums.length - 2), helper(nums, 1, nums.length - 1)); + } + + // dp 中存储当前元素必须偷的最大值 + // dp[i] = Math.max(dp[i - 2] + nums[i] , dp[i - 1]); + private int helper(int[] nums, int start, int end) { + int pre = 0, cur = 0, temp = 0; + for (int i = start; i <= end; i ++) { + temp = cur; + cur = Math.max(pre + nums[i], cur); + pre = temp; + } + return cur; + } + + // [start,end] 使用include 和 exclude 命名会更好点 + private int helper2(int[] nums, int start, int end) { + int preInclude = nums[start], preExclude = 0; + for (int i = start + 1; i <= end; i ++) { + int include = preExclude + nums[i]; + int exclude = Math.max(preInclude, preExclude); + preInclude = include; + preExclude = exclude; + } + return Math.max(preInclude, preExclude); + } + + +} diff --git a/Week 05/id_363/practice/LeetCode_322_363.java b/Week 05/id_363/practice/LeetCode_322_363.java new file mode 100644 index 000000000..44845f07c --- /dev/null +++ b/Week 05/id_363/practice/LeetCode_322_363.java @@ -0,0 +1,246 @@ +package com.test.leetcode.week03; + +import org.junit.Test; + +import java.util.Arrays; + + +public class SolutionCoinChange322 { + + + @Test + public void test1() { + System.out.println(coinChange(new int[]{2}, 3)); + System.out.println(coinChange(new int[]{1,2,5}, 11)); + System.out.println(coinChange2(new int[]{2}, 3)); + System.out.println(coinChange2(new int[]{1,2,5}, 11)); + System.out.println(coinChange3(new int[]{2}, 3)); + System.out.println(coinChange3(new int[]{1,2,5}, 11)); + + System.out.println("-----------------------"); + System.out.println(coinChange_20191115_1(new int[]{2}, 3)); + System.out.println(coinChange_20191115_1(new int[]{1,2,5}, 11)); + System.out.println(coinChange_20191115_2(new int[]{2}, 3)); + System.out.println(coinChange_20191115_2(new int[]{1,2,5}, 11)); + System.out.println(coinChange_20191115_3(new int[]{2}, 3)); + System.out.println(coinChange_20191115_3(new int[]{1,2,5}, 11)); + } + + + + + /** + * 动态规划讨论:递归(暴力)-递归+记忆表(自顶想下)-循环(自下向上) + * 给定不同面额的coins 和 一个总金额amount,求凑成总金额所需要的最少的硬币个数 + * 1.暴力方法:f(amount) = 1 + min(f(amount - coin1), f(amount - coin2), f(amount - coin3)) + * + * 1.子问题 求解 amount-coint1 所需要的最少硬币树 + * 2.状态数组 dp[amount]=amount需要的最少的硬币个数 + * 3.dp方程 dp[i] = min(dp[i], dp[i - coin]) + * + * 时间复杂度分析: + * @param coins + * @param amount + * @return + */ + public int coinChange_20191115_1(int[] coins, int amount) { + if (coins == null || coins.length == 0) { + return -1; + } + return helper_20191115_1(coins, amount); + } + + + /** + * 如果存在可以最少硬币个数,返回硬币个数 + * 如果不存在硬币个数,返回 -1 + * @param coins + * @param amount + * @return + */ + private int helper_20191115_1(int[] coins, int amount) { + // termination + if (amount == 0) { + return 0; + } + // process + int res = Integer.MAX_VALUE; + for (int coin : coins) { + if (amount < coin) { + continue; + } + int subRes = helper_20191115_1(coins, amount - coin); + if (subRes == -1) { // 子问题无解 + continue; + } + res = Math.min(res, subRes); + } + // drill down + // reverse + return res == Integer.MAX_VALUE ? -1 : res + 1; + } + + /** + * 时间复杂度是O(nk) + * @param coins + * @param amount + * @return + */ + public int coinChange_20191115_2(int[] coins, int amount) { + if (coins == null || coins.length == 0) { + return -1; + } + int[] memo = new int[amount + 1]; + Arrays.fill(memo, -2); + return helper_20191115_2(coins, memo, amount); + } + + private int helper_20191115_2(int[] coins, int[] memo, int amount) { + // termination + if (amount == 0) { + return 0; + } + if (memo[amount] > -2) { + return memo[amount]; + } + // process + int res = Integer.MAX_VALUE; + for (int coin : coins) { + if (amount - coin < 0) { + continue; + } + int subRes = helper_20191115_2(coins, memo, amount - coin); + if (subRes == -1) { + continue; + } + res = Math.min(res, subRes); + } + // drill down + // reverse + if (res == Integer.MAX_VALUE) { + memo[amount] = -1; + } else { + memo[amount] = res + 1; + } + return memo[amount]; + } + + /** + * 时间复杂度是O(nk) + * @param coins + * @param amount + * @return + */ + public int coinChange_20191115_3(int[] coins, int amount) { + if (coins == null || coins.length == 0) { + return -1; + } + int[] dp = new int[amount + 1]; + Arrays.fill(dp, amount + 1); + dp[0] = 0; + for (int i = 1; i <= amount; i ++) { + for (int coin : coins) { + if (i >= coin) { + dp[i] = Math.min(dp[i], dp[i - coin] + 1); + } + } + } + return dp[amount] > amount ? -1 : dp[amount]; + } + + + /** + * 给定不同面额的硬币coins 和 一个总金额amount.求凑成总金额所需要的最少的硬币个数 + * 如果没有任何一种硬币组合能组成总金额,返回-1 + * + * 子问题:[1,2,5] amount = 11 11 所需要的最少硬币数 = 1 + min ( 11 - 1, 11- 2, 11 - 5); + * 状态数组:dp[] 存储每个金额所需要的最少的硬币个数 + * dp方程:dp[i] = 1+ min(dp[i - coin1], dp[i - coin2], dp[i - coin3]) + * + * @param coins + * @param amount + * @return + */ + public int coinChange(int[] coins, int amount) { + if (coins == null ) { + return -1; + } + return helper(coins, amount); + } + + private int helper(int[] coins, int amount) { + // termination + if (amount == 0) { + return 0; + } + // process + int res = Integer.MAX_VALUE; + for (int coin : coins) { + if (amount - coin < 0) { + continue; + } + int subRes = helper(coins, amount - coin); + if (subRes == -1) { + continue; + } + res = Math.min(res, subRes); + } + // drill down + return res == Integer.MAX_VALUE ? -1 : res + 1; + // reverse + } + + + + public int coinChange2(int[] coins, int amount) { + int[] memo = new int[amount + 1]; + for (int m : memo) { + memo[m] = -2; + } + return helper2(coins, memo, amount); + + } + + private int helper2(int[] coins,int[] memo, int amount) { + // termination + if (amount == 0) { + return 0; + } + if (memo[amount] > 0) { + return memo[amount]; + } + // process + int res = Integer.MAX_VALUE; + for (int coin : coins) { + if (amount - coin < 0) { + continue; + } + int subRes = helper2(coins, memo, amount - coin); + if (subRes == -1) { + continue; + } + res = Math.min(res, subRes); + } + // drill down + // reverse + memo[amount] = res == Integer.MAX_VALUE ? -1 : res + 1; + return memo[amount]; + } + + + public int coinChange3(int[] coins, int amount) { + int[] dp = new int[amount + 1]; + for (int i = 1; i < dp.length; i ++){ + dp[i] = amount + 1; + } + for (int i = 1; i <= amount; i ++) { + for (int coin : coins) { + if (i >= coin) { + dp[i] = Math.min(dp[i], dp[i - coin] + 1); + } + } + } + return dp[amount] > amount ? -1 : dp[amount]; + } + +} diff --git a/Week 05/id_363/practice/LeetCode_62_363.java b/Week 05/id_363/practice/LeetCode_62_363.java new file mode 100644 index 000000000..a99fbcc1c --- /dev/null +++ b/Week 05/id_363/practice/LeetCode_62_363.java @@ -0,0 +1,152 @@ +package com.test.leetcode.week05; + +import org.junit.Test; + +import java.util.Arrays; + + +public class SolutionUniquePaths62 { + + + @Test + public void test1() { + System.out.println(uniquePaths(3, 2)); + System.out.println(uniquePaths(7, 3)); + + System.out.println(uniquePaths2(3, 2)); + System.out.println(uniquePaths2(7, 3)); + + System.out.println(uniquePaths3(3, 2)); + System.out.println(uniquePaths3(7, 3)); + + System.out.println(uniquePaths4(3, 2)); + System.out.println(uniquePaths4(7, 3)); + System.out.println(uniquePaths5(3, 2)); + System.out.println(uniquePaths5(7, 3)); + + } + + /** + * 问题:一个机器人位于m*n网格的左上角,机器人只能想上或者向下移动一步,机器人到达右下角有多少总不同的路径 + * 数学归纳法:先计算机器人在最右边和最下面的路径 + * + * 子问题 + * 状态数组 状态数组中存储 当前格子到终点的路径数 + * 动态规划方程 dp[i , j] = dp[i - 1, j] + dp[i,j + 1] + * + * + * 代码:递归 -> 自顶想下 -> 自下向上 -> 时间复杂度和空间复杂度优化 + * + * m 表示列 n表示行 + * + * @param m + * @param n + * @return + */ + public int uniquePaths(int m, int n) { + return helper(m - 1, n - 1); + } + + private int helper(int c, int r) { + // termination + if ( c == 0 || r == 0) { + return 1; + } + // process + int right = helper(c - 1, r); + int below = helper(c, r - 1); + return right + below; + // drill down + // reverse + } + // m是列 n是行 + public int uniquePaths2(int m, int n) { + // 记忆表初始化 + int[][] memo = new int[n][m]; + for (int i = 0; i < m; i ++) { + memo[0][i] = 1; + } + for (int i = 0; i < n; i ++) { + memo[i][0] = 1; + } + + return helper2(m - 1, n - 1, memo); + } + + private int helper2(int c, int r, int[][] memo) { + // termination: 终止条件是记忆表中已经计算好了 + if (memo[r][c] > 0) { + return memo[r][c]; + } + // process + int right = helper2(c - 1, r, memo); + int below = helper2(c, r - 1, memo); + // drill down:把算好的结果放入到记忆表中 + memo[r][c] = right + below; + // reverse + return memo[r][c]; + } + + // m表示列 n表示行 + public int uniquePaths3(int m, int n) { + // 记忆表初始化 + int[][] memo = new int[n][m]; + for (int i = 0; i < m; i ++) { + memo[0][i] = 1; + } + for (int i = 0; i < n; i ++) { + memo[i][0] = 1; + } + for (int r = 1; r < n; r ++) { + for (int c = 1; c < m ; c ++ ) { + memo[r][c] = memo[r - 1][c] + memo[r][c - 1]; + } + } + return memo[n - 1][m - 1]; + } + + // m 表示列 n表示行 + public int uniquePaths4(int m, int n) { + // 记忆表初始化 + int[] memo = new int[m]; + Arrays.fill(memo, 1); + + for (int r = 1; r < n; r ++) { + for (int c = 1; c < m; c ++) { + memo[c] = memo[c] + memo[c - 1]; + } + } + return memo[m - 1]; + } + + + /** + * 使用公式 + * m表示列 n表示行 需要 m -1 right 和 n -1 down 才能到达终点 + * 所以这也是全排列的问题 m-1个 R 和 n- 1个D的全排列 + * 方程是(m + n)! / (m ! * n !); + * @param m + * @param n + * @return + */ + public int uniquePaths5(int m, int n) { + if (m == 1 || n == 1) { + return 1; + } + m --; + n --; + if (m < n) { + int temp = m; + m = n; + n = temp; + } + long j = 1, res = 1; + for (int i = m + 1; i <= m + n; i ++, j ++) { + res *= i; + res /= j; + } + return (int)res; + } + + +} diff --git a/Week 05/id_363/practice/LeetCode_63_363.java b/Week 05/id_363/practice/LeetCode_63_363.java new file mode 100644 index 000000000..37cc755f7 --- /dev/null +++ b/Week 05/id_363/practice/LeetCode_63_363.java @@ -0,0 +1,160 @@ +package com.test.leetcode.week05; + +import org.junit.Test; + +import java.util.Arrays; + + +public class SolutionUniquePathWIthObs63 { + + + @Test + public void test1() { + int[][] obstacleGrid = { + {0,0,0}, + {0,1,0}, + {0,0,0} + }; + int[][] obstacleGrid2 = { + {0,0}, + {1,1}, + {0,0} + }; + System.out.println(uniquePathsWithObstacles2(obstacleGrid)); + System.out.println(uniquePathsWithObstacles2(obstacleGrid2)); + System.out.println(uniquePathsWithObstacles3(obstacleGrid)); + System.out.println(uniquePathsWithObstacles3(obstacleGrid2)); + System.out.println(uniquePathsWithObstacles4(obstacleGrid)); + System.out.println(uniquePathsWithObstacles4(obstacleGrid2)); + + } + + + /** + * 网格中可能有障碍物 + * 遇到障碍物:加0, 正常情况 是 dp[i, j] = dp[i + 1][j] + dp[i][j + 1] + * + * 子问题:把网格缩小 + * 状态数组:当前位置到达终点的可能的路径个数 + * dp方程:dp[i, j] = dp[i + 1][j] + dp[i][j + 1] + * + * 递归 -> 自顶向下 ->自下向上 -> 优化空间复杂度 + * + * @param obstacleGrid + * @return + */ + public int uniquePathsWithObstacles2(int[][] obstacleGrid) { + int row = obstacleGrid.length; + int col = obstacleGrid[0].length; + if (row == 1 && col == 1) { + return obstacleGrid[0][0] == 1 ? 0 : 1; + } + int[][] memo = new int[row][col]; + for (int[] m : memo) { + Arrays.fill(m, -1); + } + memo[row - 1][col - 1] = obstacleGrid[row - 1][col -1] == 1? 0: 1; + for (int i = row - 2; i >= 0; i --) { + if (obstacleGrid[i][col - 1] == 1) { + memo[i][col - 1] = 0; + } else { + memo[i][col - 1] = memo[i + 1][col - 1]; + } + } + for (int i = col - 2; i >= 0; i --) { + if (obstacleGrid[row - 1][i] == 1) { + memo[row - 1][i] = 0; + } else { + memo[row - 1][i] = memo[row - 1][i + 1]; + } + } + return help2(obstacleGrid, memo, 0, 0); + } + + private int help2(int[][] obstacleGrid, int[][] memo, int row, int col) { + // termination + if (memo[row][col] > -1) { + return memo[row][col]; + } + if (obstacleGrid[row][col] == 1) { + memo[row][col] = 0; + return 0; + } + // process + + // drill down + int right = help2(obstacleGrid, memo, row , col + 1); + int below = help2(obstacleGrid, memo, row + 1 , col); + // reverse + memo[row][col] = right + below; + return memo[row][col]; + } + + // 自下向上 + // 考虑障碍物在终点 考虑障碍物在最左边或者最下边 初始化需要注意 + public int uniquePathsWithObstacles3(int[][] obstacleGrid) { + int row = obstacleGrid.length; + int col = obstacleGrid[0].length; + // 考虑终点 + if (obstacleGrid[row - 1][col - 1] == 1) { + return 0; + } + // 初始化两边的值 + int[][] memo = new int[row][col]; + memo[row - 1][col - 1]= 1; + for (int i = row - 2;i >= 0; i --) { + if (obstacleGrid[i][col - 1] != 1) { + memo [i][col - 1] = memo[i + 1][col - 1]; + } + } + for (int i = col - 2; i >= 0; i --) { + if (obstacleGrid[row - 1][i] != 1) { + memo[row - 1][i] = memo[row - 1][i + 1]; + } + } + + for (int r = row - 2; r >= 0; r --) { + for (int c = col -2; c >= 0; c --) { + if (obstacleGrid[r][c] != 1) { + memo[r][c] = memo[r][c + 1] + memo[r + 1][c]; + } + } + } + return memo[0][0]; + } + + + /** + * 最简单写法 + * @param obstacleGrid + * @return + */ + public int uniquePathsWithObstacles4(int[][] obstacleGrid) { + int row = obstacleGrid.length; + int col = obstacleGrid[0].length; + if (obstacleGrid[row - 1][col -1] == 1) { + return 0; + } + int[] dp = new int[col]; + dp[col - 1] = 1; + for (int i = col - 2; i >= 0; i --) { + if (obstacleGrid[row - 1][i] != 1) { + dp[i] = dp[i + 1]; + } + } + + for (int r = row - 2; r >= 0; r --) { + dp[col - 1] = obstacleGrid[r][col -1] == 1 ? 0 : dp[col - 1]; + for (int c = col - 2; c >= 0; c --) { + if (obstacleGrid[r][c] != 1) { + dp[c] = dp[c] + dp[c + 1]; + } else { + dp[c] = 0; + } + } + } + return dp[0]; + } + + +} diff --git a/Week 05/id_373/week05_373_homework/32.cpp b/Week 05/id_373/week05_373_homework/32.cpp new file mode 100644 index 000000000..87babf6bf --- /dev/null +++ b/Week 05/id_373/week05_373_homework/32.cpp @@ -0,0 +1,26 @@ +public class Solution { + public boolean isValid(String s) { + Stack stack = new Stack(); + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '(') { + stack.push('('); + } else if (!stack.empty() && stack.peek() == '(') { + stack.pop(); + } else { + return false; + } + } + return stack.empty(); + } + public int longestValidParentheses(String s) { + int maxlen = 0; + for (int i = 0; i < s.length(); i++) { + for (int j = i + 2; j <= s.length(); j+=2) { + if (isValid(s.substring(i, j))) { + maxlen = Math.max(maxlen, j - i); + } + } + } + return maxlen; + } +} \ No newline at end of file diff --git a/Week 05/id_373/week05_373_homework/64.cpp b/Week 05/id_373/week05_373_homework/64.cpp new file mode 100644 index 000000000..7d0e8e5b1 --- /dev/null +++ b/Week 05/id_373/week05_373_homework/64.cpp @@ -0,0 +1,18 @@ +public class Solution { + public int minPathSum(int[][] grid) { + int[][] dp = new int[grid.length][grid[0].length]; + for (int i = grid.length - 1; i >= 0; i--) { + for (int j = grid[0].length - 1; j >= 0; j--) { + if(i == grid.length - 1 && j != grid[0].length - 1) + dp[i][j] = grid[i][j] + dp[i][j + 1]; + else if(j == grid[0].length - 1 && i != grid.length - 1) + dp[i][j] = grid[i][j] + dp[i + 1][j]; + else if(j != grid[0].length - 1 && i != grid.length - 1) + dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]); + else + dp[i][j] = grid[i][j]; + } + } + return dp[0][0]; + } +} \ No newline at end of file diff --git a/Week 05/id_373/week05_373_homework/72.cpp b/Week 05/id_373/week05_373_homework/72.cpp new file mode 100644 index 000000000..a43f1e70a --- /dev/null +++ b/Week 05/id_373/week05_373_homework/72.cpp @@ -0,0 +1,31 @@ +class Solution { + public int minDistance(String word1, String word2) { + int n = word1.length(); + int m = word2.length(); + + if (n * m == 0) + return n + m; + + int [][] d = new int[n + 1][m + 1]; + + for (int i = 0; i < n + 1; i++) { + d[i][0] = i; + } + for (int j = 0; j < m + 1; j++) { + d[0][j] = j; + } + + for (int i = 1; i < n + 1; i++) { + for (int j = 1; j < m + 1; j++) { + int left = d[i - 1][j] + 1; + int down = d[i][j - 1] + 1; + int left_down = d[i - 1][j - 1]; + if (word1.charAt(i - 1) != word2.charAt(j - 1)) + left_down += 1; + d[i][j] = Math.min(left, Math.min(down, left_down)); + + } + } + return d[n][m]; + } +} \ No newline at end of file diff --git a/Week 05/id_383/LeetCode_64_383.java b/Week 05/id_383/LeetCode_64_383.java new file mode 100644 index 000000000..e34c86ff0 --- /dev/null +++ b/Week 05/id_383/LeetCode_64_383.java @@ -0,0 +1,25 @@ +import java.util.Stack; + +public class LeetCode_64_383 { + + public int minPathSum(int[][] grid) { + int rows = grid.length; + int cols = grid[0].length; + int[][] dp = new int[rows][cols]; + for (int i = rows - 1; i > -1; i--) { + for (int j = cols - 1; j > -1; j--) { + if (i == rows - 1 && j == cols - 1) { + dp[i][j] = grid[i][j]; + } else if (i == rows - 1 && j != cols - 1) { + dp[i][j] = grid[i][j] + dp[i][j + 1]; + } else if (j == cols - 1 && i != rows - 1) { + dp[i][j] = grid[i][j] + dp[i + 1][j]; + } else { + dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]); + } + } + } + return dp[0][0]; + } + +} diff --git a/Week 05/id_383/LeetCode_91_383.java b/Week 05/id_383/LeetCode_91_383.java new file mode 100644 index 000000000..2660cd4c6 --- /dev/null +++ b/Week 05/id_383/LeetCode_91_383.java @@ -0,0 +1,27 @@ +public class LeetCode_91_383 { + + public int numDecodings(String s) { + int len = s.length(); + if (len == 0 || s.charAt(0) == '0') { + return 0; + } + int pre = 1; + int curr = 1; + for (int i = 1; i < len; i++) { + int tmp = curr; + int currNum = s.charAt(i); + int lastNum = s.charAt(i - 1); + if (currNum == '0') { + if (lastNum == '1' || lastNum == '2') { + curr = pre; + } else { + return 0; + } + } else if (lastNum == '1' || lastNum == '2' && currNum >= '1' && currNum <= '6') { + curr = curr + pre; + } + pre = tmp; + } + return curr; + } +} diff --git a/Week 05/id_388/LeeCode_1143_388.java b/Week 05/id_388/LeeCode_1143_388.java new file mode 100644 index 000000000..107b902ea --- /dev/null +++ b/Week 05/id_388/LeeCode_1143_388.java @@ -0,0 +1,101 @@ +package com.company.leetcode.editor.cn; + +//给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列。 +//count = 1 +// 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 +//例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。 +// +// 若这两个字符串没有公共子序列,则返回 0。 +// +// +// +// 示例 1: +// +// 输入:text1 = "abcde", text2 = "ace" +//输出:3 +//解释:最长公共子序列是 "ace",它的长度为 3。 +// +// +// 示例 2: +// +// 输入:text1 = "abc", text2 = "abc" +//输出:3 +//解释:最长公共子序列是 "abc",它的长度为 3。 +// +// +// 示例 3: +// +// 输入:text1 = "abc", text2 = "def" +//输出:0 +//解释:两个字符串没有公共子序列,返回 0。 +// +// +// +// +// 提示: +// +// +// 1 <= text1.length <= 1000 +// 1 <= text2.length <= 1000 +// 输入的字符串只含有小写英文字符。 +// +// Related Topics 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_1143 { + public int longestCommonSubsequence(String text1, String text2) { + + + char[] char1 = text1.toCharArray(); + char[] char2 = text2.toCharArray(); + + int[][] dp = new int[char2.length][char1.length]; + //处理边缘 + if (char1[0] == char2[0]) { + dp[0][0] = 1; + } + + //row0 + for (int i = 1; i < char1.length; i++) { + if (char1[i] == char2[0]) { + dp[0][i] = 1; + } else { + dp[0][i] = dp[0][i - 1]; + } + } + + //col0 + for (int i = 1; i < char2.length; i++) { + if (char1[0] == char2[i]) { + dp[i][0] = 1; + } else { + dp[i][0] = dp[i - 1][0]; + } + } + + //状态转移 + for (int i = 1; i < char2.length; i++) { + for (int j = 1; j < char1.length; j++) { + if (char1[j] == char2[i]) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = Math.max(dp[i - 1][j],dp[i][j - 1]); + } + } + } + + return dp[char2.length - 1][char1.length - 1]; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// String text1 = "bsbininm"; +// String text2 = "jmjkbkjkv"; +// //Wrong Answer: input:"bsbininm" "jmjkbkjkv" Output:2 Expected:1 +// int len = s.longestCommonSubsequence(text1,text2); +// System.out.println(len); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_388/LeeCode_120_388.java b/Week 05/id_388/LeeCode_120_388.java new file mode 100644 index 000000000..cc5076224 --- /dev/null +++ b/Week 05/id_388/LeeCode_120_388.java @@ -0,0 +1,79 @@ +package com.company.leetcode.editor.cn; +//给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。 +// count = 1 +// 例如,给定三角形: +// +// [ +// [2], +// [3,4], +// [6,5,7], +// [4,1,8,3] +//] +// +// +// 自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。 +// +// 说明: +// +// 如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。 +// Related Topics 数组 动态规划 + + +import java.util.ArrayList; +import java.util.List; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_120 { + public int minimumTotal(List> triangle) { + + for (int i = triangle.size() - 2; i >= 0 ; i--) { + List tmp = triangle.get(i + 1); + for (int j = 0; j < triangle.get(i).size(); j++) { + int current = triangle.get(i).get(j); + int nextL = tmp.get(j); + int nextR = tmp.get(j + 1); + int res = Math.min(current + nextL,current + nextR); + triangle.get(i).set(j,res); + } + } + + return triangle.get(0).get(0); + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// +// List> list = new ArrayList<>(); +// // [2], +//// [3,4], +//// [6,5,7], +//// [4,1,8,3] +// List l1 = new ArrayList<>(); +// l1.add(2); +// +// List l2 = new ArrayList<>(); +// l2.add(3); +// l2.add(4); +// +// List l3 = new ArrayList<>(); +// l3.add(6); +// l3.add(5); +// l3.add(7); +// +// List l4 = new ArrayList<>(); +// l4.add(4); +// l4.add(1); +// l4.add(8); +// l4.add(3); +// +// list.add(l1); +// list.add(l2); +// list.add(l3); +// list.add(l4); +// +// int res = s.minimumTotal(list); +// System.out.println(res); +// +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_388/LeeCode_123_388.java b/Week 05/id_388/LeeCode_123_388.java new file mode 100644 index 000000000..fd60c3549 --- /dev/null +++ b/Week 05/id_388/LeeCode_123_388.java @@ -0,0 +1,65 @@ +package com.company.leetcode.editor.cn; + +//给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。 +// +// 设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。 +// +// 注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 +// +// 示例 1: +// +// 输入: [3,3,5,0,0,3,1,4] +//输出: 6 +//解释: 在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。 +//  随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。 +// +// 示例 2: +// +// 输入: [1,2,3,4,5] +//输出: 4 +//解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。   +//  注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。   +//  因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 +// +// +// 示例 3: +// +// 输入: [7,6,4,3,1] +//输出: 0 +//解释: 在这个情况下, 没有交易完成, 所以最大利润为 0。 +// Related Topics 数组 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_123 { + public int maxProfit(int[] prices) { + if (prices == null || prices.length <= 1) { + return 0; + } + int max_k = 2; + int[][][] dp = new int[prices.length][max_k + 1][2]; + for (int i = 0; i < prices.length; i++) { + for (int j = max_k; j >= 1; j--) { + //base + if (i == 0) { + dp[i][j][0] = 0; + dp[i][j][1] = -prices[i]; + continue; + } + + dp[i][j][0] = Math.max(dp[i - 1][j][0],dp[i - 1][j][1] + prices[i]); + dp[i][j][1] = Math.max(dp[i - 1][j][1],dp[i - 1][j - 1][0] - prices[i]); + } + } + + return dp[prices.length - 1][max_k][0]; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// int[] prices = new int[]{3,3,5,0,0,3,1,4}; +// System.out.println(s.maxProfit(prices)); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_388/LeeCode_152_388.java b/Week 05/id_388/LeeCode_152_388.java new file mode 100644 index 000000000..307992b7f --- /dev/null +++ b/Week 05/id_388/LeeCode_152_388.java @@ -0,0 +1,49 @@ +package com.company.leetcode.editor.cn;//给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。 +// +// 示例 1: +// +// 输入: [2,3,-2,4] +//输出: 6 +//解释: 子数组 [2,3] 有最大乘积 6。 +// +// +// 示例 2: +// +// 输入: [-2,0,-1] +//输出: 0 +//解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。 +// Related Topics 数组 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_152 { + public int maxProduct(int[] nums) { + int res = nums[0]; + int imax = res; + int imin = res; + + for (int i = 1; i < nums.length; i++) { + if (nums[i] < 0) { + int temp = imax; + imax = imin; + imin = temp; + } + + imax = Math.max(imax * nums[i],nums[i]); + imin = Math.min(imin * nums[i],nums[i]); + + res = Math.max(imax,res); + } + + return res; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// int[] nums = new int[]{2,3,-2,4}; +// int res = s.maxProduct(nums); +// System.out.println(res); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_388/LeeCode_188_388.java b/Week 05/id_388/LeeCode_188_388.java new file mode 100644 index 000000000..aaf6630f5 --- /dev/null +++ b/Week 05/id_388/LeeCode_188_388.java @@ -0,0 +1,72 @@ +package com.company.leetcode.editor.cn; + +//给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。 +// +// 设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。 +// +// 注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 +// +// 示例 1: +// +// 输入: [2,4,1], k = 2 +//输出: 2 +//解释: 在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。 +// +// +// 示例 2: +// +// 输入: [3,2,6,5,0,3], k = 2 +//输出: 7 +//解释: 在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。 +//  随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。 +// +// Related Topics 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_188 { + public int maxProfit(int k, int[] prices) { + + if (prices == null || prices.length <= 1) { + return 0; + } + int len = prices.length; + int profit = 0; + if (k >= len / 2) { + for (int i = 1; i < len; i++) { + if (prices[i] > prices[i - 1]) { + profit += prices[i] - prices[i - 1]; + } + } + return profit; + } + + //动态规划 + int[][][] dp = new int[len][k + 1][2]; + + for (int i = 0; i < prices.length; i++) { + for (int j = k; j >= 1 ; j--) { + if (i == 0) { + dp[i][j][0] = 0; + dp[i][j][1] = -prices[i]; + continue; + } + dp[i][j][0] = Math.max(dp[i - 1][j][0],dp[i - 1][j][1] + prices[i]); + dp[i][j][1] = Math.max(dp[i - 1][j][1],dp[i - 1][j - 1][0] - prices[i]); + } + } + + return dp[len -1][k][0]; + } + + + +// public static void main(String[] args) { +// Solution s = new Solution(); +// //Wrong Answer: input:2 [3,2,6,5,0,3] Output:6 Expected:7 +// int[] prices = new int[]{3,2,6,5,0,3}; +// System.out.println(s.maxProfit(2,prices)); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_388/LeeCode_198_388.java b/Week 05/id_388/LeeCode_198_388.java new file mode 100644 index 000000000..6c700c7e3 --- /dev/null +++ b/Week 05/id_388/LeeCode_198_388.java @@ -0,0 +1,48 @@ +package com.company.leetcode.editor.cn; + +//你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 +// +// 给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。 +// +// 示例 1: +// +// 输入: [1,2,3,1] +//输出: 4 +//解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 +//  偷窃到的最高金额 = 1 + 3 = 4 。 +// +// 示例 2: +// +// 输入: [2,7,9,3,1] +//输出: 12 +//解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 +//  偷窃到的最高金额 = 2 + 9 + 1 = 12 。 +// +// Related Topics 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_198 { + public int rob(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int[][] dp = new int[nums.length][2]; + dp[0][1] = nums[0]; + for (int i = 1; i < nums.length; i++) { + dp[i][0] = Math.max(dp[i - 1][0],dp[i - 1][1]); + dp[i][1] = dp[i - 1][0] + nums[i]; + } + + return Math.max(dp[nums.length - 1][0],dp[nums.length - 1][1]); + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// int[] nums = new int[]{2,7,9,3,1}; +// int res = s.rob(nums); +// System.out.println(res); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_388/LeeCode_213_388.java b/Week 05/id_388/LeeCode_213_388.java new file mode 100644 index 000000000..ad6be01e2 --- /dev/null +++ b/Week 05/id_388/LeeCode_213_388.java @@ -0,0 +1,62 @@ +package com.company.leetcode.editor.cn; +//你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。 +// 这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。 +// 同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入, +// 系统会自动报警。 +// +// 给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下, +// 能够偷窃到的最高金额。 +// +// 示例 1: +// +// 输入: [2,3,2] +//输出: 3 +//解释: 你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。 +// +// +// 示例 2: +// +// 输入: [1,2,3,1] +//输出: 4 +//解释: 你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。 +//  偷窃到的最高金额 = 1 + 3 = 4 。 +// Related Topics 动态规划 + + +import java.util.Arrays; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_213 { + public int rob(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + if (nums.length == 1) { + return nums[0]; + } + + //copy + int[] arr1 = Arrays.copyOfRange(nums,1,nums.length); + int[] arr2 = Arrays.copyOfRange(nums,0,nums.length - 1); + + return Math.max(helper(arr1),helper(arr2)); + } + + private int helper(int[] nums) { + int[][] dp = new int[nums.length][2]; + dp[0][1] = nums[0]; + + for (int i = 1; i < nums.length; i++) { + dp[i][0] = Math.max(dp[i - 1][0],dp[i - 1][1]); //不偷 + dp[i][1] = dp[i - 1][0] + nums[i]; //偷 + } + return Math.max(dp[nums.length - 1][0],dp[nums.length - 1][1]); + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// int[] nums = new int[]{2,3,2}; +// System.out.println(s.rob(nums)); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_388/LeeCode_279_388.java b/Week 05/id_388/LeeCode_279_388.java new file mode 100644 index 000000000..7cdb12587 --- /dev/null +++ b/Week 05/id_388/LeeCode_279_388.java @@ -0,0 +1,45 @@ +package com.company.leetcode.editor.cn; +//给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。 +// +// 示例 1: +// +// 输入: n = 12 +//输出: 3 +//解释: 12 = 4 + 4 + 4. +// +// 示例 2: +// +// 输入: n = 13 +//输出: 2 +//解释: 13 = 4 + 9. +// Related Topics 广度优先搜索 数学 动态规划 + + +import java.util.Arrays; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_279 { + public int numSquares(int n) { + int[] dp = new int[n + 1]; + Arrays.fill(dp, Integer.MAX_VALUE); + + dp[0] = 0; + for (int i = 1; i <= n ; i++) { + int min = Integer.MAX_VALUE; + int j = 1; + while (i - j * j >= 0) { + min = Math.min(min,dp[i - j * j] + 1); + j++; + } + dp[i] = min; + } + + return dp[n]; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// System.out.println(s.numSquares(12)); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_388/LeeCode_309_388.java b/Week 05/id_388/LeeCode_309_388.java new file mode 100644 index 000000000..29dceb9a7 --- /dev/null +++ b/Week 05/id_388/LeeCode_309_388.java @@ -0,0 +1,56 @@ +package com.company.leetcode.editor.cn; +//给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。 +// +// 设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票): +// +// +// 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 +// 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。 +// +// +// 示例: +// +// 输入: [1,2,3,0,2] +//输出: 3 +//解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出] +// Related Topics 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_309 { + public int maxProfit(int[] prices) { + if (prices == null || prices.length <= 1) { + return 0; + } + int[][] dp = new int[prices.length][2]; + + for (int i = 0; i < prices.length; i++) { + if (i - 1 == -1) { + dp[i][0] = 0; + dp[i][1] = -prices[i]; + continue; + } + + if (i - 2 == -1) { + 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]); + continue; + } + + //1、昨天就没有股票 2、昨天有股票,今天卖掉 + dp[i][0] = Math.max(dp[i - 1][0],dp[i-1][1] + prices[i]); + + //1、昨天就有股票 2、前天卖了,冷冻一天,今天买的 + dp[i][1] = Math.max(dp[i - 1][1],dp[i-2][0] - prices[i]); + } + return dp[prices.length - 1][0]; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// int[] prices = new int[]{1,2,3,0,2}; +// System.out.println(s.maxProfit(prices)); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_388/LeeCode_322_388.java b/Week 05/id_388/LeeCode_322_388.java new file mode 100644 index 000000000..7caf8a15b --- /dev/null +++ b/Week 05/id_388/LeeCode_322_388.java @@ -0,0 +1,56 @@ +package com.company.leetcode.editor.cn; +//给定不同面额的硬币 coins 和一个总金额 amount。 +// 编写一个函数来计算可以凑成总金额所需的最少的硬币个数。 +// 如果没有任何一种硬币组合能组成总金额,返回 -1。 +// count = 4 +// 示例 1: +// +// 输入: coins = [1, 2, 5], amount = 11 +//输出: 3 +//解释: 11 = 5 + 5 + 1 +// +// 示例 2: +// +// 输入: coins = [2], amount = 3 +//输出: -1 +// +// 说明: +//你可以认为每种硬币的数量是无限的。 +// Related Topics 动态规划 + + +import java.util.*; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_322 { + public int coinChange(int[] coins, int amount) { + if (amount == 0) { + return 0; + } + int[] dp = new int[amount + 1]; + + int sum = 0; + while (sum <= amount) { + int min = -1; + for (int coin:coins) { + if (sum >= coin && dp[sum - coin] != -1) { + int temp = dp[sum - coin] + 1; + min = min < 0 ? temp : Math.min(temp,min); + } + } + dp[sum] = min; + sum++; + } + + return dp[amount]; + } +// +// public static void main(String[] args) { +// Solution s = new Solution(); +// //Wrong Answer: input:[186,419,83,408] 6249 Output:-1 Expected:20 +//// int res = s.coinChange(new int[]{186,419,83,408},6249); +// int res = s.coinChange(new int[]{1,2,5},11); +// System.out.println(res); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_388/LeeCode_32_388.java b/Week 05/id_388/LeeCode_32_388.java new file mode 100644 index 000000000..a104007b7 --- /dev/null +++ b/Week 05/id_388/LeeCode_32_388.java @@ -0,0 +1,47 @@ +package com.company.leetcode.editor.cn;//给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。 +// +// 示例 1: +// +// 输入: "(()" +//输出: 2 +//解释: 最长有效括号子串为 "()" +// +// +// 示例 2: +// +// 输入: ")()())" +//输出: 4 +//解释: 最长有效括号子串为 "()()" +// +// Related Topics 字符串 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_32 { + public int longestValidParentheses(String s) { + int res = 0; + int len = s.length(); + int[] dp = new int[len]; + for (int i = 1; i < len ; i++) { + if (s.charAt(i) != ')') { + continue; + } + // == '()' + if (s.charAt(i - 1) == '(') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + } else if (i - dp[i - 1] > 0 && s.charAt(dp[i - dp[i - 1] - 1]) == '(') { + dp[i] = dp[i - 1] + ((i - dp[i - 1] - 2) >= 0 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + res = Math.max(dp[i],res); + } + + return res; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// System.out.println(s.longestValidParentheses(")(((((()())()()))()(()))(")); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_388/LeeCode_53_388.java b/Week 05/id_388/LeeCode_53_388.java new file mode 100644 index 000000000..b1985e5d2 --- /dev/null +++ b/Week 05/id_388/LeeCode_53_388.java @@ -0,0 +1,38 @@ +package com.company.leetcode.editor.cn; +//给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 +// +// 示例: +// +// 输入: [-2,1,-3,4,-1,2,1,-5,4], +//输出: 6 +//解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 +// +// +// 进阶: +// +// 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 +// Related Topics 数组 分治算法 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_53 { + public int maxSubArray(int[] nums) { + int maxSofar = nums[0]; + int maxHere = nums[0]; + for (int i = 1; i < nums.length; i++) { + maxHere = Math.max(maxHere + nums[i],nums[i]); + maxSofar = Math.max(maxHere,maxSofar); + } + + return maxSofar; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// int[] nums = new int[]{-2,1,-3,4,-1,2,1,-5,4}; +// int res = s.maxSubArray(nums); +// System.out.println(res); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_388/LeeCode_714_388.java b/Week 05/id_388/LeeCode_714_388.java new file mode 100644 index 000000000..7638f1fec --- /dev/null +++ b/Week 05/id_388/LeeCode_714_388.java @@ -0,0 +1,54 @@ +package com.company.leetcode.editor.cn; +//给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。 +// +// 你可以无限次地完成交易,但是你每次交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。 +// +// 返回获得利润的最大值。 +// +// 示例 1: +// +// 输入: prices = [1, 3, 2, 8, 4, 9], fee = 2 +//输出: 8 +//解释: 能够达到的最大利润: +//在此处买入 prices[0] = 1 +//在此处卖出 prices[3] = 8 +//在此处买入 prices[4] = 4 +//在此处卖出 prices[5] = 9 +//总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8. +// +// 注意: +// +// +// 0 < prices.length <= 50000. +// 0 < prices[i] < 50000. +// 0 <= fee < 50000. +// +// Related Topics 贪心算法 数组 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_714 { + public int maxProfit(int[] prices, int fee) { + if (prices == null || prices.length <= 1) { + return 0; + } + int len = prices.length; + int[][] dp = new int[len][2]; + + dp[0][1] = -prices[0]; + for (int i = 1; i < len; i++) { + dp[i][0] = Math.max(dp[i - 1][0],dp[i - 1][1] + prices[i] - fee); + dp[i][1] = Math.max(dp[i - 1][1],dp[i - 1][0] - prices[i]); + } + + return dp[len - 1][0]; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// int[] prices = new int[]{1, 3, 2, 8, 4, 9}; +// System.out.println(s.maxProfit(prices,2)); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_388/LeeCode_72_388.java b/Week 05/id_388/LeeCode_72_388.java new file mode 100644 index 000000000..9f95b7d7a --- /dev/null +++ b/Week 05/id_388/LeeCode_72_388.java @@ -0,0 +1,87 @@ +package com.company.leetcode.editor.cn;//给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。 +// +// 你可以对一个单词进行如下三种操作: +// +// +// 插入一个字符 +// 删除一个字符 +// 替换一个字符 +// +// +// 示例 1: +// +// 输入: word1 = "horse", word2 = "ros" +//输出: 3 +//解释: +//horse -> rorse (将 'h' 替换为 'r') +//rorse -> rose (删除 'r') +//rose -> ros (删除 'e') +// +// +// 示例 2: +// +// 输入: word1 = "intention", word2 = "execution" +//输出: 5 +//解释: +//intention -> inention (删除 't') +//inention -> enention (将 'i' 替换为 'e') +//enention -> exention (将 'n' 替换为 'x') +//exention -> exection (将 'n' 替换为 'c') +//exection -> execution (插入 'u') +// +// Related Topics 字符串 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_72 { + public int minDistance(String word1, String word2) { + + int l1 = word1.length(); + int l2 = word2.length(); + + if (l1 == 0) { + return l2;//插入 + } + if (l2 == 0) { + return l1;//删除 + } +// + int[][] dp = new int[l1 + 1][l2 + 1]; +// //base case +// //行 + for(int i = 1; i < dp.length; i++) dp[i][0] = i; + for(int i = 1; i < dp[0].length; i++) dp[0][i] = i; +// //状态方程 = 替换或跳过,删除,插入 + for (int i = 1; i <= l1; i++) { + for (int j = 1; j <= l2 ; j++) { + + if (word1.charAt(i - 1) == word2.charAt(j - 1)) { //跳过,不记录步数 + dp[i][j] = dp[i - 1][j - 1]; + continue; + } + //替换、删除、插入 + dp[i][j] = min( + dp[i - 1][j - 1], + dp[i][j - 1], + dp[i - 1][j] + ) + 1; + + } + } + return dp[word1.length()][word2.length()]; + } + + private int min(int a,int b,int c) { + return Math.min(a,Math.min(b,c)); + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// //Wrong Answer: input:"distance" "springbok" Output:8 Expected:9 +// String word1 = "distance"; +// String word2 = "springbok"; +// System.out.println(s.minDistance(word1,word2)); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_388/NOTE.md b/Week 05/id_388/NOTE.md index a6321d6e2..0c9384c36 100644 --- a/Week 05/id_388/NOTE.md +++ b/Week 05/id_388/NOTE.md @@ -1,4 +1,40 @@ # NOTE +总结: +1、定义:将复杂问题分解成简单的子问题,递归和分治的方式 - +共性:找到重复子问题 +差异性:最优子结构,中途可以淘汰次优解 +2、几个状态转移方程: + + + ``` + a、股票问题 + base case: + dp[-1][k][0] = dp[i][0][0] = 0 + dp[-1][k][1] = dp[i][0][1] = -infinity + + 状态转移方程: + dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i]) + dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i]) + ``` + + b、编辑距离 + + + defdp(i, j): + + dp(i - 1, j - 1) #1 + + dp(i, j - 1) #2 + + dp(i - 1, j) #3 + + c、打家劫舍 + dp[n] = MAX( dp[n-1], dp[n-2] + num ) + + d、最长有效括号 + dp[i]=dp[i−1]+dp[i−dp[i−1]−2]+2 + + ``` + \ No newline at end of file diff --git a/Week 05/id_393/LeetCode_221_393.java b/Week 05/id_393/LeetCode_221_393.java new file mode 100644 index 000000000..75ac643b1 --- /dev/null +++ b/Week 05/id_393/LeetCode_221_393.java @@ -0,0 +1,22 @@ +package no221; + +/** + * @author boyiren + * @date 2019-11-17 + */ +public class Solution { + public int maximalSquare(char[][] matrix) { + int rows = matrix.length, cols = rows > 0 ? matrix[0].length : 0; + int[][] dp = new int[rows + 1][cols + 1]; + int maxsqlen = 0; + for (int i = 1; i <= rows; i++) { + for (int j = 1; j <= cols; j++) { + if (matrix[i - 1][j - 1] == '1') { + dp[i][j] = Math.min(Math.min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; + maxsqlen = Math.max(maxsqlen, dp[i][j]); + } + } + } + return maxsqlen * maxsqlen; + } +} diff --git a/Week 05/id_393/LeetCode_64_393.java b/Week 05/id_393/LeetCode_64_393.java new file mode 100644 index 000000000..dbe1d1d36 --- /dev/null +++ b/Week 05/id_393/LeetCode_64_393.java @@ -0,0 +1,25 @@ +package no64; + +/** + * @author boyiren + * @date 2019-11-17 + */ +public class Solution { + public int minPathSum(int[][] grid) { + int[][] dp = new int[grid.length][grid[0].length]; + for (int i = grid.length - 1; i >= 0; i--) { + for (int j = grid[0].length - 1; j >= 0; j--) { + if (i == grid.length - 1 && j != grid[0].length - 1) { + dp[i][j] = grid[i][j] + dp[i][j + 1]; + } else if (j == grid[0].length - 1 && i != grid.length - 1) { + dp[i][j] = grid[i][j] + dp[i + 1][j]; + } else if (j != grid[0].length - 1 && i != grid.length - 1) { + dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]); + } else { + dp[i][j] = grid[i][j]; + } + } + } + return dp[0][0]; + } +} diff --git a/Week 05/id_408/LeetCode_32_408.c++ b/Week 05/id_408/LeetCode_32_408.c++ new file mode 100644 index 000000000..db0e22e33 --- /dev/null +++ b/Week 05/id_408/LeetCode_32_408.c++ @@ -0,0 +1,53 @@ +class Solution { +public: + int longestValidParentheses(string s) + { + stack vec; + stack num; + vector pos; + int maxlen=0; + int len=0; + if(s.size()<=1) return 0; + for(int i=0;i>& grid) + { + int rows=grid.size(); + if(rows==0) + return 0; + int cols=grid[0].size(); + if(cols==0) + return 0; + int* opt=new int[cols]; + for(int i=0;i0) + up=opt[j]; + if(j>0) + left=opt[j-1]; + if(i==0&&j==0) + opt[j]=grid[i][j]; + else + opt[j]=min(left,up)+grid[i][j]; + } + } + int ans=opt[cols-1]; + delete []opt; + return ans; + } +}; + diff --git a/Week 05/id_418/LeetCode_32_418.java b/Week 05/id_418/LeetCode_32_418.java new file mode 100644 index 000000000..7a7aa8790 --- /dev/null +++ b/Week 05/id_418/LeetCode_32_418.java @@ -0,0 +1,127 @@ +package com.ljg.leetcode.week05.a02; + +import java.util.Stack; + +/** + * LongestValidParentheses + */ +public class LongestValidParentheses { + + public static void main(String[] args) { + LongestValidParentheses lvp = new LongestValidParentheses(); + String s1 = "(()"; + String s2 = ")()())"; + String s3 = ""; + String s4 = ")"; + String s5 = "("; + String s6 = "()(()"; + String s7 = ")(((((()())()()))()(()))("; + String s8 = "))))((()(("; + String s9 = "(())()(()(("; + String sa = "((()()(()((()"; + String sb = "(()()(()(()))()((()))((()(()())()(()))())))()(()()))())))))))()()()()))(((()())((()()(((())))()(()()(())((()))))))(()(()))(((()())()))(()))((((()(()()()())()()(()))(()()(())()((()()())))(())()())()("; + + System.out.println("maxLen1=" + lvp.longestValidParentheses(s1) + ", " + s1); + System.out.println("maxLen2=" + lvp.longestValidParentheses(s2) + ", " + s2); + System.out.println("maxLen3=" + lvp.longestValidParentheses(s3) + ", " + s3); + System.out.println("maxLen4=" + lvp.longestValidParentheses(s4) + ", " + s4); + System.out.println("maxLen5=" + lvp.longestValidParentheses(s5) + ", " + s5); + System.out.println("maxLen6=" + lvp.longestValidParentheses(s6) + ", " + s6); + System.out.println("maxLen7=" + lvp.longestValidParentheses(s7) + ", " + s7); + System.out.println("maxLen8=" + lvp.longestValidParentheses(s8) + ", " + s8); + System.out.println("maxLen9=" + lvp.longestValidParentheses(s9) + ", " + s9); + System.out.println("maxLena=" + lvp.longestValidParentheses(sa) + ", " + sa); + System.out.println("maxLenb=" + lvp.longestValidParentheses(sb) + ", " + sb); + } + + int getMaxLen(Stack stack, int end, int maxLen, int tempLen) { + Stack stack2 = (Stack) stack.clone(); + while (!stack2.isEmpty()) { + int index = stack2.pop(); + if (index > end) { + tempLen--; + } else { + tempLen = end - index; + break; + } + } + + if (tempLen > maxLen) { + maxLen = tempLen; + } + return maxLen; + } + + int getMaxLen2(int lastIndex, int end, int maxLen) { + if ((end - lastIndex) > maxLen) { + maxLen = end - lastIndex; + } + return maxLen; + } + + public int longestValidParentheses(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int maxLen = 0; + int tempLen = 0; + + int end = -1; + int lastIndex = -1; + int lastIndex2 = -1; + + Stack stack = new Stack(); + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + if (ch == '(') { + // if (stack.isEmpty()) { + // lastIndex = i - 1; + // } + // maxLen = getMaxLen(stack, end, maxLen, tempLen); + maxLen = getMaxLen2(lastIndex, end, maxLen); + stack.push(i); + tempLen++; + } else { + if (stack.isEmpty()) { + if (tempLen > maxLen) { + maxLen = tempLen; + } + tempLen = 0; + lastIndex = i; + lastIndex2 = i; + + continue; + } else { + stack.pop(); + if (!stack.isEmpty()) { + lastIndex = stack.peek(); + + if(lastIndex>69 && i end) { + tempLen--; + } else { + tempLen = end - index; + break; + } + } + + if (tempLen > maxLen) { + maxLen = tempLen; + } + + return maxLen; + } +} \ No newline at end of file diff --git a/Week 05/id_418/LeetCode_64_418.java b/Week 05/id_418/LeetCode_64_418.java new file mode 100644 index 000000000..a2b09e9d6 --- /dev/null +++ b/Week 05/id_418/LeetCode_64_418.java @@ -0,0 +1,38 @@ +package com.ljg.leetcode.week05.a03; + +/** + * MinimumPathSum + */ +public class MinimumPathSum { + + public static void main(String[] args) { + int[][] arrs = new int[][] { { 1, 3, 1 }, { 1, 5, 1 }, { 4, 2, 1 } }; + + MinimumPathSum mps = new MinimumPathSum(); + int minPathSum = mps.minPathSum(arrs); + System.out.println("minPathSum: " + minPathSum); + + } + + public int minPathSum(int[][] grid) { + int height = grid.length; + int width = grid[0].length; + int[][] arrs = new int[height][width]; + + arrs[0][0] = grid[0][0]; + for (int i = 1; i < height; i++) { + arrs[i][0] = grid[i][0] + arrs[i - 1][0]; + } + for (int j = 1; j < width; j++) { + arrs[0][j] = grid[0][j] + arrs[0][j - 1]; + } + + for (int i = 1; i < height; i++) { + for (int j = 1; j < width; j++) { + arrs[i][j] = Math.min(arrs[i][j - 1], arrs[i - 1][j]) + grid[i][j]; + } + } + + return arrs[height - 1][width - 1]; + } +} \ No newline at end of file diff --git a/Week 05/id_423/LeetCode_621_423.java b/Week 05/id_423/LeetCode_621_423.java new file mode 100644 index 000000000..766c37652 --- /dev/null +++ b/Week 05/id_423/LeetCode_621_423.java @@ -0,0 +1,14 @@ +class Solution { + public int leastInterval(char[] tasks, int n) { + int[] map = new int[26]; + for (char c: tasks) + map[c - 'A']++; + Arrays.sort(map); + int max_val = map[25] - 1, idle_slots = max_val * n; + for (int i = 24; i >= 0 && map[i] > 0; i--) { + idle_slots -= Math.min(map[i], max_val); + } + return idle_slots > 0 ? idle_slots + tasks.length : tasks.length; + } + +} \ No newline at end of file diff --git a/Week 05/id_423/LeetCode_64_423.java b/Week 05/id_423/LeetCode_64_423.java new file mode 100644 index 000000000..46a671703 --- /dev/null +++ b/Week 05/id_423/LeetCode_64_423.java @@ -0,0 +1,15 @@ +class Solution { + public int minPathSum(int[][] grid) { + for (int i = grid.length - 1; i >= 0; i--) { + for (int j = grid[0].length - 1; j >= 0; j--) { + if(i == grid.length - 1 && j != grid[0].length - 1) + grid[i][j] = grid[i][j] + grid[i][j + 1]; + else if(j == grid[0].length - 1 && i != grid.length - 1) + grid[i][j] = grid[i][j] + grid[i + 1][j]; + else if(j != grid[0].length - 1 && i != grid.length - 1) + grid[i][j] = grid[i][j] + Math.min(grid[i + 1][j],grid[i][j + 1]); + } + } + return grid[0][0]; + } +} \ No newline at end of file diff --git a/Week 05/id_428/LeeCode_32_428.java b/Week 05/id_428/LeeCode_32_428.java new file mode 100644 index 000000000..a4fdd7fc1 --- /dev/null +++ b/Week 05/id_428/LeeCode_32_428.java @@ -0,0 +1,19 @@ + +class Solution { + public int longestValidParentheses(String s) { + int maxLen = 0; + int dp[] = new int[s.length()]; + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) == ')') { + if (s.charAt(i - 1) == '(') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + maxLen = Math.max(maxLen, dp[i]); + } + } + return maxLen; + } + +} \ No newline at end of file diff --git a/Week 05/id_428/LeeCode_64_428.java b/Week 05/id_428/LeeCode_64_428.java new file mode 100644 index 000000000..1e877005f --- /dev/null +++ b/Week 05/id_428/LeeCode_64_428.java @@ -0,0 +1,20 @@ + +class Solution { + public int minPathSum(int[][] grid) { + int[] dp = new int[grid[0].length]; + for (int i = grid.length - 1; i >= 0; i--) { + for (int j = grid[0].length - 1; j >= 0; j--) { + if(i == grid.length - 1 && j != grid[0].length - 1) + dp[j] = grid[i][j] + dp[j + 1]; + else if(j == grid[0].length - 1 && i != grid.length - 1) + dp[j] = grid[i][j] + dp[j]; + else if(j != grid[0].length - 1 && i != grid.length - 1) + dp[j] = grid[i][j] + Math.min(dp[j], dp[j + 1]); + else + dp[j] = grid[i][j]; + } + } + return dp[0]; + } + +} \ No newline at end of file diff --git a/Week 05/id_428/NOTE.md b/Week 05/id_428/NOTE.md index a6321d6e2..aeb795d9d 100644 --- a/Week 05/id_428/NOTE.md +++ b/Week 05/id_428/NOTE.md @@ -1,4 +1,89 @@ -# NOTE +# Week_05_学习总结 - +## 1、学习过程 +​ 1)难啃的动态规划 + +## 2、本周学习内容(待细化) + +​ 1)动态规划理解 + +​ 2)例题:Fibonacci数列 + +​ 3)例题:最长公共子序列 + +​ 4)例题:三角形最小路径和 + +​ 5)例题:最大子序列和 + +​ 6)例题:打家劫舍 + +## 3、动态规划 + +### 3.1、思想与性质 + + + +------ + + 首先,动态规划最重要的是掌握他的思想,动态规划的核心思想是把原问题分解成子问题进行求解,也就是分治的思想。 + +### 3.2、**过程** + + + +------ + +动态规划问题,大致可以通过以下四部进行解决。 + +1.划分状态,即划分子问题,例如上面的例子,我们可以认为每个组下面、每个部门、每个中心下面最优秀的3个人,都是全公司最优秀的3个人的子问题 + +2.状态表示,即如何让计算机理解子问题。上述例子,我们可以实用f[i][3]表示第i个人,他手下最优秀的3个人是谁。 + +3.状态转移,即父问题是如何由子问题推导出来的。上述例子,每个人大Leader下面最优秀的人等于他下面的小Leader中最优秀的人中最优秀的几个。 + +4.确定边界,确定初始状态是什么?最小的子问题?最终状态又是什么。例如上述问题,最小的子问题就是每个小组长下面最优秀的人,最终状态是整个企业,初始状态为每个领导下面都没有最优名单,但是小组长下面拥有每个人的评分。 + +### 3.3、**经典模型** + +​ 1.线性模型 + +最经典的问题就是斐波那楔数列的问题,每个数的值都是一个状态,可以用F[i]表示表示第i个数的值是多少。每个数都是由F[i-1]+F[i-2]转移而来。 + +另外一个经典的问题就是最长上升自序列(LIS),有一串序列,要求找出它的一串子序列,这串子序列可以不连续,但必须满足它是严格的单调递増的且为最长的。把这个长度输出。示例:1 7 3 5 9 4 8 结果为4。 + +我们非常容易表示他的状态,我们用f[i]表示以第i个数结尾的,最大的上升自序列是多少?那么它是怎么转移的呢?非常容易想到肯定是从左边的的数转移而来,能转移的数满足什么条件呢?肯定是比a[i]更小的。 + +线性模式还可以拓展成二维问题,例如背包问题,用f[i][j]表示前i个物品,凑成大小为j的背包,最大的价值是多少。 + +这类问题非常的多,但是思路都是这样,无非就是从左往右,从上到下,从低维到高维进行转移。 + +2.区间模型 + +对于每个问题,都是由子区间推导过来的,我们称之为区间模型,下面是一个例子。 + +我们有一个连续的序列,每个序列上面都是一个数字c[i],每次我们都能够消灭一个连续的回文子序列,消灭之后左右会合并,成为一个新序列,问最少需要多少次才能够把整个序列消灭掉。回文就是从左到有从右到左读到的序列都是一样的。题目比较抽象,我们通过一些例子来说明这个问题吧?例如一开始的序列是1 4 4 2 3 2 1,那么我们最少需要2次,先消灭掉4 4 , 然后再消灭调1 2 3 2 1.第二个例子是 1 2 3 4 5 5 3 1,我们先消灭掉2 然后再消灭掉4, 最后消灭 1 3 5 5 3 1, 需要3次。 + +我们经常用f[i][j]来表示消灭i,j区间需要的代价,文末有链接详细叙述这个问题,大家感兴趣的可以看一看。 + +3.树状模型 + +我们在数据结构树上面进行最求最优解、最大值等问题,上述我们讲的这个绩效考核就是一个树状模型,具体不再累叙。 + +### 3.4、**实现的套路** + +我们实现动态规划算法,常用的是2个实现套路,一个是自底向上,另外一个是自顶向下。无论是何种方式,我们都要明确动态规划的过程,把状态表示、状态转移、边界都考虑好。 + +**1.自底向上**,简单来说就是根据初始状态,逐步推导到最终状态,而这个转移的过程,必定是一个拓扑序。如何理解这个拓扑序问题呢,甲总监下面有X,Y,Z两个小组,甲总监不会一拿到X组最优秀的三个人,就立马去跟A经理汇报,而是要等到Y,Z小组也选出来之后,也就是自己下面所有子问题都解决了,才会继续向汇报。如果推导的过程不是一个拓扑序,那么要么得到错误的结果,要么算法就要退化。 + +自底向上一般用来解决什么问题呢?那就是可以轻松确定拓扑序的问题,例如线性模型,都是从左往右进行转移,区间模型,一般都是从小区间推导到大区间。自底向上的一个经典实现是斐波那楔数列的递推实现,即F[i] = F[i - 1] + F[i - 2] 。 + +**2.自顶向下**,也就是从最终状态出发,如果遇到一个子问题还未求解,那么就先求解子问题。如果子问题已经求解,那么直接使用子问题的解,所以自顶向下动态规划又有一个形象生动的名字,叫做记忆化搜索,一般我们采用递归的方式进行求解。 + +![img](https://pics1.baidu.com/feed/9358d109b3de9c826f9462e3e4d9330e18d84342.jpeg?token=f67dac29f65e36e5f204cb310d8ad02d&s=0550613297306C205AFD01DB0000D0B2) + +自顶向下,我们一般用在树上面,因为我们根据父亲结点,很容易找到所有的子问题,也就是所有的子结点,而自底向上的话,我们要去统计这个结点的所有兄弟结点是否已经实现。会稍微复杂一点,而且比较难理解。 + +无论是自顶向下还是自底向上,都只是代码实现的一种套路,随便你采用哪一个,都是可以解的,只是看你的选择而已。 + +参考:https://baijiahao.baidu.com/s?id=1631319141857419948&wfr=spider&for=pc \ No newline at end of file diff --git a/Week 05/id_433/LeetCode_64_433.py b/Week 05/id_433/LeetCode_64_433.py new file mode 100644 index 000000000..73ce3cfd2 --- /dev/null +++ b/Week 05/id_433/LeetCode_64_433.py @@ -0,0 +1,15 @@ +from typing import List + + +class Solution: + def minPathSum(self, grid: List[List[int]]) -> int: + for m in range(len(grid)): + for n in range(len(grid[0])): + if m > 0 and n > 0: + grid[m][n] += min(grid[m-1][n], grid[m][n-1]) + elif m == 0 and n > 0: + grid[m][n] += grid[m][n-1] + elif m > 0 and n == 0: + grid[m][n] += grid[m-1][n] + + return grid[-1][-1] diff --git a/Week 05/id_433/LeetCode_91_433.py b/Week 05/id_433/LeetCode_91_433.py new file mode 100644 index 000000000..d275032fa --- /dev/null +++ b/Week 05/id_433/LeetCode_91_433.py @@ -0,0 +1,19 @@ +from typing import List + + +class Solution: + def numDecodings(self, s: str) -> int: + if not s: + return 0 + + dp = [0 for x in range(len(s) + 1)] + dp[0] = 1 + dp[1] = 1 if 0 < int(s[0]) <= 9 else 0 + + for i in range(2, len(s) + 1): + if 0 < int(s[i-1:i]) <= 9: + dp[i] += dp[i - 1] + if s[i-2:i][0] != '0' and int(s[i-2:i]) <= 26: + dp[i] += dp[i - 2] + return dp[len(s)] + diff --git a/Week 05/id_443/LeetCode_64_443.java b/Week 05/id_443/LeetCode_64_443.java new file mode 100644 index 000000000..5204f3dd3 --- /dev/null +++ b/Week 05/id_443/LeetCode_64_443.java @@ -0,0 +1,96 @@ +//给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 +// +// 说明:每次只能向下或者向右移动一步。 +// +// 示例: +// +// 输入: +//[ +//  [1,3,1], +// [1,5,1], +// [4,2,1] +//] +//输出: 7 +//解释: 因为路径 1→3→1→1→1 的总和最小。 +// +// Related Topics 数组 动态规划 + +package com.modds.alltest.leetcode.editor.cn; + +public class LeetCode_64_443_MinimumPathSum { + public static void main(String[] args) { + Solution solution = new LeetCode_64_443_MinimumPathSum().new Solution(); + } + + + //leetcode submit region begin(Prohibit modification and deletion) + class Solution { + /** + * 状态变量:一维数组 a[n] + * 重复子结构: a[j] = Min(a[j],a[j+1])+grid[x][j] + * + * @param grid + * @return + */ + public int minPathSum(int[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + int m = grid.length; + int n = grid[0].length; + + int[] dp = new int[n]; + dp[n - 1] = grid[m - 1][n - 1]; + + for (int i = n - 2; i >= 0; i--) { + dp[i] = dp[i + 1] + grid[m - 1][i]; + } + + for (int i = m - 2; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + if (j == n - 1) { + dp[j] = dp[j] + grid[i][j]; + } else { + dp[j] = Math.min(dp[j], dp[j + 1]) + grid[i][j]; + } + } + } + return dp[0]; + } + + /** + * 状态变量:二维数组 + * 重复子结构: sub[i][j] = Min(sub[i+1][j],sub[i][j+1])+a[i][j] + * + * @param grid + * @return + */ + public int minPathSum1(int[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + int m = grid.length; + int n = grid[0].length; + int[][] a = new int[m][n]; + a[m - 1][n - 1] = grid[m - 1][n - 1]; + + // 最后一行 + for (int i = grid[0].length - 2; i >= 0; i--) { + a[m - 1][i] = a[m - 1][i + 1] + grid[m - 1][i]; + } + + // 最后一列 + for (int i = grid.length - 2; i >= 0; i--) { + a[i][n - 1] = a[i + 1][n - 1] + grid[i][n - 1]; + } + + for (int i = grid.length - 2; i >= 0; i--) { + for (int j = grid[0].length - 2; j >= 0; j--) { + a[i][j] = Math.min(a[i + 1][j], a[i][j + 1]) + grid[i][j]; + } + } + return a[0][0]; + } + } +//leetcode submit region end(Prohibit modification and deletion) +} \ No newline at end of file diff --git a/Week 05/id_443/LeetCode_91_443.java b/Week 05/id_443/LeetCode_91_443.java new file mode 100644 index 000000000..c44293288 --- /dev/null +++ b/Week 05/id_443/LeetCode_91_443.java @@ -0,0 +1,77 @@ +//一条包含字母 A-Z 的消息通过以下方式进行了编码: +// +// 'A' -> 1 +//'B' -> 2 +//... +//'Z' -> 26 +// +// +// 给定一个只包含数字的非空字符串,请计算解码方法的总数。 +// +// 示例 1: +// +// 输入: "12" +//输出: 2 +//解释: 它可以解码为 "AB"(1 2)或者 "L"(12)。 +// +// +// 示例 2: +// +// 输入: "226" +//输出: 3 +//解释: 它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。 +// +// Related Topics 字符串 动态规划 + +package com.modds.alltest.leetcode.editor.cn; + +public class LeetCode_91_443_DecodeWays { + public static void main(String[] args) { + Solution solution = new LeetCode_91_443_DecodeWays().new Solution(); + System.out.println(solution.numDecodings("1010")); + } + + + //leetcode submit region begin(Prohibit modification and deletion) + class Solution { + /** + * 不能逆序 : 1010 , 010 无法通过 + * 重复子问题: a[i] = v(i,1)?a[i-1]:0 + v(i-1,2)?a[i-2]:0 + * + * @param s + * @return + */ + public int numDecodings(String s) { + if (s.length() == 1) { + return valid(s, s.length() - 1, 1) ? 1 : 0; + } + + int x = valid(s, 0, 1) ? 1 : 0; + int y = (valid(s, 1, 1) ? x : 0) + + (valid(s, 0, 2) ? 1 : 0); + + for (int i = 2; i < s.length(); i++) { + int one = valid(s, i, 1) ? y : 0; + int two = valid(s, i - 1, 2) ? x : 0; + if (one == two && one == 0) { + return 0; + } + x = y; + y = one + two; + } + return y; + } + + private boolean valid(String s, int start, int n) { + int m1 = s.charAt(start) - '0'; + if (m1 > 0 && n == 2) { + int num = m1 * 10 + s.charAt(start + 1) - '0'; + return num < 27; + } else { + return m1 > 0; + } + } + } +//leetcode submit region end(Prohibit modification and deletion) + +} \ No newline at end of file diff --git a/Week 05/id_468/leetcode1143_5_468.java b/Week 05/id_468/leetcode1143_5_468.java new file mode 100644 index 000000000..1e98702fe --- /dev/null +++ b/Week 05/id_468/leetcode1143_5_468.java @@ -0,0 +1,12 @@ +package week4; + +/** + * @program: leetcode + * @description: 最长公共子序列 + * @author: 王瑞全 + * @create: 2019-11-1223:43 + **/ + + +public class leetcode1143_5_468 { +} diff --git a/Week 05/id_468/leetcode121_5_468.java b/Week 05/id_468/leetcode121_5_468.java new file mode 100644 index 000000000..43d4ece70 --- /dev/null +++ b/Week 05/id_468/leetcode121_5_468.java @@ -0,0 +1,21 @@ +package week4; + +/** + * @program: leetcode + * @description: Best Time to Buy and Sell Stock + * @author: 王瑞全 + * @create: 2019-11-1800:10 + **/ + + +public class leetcode121_5_468 { + public int maxProfit(int[] prices) { + int diff=0; + int min=Integer.MAX_VALUE; + for(int i=0;iprices[i-1]){ + max+=prices[i]-prices[i-1]; + } + } + return max; + } +} diff --git a/Week 05/id_468/leetcode123_5_123.java b/Week 05/id_468/leetcode123_5_123.java new file mode 100644 index 000000000..685eb70f6 --- /dev/null +++ b/Week 05/id_468/leetcode123_5_123.java @@ -0,0 +1,12 @@ +package week4; + +/** + * @program: leetcode + * @description: 买卖股票的最佳时机 III + * @author: 王瑞全 + * @create: 2019-11-1823:03 + **/ + + +public class leetcode123_5_123 { +} diff --git a/Week 05/id_468/leetcode198_5_468.java b/Week 05/id_468/leetcode198_5_468.java new file mode 100644 index 000000000..9d4b38b89 --- /dev/null +++ b/Week 05/id_468/leetcode198_5_468.java @@ -0,0 +1,51 @@ +package week4; + +import sun.nio.cs.ext.MacThai; + +/** + * @program: leetcode + * @description: 打家劫舍 + * @author: 王瑞全 + * @create: 2019-11-1723:32 + **/ + + +public class leetcode198_5_468 { + + /** + * dp方程 MAX(dp[n-1],dp[n-2]+num) + * @param nums + * @return + */ + public int rob(int[] nums) { + if(nums.length==0){ + return 0; + } + if(nums.length==1){ + return nums[0]; + } + int[] dp=new int[nums.length]; + dp[0]=nums[0]; + dp[1]=Math.max(dp[0],nums[1]); + for(int i=2;i stack = new Stack(); + for (int i = 0; i < str.length(); i++) { + if(str.charAt(i) == '('){ + stack.push('('); + }else if (!stack.empty() && stack.peek() == '('){ + stack.pop(); + }else { + return false; + } + } + return stack.empty(); + }*/ + + + //动态规划 + public static int longestValidParentheses(String str) { + int maxlen = 0; + int[] dp = new int[str.length()]; + for (int i = 1; i < str.length(); i++) { + if (str.charAt(i) == ')') { + if (str.charAt(i - 1) == '('){ + //前面是左括号,则i大于等于2时,前两位的dp 加上2 + dp[i] = (i >= 2? dp[i - 2]: 0) +2; + }else if (i - dp[i - 1] > 0 && str.charAt(i - dp[i - 1] - 1) == '(') { + //前面是右括号,且除去前面dp个数的字符串 还有字符,并且是左括号,则用前一位的dp 加上 前面对应左括号的前一位dp 再加上2,就是当前括号的dp + dp[i] = dp[i - 1] + ((i - dp[i -1]) >= 2? dp[i - dp[i - 1] -2]: 0) + 2; + } + maxlen = Math.max(maxlen, dp[i]); + } + } + return maxlen; + } +} diff --git a/Week 05/id_473/LeetCode_64.java b/Week 05/id_473/LeetCode_64.java new file mode 100644 index 000000000..430ab5c9d --- /dev/null +++ b/Week 05/id_473/LeetCode_64.java @@ -0,0 +1,33 @@ +package LeetCode; + +/** + * 64.最小路径和 + */ +public class LeetCode_64 { + public static void main(String[] args) { + int[][] grid = new int[][]{ + {1,3,1}, + {1,5,1}, + {4,2,1} + }; + System.out.println(minPathSum(grid)); + } + + //动态规划 + public static int minPathSum(int[][] grid) { + int[][] dp = new int[grid.length][grid[0].length]; + for (int i = grid.length - 1; i >= 0; i--) { + for (int j = grid[0].length - 1; j >= 0; j--) { + if(i == grid.length - 1 && j != grid[0].length - 1) + dp[i][j] = grid[i][j] + dp[i][j + 1]; + else if(j == grid[0].length - 1 && i != grid.length - 1) + dp[i][j] = grid[i][j] + dp[i + 1][j]; + else if(j != grid[0].length - 1 && i != grid.length - 1) + dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]); + else + dp[i][j] = grid[i][j]; + } + } + return dp[0][0]; + } +} diff --git a/Week 05/id_473/NOTE.md b/Week 05/id_473/NOTE.md index a6321d6e2..d41f88b1e 100644 --- a/Week 05/id_473/NOTE.md +++ b/Week 05/id_473/NOTE.md @@ -1,4 +1,35 @@ -# NOTE +# 第五周学习总结 + +#### 动态规划 + +定义:是求解决策过程(decision process)最优化的数学方法。也可以叫做动态地推,本质上与分治和递归没有任何差别。 + +- 不要使用人肉递归,人脑记忆性较差,容易出错。 +- 找到最简方法,将其拆解成可重复解决的问题。 +- 找到规律进行归纳。 + + +#### 斐波那契数列 + +- 可以添加一个缓存,将计算过的结果存到一个数组中,避免重复计算,也叫记忆搜索。 +- 自底向上(可以用循环处理)或自顶向下(可以用递归处理)搜索。 +- 自底向上为终极模板。 + +#### 路径计算 +状态转移方程(动态规划方程): + +`opt[i,j] = opt[i + 1, j] + opt[i, j + 1];` + +完整逻辑: +``` +if (a[i, j] == '空地' ){ + opt[i, j] = opt[i + 1,j] + opt[i,j + 1]; +}else { + opt[i, j] = 0; +} +``` + + diff --git a/Week 05/id_503/NOTE.md b/Week 05/id_503/NOTE.md index a6321d6e2..513d92b6c 100644 --- a/Week 05/id_503/NOTE.md +++ b/Week 05/id_503/NOTE.md @@ -1,4 +1,60 @@ -# NOTE +# 复习 +* 递归代码模板 + ```javascript + function recursion(level, ...params) { + // 1. recursion terminator 递归终止条件 + if (level > MAX_LEVEL) { + // 2. process result 处理结果 + return; + } + + // 3. process current logic 处理当前逻辑 + + // 4. drill down 递归处理下一层 + recursion(level + 1, ...params); + + // 5. restore current status if needed 如果需要,**还原当前状态** + } + ``` +* 分治代码模板 + ```javascript + function divideConquer(problem, ...params) { + // 递归终止条件 + if (!problem) { + return + } - + // 处理当前数据 + const data = prepareData(problem) + // 拆分这问题 + const subProblems = splitProblem(problem, data) + + // 处理每个子问题结果 + subResult1 = divideConquer(subProblems[0], params) + subResult2 = divideConquer(subProblems[1], params) + subResult3 = divideConquer(subProblems[2], params) + // … + + // 将子问题结果处理为当前结果 + result = processResult(subResult1, subResult2, subResult3) + + // 如果需要,回复当前状态 + } + ``` + +# 动态规定(Dynamic Programming) +* Wiki 定义: https://en.wikipedia.org/wiki/Dynamic_programming +* “Simplifying a complicated problem by breaking it down into simpler sub-problems”(**in a recursive manner**) +* Divide & Conquer + Optimal substructure (分治 + 最优子结构) +* 关键点 + * **动态规划**和**递归**或者**分治**没有根本上的区别(关键看有无**最优的子结构**) + * 共性:找到重复子问题 + * 差异性:最优子结构、中途可以淘汰次优解 +* 解题关键步骤 + 1. 找出**最优子结构**:opt[n] = best_of(opt[n-1], opt[n-2], …) + 2. 储存**中间状态**:opt[i] + 3. 写出**递推公式**(美其名曰:状态转移方程或者 DP 方程) + * eg. + * Fib: opt[i] = opt[n-1] + opt[n-2] + * 二维路径:opt[i,j] = opt[i+1][j] + opt[i][j+1](且判断a[i,j]是否空地) \ No newline at end of file diff --git a/Week 05/id_503/leetcode_32_503.js b/Week 05/id_503/leetcode_32_503.js new file mode 100644 index 000000000..a5caa202a --- /dev/null +++ b/Week 05/id_503/leetcode_32_503.js @@ -0,0 +1,70 @@ +/** + * DP 方案 + * @param {string} s + * @return {number} + */ +var longestValidParentheses = function (s) { + + if (s.length < 2) { + return 0; + } + let maxLen = 0; + const dp = new Array(s.length).fill(0); + if (s[0] === "(" && s[1] === ")") { + maxLen = dp[1] = 2; + } + + for (let i = 2; i < s.length; i++) { + + if (s[i] === ")") { + if (s[i - 1] === "(") { + + dp[i] = dp[i - 2] + 2; + maxLen = Math.max(maxLen, dp[i]); + + } else if (i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] === "(") { + + dp[i] = dp[i - 1] + 2 + (i - dp[i - 1] >= 2 ? dp[i - dp[i - 1] - 2] : 0); + + maxLen = Math.max(maxLen, dp[i]); + } + } + } + + return maxLen; +}; + +/** + * 栈方案 + * @param {string} s + * @return {number} + */ +var longestValidParenthesesWithStack = function (s) { + + if (s.length < 2) { + return 0; + } + let maxLen = 0; + const stack = [-1] // 首次入栈 -1,方便计算边缘情况 + + for (let i = 0; i < s.length; i++) { + + if (s[i] === "(") { + stack.push(i); + continue; + } + + stack.pop(); + if (stack.length === 0) { // 栈空表示此时的 ")" 无效,进行入栈;比如 "()))" 中的第三个字符 + stack.push(i) + continue; + } + + const top = stack[stack.length - 1]; + // 通过计算当前元素下标和栈顶元素下标得到有效字符长度 + // 并对比最大值 + maxLen = Math.max(maxLen, i - top); + } + + return maxLen; +}; \ No newline at end of file diff --git a/Week 05/id_503/leetcode_64_503.js b/Week 05/id_503/leetcode_64_503.js new file mode 100644 index 000000000..680f7ea5a --- /dev/null +++ b/Week 05/id_503/leetcode_64_503.js @@ -0,0 +1,31 @@ +/** + * @param {number[][]} grid + * @return {number} + */ +var minPathSum = function (grid) { + if (!grid && grid.length) { + return 0; + } + if (!grid[0].length) { + return 0; + } + + const m = grid.length - 1; + const n = grid[0].length - 1; + const dp = []; + for (let i = m; i >= 0; i--) { + for (let j = n; j >= 0; j--) { + if (i === m && j === n) { + dp[j] = grid[i][j]; + } else if (i === m && j !== n) { + dp[j] = grid[i][j] + dp[j + 1]; + } else if (i !== m && j === n) { + dp[j] = grid[i][j] + dp[j]; + } else { + dp[j] = grid[i][j] + Math.min(dp[j], dp[j + 1]); + } + } + } + + return dp[0]; +}; \ No newline at end of file diff --git a/Week 05/id_508/LeetCode_064_508.cpp b/Week 05/id_508/LeetCode_064_508.cpp new file mode 100644 index 000000000..aa719ef89 --- /dev/null +++ b/Week 05/id_508/LeetCode_064_508.cpp @@ -0,0 +1,20 @@ +public: + int minPathSum(vector>& grid) { + //problem(i,j) = min(subproblem(i+1,j),subproblem(i,j+1)) + //dp[] + //backward + //dp[i] = min(dp[i],dp[i+1]); + int ROW = grid.size(); + int COL = ROW>0?grid[0].size():0; + bool start = false; + for(int i = ROW-1;i>=0;i--) { + for(int j = COL -1;j>=0;j--) { + if(!start) {start = true;continue;}; + grid[i][j] = min((i<(ROW-1)?grid[i+1][j]:INT_MAX), + (j<(COL-1)?grid[i][j+1]:INT_MAX))+grid[i][j]; + } + } + return grid[0][0]; + } + +}; diff --git a/Week 05/id_508/LeetCode_221_508.cpp b/Week 05/id_508/LeetCode_221_508.cpp new file mode 100644 index 000000000..48b27ea13 --- /dev/null +++ b/Week 05/id_508/LeetCode_221_508.cpp @@ -0,0 +1,25 @@ +class Solution { +public: + int maximalSquare(vector>& matrix) { + int max_area=0; + vector dp; + for (int i=0;idp[j]?max_area:dp[j]; + + } + } + return pow(max_area,2); + } +}; diff --git a/Week 05/id_518/LeetCode_647_518.java b/Week 05/id_518/LeetCode_647_518.java new file mode 100644 index 000000000..9aac4248d --- /dev/null +++ b/Week 05/id_518/LeetCode_647_518.java @@ -0,0 +1,10 @@ +#include + +int numDecodings (char *s) { + +} + +int main (void) { + char *s = "226"; + printf("%d\n", numDecodings(s)); +} \ No newline at end of file diff --git a/Week 05/id_518/LeetCode_91_518.java b/Week 05/id_518/LeetCode_91_518.java new file mode 100644 index 000000000..0c6331801 --- /dev/null +++ b/Week 05/id_518/LeetCode_91_518.java @@ -0,0 +1,22 @@ +public class Solution { + public int numDecodings(String s) { + if(s == null || s.length() == 0) { + return 0; + } + int n = s.length(); + int[] dp = new int[n+1]; + dp[0] = 1; + dp[1] = s.charAt(0) != '0' ? 1 : 0; + for(int i = 2; i <= n; i++) { + int first = Integer.valueOf(s.substring(i-1, i)); + int second = Integer.valueOf(s.substring(i-2, i)); + if(first >= 1 && first <= 9) { + dp[i] += dp[i-1]; + } + if(second >= 10 && second <= 26) { + dp[i] += dp[i-2]; + } + } + return dp[n]; + } +} \ No newline at end of file diff --git a/Week 05/id_523/LeetCode_32_523.cs b/Week 05/id_523/LeetCode_32_523.cs new file mode 100644 index 000000000..9ec6056bb --- /dev/null +++ b/Week 05/id_523/LeetCode_32_523.cs @@ -0,0 +1,24 @@ +public class Solution +{ + public int LongestValidParentheses(string s) + { + int maxans = 0; + var dp = new int[s.Length]; + for (int i = 1; i < s.Length; i++) + { + if (s[i] == ')') + { + if (s[i - 1] == '(') + { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + } + else if (i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '(') + { + dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + maxans = Math.Max(maxans, dp[i]); + } + } + return maxans; + } +} \ No newline at end of file diff --git a/Week 05/id_523/LeetCode_64_523.cs b/Week 05/id_523/LeetCode_64_523.cs new file mode 100644 index 000000000..22e21a572 --- /dev/null +++ b/Week 05/id_523/LeetCode_64_523.cs @@ -0,0 +1,33 @@ +public class Solution +{ + public int MinPathSum(int[,] grid) + { + var xLength = grid.GetLength(0); + var yLength = grid.GetLength(1); + + for (int x = 0; x < xLength; x++) + { + for (int y = 0; y < yLength; y++) + { + if (x == 0 && y == 0) + { + grid[x, y] = grid[x, y]; + } + else if (x == 0) + { + grid[x, y] = grid[x, y] + grid[x, y - 1]; + } + else if (y == 0) + { + grid[x, y] = grid[x, y] + grid[x - 1, y]; + } + else + { + grid[x, y] = grid[x, y] + Math.Min(grid[x - 1, y], grid[x, y - 1]); + } + } + } + + return grid[xLength - 1, yLength - 1]; + } +} \ No newline at end of file diff --git a/Week 05/id_533/LeetCode_1143_533.js b/Week 05/id_533/LeetCode_1143_533.js new file mode 100644 index 000000000..23e3d596b --- /dev/null +++ b/Week 05/id_533/LeetCode_1143_533.js @@ -0,0 +1,31 @@ +// https://leetcode-cn.com/problems/longest-common-subsequence/ + +/** + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ +var longestCommonSubsequence = function(text1, text2) { + if (!text1.length || !text2.length) return 0 + var dp = [], + m = text1.length, + n = text2.length; + for (var i = 0; i < n; i++) { + dp[i] = [] + dp[i][0] = text2[i] === text1[0] ? 1 : (i === 0 ? 0 : dp[i - 1][0]); + } + for (var i = 0; i < m; i++) { + dp[0][i] = text1[i] === text2[0] ? 1 : (i === 0 ? 0 : dp[0][i - 1]); + } + for (var i = 1; i < n; i++) { + for (var j = 1; j < m; j++) { + if (text2[i] === text1[j]) + dp[i][j] = dp[i - 1][j - 1] + 1; + else + dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]); + } + } + return dp[n - 1][m - 1]; +}; + +console.log(longestCommonSubsequence('abcde', 'ace')); \ No newline at end of file diff --git a/Week 05/id_533/LeetCode_120_533.js b/Week 05/id_533/LeetCode_120_533.js new file mode 100644 index 000000000..a38db97ec --- /dev/null +++ b/Week 05/id_533/LeetCode_120_533.js @@ -0,0 +1,41 @@ +// https://leetcode-cn.com/problems/triangle/ + +/** + * @param {number[][]} triangle + * @return {number} + */ +// dp状态定义为一维数组 +// 时间复杂度O(n * m) 空间复杂度O(m) +var minimumTotal = function(triangle) { + var n = triangle.length, + dp = triangle[n - 1]; + for (var i = n - 2; i >= 0; i--) { + for (var j = 0; j < triangle[i].length; j++) { + dp[j] = triangle[i][j] + Math.min(dp[j], dp[j + 1]); + } + } + return dp[0]; +}; + +// dp状态复用triangle(不推荐) +// 时间复杂度O(n * m) 空间复杂度O(1) +var minimumTotal = function(triangle) { + var n = triangle.length; + for (var i = n - 2; i >= 0; i--) { + for (var j = 0; j < triangle[i].length; j++) { + triangle[i][j] += Math.min(triangle[i + 1][j], triangle[i + 1][j + 1]) + } + } + return triangle[0][0]; +}; + +var res = minimumTotal( + [ + [2], + [3,4], + [6,5,7], + [4,1,8,3] + ] +) + +console.log(res); \ No newline at end of file diff --git a/Week 05/id_533/LeetCode_152_533.js b/Week 05/id_533/LeetCode_152_533.js new file mode 100644 index 000000000..cb66521cc --- /dev/null +++ b/Week 05/id_533/LeetCode_152_533.js @@ -0,0 +1,29 @@ +// https://leetcode-cn.com/problems/maximum-product-subarray/ + +/** + * @param {number[]} nums + * @return {number} + */ +// 本题关键 负数乘以较小的数反而会更大 +// dp状态存储当前最大正值和最小负值 +var maxProduct = function(nums) { + var length = nums.length, + dp1 = nums[0], + dp2 = nums[0], + max = nums[0]; + for (var i = 1; i < length; i++) { + if (nums[i] < 0) { + var tmp = dp1; + dp1 = dp2; + dp2 = tmp; + } + dp1 = Math.max(dp1 * nums[i], nums[i]); + dp2 = Math.min(dp2 * nums[i], nums[i]); + max = Math.max(max, dp1); + } + return max; +}; + +console.log(maxProduct([2, 3, -2, 4])) +console.log(maxProduct([2, 3, -2, 4, -2])) +console.log(maxProduct([2, 3, -2, 4, -2, -60])) \ No newline at end of file diff --git a/Week 05/id_533/LeetCode_198_533.js b/Week 05/id_533/LeetCode_198_533.js new file mode 100644 index 000000000..c7e6cfcc3 --- /dev/null +++ b/Week 05/id_533/LeetCode_198_533.js @@ -0,0 +1,67 @@ +// https://leetcode-cn.com/problems/house-robber/ + +/** + * @param {number[]} nums + * @return {number} + */ +// dp状态使用二维数组记录偷窃每一个房子的可能情况 +// 时间复杂度O(n) 空间复杂度O(n) +var rob = function(nums) { + var length = nums.length, + dp = []; + dp[0] = []; + dp[0][0] = 0; + dp[0][1] = nums[0] || 0; + for (var i = 1; i < length; i++) { + dp[i] = []; + dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1]); + dp[i][1] = dp[i-1][0] + nums[i]; + } + return Math.max(dp[length-1][0], dp[length-1][1]); +}; + +// dp状态使用两个变量记录当前房子可能的偷窃情况 +// 时间复杂度O(n) 空间复杂度O(1) +var rob = function(nums) { + var length = nums.length, + dp1 = 0, // 不偷 + dp2 = nums[0] || 0; // 偷 + for (var i = 1; i < length; i++) { + var tmp = dp1; + dp1 = Math.max(dp1, dp2); + dp2 = tmp + nums[i]; + } + return Math.max(dp1, dp2); +}; + +// dp状态使用一维数组记录偷或者不偷当前房子所能得到的最大值 +// 时间复杂度O(n) 空间复杂度O(1) +var rob = function(nums) { + var length = nums.length, + dp = []; + dp[0] = nums[0] || 0; + dp[1] = Math.max(nums[0], nums[1]) || 0; + for (var i = 2; i < length; i++) { + dp[i] = Math.max(dp[i-2] + nums[i], dp[i-1]); + } + return dp[dp.length - 1]; +}; + +// dp状态两个变量记录前一个和当前偷或者不偷当前房子所能得到的最大值 +// 时间复杂度O(n) 空间复杂度O(1) +var rob = function(nums) { + var length = nums.length, + prevMax = nums[0] || 0, + curMax = Math.max(prevMax, nums[1] || 0); + for (var i = 2; i < length; i++) { + var tmp = curMax; + curMax = Math.max(curMax, prevMax + nums[i]) + prevMax = tmp; + } + return curMax; +}; + +console.log(rob([1,2,3,1])); +console.log(rob([2,7,9,3,1])); +console.log(rob([])); +console.log(rob([1])); \ No newline at end of file diff --git a/Week 05/id_533/LeetCode_213_533.js b/Week 05/id_533/LeetCode_213_533.js new file mode 100644 index 000000000..f01d5614e --- /dev/null +++ b/Week 05/id_533/LeetCode_213_533.js @@ -0,0 +1,26 @@ +// https://leetcode-cn.com/problems/house-robber-ii/ + +/** + * @param {number[]} nums + * @return {number} + */ +var rob = function (nums) { + var max1 = r(nums); + var max2 = r(nums.concat([]).reverse()); + return Math.max(max1, max2); + function r (nums) { + if (nums.length === 0) return 0; + if (nums.length === 1) return nums[0]; + var prevMax = 0, + curMax = nums[1]; + for (var i = 2; i < nums.length; i++) { + var tmp = curMax; + curMax = Math.max(curMax, prevMax + nums[i]); + prevMax = tmp; + } + return curMax; + } +}; + +console.log(rob([2, 3, 2])); +console.log(rob([1, 2, 3, 1])); \ No newline at end of file diff --git a/Week 05/id_533/LeetCode_53_533.js b/Week 05/id_533/LeetCode_53_533.js new file mode 100644 index 000000000..9ac1aefa8 --- /dev/null +++ b/Week 05/id_533/LeetCode_53_533.js @@ -0,0 +1,37 @@ +// https://leetcode-cn.com/problems/maximum-subarray/ + +/** + * @param {number[]} nums + * @return {number} + */ +// dp状态使用一维数组 +// 时间复杂度O(n) 空间复杂度O(n); +var maxSubArray = function(nums) { + var length = nums.length, + dp = [nums[0]], + max = nums[0] + for (var i = 1; i < length; i++) { + dp[i] = Math.max(dp[i - 1], 0) + nums[i]; + max = Math.max(max, dp[i]) + } + return max; +}; + +/** + * @param {number[]} nums + * @return {number} + */ +// dp状态只记录当前的最大值 +// 时间复杂度O(n) 空间复杂度O(1); +var maxSubArray = function(nums) { + var length = nums.length, + dp = nums[0], + max = nums[0] + for (var i = 1; i < length; i++) { + dp = Math.max(dp, 0) + nums[i]; + max =Math.max(max, dp); + } + return max; +}; + +console.log(maxSubArray([-2, 1, -3, 4, -1, 2, 1, -5, 4])); \ No newline at end of file diff --git a/Week 05/id_533/LeetCode_62_533.js b/Week 05/id_533/LeetCode_62_533.js new file mode 100644 index 000000000..6a3a32225 --- /dev/null +++ b/Week 05/id_533/LeetCode_62_533.js @@ -0,0 +1,25 @@ +// https://leetcode-cn.com/problems/unique-paths/ + +/** + * @param {number} m + * @param {number} n + * @return {number} + */ +var uniquePaths = function (m, n) { + if(m <= 0 || n <= 0) return 0; + var dp = []; + for (var i = 0; i < n; i++) { + dp[i] = []; + dp[i][0] = 1; + } + for (var i = 0; i < m; i++) + dp[0][i] = 1; + for (var i = 1; i < n; i++) { + for (var j = 1; j < m; j++) + dp[i][j] = dp[i][j - 1] + dp[i - 1][j]; + } + return dp[n-1][m-1]; +}; + +console.log(uniquePaths(3, 2)); +console.log(uniquePaths(7, 3)); \ No newline at end of file diff --git a/Week 05/id_533/LeetCode_63_533.js b/Week 05/id_533/LeetCode_63_533.js new file mode 100644 index 000000000..31695c1a0 --- /dev/null +++ b/Week 05/id_533/LeetCode_63_533.js @@ -0,0 +1,35 @@ +// https://leetcode-cn.com/problems/unique-paths-ii/ + +/** + * @param {number[][]} obstacleGrid + * @return {number} + */ +var uniquePathsWithObstacles = function(obstacleGrid) { + if (!obstacleGrid || !obstacleGrid.length) return 0; + var n = obstacleGrid.length, + m = obstacleGrid[0].length; + if (obstacleGrid[0][0] === 1) return 0; + obstacleGrid[0][0] = 1; + for (var i = 1; i < n; i++) + obstacleGrid[i][0] = obstacleGrid[i][0] === 1 ? 0 : obstacleGrid[i - 1][0]; + for (var i = 1; i < m; i++) + obstacleGrid[0][i] = obstacleGrid[0][i] === 1 ? 0 : obstacleGrid[0][i - 1]; + for (var i = 1; i < n; i++) { + for (var j = 1; j < m; j++) { + if (obstacleGrid[i][j] === 1) + obstacleGrid[i][j] = 0 + else + obstacleGrid[i][j] = obstacleGrid[i][j - 1] + obstacleGrid[i - 1][j]; + } + } + return obstacleGrid[n - 1][m - 1]; +}; + + +console.log(uniquePathsWithObstacles( + [ + [0,0,0], + [0,1,0], + [0,0,0] + ] +)) \ No newline at end of file diff --git a/Week 05/id_538/LeetCode_32_538.java b/Week 05/id_538/LeetCode_32_538.java new file mode 100644 index 000000000..78d93f2af --- /dev/null +++ b/Week 05/id_538/LeetCode_32_538.java @@ -0,0 +1,21 @@ +class LeetCode_32_538 { + public static int longestValidParentheses(String s) { + //边界检查 + if (s.length() == 0 || null == s) { + return 0; + } + int max = 0; + int dp[] = new int[s.length()]; + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) == ')') { + if (s.charAt(i - 1) == '(') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + max = Math.max(max, dp[i]); + } + } + return max; + } +} diff --git a/Week 05/id_538/LeetCode_64_538.java b/Week 05/id_538/LeetCode_64_538.java new file mode 100644 index 000000000..1991d24a7 --- /dev/null +++ b/Week 05/id_538/LeetCode_64_538.java @@ -0,0 +1,19 @@ +class LeetCode_64_538 { + public int minPathSum(int[][] grid) { + int[][] dp = new int[grid.length][grid[0].length]; + for (int i = grid.length - 1;i >= 0; i--){ + for (int j = grid[0].length - 1; j >= 0 ;j--){ + if (i == grid.length - 1 && j != grid[0].length - 1){ + dp[i][j] = grid[i][j] + dp[i][j + 1]; + }else if (j == grid[0].length - 1 && i != grid.length - 1){ + dp[i][j] = grid[i][j] + dp[i + 1][j]; + }else if (j != grid[0].length - 1 && i != grid.length - 1){ + dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j],dp[i][j+1]); + }else{ + dp[i][j] = grid[i][j]; + } + } + } + return dp[0][0]; + } +} diff --git a/Week 05/id_543/LeetCode_64_543.java b/Week 05/id_543/LeetCode_64_543.java new file mode 100644 index 000000000..0cea20f75 --- /dev/null +++ b/Week 05/id_543/LeetCode_64_543.java @@ -0,0 +1,17 @@ +class Solution { + public int minPathSum(int[][] grid) { + int m = grid.length; + int n = grid[0].length; + + for(int i = 0;i < m;i++) + for(int j = 0;j < n;j++){ + if(i == 0 && j != 0) grid[i][j] = grid[i][j] + grid[i][j-1]; + if(i != 0 && j == 0) grid[i][j] = grid[i][j] + grid[i-1][j]; + if(i != 0 && j != 0) grid[i][j] = grid[i][j] + Math.min(grid[i-1][j],grid[i][j-1]); + if(i == 0 && j == 0) grid[i][j] = grid[i][j]; + } + return grid[m-1][n-1]; + } +} + +//左上角出发 \ No newline at end of file diff --git a/Week 05/id_543/LeetCode_91_543.java b/Week 05/id_543/LeetCode_91_543.java new file mode 100644 index 000000000..f19f5e723 --- /dev/null +++ b/Week 05/id_543/LeetCode_91_543.java @@ -0,0 +1,48 @@ +//一条包含字母 A-Z 的消息通过以下方式进行了编码: +// +// 'A' -> 1 +//'B' -> 2 +//... +//'Z' -> 26 +// +// +// 给定一个只包含数字的非空字符串,请计算解码方法的总数。 +// +// 示例 1: +// +// 输入: "12" +//输出: 2 +//解释: 它可以解码为 "AB"(1 2)或者 "L"(12)。 +// +// +// 示例 2: +// +// 输入: "226" +//输出: 3 +//解释: 它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。 +// +// Related Topics 字符串 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int numDecodings(String s) { + if(s == null || s.length() == 0) return 0; + int[] nums = new int[s.length()+1]; + nums[0] = 1; + nums[1] = s.charAt(0) == '0' ? 0 : 1; + for(int i = 2;i <= s.length();i++){ + int first = Integer.parseInt(s.substring(i-1,i)); + int second = Integer.parseInt(s.substring(i-2,i)); + if(first >= 1 && first <=9){ + nums[i] += nums[i-1]; + } + if(second >= 10 && second <=26){ + nums[i] += nums[i-2]; + } + } + return nums[s.length()]; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_558/LeetCode_1143_558.java b/Week 05/id_558/LeetCode_1143_558.java new file mode 100644 index 000000000..309afb7e9 --- /dev/null +++ b/Week 05/id_558/LeetCode_1143_558.java @@ -0,0 +1,25 @@ +package Week04; + +import java.util.Arrays; + +/** + * @see https://leetcode-cn.com/problems/longest-common-subsequence/submissions/ + * 最长公共子序列 + */ +public class LeetCode_1143_558 { + public int longestCommonSubsequence(String text1, String text2) { + char[] s1 = text1.toCharArray(); + char[] s2 = text2.toCharArray(); + int[][] dp = new int[s1.length + 1][s2.length + 1]; + for (int i = 1; i < s1.length + 1; i++) { + for (int j = 1; j < s2.length + 1; j++) { + if (s1[i - 1] == s2[j - 1]) { //如果末端相同 + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { //如果末端不同 + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + } + } + } + return dp[s1.length][s2.length]; + } +} diff --git a/Week 05/id_558/LeetCode_509_558.java b/Week 05/id_558/LeetCode_509_558.java new file mode 100644 index 000000000..37fcb26c9 --- /dev/null +++ b/Week 05/id_558/LeetCode_509_558.java @@ -0,0 +1,22 @@ +package Week04; + +/** + * @see https://leetcode-cn.com/problems/fibonacci-number/submissions/ + * 斐波那契数 + */ +public class LeetCode_509_558 { + /** + * 解法: + * 1、递推 + * 2、递归(递归优化) + */ + public int fib(int N) { + return recursion(N, new int[N + 1]); + } + + public int recursion(int n, int[] cache) { + if (n <= 1) return n; + if (cache[n] == 0) cache[n] = recursion(n - 1, cache) + recursion(n - 2, cache); + return cache[n]; + } +} diff --git a/Week 05/id_558/LeetCode_62_558.java b/Week 05/id_558/LeetCode_62_558.java new file mode 100644 index 000000000..5875761e2 --- /dev/null +++ b/Week 05/id_558/LeetCode_62_558.java @@ -0,0 +1,56 @@ +package Week04; + +import java.util.Arrays; + +/** + * @see https://leetcode-cn.com/problems/unique-paths/ + * 不同路径 + */ +public class LeetCode_62_558 { + public static int uniquePaths1(int m, int n) { + return getTotalPaths1(m, n, 0, 0); + } + + private static int getTotalPaths1(int m, int n, int row, int col) { + if (m <= 1 || n <= 1) return Math.min(m, n); + if (row >= n || col >= m) return 0; //越界 + if (col == m - 2 && row == n - 1) return 1; //最后一行,倒数第一列 + if (col == m - 1 && row == n - 2) return 1;//最后一列,倒数第一行 + return getTotalPaths1(m, n, row, col + 1) + getTotalPaths1(m, n, row + 1, col); + } + + public static int uniquePaths2(int col, int row) { + int[][] paths = new int[row][col]; + for (int i = 0; i < row; i++) { + paths[i][col - 1] = 1; + } + for (int i = 0; i < col; i++) { + paths[row - 1][i] = 1; + } + for (int i = row - 2; i >= 0; i--) { + for (int j = col - 2; j >= 0; j--) { + paths[i][j] = paths[i + 1][j] + paths[i][j + 1]; + } + } + return paths[0][0]; + } + + /** + * 杨辉三角形,每个位置的路径 = 该位置左边的路径 + 该位置上边的路径 + */ + public static int uniquePaths3(int col, int row) { + int[] cur = new int[row]; + Arrays.fill(cur, 1); + for (int i = 1; i < col; i++) { + for (int j = 1; j < row; j++) { + cur[j] += cur[j - 1]; + } + } + return cur[row - 1]; + } + + + public static void main(String[] args) { + System.out.println(uniquePaths2(36, 7)); + } +} diff --git a/Week 05/id_558/NOTE.md b/Week 05/id_558/NOTE.md index a6321d6e2..5e838ce10 100644 --- a/Week 05/id_558/NOTE.md +++ b/Week 05/id_558/NOTE.md @@ -1,4 +1,24 @@ # NOTE +####  +*  + *  + *  + *  +*  + 1.  opt[n] = best_of(opt[n-1], opt[n-2], ) + 2. opt[i] + 3. DP +####  + +* [](https://leetcode-cn.com/problems/unique-paths/) +* [](https://leetcode-cn.com/problems/fibonacci-number/submissions/) +* [](https://leetcode-cn.com/problems/longest-common-subsequence/submissions/) + +####  + * DP + +####  +* ! diff --git a/Week 05/id_563/Leecode_1143_563.go b/Week 05/id_563/Leecode_1143_563.go new file mode 100644 index 000000000..1cb7ff309 --- /dev/null +++ b/Week 05/id_563/Leecode_1143_563.go @@ -0,0 +1,22 @@ +func longestCommonSubsequence(s1 string, s2 string) int { + m, n, A, B:= len(s1), len(s2), []byte(s1), []byte(s2) + cur, prev := make([]int, m+1), make([]int, m+1) + + for j := 0; j < n; j++ { + for i := 0; i < m; i++ { + maxLen := max(prev[i+1], cur[i]) + if A[i] == B[j] { + cur[i+1] = max(maxLen, prev[i]+1) + } else { + cur[i+1] = maxLen + } + } + cur, prev = prev, cur + } + return prev[m] +} + +func max(a, b int) int { + if a > b {return a} + return b +} \ No newline at end of file diff --git a/Week 05/id_563/Leecode_62_563.go b/Week 05/id_563/Leecode_62_563.go new file mode 100644 index 000000000..a8469bc7d --- /dev/null +++ b/Week 05/id_563/Leecode_62_563.go @@ -0,0 +1,10 @@ +func uniquePaths(m int, n int) int { + opt := make([]int, m) + opt[0] = 1 + for i := 0; i < n; i++ { + for j := 1; j < len(opt); j++ { + opt[j] += opt[j-1] + } + } + return opt[len(opt)-1] +} diff --git a/Week 05/id_563/Leecode_63_563.go b/Week 05/id_563/Leecode_63_563.go new file mode 100644 index 000000000..147626723 --- /dev/null +++ b/Week 05/id_563/Leecode_63_563.go @@ -0,0 +1,23 @@ +/** +* 这题也太难了 +*/ + +func uniquePathsWithObstacles(obstacleGrid [][]int) int { + if len(obstacleGrid) == 0 || len(obstacleGrid[0]) == 0{return 0} + m, n := len(obstacleGrid), len(obstacleGrid[0]) + opt := make([][]int, m) + for index, _ := range opt { + opt[index] = make([]int, n) + } + if obstacleGrid[0][0] == 0 {opt[0][0] = 1} + + for i := 0; i < m; i++ { + for j := 0; j < n; j++ { + if obstacleGrid[i][j] == 1{continue} + if j > 0 {opt[i][j] += opt[i][j-1]} + if i > 0 {opt[i][j] += opt[i-1][j]} + } + } + + return opt[m-1][n-1] +} \ No newline at end of file diff --git a/Week 05/id_563/Leecode_64_563.go b/Week 05/id_563/Leecode_64_563.go new file mode 100644 index 000000000..54add3729 --- /dev/null +++ b/Week 05/id_563/Leecode_64_563.go @@ -0,0 +1,31 @@ +func minPathSum(grid [][]int) int { + rowCount, columnCount := len(grid), len(grid[0]) + + d := make([][]int, rowCount) + for i := range d { + d[i] = make([]int, columnCount) + } + + d[0][0] = grid[0][0] + for c := 1; c < columnCount; c++ { + d[0][c] = d[0][c-1] + grid[0][c] + } + for r := 1; r < rowCount; r++ { + d[r][0] = d[r-1][0] + grid[r][0] + } + + for r := 1; r < rowCount; r++ { + for c := 1; c < columnCount; c++ { + d[r][c] = min(d[r-1][c], d[r][c-1]) + grid[r][c] + } + } + + return d[rowCount-1][columnCount-1] +} + +func min(a, b int) int { + if a > b { + return b + } + return a +} \ No newline at end of file diff --git a/Week 05/id_563/Leecode_72_563.go b/Week 05/id_563/Leecode_72_563.go new file mode 100644 index 000000000..c09a10c37 --- /dev/null +++ b/Week 05/id_563/Leecode_72_563.go @@ -0,0 +1,32 @@ +func minDistance(word1 string, word2 string) int { + dp:=make([][]int,len(word1)+1) + for i:=0;iy{ + x=y + } + if x>z{ + x=z + } + return x +} diff --git a/Week 05/id_563/NOTE.md b/Week 05/id_563/NOTE.md index a6321d6e2..61ac11914 100644 --- a/Week 05/id_563/NOTE.md +++ b/Week 05/id_563/NOTE.md @@ -1,4 +1,32 @@ -# NOTE +/** +* 递归范本 +*/ - +func resur(level int, param int) { + //终结条件 + if level > MAX_LEVEL {return} + //处理正确逻辑 + process(level, param) + + //递归 + recur(level+1, newParam) + + //存储正确状态 +} + + +/** +* fib优化 +*/ + +func fib (n int) int { + if n < 2 {return n} + + memo := map[int]int{0: 0, 1: 1} + for i := 2; i <= n; i++ { + memo[i] = memo[i-1] + memo[i-2] + } + + return memo[n] +} \ No newline at end of file diff --git a/Week 05/id_568/LeetCode_32_568.java b/Week 05/id_568/LeetCode_32_568.java new file mode 100644 index 000000000..bbefd235d --- /dev/null +++ b/Week 05/id_568/LeetCode_32_568.java @@ -0,0 +1,21 @@ +/** + * @author kelvin + * @date 2019/11/17 10:34 PM + */ +public class LeetCode_32_568 { + public int longestValidParentheses(String s) { + int maxans = 0; + int dp[] = new int[s.length()]; + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) == ')') { + if (s.charAt(i - 1) == '(') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + maxans = Math.max(maxans, dp[i]); + } + } + return maxans; + } +} diff --git a/Week 05/id_568/LeetCode_64_568.java b/Week 05/id_568/LeetCode_64_568.java new file mode 100644 index 000000000..29005fc11 --- /dev/null +++ b/Week 05/id_568/LeetCode_64_568.java @@ -0,0 +1,19 @@ +/** + * @author kelvin + * @date 2019/11/17 10:36 PM + */ +public class LeetCode_64_568 { + public int minPathSum(int[][] grid) { + for (int i = grid.length - 1; i >= 0; i--) { + for (int j = grid[0].length - 1; j >= 0; j--) { + if(i == grid.length - 1 && j != grid[0].length - 1) + grid[i][j] = grid[i][j] + grid[i][j + 1]; + else if(j == grid[0].length - 1 && i != grid.length - 1) + grid[i][j] = grid[i][j] + grid[i + 1][j]; + else if(j != grid[0].length - 1 && i != grid.length - 1) + grid[i][j] = grid[i][j] + Math.min(grid[i + 1][j],grid[i][j + 1]); + } + } + return grid[0][0]; + } +} diff --git a/Week 05/id_568/LeetCode_72_568.java b/Week 05/id_568/LeetCode_72_568.java new file mode 100644 index 000000000..46c71924b --- /dev/null +++ b/Week 05/id_568/LeetCode_72_568.java @@ -0,0 +1,32 @@ +/** + * @author kelvin + * @date 2019/11/17 10:38 PM + */ +public class LeetCode_72_568 { + public int minDistance(String word1, String word2) { + int n = word1.length(); + int m = word2.length(); + if (n * m == 0) { + return n + m; + } + int[][] d = new int[n + 1][m + 1]; + for (int i = 0; i < n + 1; i++) { + d[i][0] = i; + } + for (int j = 0; j < m + 1; j++) { + d[0][j] = j; + } + for (int i = 1; i < n + 1; i++) { + for (int j = 1; j < m + 1; j++) { + int left = d[i - 1][j] + 1; + int down = d[i][j - 1] + 1; + int left_down = d[i - 1][j - 1]; + if (word1.charAt(i - 1) != word2.charAt(j - 1)) + left_down += 1; + d[i][j] = Math.min(left, Math.min(down, left_down)); + + } + } + return d[n][m]; + } +} diff --git a/Week 05/id_573/NOTE.md b/Week 05/id_573/NOTE.md index a6321d6e2..ed85d9523 100644 --- a/Week 05/id_573/NOTE.md +++ b/Week 05/id_573/NOTE.md @@ -1,4 +1,77 @@ # NOTE +#### 定义 + +``` +动态规划(英语:Dynamic programming,简称DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题[1]和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再根据子问题的解以得出原问题的解。通常许多子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,从而减少计算量:一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有用。 +``` + +#### 适用情况 +``` +最优子结构性质。如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质(即满足最优化原理)。最优子结构性质为动态规划算法解决问题提供了重要线索。 +无后效性。即子问题的解一旦确定,就不再改变,不受在这之后、包含它的更大的问题的求解决策影响。 +子问题重叠性质。子问题重叠性质是指在用递归算法自顶向下对问题进行求解时,每次产生的子问题并不总是新问题,有些子问题会被重复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只计算一次,然后将其计算结果保存在一个表格中,当再次需要计算已经计算过的子问题时,只是在表格中简单地查看一下结果,从而获得较高的效率。 +``` + +#### 分类 +``` +动态规划一般可分为线性动规,区域动规,树形动规,背包动规四类。 +举例: +线性动规:拦截导弹,合唱队形,挖地雷,建学校,剑客决斗等; +区域动规:石子合并, 加分二叉树,统计单词个数,炮兵布阵等; +树形动规:贪吃的九头龙,二分查找树,聚会的欢乐,数字三角形等; +背包问题:01背包问题,完全背包问题,分组背包问题,二维背包,装箱问题,挤牛奶(同济ACM第1132题)等; +应用实例: +最短路径问题 ,项目管理,网络流优化等; +``` + +#### 动态规划问题实例 + +``` +解决动态规划类问题,分为两步:1.确定状态,2.根据状态列状态转移方程 + 确定该状态上可以执行的操作,然后是该状态和前一个状态或者前多个状态有什么关联,通常该状态下可执行的操作必定是关联到我们之前的几个状态。 + +1、数字三角形 + +2、背包问题两讲 + 这里解决了两张背包问题,一个是确定最多可以装的下多少的背包盛放物品问题,还有一个是背包中放置的物品具有价值,要来确定其价值为多少。解决方法都是通过动态规划来解决。 + +3、公共子序列,公共子串问题 + 公共子串 + 给出两个字符串,找到最长公共子串,并返回其长度 + + 状态,字符串的每一位对应另一个字符串的每一个位置,因此通过一个二维数组来表示这每一个状态位,然后是找状态转移方程,转移方程即为其前一个位置的前一个的比对的结果累计当前的结果,如果相同则加1,否则为0 + +4、打劫房屋 + 问题描述 + 假设你是一个专业的窃贼,准备沿着一条街打劫房屋。每个房子都存放着特定金额的钱。你面临的唯一约束条件是:相邻的房子装着相互联系的防盗系统,且 当相邻的两个房子同一天被打劫时,该系统会自动报警。 + 给定一个非负整数列表,表示每个房子中存放的钱, 算一算,如果今晚去打劫,你最多可以得到多少钱 在不触动报警装置的情况下。 + +5、编辑距离 + 题目描述 + 给出两个单词word1和word2,计算出将word1 转换为word2的最少操作次数。 + + 你总共三种操作方法: + + 插入一个字符 + + 删除一个字符 + + 替换一个字符 + + 三种操作,因此我们在一个状态上面可以进行三种状态的变化,确定每一个状态,通过第二个字符串和第一个字符串的每一个位置的对应作为一个状态,处在该状态上,我们可以进行的操作,改,进行改操作,那么与之关联的前一个状态是其前一个字符对应另一个字符串的当前对应的前一个字符,增,则是说当前字符串的当前位对应到前一个字符串的前一个位置,删,则为当前字符串的当前位对应前一个字符串的前一个位置。为了增加一个增的位置,需要我们在其前面,所以我们在两个字符串的开始处设置一增加的位置。 + +6、N皇后问题 + n皇后问题是将n个皇后放置在n*n的棋盘上,皇后彼此之间不能相互攻击。 + 给定一个整数n,返回所有不同的n皇后问题的解决方案。 +<<<<<<< HEAD +======= + + 对于n皇后的问题,下一个皇后的布局位置将与之前的所有王后布局有关,因此通过动态规划,没安置一个皇后就作为一个状态,然后判断之前的已经安放的所有皇后的状态,确定是否可以按这一个皇后,通过递归的方式实现。 + +``` +>>>>>>> 2b197e0935af130a7dbda536ba2b34213711c6d7 + 对于n皇后的问题,下一个皇后的布局位置将与之前的所有王后布局有关,因此通过动态规划,没安置一个皇后就作为一个状态,然后判断之前的已经安放的所有皇后的状态,确定是否可以按这一个皇后,通过递归的方式实现。 +``` \ No newline at end of file diff --git a/Week 05/id_573/leetcode_32_573.java b/Week 05/id_573/leetcode_32_573.java new file mode 100644 index 000000000..74c9d5985 --- /dev/null +++ b/Week 05/id_573/leetcode_32_573.java @@ -0,0 +1,41 @@ +<<<<<<< HEAD +public class SolutionFive { public int longestValidParentheses(String s) { int left = 0, right = 0, maxlength = 0; for (int i = 0; i < s.length(); i++) { if (s.charAt(i) == '(') { left++; } else { right++; } if (left == right) { maxlength = Math.max(maxlength, 2 * right); } else if (right >= left) { left = right = 0; } } left = right = 0; for (int i = s.length() - 1; i >= 0; i--) { if (s.charAt(i) == '(') { left++; } else { right++; } if (left == right) { maxlength = Math.max(maxlength, 2 * left); } else if (left >= right) { left = right = 0; } } return maxlength; } public static void main(String[] args) { SolutionFive solutionFive = new SolutionFive(); System.out.println(solutionFive.longestValidParentheses(")()())")); } } +======= +public class SolutionFive { + + public int longestValidParentheses(String s) { + int left = 0, right = 0, maxlength = 0; + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '(') { + left++; + } else { + right++; + } + if (left == right) { + maxlength = Math.max(maxlength, 2 * right); + } else if (right >= left) { + left = right = 0; + } + } + left = right = 0; + for (int i = s.length() - 1; i >= 0; i--) { + if (s.charAt(i) == '(') { + left++; + } else { + right++; + } + if (left == right) { + maxlength = Math.max(maxlength, 2 * left); + } else if (left >= right) { + left = right = 0; + } + } + return maxlength; + } + + public static void main(String[] args) { + SolutionFive solutionFive = new SolutionFive(); + System.out.println(solutionFive.longestValidParentheses(")()())")); + } +} +>>>>>>> 2b197e0935af130a7dbda536ba2b34213711c6d7 diff --git a/Week 05/id_573/leetcode_64_573.java b/Week 05/id_573/leetcode_64_573.java new file mode 100644 index 000000000..cace53448 --- /dev/null +++ b/Week 05/id_573/leetcode_64_573.java @@ -0,0 +1,28 @@ +<<<<<<< HEAD +public class SolutionFive { public int minPathSum(int[][] grid) { int[][] dp = new int[grid.length][grid[0].length]; for (int i = grid.length - 1; i >= 0; i--) { for (int j = grid[0].length - 1; j >= 0; j--) { if(i == grid.length - 1 && j != grid[0].length - 1) dp[i][j] = grid[i][j] + dp[i][j + 1]; else if(j == grid[0].length - 1 && i != grid.length - 1) dp[i][j] = grid[i][j] + dp[i + 1][j]; else if(j != grid[0].length - 1 && i != grid.length - 1) dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]); else dp[i][j] = grid[i][j]; } } return dp[0][0]; } public static void main(String[] args) { SolutionFive solutionFive = new SolutionFive(); System.out.println(solutionFive.minPathSum(new int[3][3]{[1,3,1],[1,5,1],[4,2,1]})); } } +======= +public class SolutionFive { + + public int minPathSum(int[][] grid) { + int[][] dp = new int[grid.length][grid[0].length]; + for (int i = grid.length - 1; i >= 0; i--) { + for (int j = grid[0].length - 1; j >= 0; j--) { + if(i == grid.length - 1 && j != grid[0].length - 1) + dp[i][j] = grid[i][j] + dp[i][j + 1]; + else if(j == grid[0].length - 1 && i != grid.length - 1) + dp[i][j] = grid[i][j] + dp[i + 1][j]; + else if(j != grid[0].length - 1 && i != grid.length - 1) + dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]); + else + dp[i][j] = grid[i][j]; + } + } + return dp[0][0]; + } + + public static void main(String[] args) { + SolutionFive solutionFive = new SolutionFive(); + System.out.println(solutionFive.minPathSum(new int[3][3]{[1,3,1],[1,5,1],[4,2,1]})); + } +} +>>>>>>> 2b197e0935af130a7dbda536ba2b34213711c6d7 diff --git a/Week 05/id_573/leetcode_72_573.java b/Week 05/id_573/leetcode_72_573.java new file mode 100644 index 000000000..40aee2edb --- /dev/null +++ b/Week 05/id_573/leetcode_72_573.java @@ -0,0 +1,40 @@ +<<<<<<< HEAD +public class SolutionFive { public int minDistance(String word1, String word2) { int n = word1.length(); int m = word2.length(); // if one of the strings is empty if (n * m == 0) return n + m; // array to store the convertion history int [][] d = new int[n + 1][m + 1]; // init boundaries for (int i = 0; i < n + 1; i++) { d[i][0] = i; } for (int j = 0; j < m + 1; j++) { d[0][j] = j; } // DP compute for (int i = 1; i < n + 1; i++) { for (int j = 1; j < m + 1; j++) { int left = d[i - 1][j] + 1; int down = d[i][j - 1] + 1; int left_down = d[i - 1][j - 1]; if (word1.charAt(i - 1) != word2.charAt(j - 1)) left_down += 1; d[i][j] = Math.min(left, Math.min(down, left_down)); } } return d[n][m]; } } +======= +public class SolutionFive { + + public int minDistance(String word1, String word2) { + int n = word1.length(); + int m = word2.length(); + + // if one of the strings is empty + if (n * m == 0) + return n + m; + + // array to store the convertion history + int [][] d = new int[n + 1][m + 1]; + + // init boundaries + for (int i = 0; i < n + 1; i++) { + d[i][0] = i; + } + for (int j = 0; j < m + 1; j++) { + d[0][j] = j; + } + + // DP compute + for (int i = 1; i < n + 1; i++) { + for (int j = 1; j < m + 1; j++) { + int left = d[i - 1][j] + 1; + int down = d[i][j - 1] + 1; + int left_down = d[i - 1][j - 1]; + if (word1.charAt(i - 1) != word2.charAt(j - 1)) + left_down += 1; + d[i][j] = Math.min(left, Math.min(down, left_down)); + + } + } + return d[n][m]; + } +} +>>>>>>> 2b197e0935af130a7dbda536ba2b34213711c6d7 diff --git a/Week 05/id_573/leetcode_91_573.go b/Week 05/id_573/leetcode_91_573.go new file mode 100644 index 000000000..ec847e28d --- /dev/null +++ b/Week 05/id_573/leetcode_91_573.go @@ -0,0 +1,26 @@ +package main + +func numDecodings(s string)int{ + pre,cur:=1,1 + if s[0:1]=="0"{ + return 0 + } + for i:=1;i= 0; i--) { //当前数字时 0 ,直接跳过,0 不代表任何字母 if (s.charAt(i) == '0') { continue; } int ans1 = dp[i + 1]; //判断两个字母组成的数字是否小于等于 26 int ans2 = 0; int ten = (s.charAt(i) - '0') * 10; int one = s.charAt(i + 1) - '0'; if (ten + one <= 26) { ans2 = dp[i + 2]; } dp[i] = ans1 + ans2; } return dp[0]; } } \ No newline at end of file diff --git a/Week 05/id_578/LeetCode_221_578.java b/Week 05/id_578/LeetCode_221_578.java new file mode 100644 index 000000000..048bcd6ff --- /dev/null +++ b/Week 05/id_578/LeetCode_221_578.java @@ -0,0 +1,49 @@ +package com.hand.week5; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/11/2 + */ +public class LeetCode_221_578 { +// /** +// * 二维数组 +// * +// * @param matrix +// * @return +// */ +// public int maximalSquare(char[][] matrix) { +// int m = matrix.length, n = m > 0 ? matrix[0].length : 0; +// int[][] dp = new int[m + 1][n + 1]; +// int maxsqlen = 0; +// for (int i = 1; i < m + 1; ++i) { +// for (int j = 1; j < n + 1; ++j) { +// if (matrix[i - 1][j - 1] == '1') { +// dp[i][j] = Math.min(Math.min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; +// maxsqlen = Math.max(maxsqlen, dp[i][j]); +// } +// } +// } +// return maxsqlen * maxsqlen; +// } + + public int maximalSquare(char[][] matrix) { + int m = matrix.length, n = m > 0 ? matrix[0].length : 0; + int maxsqlen = 0, pre = 0; + int[] dp = new int[n + 1]; + for (int i = 1; i < m + 1; ++i) { + for (int j = 1; j < n + 1; ++j) { + int tmp = dp[j]; + if (matrix[i - 1][j - 1] == '1') { + dp[j] = Math.min(Math.min(dp[j - 1], dp[j]), pre) + 1; + maxsqlen = Math.max(maxsqlen, dp[j]); + } else { + dp[j] = 0; + } + pre = tmp; + } + } + return maxsqlen * maxsqlen; + } +} diff --git a/Week 05/id_578/LeetCode_32_578.java b/Week 05/id_578/LeetCode_32_578.java new file mode 100644 index 000000000..b48a9736f --- /dev/null +++ b/Week 05/id_578/LeetCode_32_578.java @@ -0,0 +1,25 @@ +package com.hand.week5; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/11/16 + */ +public class LeetCode_32_578 { + public int longestValidParentheses(String s) { + int maxLength = 0; + int[] dp = new int[s.length()]; + for (int i = 1; i < s.length(); ++i) { + if (s.charAt(i) == ')') { + if (s.charAt(i - 1) == '(') { + dp[i] = (i - 2 >= 0 ? dp[i - 2] : 0) + 2; + } else if (i - dp[i - 1] - 1 >= 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + (i - dp[i - 1] - 2 >= 0 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + maxLength = Math.max(maxLength, dp[i]); + } + } + return maxLength; + } +} diff --git a/Week 05/id_578/LeetCode_64_578.java b/Week 05/id_578/LeetCode_64_578.java new file mode 100644 index 000000000..d6a7956e5 --- /dev/null +++ b/Week 05/id_578/LeetCode_64_578.java @@ -0,0 +1,62 @@ +package com.hand.week5; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/11/2 + */ +public class LeetCode_64_578 { + + /** + * 二维数组 + * DP方程:grid[i, j]=grid[i, j]+Math.min(grid[i + 1][j], grid[i][j + 1]) + * + * @param grid + * @return + */ +// public int minPathSum(int[][] grid) { +// int m = grid.length; +// int n = grid[0].length; +// int[][] dp = new int[m][n]; +// for (int i = m - 1; i >= 0; --i) { +// for (int j = n - 1; j >= 0; --j) { +// if (i + 1 < m && j + 1 < n) +// dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]); +// else if (i + 1 == m && j + 1 < n) +// dp[i][j] = grid[i][j] + dp[i][j + 1]; +// else if (j + 1 == n && i + 1 < m) +// dp[i][j] = grid[i][j] + dp[i + 1][j]; +// else +// dp[i][j] = grid[i][j]; +// } +// } +// return dp[0][0]; +// } + + /** + * 优化成一维数组 + * DP方程:dp[j] = grid[i][j] + Math.min(dp[j], dp[j + 1]) + * + * @param grid + * @return + */ + public int minPathSum(int[][] grid) { + int m = grid.length; + int n = grid[0].length; + int[] dp = new int[n]; + for (int i = m - 1; i >= 0; --i) { + for (int j = n - 1; j >= 0; --j) { + if (i + 1 < m && j + 1 < n) + dp[j] = grid[i][j] + Math.min(dp[j], dp[j + 1]); + else if (i + 1 == m && j + 1 < n) + dp[j] = grid[i][j] + dp[j + 1]; + else if (j + 1 == n && i + 1 < m) + dp[j] = grid[i][j] + dp[j]; + else + dp[j] = grid[i][j]; + } + } + return dp[0]; + } +} diff --git a/Week 05/id_578/LeetCode_72_578.java b/Week 05/id_578/LeetCode_72_578.java new file mode 100644 index 000000000..e856ae11c --- /dev/null +++ b/Week 05/id_578/LeetCode_72_578.java @@ -0,0 +1,26 @@ +package com.hand.week5; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/11/17 + */ +public class LeetCode_72_578 { + public int minDistance(String word1, String word2) { + int[][] dp = new int[word1.length() + 1][word2.length() + 1]; + for (int i = 1; i < dp.length; ++i) + dp[i][0] = i; + for (int j = 1; j < dp[0].length; ++j) + dp[0][j] = j; + for (int i = 1; i < dp.length; ++i) { + for (int j = 1; j < dp[0].length; ++j) { + if (word1.charAt(i - 1) == word2.charAt(j - 1)) + dp[i][j] = dp[i - 1][j - 1]; + else + dp[i][j] = Math.min(Math.min(dp[i - 1][j - 1], dp[i - 1][j]), dp[i][j - 1]) + 1; + } + } + return dp[word1.length()][word2.length()]; + } +} diff --git a/Week 05/id_578/LeetCode_91_578.java b/Week 05/id_578/LeetCode_91_578.java new file mode 100644 index 000000000..8fdd42f61 --- /dev/null +++ b/Week 05/id_578/LeetCode_91_578.java @@ -0,0 +1,44 @@ +package com.hand.week5; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/11/16 + */ +public class LeetCode_91_578 { +// public int numDecodings(String s) { +// char[] s1 = s.toCharArray(); +// if (s1[0] == '0') return 0; +// int[] dp = new int[s1.length]; +// dp[0] = 1; +// for (int i = 1; i < dp.length; ++i) { +// if (s1[i] == '0') { +// if (s1[i - 1] == '1' || s1[i - 1] == '2') dp[i] = (i >= 2 ? dp[i - 2] : 1); +// else return 0; +// } else if (s1[i - 1] == '1' || (s1[i - 1] == '2' && s1[i] >= '1' && s1[i] <= '6')) { +// dp[i] = dp[i - 1] + (i >= 2 ? dp[i - 2] : 1); +// } else { +// dp[i] = dp[i - 1]; +// } +// } +// return dp[s1.length - 1]; +// } + + public int numDecodings(String s) { + char[] s1 = s.toCharArray(); + if (s1[0] == '0') return 0; + int pre = 1, current = 1; + for (int i = 1; i < s1.length; ++i) { + int tmp = current; + if (s1[i] == '0') { + if (s1[i - 1] == '1' || s1[i - 1] == '2') current = (i >= 2 ? pre : 1); + else return 0; + } else if (s1[i - 1] == '1' || (s1[i - 1] == '2' && s1[i] >= '1' && s1[i] <= '6')) { + current = current + (i >= 2 ? pre : 1); + } + pre = tmp; + } + return current; + } +} diff --git a/Week 05/id_583/LeetCode_32_583.php b/Week 05/id_583/LeetCode_32_583.php new file mode 100644 index 000000000..095351e54 --- /dev/null +++ b/Week 05/id_583/LeetCode_32_583.php @@ -0,0 +1,40 @@ += 2 ? $dp[$i - 2] : 0) + 2; + } else if ($i - $dp[$i - 1] > 0 && $s[$i - $dp[$i - 1] - 1] == '(') { + $dp[$i] = $dp[$i - 1] + (($i - $dp[$i - 1]) >= 2 ? $dp[$i - $dp[$i - 1] - 2] : 0) + 2; + } + $max = max($max, $dp[$i]); + } + } + return $max; + } +} + +$so = new Solution(); +$s = '(()'; + +$result = $so->longestValidParentheses($s); + +echo $result; \ No newline at end of file diff --git a/Week 05/id_583/LeetCode_64_583.php b/Week 05/id_583/LeetCode_64_583.php new file mode 100644 index 000000000..3ad4b7492 --- /dev/null +++ b/Week 05/id_583/LeetCode_64_583.php @@ -0,0 +1,40 @@ += 0; $i--) { + for ($j = count($grid[0]) - 1; $j >= 0; $j--) { + if ($i == count($grid) - 1 && $j != count($grid[0]) - 1) + $grid[$i][$j] = $grid[$i][$j] + $grid[$i][$j + 1]; + else if ($j == count($grid[0]) - 1 && $i != count($grid) - 1) + $grid[$i][$j] = $grid[$i][$j] + $grid[$i + 1][$j]; + else if ($j != count($grid[0]) - 1 && $i != count($grid) - 1) + $grid[$i][$j] = $grid[$i][$j] + min($grid[$i + 1][$j], $grid[$i][$j + 1]); + } + } + return $grid[0][0]; + } +} + +$s = new Solution(); +$grid = [ + [1, 3, 1], + [1, 5, 1], + [4, 2, 1] +]; + +$result = $s->minPathSum($grid); + +echo $result; \ No newline at end of file diff --git a/Week 05/id_588/LeetCode_221_588.java b/Week 05/id_588/LeetCode_221_588.java new file mode 100644 index 000000000..211461f3c --- /dev/null +++ b/Week 05/id_588/LeetCode_221_588.java @@ -0,0 +1,21 @@ +/** + * 最大正方形 + * https://leetcode-cn.com/problems/maximal-square/ + */ +public class LeetCode_221_588 { + + public int maximalSquare(char[][] matrix) { + int rows = matrix.length, cols = rows > 0 ? matrix[0].length : 0; + int[][] dp = new int[rows + 1][cols + 1]; + int maxsqlen = 0; + for (int i = 1; i <= rows; i++) { + for (int j = 1; j <= cols; j++) { + if (matrix[i-1][j-1] == '1'){ + dp[i][j] = Math.min(Math.min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; + maxsqlen = Math.max(maxsqlen, dp[i][j]); + } + } + } + return maxsqlen * maxsqlen; + } +} diff --git a/Week 05/id_588/LeetCode_64_588.java b/Week 05/id_588/LeetCode_64_588.java new file mode 100644 index 000000000..378da4d58 --- /dev/null +++ b/Week 05/id_588/LeetCode_64_588.java @@ -0,0 +1,25 @@ +/** + * 最小路径和 + * https://leetcode-cn.com/problems/minimum-path-sum/ + */ +public class LeetCode_64_588 { + + public int minPathSum(int[][] grid) { + int[][] dp = new int[grid.length][grid[0].length]; + // 从右下角往左上角 + for (int i = grid.length - 1; i >= 0; i--) { + for (int j = grid[0].length - 1; j >= 0; j--) { + if(i == grid.length - 1 && j != grid[0].length - 1) { + dp[i][j] = grid[i][j] + dp[i][j + 1]; + } else if(j == grid[0].length - 1 && i != grid.length - 1) { + dp[i][j] = grid[i][j] + dp[i + 1][j]; + } else if (j != grid[0].length - 1 && i != grid.length - 1) { + dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]); + } else { + dp[i][j] = grid[i][j]; + } + } + } + return dp[0][0]; + } +} diff --git a/Week 05/id_588/NOTE.md b/Week 05/id_588/NOTE.md index a6321d6e2..c7db26003 100644 --- a/Week 05/id_588/NOTE.md +++ b/Week 05/id_588/NOTE.md @@ -1,4 +1,21 @@ -# NOTE +# 第五周学习总结 + +动态规划的特点: + +1. 与递归、分支没有根本上的区别 +2. 共性都是找到重复子问题 +3. 差异性是动态规划是找到问题的最优子结构,中途可以淘汰次优解 + +动态规划关键点: + +1. 最优子结构 opt[n] = best_of(opt[n-1], opt[n-2], …) +2. 存储中间状态 opt[i] +3. 递归公式 状态转移方程或者DP方程) + +动态规划解题技巧: +1. 打破自己的思维惯性,形成机器思维(找重复性) +2. 理解复杂逻辑的关键,寻找递归公式 +3. 不要进行人肉递归 diff --git a/Week 05/id_593/LeetCode_1143_593.java b/Week 05/id_593/LeetCode_1143_593.java new file mode 100644 index 000000000..cff1a4762 --- /dev/null +++ b/Week 05/id_593/LeetCode_1143_593.java @@ -0,0 +1,59 @@ +/** + * 1143. 最长公共子序列 + * 给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列。 + *

+ * 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 + * 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。 + *

+ * 若这两个字符串没有公共子序列,则返回 0。 + *

+ *   + *

+ * 示例 1: + *

+ * 输入:text1 = "abcde", text2 = "ace" + * 输出:3 + * 解释:最长公共子序列是 "ace",它的长度为 3。 + * 示例 2: + *

+ * 输入:text1 = "abc", text2 = "abc" + * 输出:3 + * 解释:最长公共子序列是 "abc",它的长度为 3。 + * 示例 3: + *

+ * 输入:text1 = "abc", text2 = "def" + * 输出:0 + * 解释:两个字符串没有公共子序列,返回 0。 + *   + *

+ * 提示: + *

+ * 1 <= text1.length <= 1000 + * 1 <= text2.length <= 1000 + * 输入的字符串只含有小写英文字符。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/longest-common-subsequence + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * dp 状态 + * dp 状态方程 + * + * @author jaryoung + */ +public class LeetCode_1143_593 { + public int longestCommonSubsequence(String text1, String text2) { + int row = text1.length() + 1; + int column = text2.length() + 1; + int[][] dp = new int[row][column]; + for (int i = 1; i < row; i++) { + for (int j = 1; j < column; j++) { + if (text1.charAt(i - 1) == text2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + } + } + } + return dp[row - 1][column - 1]; + } +} diff --git a/Week 05/id_593/LeetCode_120_593.java b/Week 05/id_593/LeetCode_120_593.java new file mode 100644 index 000000000..1d5d42990 --- /dev/null +++ b/Week 05/id_593/LeetCode_120_593.java @@ -0,0 +1,85 @@ + +import java.util.List; + +/** + * 120. 三角形最小路径和 + *

+ * 给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。 + *

+ * 例如,给定三角形: + *

+ *

[ + *

[2], + *

[3,4], + *

[6,5,7], + *

[4,1,8,3] + *

] + * 自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。 + *

+ * 说明: + *

+ * 如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/triangle + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + *

+ * 思路:递归(傻和记忆化);dp + * dp(自底向上): + * 找重复性:当前的最小的路径和,肯定是等于上一层并且它能触摸到的(例如,[6,5,7] 中的 6,它能触摸最小值应该是min(4,1) , 加上它本身 + * problem[i,j] = current[i,j] + sub(dp[i+1,j], dp[i+1,j+1]); + * dp状态数组:dp[i,j] + * dp方程(dp状态转移方程): + * dp[i,j] = current[i,j] + min(dp[i+1,j], dp[i+1,j+1]); + * + * @author jaryoung + */ +public class LeetCode_120_593 { + + public int minimumTotal(List> triangle) { + int row = triangle.size() - 1; + int[] dp = new int[triangle.get(row).size() + 1]; + for (int i = row; i >= 0; i--) { + for (int j = 0; j < triangle.get(i).size(); j++) { + dp[j] = triangle.get(i).get(j) + Math.min(dp[j], dp[j + 1]); + } + } + return dp[0]; + } + + /** + * 记忆化递归 + * + * @param triangle 数组 + * @return 最小路径和 + */ + public int minimumTotalByRecursion(List> triangle) { + int row = triangle.size(); + int col = triangle.get(row - 1).size(); + Integer[][] mono = new Integer[row][col]; + return minimumTotalByRecursion(0, 0, triangle, mono); + } + + /** + * 记忆化搜索 + * + * @param level 层级 + * @param index 当前位置 + * @param triangle 数组 + * @param mono 记忆缓存 + * @return 最小路径 + */ + private int minimumTotalByRecursion(int level, int index, List> triangle, Integer[][] mono) { + if (level == triangle.size() - 1) { + return triangle.get(level).get(index); + } + if (mono[level][index] != null) { + return mono[level][index]; + } + int left = minimumTotalByRecursion(level + 1, index, triangle, mono); + int right = minimumTotalByRecursion(level + 1, index + 1, triangle, mono); + mono[level][index] = Math.min(left, right) + triangle.get(level).get(index); + return mono[left][index]; + } + +} diff --git a/Week 05/id_593/LeetCode_322_593.java b/Week 05/id_593/LeetCode_322_593.java new file mode 100644 index 000000000..0c33eda2d --- /dev/null +++ b/Week 05/id_593/LeetCode_322_593.java @@ -0,0 +1,80 @@ +import java.util.Arrays; + +/** + * 322. 零钱兑换 + *

+ * 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。 + *

+ * 示例 1: + *

+ * 输入: coins = [1, 2, 5], amount = 11 + * 输出: 3 + * 解释: 11 = 5 + 5 + 1 + * 示例 2: + *

+ * 输入: coins = [2], amount = 3 + * 输出: -1 + * 说明: + * 你可以认为每种硬币的数量是无限的。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/coin-change + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * 思路: + * 暴力递归 + * 状态树 -> BFS + * dp + * a. 找重复性 + * f(n) = f(n - k) (n 到 k 累加之和),类似 斐波那契数列 f(n) = f(n-1) + f(n-2) , + * 零钱兑换,就相当于,它能跳 不同的面额 的步数。 + * b. dp状态数组 + * f(n) = min{f(n-k), for k in [1, 2, 5]}) + 1 (我们是从中选取一枚硬币,所以+1) + * c. dp方程 + * + * @author jaryoung + */ +public class LeetCode_322_593 { + + public int coinChange(int[] coins, int amount) { + if (amount < 0) { + return -1; + } + return coinChangeByRecursion(amount, coins, new int[amount]); + } + + private int coinChangeByRecursion(int remain, int[] coins, int[] mono) { + if (remain < 0) { + return -1; + } + if (remain == 0) { + return 0; + } + if (mono[remain - 1] != 0) { + return mono[remain - 1]; + } + int min = Integer.MAX_VALUE; + for (int coin : coins) { + int result = coinChangeByRecursion(remain - coin, coins, mono); + if (result >= 0 && result < min) { + min = result + 1; + } + } + mono[remain - 1] = min == Integer.MAX_VALUE ? -1 : min; + return mono[remain - 1]; + } + + public int coinChangeByDp(int[] coins, int amount) { + int max = amount + 1; + int[] dp = new int[max]; + Arrays.fill(dp, max); + dp[0] = 0; + for (int n = 1; n < max; n++) { + for (int coin : coins) { + if (coin <= n) { + dp[n] = Math.min(dp[n], dp[n - coin] + 1); + } + } + } + return dp[amount] != max ? dp[amount] : -1; + } +} diff --git a/Week 05/id_593/LeetCode_53_593.java b/Week 05/id_593/LeetCode_53_593.java new file mode 100644 index 000000000..37feb7b54 --- /dev/null +++ b/Week 05/id_593/LeetCode_53_593.java @@ -0,0 +1,46 @@ + +/** + * 53. 最大子序和 + *

+ * 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 + *

+ * 示例: + *

+ * 输入: [-2,1,-3,4,-1,2,1,-5,4], + * 输出: 6 + * 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 + * 进阶: + *

+ * 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/maximum-subarray + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + *

+ * 思路: + * dp + * a. 找重复子问题 + * f(i) = max( a(i), f(i+1) + a(i) ); + * b. 状态数组 + * dp[i] + * c. dp方程 + * dp[i] = max(a[i], dp[i+1] + a[i]) + * + * @author jaryoung + */ +public class LeetCode_53_593 { + + public int maxSubArray(int[] nums) { + int length = nums.length; + int[] dp = new int[length]; + dp[length - 1] = nums[length - 1]; + for (int i = length - 2; i >= 0; i--) { + dp[i] = Math.max(nums[i], dp[i + 1] + nums[i]); + } + for (int i = 1; i < dp.length; i++) { + if (dp[0] < dp[i]) { + dp[0] = dp[i]; + } + } + return dp[0]; + } diff --git a/Week 05/id_593/LeetCode_621_593.java b/Week 05/id_593/LeetCode_621_593.java new file mode 100644 index 000000000..aaaace3a6 --- /dev/null +++ b/Week 05/id_593/LeetCode_621_593.java @@ -0,0 +1,48 @@ + +import java.util.Arrays; + +/** + * 621. 任务调度器 + * 给定一个用字符数组表示的 CPU 需要执行的任务列表。其中包含使用大写的 A - Z 字母表示的26 种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。CPU 在任何一个单位时间内都可以执行一个任务,或者在待命状态。 + *

+ * 然而,两个相同种类的任务之间必须有长度为 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。 + *

+ * 你需要计算完成所有任务所需要的最短时间。 + *

+ * 示例 1: + *

+ * 输入: tasks = ["A","A","A","B","B","B"], n = 2 + * 输出: 8 + * 执行顺序: A -> B -> (待命) -> A -> B -> (待命) -> A -> B. + * 注: + *

+ * 任务的总个数为 [1, 10000]。 + * n 的取值范围为 [0, 100]。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/task-scheduler + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + * @version 1.0 默写官方题解 + */ +public class LeetCode_621_593 { + + public int leastInterval(char[] tasks, int n) { + int[] dict = new int[26]; + for (char c : tasks) { + dict[c - 'A']++; + } + Arrays.sort(dict); + // 某个任务最大执行次数 - 1 + int maxTimes = dict[25] - 1; + // 增加虚拟空闲的槽 + int idleSlots = maxTimes * n; + // dict[i] > 0 表示存在某个任务 + for (int i = 24; i >= 0 && dict[i] > 0; i--) { + idleSlots = idleSlots - Math.min(dict[i], maxTimes); + } + return idleSlots > 0 ? idleSlots + tasks.length : tasks.length; + } + +} diff --git a/Week 05/id_593/LeetCode_62_593.java b/Week 05/id_593/LeetCode_62_593.java new file mode 100644 index 000000000..a02e9890e --- /dev/null +++ b/Week 05/id_593/LeetCode_62_593.java @@ -0,0 +1,49 @@ +/** + * 62. 不同路径 + * 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 + *

+ * 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 + *

+ * 问总共有多少条不同的路径? + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/unique-paths + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + * @version 2.0 + */ +public class LeetCode_62_593 { + public int uniquePaths(int m, int n) { + int[] dp = new int[m]; + dp[0] = 1; + for (int i = 0; i < n; i++) { + for (int j = 1; j < m; j++) { + dp[j] += dp[j - 1]; + } + } + return dp[m - 1]; + } + + /** + * 递归实现 + * + * @param m 列 + * @param n 行 + * @param temp 备忘录作用 + * @return 当前 位置的走法总和 + */ + private int uniquePathsByRecursion(int m, int n, int[][] temp) { + if (m < 0 || n > 0) { + return 0; + } + if (m + n == 1 || (m == 0 && n == 0)) { + return 1; + } + if (temp[m][n] == 0) { + temp[m][n] = uniquePathsByRecursion(m - 1, n, temp) + uniquePathsByRecursion(m, n - 1, temp); + } + return temp[m][n]; + } + +} diff --git a/Week 05/id_593/LeetCode_63_593.java b/Week 05/id_593/LeetCode_63_593.java new file mode 100644 index 000000000..0f949d038 --- /dev/null +++ b/Week 05/id_593/LeetCode_63_593.java @@ -0,0 +1,35 @@ +/** + * 63. 不同路径 II + *

+ * 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 + *

+ * 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 + *

+ * 现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径? + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/unique-paths-ii + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + * @version 1.0 + */ +public class LeetCode_63_593 { + public int uniquePathsWithObstacles(int[][] obstacleGrid) { + int col = obstacleGrid[0].length; + int[] dp = new int[col + 1]; + dp[0] = 1; + for (int[] ints : obstacleGrid) { + for (int i = 0; i < ints.length; i++) { + if (ints[i] == 1) { + dp[i] = 0; + } else if (i > 0) { + dp[i] = dp[i - 1] + dp[i]; + } + } + } + return dp[col - 1]; + } + + +} \ No newline at end of file diff --git a/Week 05/id_593/LeetCode_72_593.java b/Week 05/id_593/LeetCode_72_593.java new file mode 100644 index 000000000..7372005c1 --- /dev/null +++ b/Week 05/id_593/LeetCode_72_593.java @@ -0,0 +1,71 @@ +/** + * 72. 编辑距离 + * 给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。 + *

+ * 你可以对一个单词进行如下三种操作: + *

+ * 插入一个字符 + * 删除一个字符 + * 替换一个字符 + * 示例 1: + *

+ * 输入: word1 = "horse", word2 = "ros" + * 输出: 3 + * 解释: + * horse -> rorse (将 'h' 替换为 'r') + * rorse -> rose (删除 'r') + * rose -> ros (删除 'e') + * 示例 2: + *

+ * 输入: word1 = "intention", word2 = "execution" + * 输出: 5 + * 解释: + * intention -> inention (删除 't') + * inention -> enention (将 'i' 替换为 'e') + * enention -> exention (将 'n' 替换为 'x') + * exention -> exection (将 'n' 替换为 'c') + * exection -> execution (插入 'u') + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/edit-distance + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + *

+ * 思路: + * dp + * a. 子问题 + * b. 状态定义 + * c. dp方程 + * word1[i] == word2[j], dp[i][j] = dp[i-1][j-1]; + * word1[i] != word2[j], + * dp[i-1][j] 到 d[i][j] 需要进行删除操作,dp[i-1][j-1] 到 dp[i][j] 需要进行替换操作,dp[i][j-1] 到 d[i][j] 需要进行插入操作。 + * dp[i][j] = min(dp[i-1][j], dp[i-1][j-1], dp[i][j-1]) + 1; + * + * @author jaryoung + * @version 1.0 默写,别人的思路 + */ +public class LeetCode_72_593 { + + public int minDistance(String word1, String word2) { + int row = word1.length(); + int column = word2.length(); + int[][] dp = new int[row + 1][column + 1]; + // 第一行,word1 变成 word2 最少操作数,就是插入操作 + for (int i = 1; i <= row; i++) { + dp[i][0] = dp[i - 1][0] + 1; + } + // 第一列,word1 变成 '', 最少操作数,就是删除操作 + for (int j = 1; j <= column; j++) { + dp[0][j] = dp[0][j - 1] + 1; + } + for (int i = 1; i <= row; i++) { + for (int j = 1; j <= column; j++) { + if (word1.charAt(i - 1) == word2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i - 1][j - 1]), dp[i][j - 1]) + 1; + } + } + } + return dp[row][column]; + } +} diff --git a/Week 05/id_593/NOTE.md b/Week 05/id_593/NOTE.md index a6321d6e2..1e86bbf84 100644 --- a/Week 05/id_593/NOTE.md +++ b/Week 05/id_593/NOTE.md @@ -2,3 +2,17 @@ +## 【593-Week 05】学习总结 + +> Dynamic Programing ,动态规划,不要被名字误导。 +> +> 定义,动态规划(英语:Dynamic programming,简称DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。 + + + +### 本周核心点 + +- 动态规划三步走 + - 寻找重复子问题 + - dp状态的定义 + - dp方程 \ No newline at end of file diff --git a/Week 05/id_598/LeetCode_62_598.java b/Week 05/id_598/LeetCode_62_598.java new file mode 100644 index 000000000..e816d01f7 --- /dev/null +++ b/Week 05/id_598/LeetCode_62_598.java @@ -0,0 +1,73 @@ +/** + * @author northleaf + * @create 2019年11月14日 + */ +public class LeetCode_62_598 { + + + /** + * 递归的方式处理 + * @param m + * @param n + * @return + */ + public int uniquePaths1(int m, int n) { + + //m -1 n-1 要考虑数组是从0开始的 + return helper(0, 0, m-1, n-1); + + } + + private int helper(int row, int col, int rowMax, int colMax) { + + + //终止条件 + if(row==rowMax && col == colMax){ + return 1; + } + + //处理当前层 + //处理下一层 + int leftRes = 0; + int right = 0; + //行 + if(row= 2 ? dp[i - 2] : 0) + 2; + } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + maxans = Math.max(maxans, dp[i]); + } + } + return maxans; + } +} diff --git a/Week 05/id_603/Leetcode_32_603.py b/Week 05/id_603/Leetcode_32_603.py new file mode 100644 index 000000000..78807cf0f --- /dev/null +++ b/Week 05/id_603/Leetcode_32_603.py @@ -0,0 +1,17 @@ +public class Solution { + public int longestValidParentheses(String s) { + int maxans = 0; + int dp[] = new int[s.length()]; + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) == ')') { + if (s.charAt(i - 1) == '(') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + maxans = Math.max(maxans, dp[i]); + } + } + return maxans; + } +} diff --git a/Week 05/id_603/Leetcode_64_603.java b/Week 05/id_603/Leetcode_64_603.java new file mode 100644 index 000000000..8740c21d1 --- /dev/null +++ b/Week 05/id_603/Leetcode_64_603.java @@ -0,0 +1,18 @@ +public class Solution { + public int minPathSum(int[][] grid) { + int[][] dp = new int[grid.length][grid[0].length]; + for (int i = grid.length - 1; i >= 0; i--) { + for (int j = grid[0].length - 1; j >= 0; j--) { + if(i == grid.length - 1 && j != grid[0].length - 1) + dp[i][j] = grid[i][j] + dp[i][j + 1]; + else if(j == grid[0].length - 1 && i != grid.length - 1) + dp[i][j] = grid[i][j] + dp[i + 1][j]; + else if(j != grid[0].length - 1 && i != grid.length - 1) + dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]); + else + dp[i][j] = grid[i][j]; + } + } + return dp[0][0]; + } +} diff --git a/Week 05/id_613/NOTE.md b/Week 05/id_613/NOTE.md index a6321d6e2..3f60d78e5 100644 --- a/Week 05/id_613/NOTE.md +++ b/Week 05/id_613/NOTE.md @@ -1,4 +1,34 @@ # NOTE +# 面试解题套路 +1、人肉递归低效、很累 +2、找到最近最简方法,将其拆解成可重复解决的问题 +3、数学归纳法思维(抵制人肉递归的诱惑) + +### 为什么寻找重复性 +本质:寻找重复性 --> 计算机指令集(只会if else loop recursion) + +## 人肉递归:如果需要人肉递归的话,不要怕麻烦,把递归状态树画出来 + +# 动态规划(动态递推)的定义 +1、Simplifying a complicated problem by breaking it down into simpler +sub-problems(in a recursive manner) +2、dp本质上就是分支 Divide&Conquer + Optimal substructure +3、dp一般的题目都具有最优子结构,也就是求最大、最少、最优之类的答案 + +# 动态规划关键点 --> 动态递推 +1、动态规划 和 递归或者分支 没有根本上的区别(关键看有无最优子结构) +2、共性:找到重复子问题 +3、差异性:最优子结构、中途可以淘汰次优解 + +# 动态规划解题三步骤 +1、找重复性(分治) 或者 定义子问题 +2、定义状态数组 +3、DP方程 + + + + + diff --git a/Week 05/id_613/java/src/main/Leetcode198SolutionOne.java b/Week 05/id_613/java/src/main/Leetcode198SolutionOne.java new file mode 100644 index 000000000..088a8d36a --- /dev/null +++ b/Week 05/id_613/java/src/main/Leetcode198SolutionOne.java @@ -0,0 +1,44 @@ +/** + * 打家劫舍 + * + * 1、定义子问题:定义f(i)为从0 - i间房屋偷盗能获得的最大金额,并且第i间房屋要偷 + * 2、状态数组:f(i) + * 3、DP方程:f(i) = max(f(i-1), f(i-2) + num[i]) + * 初始值:f(-1) == 0,f(0) == nums[0] + * + * 时间复杂度O(n),空间复杂度O(n) + * + * 执行用时 : * 0 ms * , 在所有 java 提交中击败了 * 100.00% * 的用户 + * 内存消耗 : * 33.9 MB * , 在所有 java 提交中击败了 * 90.50% * 的用户 + */ +class Leetcode198SolutionOne { + public int rob(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + + if (nums.length == 1) { + return nums[0]; + } + + int[] dp = nums.clone(); + int maxValue = 0; + for (int i = 1; i < nums.length; i++) { + if (i - 2 == -1) { + dp[i] = Math.max(dp[i - 1], nums[i]); // 0 + nums[i] --> nums[i] + } else { + dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]); + } + maxValue = Math.max(dp[i], maxValue); + } + + //System.out.println(Arrays.toString(dp)); + return maxValue; + } + + public static void main(String[] args) { + int[] nums = new int[]{1, 2, 3, 1}; + Leetcode198SolutionOne solution = new Leetcode198SolutionOne(); + System.out.println(solution.rob(nums)); + } +} diff --git a/Week 05/id_613/java/src/main/Leetcode213SolutionOne.java b/Week 05/id_613/java/src/main/Leetcode213SolutionOne.java new file mode 100644 index 000000000..8ee937c7b --- /dev/null +++ b/Week 05/id_613/java/src/main/Leetcode213SolutionOne.java @@ -0,0 +1,47 @@ +import java.util.Arrays; + +/** + * 打家劫舍II + * + * 时间复杂度O(n),空间复杂度O(n) + */ +class Leetcode213SolutionOne { + public int rob(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + + if (nums.length == 1) { + return nums[0]; + } + + return Math.max(_rob(Arrays.copyOfRange(nums, 1, nums.length)), + _rob(Arrays.copyOfRange(nums, 0, nums.length - 1))); + } + + public int _rob(int[] nums) { + if (nums.length == 1) { + return nums[0]; + } + + int[] dp = nums.clone(); + int maxValue = 0; + for (int i = 1; i < nums.length; i++) { + if (i - 2 == -1) { + dp[i] = Math.max(dp[i - 1], nums[i]); // 0 + nums[i] --> nums[i] + } else { + dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]); + } + maxValue = Math.max(dp[i], maxValue); + } + + //System.out.println(Arrays.toString(dp)); + return maxValue; + } + + public static void main(String[] args) { + int[] nums = new int[]{1, 2, 3, 1}; + Leetcode213SolutionOne solution = new Leetcode213SolutionOne(); + System.out.println(solution.rob(nums)); + } +} diff --git a/Week 05/id_613/java/src/test/Leetcode198Test.java b/Week 05/id_613/java/src/test/Leetcode198Test.java new file mode 100644 index 000000000..cb59c1c53 --- /dev/null +++ b/Week 05/id_613/java/src/test/Leetcode198Test.java @@ -0,0 +1,22 @@ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * + */ +public class Leetcode198Test { + @Test + public void testSolutionOne1() { + int nums[] = new int[]{1, 2, 3, 1}; + Leetcode198SolutionOne solution = new Leetcode198SolutionOne(); + assertEquals(solution.rob(nums), 4); + } + + @Test + public void testSolutionOne2() { + int nums[] = new int[]{2, 1, 1, 2}; + Leetcode198SolutionOne solution = new Leetcode198SolutionOne(); + assertEquals(solution.rob(nums), 4); + } +} + diff --git a/Week 05/id_613/java/src/test/Leetcode213Test.java b/Week 05/id_613/java/src/test/Leetcode213Test.java new file mode 100644 index 000000000..3ea7f1185 --- /dev/null +++ b/Week 05/id_613/java/src/test/Leetcode213Test.java @@ -0,0 +1,37 @@ +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * + */ +public class Leetcode213Test { + @Test + public void testSolutionOne1() { + int nums[] = new int[]{2, 3, 2}; + Leetcode213SolutionOne solution = new Leetcode213SolutionOne(); + assertEquals(solution.rob(nums), 3); + } + + @Test + public void testSolutionOne2() { + int nums[] = new int[]{1, 2, 3, 1}; + Leetcode213SolutionOne solution = new Leetcode213SolutionOne(); + assertEquals(solution.rob(nums), 4); + } + + @Test + public void testSolutionOne3() { + int nums[] = new int[]{1, 2, 1, 1}; + Leetcode213SolutionOne solution = new Leetcode213SolutionOne(); + assertEquals(solution.rob(nums), 3); + } + + @Test + public void testSolutionOne4() { + int nums[] = new int[]{1, 1}; + Leetcode213SolutionOne solution = new Leetcode213SolutionOne(); + assertEquals(solution.rob(nums), 1); + } +} + diff --git a/Week 05/id_618/LeetCode_64_618.java b/Week 05/id_618/LeetCode_64_618.java new file mode 100644 index 000000000..4deda1dca --- /dev/null +++ b/Week 05/id_618/LeetCode_64_618.java @@ -0,0 +1,21 @@ +class Solution { + + public int minPathSum(int[][] grid) { + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (i == 0 && j == 0) { + continue; + } + if (i == 0) { + grid[i][j] = grid[i][j] + grid[i][j - 1]; + } else if (j == 0) { + grid[i][j] = grid[i][j] + grid[i - 1][j]; + } else { + grid[i][j] = grid[i][j] + Math.min(grid[i - 1][j], grid[i][j - 1]); + } + } + } + return grid[grid.length - 1][grid[0].length - 1]; + } + +} \ No newline at end of file diff --git a/Week 05/id_618/LeetCode_72_618.java b/Week 05/id_618/LeetCode_72_618.java new file mode 100644 index 000000000..c7a9bfb3c --- /dev/null +++ b/Week 05/id_618/LeetCode_72_618.java @@ -0,0 +1,41 @@ +class Solution { + public int minDistance(String word1, String word2) { + int n = word1 != null ? word1.length() : 0; + int m = word2 != null ? word2.length() : 0; + + if (n == 0) { + return m; + } + + if (m == 0) { + return n; + } + + int[][] d = new int[n + 1][m + 1]; + + for (int i = 1; i < n + 1; i++) { + d[i][0] = i; + } + + for (int j = 1; j < m + 1; j++) { + d[0][j] = j; + } + + for (int i = 1; i < n + 1; i++) { + for (int j = 1; j < m + 1; j++) { + int left = d[i - 1][j] + 1; + int down = d[i][j - 1] + 1; + int leftDown = d[i - 1][j - 1] + 1; + + if (word1.charAt(i - 1) == word2.charAt(j - 1)) { + leftDown--; + } + + d[i][j] = Math.min(left, Math.min(down, leftDown)); + + } + } + + return d[n][m]; + } +} \ No newline at end of file diff --git a/Week 05/id_628/LeetCode_62_628.java b/Week 05/id_628/LeetCode_62_628.java new file mode 100644 index 000000000..e8347acf0 --- /dev/null +++ b/Week 05/id_628/LeetCode_62_628.java @@ -0,0 +1,61 @@ +//һλһ m x n Ͻ ʼͼбΪStart +// +// ÿֻ»ƶһͼﵽ½ǣͼбΪFinish +// +// ܹжͬ· +// +// +// +// 磬ͼһ7 x 3 жٿܵ· +// +// ˵m n ֵ 100 +// +// ʾ 1: +// +// : m = 3, n = 2 +//: 3 +//: +//Ͻǿʼܹ 3 ·Ե½ǡ +//1. -> -> +//2. -> -> +//3. -> -> +// +// +// ʾ 2: +// +// : m = 7, n = 3 +//: 28 +// Related Topics ̬滮 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class LeetCode_62_628 { + //߽ + //dp + //ʼdp + //״̬תƷ + public int uniquePaths(int m, int n) { + //߽ + if(m == 0 || n == 0)return -1; + //dp + int[][] dp = new int[m][n]; + //ʼdp + dp[m-1][n-1] = 1; + for (int i = m-2; i >= 0 ; i--) { + dp[i][n-1] = 1; + } + for (int i = n-2; i >= 0 ; i--) { + dp[m-1][i] = 1; + } + //״̬תƷ + for (int i = m-2; i >= 0 ; i--) { + for (int j = n-2; j >= 0 ; j--) { + dp[i][j] = dp[i+1][j] + dp[i][j+1]; + } + } + return dp[0][0]; + + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_628/LeetCode_63_628.java b/Week 05/id_628/LeetCode_63_628.java new file mode 100644 index 000000000..4bc1d8513 --- /dev/null +++ b/Week 05/id_628/LeetCode_63_628.java @@ -0,0 +1,67 @@ +//一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 +// +// 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 +// +// 现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径? +// +// +// +// 网格中的障碍物和空位置分别用 1 和 0 来表示。 +// +// 说明:m 和 n 的值均不超过 100。 +// +// 示例 1: +// +// 输入: +//[ +//  [0,0,0], +//  [0,1,0], +//  [0,0,0] +//] +//输出: 2 +//解释: +//3x3 网格的正中间有一个障碍物。 +//从左上角到右下角一共有 2 条不同的路径: +//1. 向右 -> 向右 -> 向下 -> 向下 +//2. 向下 -> 向下 -> 向右 -> 向右 +// +// Related Topics 数组 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class LeetCode_63_628 { + public int uniquePathsWithObstacles(int[][] obstacleGrid) { + //边界条件 + int m = obstacleGrid.length; + int n = obstacleGrid[0].length; + if(m == 0 || n == 0)return -1; + //定义dp数组 + int[][] dp = new int[m][n]; + //初始化dp数组 + + dp[m-1][n-1] = obstacleGrid[m-1][n-1] != 0 ? 0 : 1; + //--若不是障碍物则标记为1,若是障碍物则标记为0 + for (int i = m-2; i >= 0 ; i--) { + dp[i][n-1] = obstacleGrid[i][n-1] != 0 ? 0 : dp[i+1][n-1]; + } + for (int i = n-2; i >= 0 ; i--) { + + dp[m-1][i] = obstacleGrid[m-1][i] != 0 ? 0 : dp[m-1][i+1]; + } + //状态转移方程 + for (int i = m-2; i >= 0 ; i--) { + for (int j = n-2; j >= 0 ; j--) { + if(obstacleGrid[i][j] != 0){ + dp[i][j] = 0; + }else{ + dp[i][j] = dp[i+1][j] + dp[i][j+1]; + } + + } + } + return dp[0][0]; + + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_628/LeetCode_64_628.java b/Week 05/id_628/LeetCode_64_628.java new file mode 100644 index 000000000..63f9017f9 --- /dev/null +++ b/Week 05/id_628/LeetCode_64_628.java @@ -0,0 +1,47 @@ +//给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 +// +// 说明:每次只能向下或者向右移动一步。 +// +// 示例: +// +// 输入: +//[ +//  [1,3,1], +// [1,5,1], +// [4,2,1] +//] +//输出: 7 +//解释: 因为路径 1→3→1→1→1 的总和最小。 +// +// Related Topics 数组 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class LeetCode_64_628 { + public int minPathSum(int[][] grid) { + if(grid == null || grid.length == 0)return -1; + //定义行列,dp数组 + int m = grid[0].length; + int n = grid.length; + int[][] dp = new int[n][m]; + //初始化dp数组 + dp[n-1][m-1]=grid[n-1][m-1]; + //--边界初始化 + for (int i = m-2; i >= 0 ; i--) { + dp[n-1][i] = dp[n-1][i+1] + grid[n-1][i]; + } + for (int i = n-2; i >= 0 ; i--) { + dp[i][m-1] = dp[i+1][m-1] + grid[i][m-1]; + } + //dp + for (int i = n-2; i >=0 ; i--) { + for (int j = m-2; j >= 0 ; j--) { + dp[i][j] = Math.min(dp[i+1][j],dp[i][j+1]) + grid[i][j]; + } + } + + return dp[0][0]; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_628/LeetCode_91_628.java b/Week 05/id_628/LeetCode_91_628.java new file mode 100644 index 000000000..7edc61709 --- /dev/null +++ b/Week 05/id_628/LeetCode_91_628.java @@ -0,0 +1,58 @@ +//一条包含字母 A-Z 的消息通过以下方式进行了编码: +// +// 'A' -> 1 +//'B' -> 2 +//... +//'Z' -> 26 +// +// +// 给定一个只包含数字的非空字符串,请计算解码方法的总数。 +// +// 示例 1: +// +// 输入: "12" +//输出: 2 +//解释: 它可以解码为 "AB"(1 2)或者 "L"(12)。 +// +// +// 示例 2: +// +// 输入: "226" +//输出: 3 +//解释: 它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。 +// +// Related Topics 字符串 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class LeetCode_91_628 { + public int numDecodings(String s) { + //边界条件 + if(s == null || s.length()==0)return -1; + //定义dp数组--编码方法总数 + int n = s.length(); + int[] dp = new int[n+1]; + //初始化dp数组 + //--空串 + dp[0] = 1; + //--以0开头不支持编码 + dp[1] = (s.charAt(0) == '0') ? 0 : 1 ; + //状态转移方程 + for (int i = 2; i <= n; i++) { + int one = Integer.valueOf(s.substring(i-1,i)); + int two = Integer.valueOf(s.substring(i-2,i)); + if (one >= 1 && one <= 9){ + dp[i] = dp[i] + dp[i-1]; + } + if(two >=10 && two <= 26){ + dp[i] = dp[i] + dp[i-2]; + } + + } + + return dp[s.length()]; + + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 05/id_628/NOTE.md b/Week 05/id_628/NOTE.md index a6321d6e2..75be330d1 100644 --- a/Week 05/id_628/NOTE.md +++ b/Week 05/id_628/NOTE.md @@ -2,3 +2,49 @@ +# 第五周总结 + +## 【本周知识点】 + +**动态规划** + +本周只进行了一个专题总结,确实目前普遍感觉最难搞的一节。 + +从dp模板开始,学习了思路的产生,以及一维、二维dp的解法、dp数组的构建等技巧。 + + + +## 【本周学习总结】 + +1、动态规划和递归或分治没有根本上的区别(关键看有无最优子结构) +2、共性:找到重复子问题 +3、差异性:最优子结构。中途可以淘汰次优解。 + +## DP 关键点 + +1、寻找最优子结构 + +```java +opt[n] = best_of(opt[n - 1], opt[n - 2], ...) +``` + +2、建立dp数组,存储中间状态 + +```java +opt[i] +``` + +3、递推公式(状态转移方程/DP方程) + +```java +一维(斐波那契数列): opt[I] = opt[n - 1] + opt[n - 2] +二维(最小路径和):opt[i, j] = opt[i+1][j] + opt[I][j + 1] +``` + +## MIT DP "五步法" + +1、定义子问题,也即是进行分治(复杂问题 -> 简单子问题); split +2、猜(part of solution),递归方程怎么递推的; +3、把子问题的解合并起来,merge; +4、递归 & 记忆化 or 自底向上解决(DP 表格建立起来) +5、解决原问题 \ No newline at end of file diff --git a/Week 05/id_633/LeetCode_221_633.java b/Week 05/id_633/LeetCode_221_633.java new file mode 100644 index 000000000..2858f6e4c --- /dev/null +++ b/Week 05/id_633/LeetCode_221_633.java @@ -0,0 +1,18 @@ +package practice_one; + +public class LeetCode_221_633 { + public int maximalSquare(char[][] matrix) { + int rows = matrix.length, cols = rows > 0 ? matrix[0].length : 0; + int[][] dp = new int[rows + 1][cols + 1]; + int maxsqlen = 0; + for (int i = 1; i <= rows; i++) { + for (int j = 1; j <= cols; j++) { + if (matrix[i-1][j-1] == '1'){ + dp[i][j] = Math.min(Math.min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; + maxsqlen = Math.max(maxsqlen, dp[i][j]); + } + } + } + return maxsqlen * maxsqlen; + } +} diff --git a/Week 05/id_633/LeetCode_64_633.java b/Week 05/id_633/LeetCode_64_633.java new file mode 100644 index 000000000..388e94ad2 --- /dev/null +++ b/Week 05/id_633/LeetCode_64_633.java @@ -0,0 +1,18 @@ +package practice_one; + +public class LeetCode_64_633 { + public int minPathSum(int[][] grid) { + for (int i = grid.length - 1; i >= 0; i--) { + for (int j = grid[0].length - 1; j >= 0; j--) { + if(i == grid.length - 1 && j != grid[0].length - 1) + grid[i][j] = grid[i][j] + grid[i][j + 1]; + else if(j == grid[0].length - 1 && i != grid.length - 1) + grid[i][j] = grid[i][j] + grid[i + 1][j]; + else if(j != grid[0].length - 1 && i != grid.length - 1) + grid[i][j] = grid[i][j] + Math.min(grid[i + 1][j],grid[i][j + 1]); + } + } + return grid[0][0]; + } + +} diff --git a/Week 05/id_638/LeetCode_64_638.java b/Week 05/id_638/LeetCode_64_638.java new file mode 100644 index 000000000..56a5c1a1a --- /dev/null +++ b/Week 05/id_638/LeetCode_64_638.java @@ -0,0 +1,31 @@ +package test1.week5; + +public class LeetCode_64_638 { + + /** + * 最小路径和 + * @param grid + * @return + */ + public int minPathSum(int[][] grid) { + //dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1]) + grid[i][j]; + int [][] dp = grid; + int row = grid.length; + int col = grid[0].length; + for (int i = 0;i < row;i++){ + for (int j = 0;j < col;j++){ + if(i == 0 && j != 0){ + dp[i][j] = dp[0][j] + grid[i][j-1]; + }else if (i != 0 && j == 0){ + dp[i][j] = dp[i][0] + grid[i-1][0]; + }else if (i != 0 && j != 0){ + dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1])+ grid[i][j]; + }else { + dp[i][j] = grid[0][0]; + } + + } + } + return dp[row -1 ][col - 1]; + } +} diff --git a/Week 05/id_638/LeetCode_91_638.java b/Week 05/id_638/LeetCode_91_638.java new file mode 100644 index 000000000..d1676e671 --- /dev/null +++ b/Week 05/id_638/LeetCode_91_638.java @@ -0,0 +1,27 @@ +package test1.week5; + +public class LeetCode_91_638 { + + /** + * 解码方法 + * @param s + * @return + */ + public static int numDecodings(String s) { + //dp[i] dp[i+1] <= 26 + //dp[-1] = dp[0] = 1 + char[] dp = s.toCharArray(); + if (dp[0] == '0') return 0; + int pre = 1, curr = 1; + for (int i = 1; i < dp.length;i++) { + int tmp = curr; + if (dp[i] == '0') + if (dp[i - 1] == '1' || dp[i - 1] == '2') curr = pre; + else return 0; + else if (dp[i - 1] == '1' || (dp[i - 1] == '2' && dp[i] >= '1' && dp[i] <= '6')) + curr = curr + pre; + pre = tmp; + } + return curr; + } +} diff --git a/Week 05/id_643/LeetCode_32_643.java b/Week 05/id_643/LeetCode_32_643.java new file mode 100644 index 000000000..ad45521f1 --- /dev/null +++ b/Week 05/id_643/LeetCode_32_643.java @@ -0,0 +1,27 @@ +public class Solution { + public boolean isValid(String s) { + Stack stack = new Stack(); + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '(') { + stack.push('('); + } else if (!stack.empty() && stack.peek() == '(') { + stack.pop(); + } else { + return false; + } + } + return stack.empty(); + } + public int longestValidParentheses(String s) { + int maxlen = 0; + for (int i = 0; i < s.length(); i++) { + for (int j = i + 2; j <= s.length(); j+=2) { + if (isValid(s.substring(i, j))) { + maxlen = Math.max(maxlen, j - i); + } + } + } + return maxlen; + } +} + diff --git a/Week 05/id_643/LeetCode_64_643.java b/Week 05/id_643/LeetCode_64_643.java new file mode 100644 index 000000000..e0cffe1bc --- /dev/null +++ b/Week 05/id_643/LeetCode_64_643.java @@ -0,0 +1,14 @@ +class Solution { + public int minPathSum(int[][] grid) { + for(int i = 0; i < grid.length; i++) { + for(int j = 0; j < grid[0].length; j++) { + if(i == 0 && j == 0) continue; + else if(i == 0) grid[i][j] = grid[i][j - 1] + grid[i][j]; + else if(j == 0) grid[i][j] = grid[i - 1][j] + grid[i][j]; + else grid[i][j] = Math.min(grid[i - 1][j], grid[i][j - 1]) + grid[i][j]; + } + } + return grid[grid.length - 1][grid[0].length - 1]; + } +} + diff --git a/Week 05/id_648/LeetCode_32_648.java b/Week 05/id_648/LeetCode_32_648.java new file mode 100644 index 000000000..8e9676bd6 --- /dev/null +++ b/Week 05/id_648/LeetCode_32_648.java @@ -0,0 +1,57 @@ +import java.util.HashMap; +import java.util.Stack; + +/** + * @Date 2019/11/17 + **/ +public class LeetCode_32_648 { + + public boolean isValid(String s){ + Stack stack = new Stack(); + for(int i=0;i=2?dp[i-1]+2:0)+2; + }else if(i-dp[i-1]>0 && s.charAt(i-dp[i-1]-1)=='(') { + dp[i] = dp[i-1] +((i-dp[i-1])>2 ? dp[i-dp[i-1]-2]:0)+2; + } + maxlen = Math.max(maxlen,dp[i]); + } + } + return maxlen; + } + + public static void main(String[] args) { + LeetCode_32_648 leetCode_32_648 = new LeetCode_32_648(); + String s=")()())"; + System.out.println(leetCode_32_648.longestValidParentheses2(s)); + } +} diff --git a/Week 05/id_648/LeetCode_34_648.java b/Week 05/id_648/LeetCode_34_648.java new file mode 100644 index 000000000..cce62ba3c --- /dev/null +++ b/Week 05/id_648/LeetCode_34_648.java @@ -0,0 +1,44 @@ +/** + * @Date 2019/11/17 + **/ +public class LeetCode_34_648 { + + public int minPathSum(int[][] grid) { + int minPath = 0; + int[] dp = new int[grid.length+1]; + for(int i=grid.length-1;i>=0;i--){ + for(int j=grid[i].length-1;j>=0;j--){ + if(i==grid.length-1&&j!=grid[i].length-1){ + dp[j] =grid[i][j]+dp[j+1]; + }else if(i!=grid.length-1&&j==grid[i].length-1){ + dp[j] =grid[i][j]+dp[j]; + }else if(i!=grid.length-1&&j!=grid[i].length-1){ + dp[j] =grid[i][j]+Math.min(dp[j+1],dp[j]); + }else { + dp[j] = grid[i][j]; + } + } + } + return dp[0]; + } + public int minPathSum2(int[][] grid) { + return calculate(grid,0,0); + } + + private int calculate(int[][] grid,int i,int j){ + if(i==grid.length||j==grid[0].length){return Integer.MAX_VALUE;} + if(i==grid.length-1&&j==grid[0].length-1){ + return grid[i][j]; + } + return grid[i][j]+Math.min(calculate(grid,i+1,j),calculate(grid,i,j+1)); + } + + public static void main(String[] args) { + LeetCode_34_648 leetCode_34_648 = new LeetCode_34_648(); + int[][] grid = new int[3][3]; + grid[0]=new int[]{1,3,1}; + grid[1]=new int[]{1,5,1}; + grid[2]=new int[]{4,2,1}; + System.out.println(leetCode_34_648.minPathSum2(grid)); + } +} diff --git a/Week 05/id_648/LeetCode_72_648.java b/Week 05/id_648/LeetCode_72_648.java new file mode 100644 index 000000000..bb2b4cdf6 --- /dev/null +++ b/Week 05/id_648/LeetCode_72_648.java @@ -0,0 +1,31 @@ +/** + * @Date 2019/11/17 + **/ +public class LeetCode_72_648 { + public int minDistance(String word1, String word2) { + int[][] dp = new int[word1.length()+1][word2.length()+1]; + for(int i=1;i<=word1.length();i++){ + dp[i][0]=i; + } + for(int i=1;i<=word2.length();i++){ + dp[0][i]=i; + } + for(int i=1;i<=word1.length();i++){ + for(int j=1;j<=word2.length();j++){ + if(word1.charAt(i-1)==word2.charAt(j-1)){ + dp[i][j] = dp[i-1][j-1]; + }else { + dp[i][j] = Math.min(Math.min(dp[i-1][j-1]+1,dp[i-1][j]+1),dp[i][j-1]+1); + } + } + } + return dp[word1.length()][word2.length()]; + } + + public static void main(String[] args) { + LeetCode_72_648 leetCode_72_648 = new LeetCode_72_648(); + String word1 = "horse"; + String word2 = "ros"; + System.out.println(leetCode_72_648.minDistance(word1,word2)); + } +} diff --git a/Week 05/id_653/LeetCode_221_653.java b/Week 05/id_653/LeetCode_221_653.java new file mode 100644 index 000000000..67dd5a6f5 --- /dev/null +++ b/Week 05/id_653/LeetCode_221_653.java @@ -0,0 +1,30 @@ +class Solution { + public int maximalSquare(char[][] matrix) { + + int rows = matrix.length; + if (matrix == null) { + return 0; + } + if (rows == 0) { + return 0; + } + int column = matrix[0].length; + int dp[][] = new int[rows][column]; + int max = 0; + for (int i = 0; i < rows; i++) { + for (int j = 0; j < column; j++) { + if (i == 0 || j == 0) { + dp[i][j] = matrix[i][j] == '1' ? 1 : 0; + } else { + if(matrix[i][j]=='1'){ + dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1])+1; + } } + + max = Math.max(max, dp[i][j]); + + } + } + + return max * max; + } +} \ No newline at end of file diff --git a/Week 05/id_653/LeetCode_32_653.java b/Week 05/id_653/LeetCode_32_653.java new file mode 100644 index 000000000..e6e6ea86d --- /dev/null +++ b/Week 05/id_653/LeetCode_32_653.java @@ -0,0 +1,27 @@ +class Solution { + public int longestValidParentheses(String s) { + int maxans = 0; + int dp[] = new int[s.length()]; + // 状态转移方程 + /** + * case 右括号 )结尾为合法字符 + * 右括号前面为( 那么 dp[i] = dp[i-1]+2 + * 右括号前面一个为 ) + * 那么要判断出前一个有效顺序的长度,以及有效长度前面一个是否为( 这样才满足合法条件 + * 这样case 情况下 dp[i] = dp[i-1]+(i-d(i-1)-1>0?:dp[i-d(i-1)-2]:0)+2; + * 依次递推 + */ + for (int i = 1; i < dp.length; i++) { + char c = s.charAt(i); + if (c == ')') { + if (s.charAt(i - 1) == '(') { + dp[i] = 2 + (i - 2 > 0 ? dp[i - 2] : 0); + } else if (i - dp[i - 1] - 1 >= 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + (i - dp[i - 1] - 2 > 0 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + maxans = Math.max(maxans, dp[i]); + } + } + return maxans; + } +} \ No newline at end of file diff --git a/Week 05/id_658/LeetCode_32_658.java b/Week 05/id_658/LeetCode_32_658.java new file mode 100644 index 000000000..6a4849a69 --- /dev/null +++ b/Week 05/id_658/LeetCode_32_658.java @@ -0,0 +1,28 @@ +/* + * @lc app=leetcode.cn id=32 lang=java + * + * [32] 最长有效括号 + */ + +// @lc code=start +class Solution { + public int longestValidParentheses(String s) { + int maxLength = 0; + Stack stack = new Stack<>(); + stack.push(-1); + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '(') { + stack.push(i); + } else { + stack.pop(); + if (stack.empty()) { + stack.push(i); + } else { + maxLength = Math.max(maxLength, i - stack.peek()); + } + } + } + return maxLength; + } +} +// @lc code=end diff --git a/Week 05/id_658/LeetCode_91_658.java b/Week 05/id_658/LeetCode_91_658.java new file mode 100644 index 000000000..44e4e96af --- /dev/null +++ b/Week 05/id_658/LeetCode_91_658.java @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode.cn id=91 lang=java + * + * [91] 解码方法 + */ + +// @lc code=start +class Solution { + public int numDecodings(String s) { + if (s.charAt(0) == '0') + return 0; + + int[] dp = new int[s.length() + 1]; + dp[0] = dp[1] = 1; + + for (int i = 2; i <= s.length(); i++) { + if (s.charAt(i - 1) != '0') { + dp[i] += dp[i - 1]; + } + if ((s.charAt(i - 2) == '1') || (s.charAt(i - 2) == '2' && s.charAt(i - 1) <= '6')) { + dp[i] += dp[i - 2]; + } + } + return dp[s.length()]; + } +} +// @lc code=end diff --git a/Week 05/id_658/NOTE.md b/Week 05/id_658/NOTE.md index a6321d6e2..5b983f72f 100644 --- a/Week 05/id_658/NOTE.md +++ b/Week 05/id_658/NOTE.md @@ -1,4 +1,37 @@ -# NOTE +# 第五周学习总结 - +## 动态规划 +- 本质上将一个复杂的问题分解成各种简单的子问题,同时找问题的重复性 +- 与递归、分治更多是一些细节上的不同,看有无最优子结构 +- 分治+最优子结构 +- 因为要找最优子结构(最大值、最优解、最少方式等等)所以可能需要记录每一步的最优值用来推导全局的最优值,所以存在缓存(状态的存储数组)以及每一步淘汰掉次优的状态 + +### 解析 + +1. 先进行递归分治,然后进行记忆化搜索再转化为自底向上的循环 +2. 自底向上递推 + + ```java + a[0] = 0, a[i] = 1; + for (int i = 2; i <= n; i++) { + a[i] = a[i - 1] + a[i - 2] + } + ``` + +### 关键点 + +- 最优子结构 `opt[n] = best_of(opt[n - 1], opt[n - 2], ...)` +- 储存中间状态 `opt[i]` +- 递推公式(状态转移方程或者 DP 方程) + - Fib: `opt[i] = opt[n - 1] + opt[n - 2]` + - 二维路径: `opt[i][j] = opt[i + 1][j] + opt[i][j + 1]` 切判断 `a[i][j]` 是否为空地 + +## 小结 + +- 人肉递归低效且累 +- 找到最近最简方法,将其拆解成可重复解决的问题 +- 数学归纳法思维(抵制人肉递归的诱惑) +- 本质还是寻找重复性 +- 打破自己的思维惯性,形成机器思维 +- 理解复杂逻辑的关键 diff --git a/Week 05/id_668/leetcode_32_668.py b/Week 05/id_668/leetcode_32_668.py new file mode 100644 index 000000000..c082515cc --- /dev/null +++ b/Week 05/id_668/leetcode_32_668.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: longest_valid_parentheses.py + @time: 2019/11/16 14:17 +""" + + +class Solution(object): + """ + 给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。 + + 示例 1: + 输入: "(()" + 输出: 2 + 解释: 最长有效括号子串为 "()" + + 示例 2: + 输入: ")()())" + 输出: 4 + 解释: 最长有效括号子串为 "()()" + """ + + def longest_valid_parentheses(self, s): + """ + :type s: str + :rtype: int + + 暴力破解:获取所有的括号组合,然后找出最长有效括号的长度 + """ + lvp = 0 + + def len_str_valid(sub): + """ + 求出合格的括号组号的长度 + """ + stack, d = [], {')': '('} + + for e in sub: + if e in d: + if stack: + if stack.pop() != d[e]: + return 0 + else: + return 0 + else: + stack.append(e) + + return len(sub) if stack == [] else 0 + + for i in range(len(s)): + if s[i] == ')': + continue + + for j in range(i, len(s)): + if s[j] == '(' or len(s[i:j + 1]) % 2 != 0: + continue + + lvp = max(len_str_valid(s[i:j + 1]), lvp) + + return lvp + + def longest_valid_parentheses2(self, s): + """ + :type s: str + :rtype: int + + DP步骤: + 1. 重复性(子问题) + 2. dp动态数组定义 + 3. dp方程 + """ + + dp, max_val = [0 for _ in range(len(s))], 0 + + for i in range(1, len(s)): + if s[i] == ')': + if s[i - 1] == '(': + # ...()这种结构,可以推导出dp[i] = dp[i-2] + 2 + dp[i] = (dp[i - 2] if i >= 2 else 0) + 2 + elif i - dp[i - 1] > 0 and s[i - dp[i - 1] - 1] == '(': + # ...))这种结构,可以推导出dp[i]=dp[i−1]+dp[i−dp[i−1]−2]+2 + dp[i] = dp[i - 1] + (dp[i - dp[i - 1] - 2] if i - dp[i - 1] >= 2 else 0) + 2 + + max_val = max(max_val, dp[i]) + + return max_val + + def longest_valid_parentheses3(self, s): + """ + :type s: str + :rtype: int + + 遇到洋葱结构的问题,可以优先想到使用栈来解决。 + """ + + max_val, stack = 0, [-1] + + for i in range(len(s)): + if s[i] == '(': + stack.append(i) + else: + stack.pop() + + if not stack: + # stack为空,说明操作的是一个有效的括号组合 + stack.append(i) + else: + max_val = max(max_val, i - stack[-1]) + + return max_val + diff --git a/Week 05/id_668/leetcode_64_668.py b/Week 05/id_668/leetcode_64_668.py new file mode 100644 index 000000000..b33967bb3 --- /dev/null +++ b/Week 05/id_668/leetcode_64_668.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: miminum_path_sum.py + @time: 2019/11/17 08:36 +""" + + +class Solution(object): + """ + 给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 + 说明:每次只能向下或者向右移动一步。 + + 示例: + 输入: + [ +   [1,3,1], + [1,5,1], + [4,2,1] + ] + 输出: 7 + 解释: 因为路径 1→3→1→1→1 的总和最小。 + """ + + def min_path_sum(self, grid): + """ + :type grid: List[List[int]] + :rtype: int + + DP步骤: + 1. 重复性(分治):自底向上思考,min_sum(i, j) = min(min_sum(i-1, j), min_sum(i, j-1)) + grid(i, j) + 2. 定义dp数组,dp=grid + 3. dp方程:dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + dp[i][j] + """ + dp = grid + + for m in range(1, len(dp[0])): # 对矩形框的第一行依次求和 + dp[0][m] += dp[0][m - 1] + + for n in range(1, len(dp)): # 对矩形框的最左边一列依次求和 + dp[n][0] += dp[n - 1][0] + + for i in range(1, len(dp)): + for j in range(1, len(dp[0])): + dp[i][j] += min(dp[i - 1][j], dp[i][j - 1]) + + return dp[-1][-1] + + def min_path_sum2(self, grid): + """ + :type grid: List[List[int]] + :rtype: int + + 递归,自行向下的思考,min_sum(i, j) = min(min_sum(i+1, j), min_sum(i, j+1)) + grid(i, j) + """ + import sys + + def helper(i, j): + if i == len(grid) or j == len(grid[0]): # 表示取值已经超出矩阵边框,因为求最小值,所以这里与最大值进行比较,即可得出原位置的值 + return sys.maxint + + if i == len(grid) - 1 and j == len(grid[0]) - 1: + return grid[i][j] + + return grid[i][j] + min(helper(i + 1, j), helper(i, j + 1)) + + return helper(0, 0) + diff --git a/Week 05/id_668/leetcode_72_668.py b/Week 05/id_668/leetcode_72_668.py new file mode 100644 index 000000000..7b343be83 --- /dev/null +++ b/Week 05/id_668/leetcode_72_668.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: edit_distance.py + @time: 2019/11/17 18:21 +""" + + +class Solution(object): + """ + 给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。 + 你可以对一个单词进行如下三种操作: + 插入一个字符 + 删除一个字符 + 替换一个字符 + + 示例 1: + 输入: word1 = "horse", word2 = "ros" + 输出: 3 + 解释: + horse -> rorse (将 'h' 替换为 'r') + rorse -> rose (删除 'r') + rose -> ros (删除 'e') + + 示例 2: + 输入: word1 = "intention", word2 = "execution" + 输出: 5 + 解释: + intention -> inention (删除 't') + inention -> enention (将 'i' 替换为 'e') + enention -> exention (将 'n' 替换为 'x') + exention -> exection (将 'n' 替换为 'c') + exection -> execution (插入 'u') + """ + + def min_distance(self, word1, word2): + """ + :type word1: str + :type word2: str + :rtype: int + + 国际站高票答案 + """ + m, n = len(word1), len(word2) + dp = [list(range(n + 1))] + [[r + 1] + [0] * n for r in range(m)] + + print dp + + for i in range(m): + for j in range(n): + dp[i + 1][j + 1] = dp[i][j] if word1[i] == word2[j] else min(dp[i][j], dp[i + 1][j], dp[i][j + 1]) + 1 + return dp[m][n] + diff --git a/Week 05/id_673/langest-valid-parentheses.cpp b/Week 05/id_673/langest-valid-parentheses.cpp new file mode 100644 index 000000000..94bf3e213 --- /dev/null +++ b/Week 05/id_673/langest-valid-parentheses.cpp @@ -0,0 +1,33 @@ +class Solution { +public: + int longestValidParentheses(string s) { + int res = 0; + int left = 0; + int mark = 0; + for (int i = 0; i < s.size(); ++i) { + int prev_mark = mark; + mark = max(0, mark + ((s[i] == '(') ? 1 : -1)); + if (mark == 0) { + if (prev_mark > 0) { + res = max(i - left + 1, res); + } else { + left = i + 1; + } + } + } + mark = 0; + int right = s.size() - 1; + for (int i = s.size() - 1; i >= 0; --i) { + int prev_mark = mark; + mark = max(0, mark + ((s[i] == ')') ? 1 : -1)); + if (mark == 0) { + if (prev_mark > 0) { + res = max(right - i + 1, res); + } else { + right = i - 1; + } + } + } + return res; + } +}; diff --git a/Week 05/id_673/minimum-window-substring.cpp b/Week 05/id_673/minimum-window-substring.cpp new file mode 100644 index 000000000..fe4773015 --- /dev/null +++ b/Week 05/id_673/minimum-window-substring.cpp @@ -0,0 +1,62 @@ +class Solution { +private: + //用于检查某一字符串是否包含t中的所有字符 + bool is_window_ok(int map_s[],int map_t[],vector &vec_t){ + for(int i=0;i vec_t; //记录t中有哪些字符 + for(int i=0;i0){ + vec_t.push_back(i); //遍历,将字符串t中出现的字符存储到vec_t中 + } + } + //判断s中是否存在这样的子串 + if(!is_window_ok(map_s,map_t,vec_t)){ + return ""; //如果不存在,返回"" + } + //如果存在,往下执行 + int begin=0; //最小窗口起始指针 + string result=s; //结果字符串 + int map_window[MAX]={0}; //记录窗口中的字符个数 + for(int i=0;imap_t[begin_ch]){//如果当前begin所指字符在当前窗口中的数量大于t中的数量 + --map_window[begin_ch]; //更新当前窗口的字符数量 + ++begin; + } + else{ + break; //如果没有上面的两种情况,结束此次begin的判断 + } + } + //检查当前窗口字符串是否符合要求 + if(is_window_ok(map_window,map_t,vec_t)){ + int new_window_len=i-begin+1; + if(new_window_len= 2 ? dp[i - 2] : 0) + 2; + } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + (i - dp[i - 1] >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + } + max = Math.max(dp[i], max); + } + return max; + } + + public static void main(String[] args) { + Assert.assertEquals(2, new LeetCode_32_693().longestValidParentheses("()(()")); + Assert.assertEquals(4, new LeetCode_32_693().longestValidParentheses(")()())")); + Assert.assertEquals(2, new LeetCode_32_693().longestValidParentheses("())")); + Assert.assertEquals(2, new LeetCode_32_693().longestValidParentheses("(()")); + } +} diff --git a/Week 05/id_693/LeetCode_621_693.java b/Week 05/id_693/LeetCode_621_693.java new file mode 100644 index 000000000..60218b70d --- /dev/null +++ b/Week 05/id_693/LeetCode_621_693.java @@ -0,0 +1,23 @@ +package id_693; + +import java.util.Arrays; + +/** + * @Desc 621. 任务调度器 https://leetcode-cn.com/problems/task-scheduler/ + * @Auther 李雷(KyLin) + * @Date 2019/11/17 + */ +public class LeetCode_621_693 { + //计数 + public int leastInterval(char[] tasks, int n) { + int[] dp = new int[26]; + for (char task : tasks) { + dp[task - 'A']++; + } + Arrays.sort(dp); + int res = (dp[25] - 1) * (n + 1); + int i = 25; + while (i >= 0 && dp[i--] == dp[25]) res++; + return Math.max(res, tasks.length); + } +} diff --git a/Week 05/id_693/LeetCode_647_693.java b/Week 05/id_693/LeetCode_647_693.java new file mode 100644 index 000000000..e95b178e7 --- /dev/null +++ b/Week 05/id_693/LeetCode_647_693.java @@ -0,0 +1,57 @@ +package id_693; + +/** + * @Desc 647. 回文子串 https://leetcode-cn.com/problems/palindromic-substrings/ + * @Auther 李雷(KyLin) + * @Date 2019/11/17 + */ +public class LeetCode_647_693 { + // 中心扩散 + public int countSubstrings2(String s) { + int len = s.length(); + if (len == 0) return 0; + int result = 0; + for (int i = 0; i < len; i++) { + //以自己为中心扩散 + result += countSubstrings(s, i, i); + //如果和下一个char相同,进行扩散 +// if (i + 1 < len && s.charAt(i) == s.charAt(i + 1)) {result += countSubstrings(s, i, i + 1);} + result += countSubstrings(s, i, i + 1); + } + return result; + } + + private int countSubstrings(String s, int i, int j) { + int count = 0; + while (i >= 0 && j < s.length() && s.charAt(i++) == s.charAt(j--)) { + count++; + } + return count; + } + + //dp方程 + /* 为true时计数 + 1、如果i==j 表示一个字符,默认为true + 2、如果s.charAt(i) == s.charAt(j)时 + a、i - j == 1 为true (代表2个字符一样) + b、dp[j + 1][i - 1] == true 那么说明之前的也是true,否则为false + */ + public int countSubstrings(String s) { + int len = s.length(); + if (len == 0) return 0; + int result = 0; + boolean[][] dp = new boolean[len][len]; + for (int i = 0; i < len; i++) { + for (int j = 0; j <= i; j++) + if (i == j || (s.charAt(i) == s.charAt(j) && (i - j == 1 || dp[j + 1][i - 1]))) { + dp[j][i] = true; + result++; + } + } + return result; + } + + public static void main(String[] args) { + new LeetCode_647_693().countSubstrings("aabbcc"); + } +} diff --git a/Week 05/id_693/LeetCode_64_693.java b/Week 05/id_693/LeetCode_64_693.java new file mode 100644 index 000000000..8a56a2f27 --- /dev/null +++ b/Week 05/id_693/LeetCode_64_693.java @@ -0,0 +1,39 @@ +package id_693; + +/** + * @Desc 64. 最小路径和 https://leetcode-cn.com/problems/minimum-path-sum/ + * @Auther 李雷(KyLin) + * @Date 2019/11/16 + */ +public class LeetCode_64_693 { + // 解题思路和路径问题、不同路径异曲同工 + /* + 1、分解子问题:f(i,j) = Math.min(f(i - 1,j), f(i,j - 1)) + f(i,j) + 2、状态数组: dp[i][j] + 3、dp方程: dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + a[i][j] + */ + public int minPathSum(int[][] grid) { + int[][] elements = grid.clone(); + if (elements.length == 0 || elements[0].length == 0) { + return 0; + } + int m = elements.length; + int n = elements[0].length; + for (int i = 1; i < n; i++) { + elements[0][i] += elements[0][i - 1]; + } + for (int i = 1; i < m; i++) { + elements[i][0] += elements[i - 1][0]; + } + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + elements[i][j] += Math.min(elements[i - 1][j], elements[i][j - 1]); + } + } + return elements[m - 1][n - 1]; + } + + public static void main(String[] args) { + new LeetCode_64_693().minPathSum(new int[][]{{1, 3, 1}, {1, 5, 1}, {4, 2, 1}}); + } +} diff --git a/Week 05/id_693/LeetCode_72_693.java b/Week 05/id_693/LeetCode_72_693.java new file mode 100644 index 000000000..6be751091 --- /dev/null +++ b/Week 05/id_693/LeetCode_72_693.java @@ -0,0 +1,62 @@ +package id_693; + +/** + * @Desc 72. 编辑距离 https://leetcode-cn.com/problems/edit-distance/ + * @Auther 李雷(KyLin) + * @Date 2019/11/15 + */ +public class LeetCode_72_693 { + //dp方程:类似1143. 最长公共子序列 + /* + 1、分解子问题:相同f(i) = f(i -1,j-1) ; + 不同f(i) = min(f(i-1,j), f(i,j-1), f(i -1,j-1)) + 1; + 2、定义状态数组:f[i][j] + 3、dp方程:相同dp[i][j] = dp[i-1][j-1] + 不同dp[i][j] = Math.min(dp[i - 1][j],dp[i][j-1],dp[i-1][j-1]) + 1 + + 注意 [i][0] = i 初始化 + [0][j] = j 初始化 + */ + public int minDistance(String word1, String word2) { + int m = word1.length(); + int n = word2.length(); + int[][] dp = new int[m + 1][n + 1]; + for (int i = 0; i <= m; i++) { + for (int j = 0; j <= n; j++) { + if (i == 0 || j == 0) { + //初始化base + dp[i][j] = i == 0 ? j : i; + continue; + } + if (word1.charAt(i - 1) == word2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1; + } + } + } + return dp[m][n]; + } + + public static void main(String[] args) { + assert new LeetCode_72_693().minDistance("horse", "ros") == 3; + assert new LeetCode_72_693().minDistance("intention", "execution") == 5; + assert new LeetCode_72_693().minDistance("intention", "nation") == 4; + } + + /*规律图 + N A T I O N + 0 1 2 3 4 5 6 + I 1 1 2 3 3 4 5 + N 2 1 2 3 4 4 4 + T 3 2 2 2 3 4 5 + E 4 3 3 3 3 4 5 + N 5 4 4 4 4 4 4 + T 6 5 5 4 5 5 5 + I 7 6 6 5 4 5 6 + O 8 7 7 6 5 4 5 + N 9 8 8 7 6 5 4 + + + */ +} diff --git a/Week 05/id_693/LeetCode_91_693.java b/Week 05/id_693/LeetCode_91_693.java new file mode 100644 index 000000000..9844cb619 --- /dev/null +++ b/Week 05/id_693/LeetCode_91_693.java @@ -0,0 +1,37 @@ +package id_693; + +import org.junit.Assert; + +/** + * @Desc 91. 解码方法 https://leetcode-cn.com/problems/decode-ways/ + * @Auther 李雷(KyLin) + * @Date 2019/11/16 + */ +public class LeetCode_91_693 { + //dp + /* + 1、分解子问题: + 2、定义状态数组: + 3、DP方程: + */ + public int numDecodings(String s) { + int len = s.length(); + int[] dp = new int[len + 1]; + dp[len] = 1; + dp[len - 1] = s.charAt(len - 1) == '0' ? 0 : 1; + for (int i = len - 2; i >= 0; i--) { + if (s.charAt(i) != '0') { + if ((s.charAt(i) - '0') * 10 + (s.charAt(i + 1) - '0') <= 26) { + dp[i] = dp[i + 1] + dp[i + 2]; + } else { + dp[i] = dp[i + 1]; + } + } + } + return dp[0]; + } + + public static void main(String[] args) { + Assert.assertEquals(1,new LeetCode_91_693().numDecodings("27")); + } +} diff --git a/Week 05/id_693/practise/LeetCode_1143_693.java b/Week 05/id_693/practise/LeetCode_1143_693.java new file mode 100644 index 000000000..f899b05e6 --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_1143_693.java @@ -0,0 +1,63 @@ +package id_693.practise; + +import org.junit.Assert; + +/** + * @Desc 1143. 最长公共子序列 https://leetcode-cn.com/problems/longest-common-subsequence/ + * @Auther 李雷(KyLin) + * @Date 2019/11/11 + */ +public class LeetCode_1143_693 { + + //dp 解法:默认设置行列为0,如果遇到相同的,则直接 用斜边+1,否则直接取上下位置的最大值。 + /* + dp方程: 相同 dp[i][j] = dp[i - 1][j - 1] + 1; + 不同 dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]); + */ + public int longestCommonSubsequence2(String text1, String text2) { + int m = text1.length(); + int n = text2.length(); + int[][] dp = new int[m + 1][n + 1]; + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + if (text1.charAt(i - 1) == text2.charAt(j - 1)) { + //此处的i - 1 j - 1 是为了获取2个字符串同时减少1 的情况下最长公共子序列的数量。 + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]); + } + } + } + return dp[m][n]; + } + + //对解法1 空间优化 二维数组优化为2个一维数组,.....可以继续优化看成一维数组,因为他只需要左上角的,如果缓存起来那不就好了。。。。 + public int longestCommonSubsequence(String text1, String text2) { + int m = text1.length(); + int n = text2.length(); + int[] dp = new int[n + 1]; + int[] tempdp = new int[n + 1];//只是用户获取原左上角的值 + for (int i = 1; i <= m; i++) { + int temp = 0; + for (int j = 1; j <= n; j++) { + if (text1.charAt(i - 1) == text2.charAt(j - 1)) { + dp[j] = tempdp[j - 1] + 1; + } else { + dp[j] = Math.max(temp, tempdp[j]); + } + temp = dp[j]; + } + tempdp = dp.clone(); + } + return dp[n]; + } + + public static void main(String[] args) { + Assert.assertEquals(3, new LeetCode_1143_693().longestCommonSubsequence("abcde", "ace")); + Assert.assertEquals(3, new LeetCode_1143_693().longestCommonSubsequence("abcde", "ace")); + Assert.assertEquals(2, new LeetCode_1143_693().longestCommonSubsequence("oxcpqrsvwf", "shmtulqrypy")); + Assert.assertEquals(2, new LeetCode_1143_693().longestCommonSubsequence("pmjghex", "hafcdeb")); + Assert.assertEquals(1, new LeetCode_1143_693().longestCommonSubsequence("ac", "hcc")); + Assert.assertEquals(4, new LeetCode_1143_693().longestCommonSubsequence("pmjghexybyrgzczy", "hafcdqbgncrcbihkd")); + } +} \ No newline at end of file diff --git a/Week 05/id_693/practise/LeetCode_120_693.java b/Week 05/id_693/practise/LeetCode_120_693.java new file mode 100644 index 000000000..53e638ea6 --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_120_693.java @@ -0,0 +1,96 @@ +package id_693.practise; + +import org.junit.Assert; + +import java.util.ArrayList; +import java.util.List; + +/** + * @Desc 120. 三角形最小路径和 https://leetcode-cn.com/problems/triangle/ + * @Auther 李雷(KyLin) + * @Date 2019/11/12 + */ +public class LeetCode_120_693 { + + //采用自顶向下的递归方式,回超时 + public int minimumTotal1(List> triangle) { + return dp(triangle, 0, 0); + } + + private int dp(List> triangle, int index, int level) { + if (level == triangle.size() - 1) { + return triangle.get(level).get(index); + } + int left = dp(triangle, index, level + 1); + int right = dp(triangle, index + 1, level + 1); + return Math.min(left, right) + triangle.get(level).get(index); + } + + //针对以上进行优化,去除重复,使用缓存(记忆化搜索) + + public int minimumTotal2(List> triangle) { + return dp2(triangle, 0, 0, new Integer[triangle.size()][triangle.size()]); + } + + private int dp2(List> triangle, int index, int level, Integer[][] temp) { + if (temp[level][index] != null) { + return temp[level][index]; + } + if (level == triangle.size() - 1) { + temp[level][index] = triangle.get(level).get(index); + return temp[level][index]; + } + int left = dp2(triangle, index, level + 1, temp); + int right = dp2(triangle, index + 1, level + 1, temp); + temp[level][index] = (Math.min(left, right) + triangle.get(level).get(index)); + return temp[level][index]; + } + + //自低向上 dp + public int minimumTotal3(List> triangle) { + int size = triangle.size(); + int[] dp = new int[size + 1];//规律1 + for (int level = size - 1; level >= 0; level--) { + for (int i = 0; i <= level; i++) {//根据规律1得出 + dp[i] = Math.min(dp[i], dp[i + 1]) + triangle.get(level).get(i); + } + } + return dp[0]; + } + + // 不用额外空间,但是这样是不好的编程习惯 + public int minimumTotal(List> triangle) { + for (int level = triangle.size() - 2; level >= 0; level--) { + for (int i = 0; i <= level; i++) { + triangle.get(level).set(i, Math.min(triangle.get(level + 1).get(i), triangle.get(level + 1).get(i + 1)) + triangle.get(level).get(i)); + } + } + return triangle.get(0).get(0); + } + + public static void main(String[] args) { + List> list = new ArrayList<>(); + List l1 = new ArrayList<>(); + l1.add(2); + List l2 = new ArrayList<>(); + l2.add(3); + l2.add(4); + List l3 = new ArrayList<>(); + l3.add(6); + l3.add(5); + l3.add(7); + List l4 = new ArrayList<>(); + l4.add(4); + l4.add(1); + l4.add(8); + l4.add(3); + list.add(l1); + list.add(l2); + list.add(l3); + list.add(l4); + Assert.assertEquals(11, new LeetCode_120_693().minimumTotal(list)); + for (List integers : list) { + System.out.println(integers); + } + } +} diff --git a/Week 05/id_693/practise/LeetCode_121_693.java b/Week 05/id_693/practise/LeetCode_121_693.java new file mode 100644 index 000000000..7a64cdbb3 --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_121_693.java @@ -0,0 +1,48 @@ +package id_693.practise; + +/** + * @Desc 121. 买卖股票的最佳时机 https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/ + * @Auther 李雷(KyLin) + * @Date 2019/11/14 + */ +public class LeetCode_121_693 { + //简单解决:找出最小的值,然后和每个值计算差值。最大的差值就是结果了 + public int maxProfit(int[] prices) { + if (prices.length == 0) { + return 0; + } + int min = Integer.MAX_VALUE; + int ans = Integer.MIN_VALUE; + for (int price : prices) { + min = Math.min(min, price); + ans = Math.max(price - min, ans); + } + return ans; + } + + //DP解法 :规律 这个只有一次交易 + /* + 原始方程: + dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]) + dp[i][1] = max(dp[i-1][1], -prices[i]) + */ + public int maxProfit1(int[] prices) { + int days = prices.length; + if (days == 0) return 0; + // 0 没有股票, 1 持有股票 + int[][] dp = new int[days][2]; + for (int i = 0; i < days; i++) { + if (i - 1 == -1){ + //base处理 + dp[i][0] = 0; + dp[i][1] = -prices[0]; + continue; + } + //当前没有股票,但是我之前有,我卖一股 + dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);//不动,卖 + //当前持有股票,但是我之前没有,我买了一股 + dp[i][1] = Math.max(dp[i - 1][1],- prices[i]);//不动,买 + } + return dp[days - 1][0]; + } +} diff --git a/Week 05/id_693/practise/LeetCode_122_693.java b/Week 05/id_693/practise/LeetCode_122_693.java new file mode 100644 index 000000000..cd9541e9a --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_122_693.java @@ -0,0 +1,36 @@ +package id_693.practise; + +/** + * @Desc 122. 买卖股票的最佳时机 II https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/description/ + * @Auther 李雷(KyLin) + * @Date 2019/11/14 + */ +public class LeetCode_122_693 { + //1、算差解题 + //2、峰谷法解题 + //3、dp解题 + /* + 原始方程: + dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]) + dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]) + */ + public int maxProfit(int[] prices) { + int days = prices.length; + if (days == 0) return 0; + // 0 没有股票, 1 持有股票 + int[][] dp = new int[days][2]; + for (int i = 0; i < days; i++) { + if (i - 1 == -1){ + //base处理 + dp[i][0] = 0; + dp[i][1] = -prices[0]; + continue; + } + //当前没有股票,但是我之前有,我卖一股 + 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[days - 1][0]; + } +} diff --git a/Week 05/id_693/practise/LeetCode_123_693.java b/Week 05/id_693/practise/LeetCode_123_693.java new file mode 100644 index 000000000..f47511d00 --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_123_693.java @@ -0,0 +1,41 @@ +package id_693.practise; + +/** + * @Desc 123. 买卖股票的最佳时机 III https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/ + * @Auther 李雷(KyLin) + * @Date 2019/11/14 + */ +public class LeetCode_123_693 { + //dp方程 + /* + 原始的动态转移方程,没有可化简的地方 + dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i]) + dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i]) + */ + public int maxProfit(int[] prices) { + int days = prices.length; + if (days == 0) return 0; + // 3 表示 可以交易2次 + int[][][] dp = new int[days][3][2]; + for (int i = 0; i < days; i++) { + for (int j = 2; j >= 1; j--) { + //跳过base + if (i - 1 == -1) { + dp[0][j][0] = 0; + dp[0][j][1] = -prices[0]; + continue; + } + //当前没有股票,但是我之前有,我卖一股 + dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);//不动,卖 + //当前持有股票,但是我之前没有,我买了一股 + dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);//不动,买 + } + } + return dp[days - 1][2][0]; + } + + public static void main(String[] args) { + assert new LeetCode_123_693().maxProfit(new int[]{3, 2, 6, 5, 0, 3}) == 7; + assert new LeetCode_123_693().maxProfit(new int[]{2, 4, 1}) == 2; + } +} diff --git a/Week 05/id_693/practise/LeetCode_152_693.java b/Week 05/id_693/practise/LeetCode_152_693.java new file mode 100644 index 000000000..68dc7b10a --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_152_693.java @@ -0,0 +1,32 @@ +package id_693.practise; + +import org.junit.Assert; + +/** + * @Desc 152. 乘积最大子序列 https://leetcode-cn.com/problems/maximum-product-subarray/ + * @Auther 李雷(KyLin) + * @Date 2019/11/14 + */ +public class LeetCode_152_693 { + public int maxProduct(int[] nums) { + int max = Integer.MIN_VALUE; + int imax = 1; + int imin = 1; + for (int num : nums) { + if (num < 0) { + imax ^= imin; + imin ^= imax; + imax ^= imin; + } + imax = Math.max(imax * num, num); + imin = Math.min(imin * num, num); + max = Math.max(max, imax); + } + return max; + } + + public static void main(String[] args) { + Assert.assertEquals(6, new LeetCode_152_693().maxProduct(new int[]{2, 3, -2, 4})); + Assert.assertEquals(0, new LeetCode_152_693().maxProduct(new int[]{-2, 0, -1})); + } +} diff --git a/Week 05/id_693/practise/LeetCode_188_693.java b/Week 05/id_693/practise/LeetCode_188_693.java new file mode 100644 index 000000000..18982bd15 --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_188_693.java @@ -0,0 +1,44 @@ +package id_693.practise; + +/** + * @Desc 188. 买卖股票的最佳时机 IV https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/ + * @Auther 李雷(KyLin) + * @Date 2019/11/14 + */ +public class LeetCode_188_693 { + + //dp解法,如果交易次数大于天数的2倍,则表示交易无限制,直接用2方法,否则直接走限制交易 + /* + 原始方程和k = 2 一样, + 只是多一个判断(防止超时),如果交易次数 > 天数 / 2 那么就直接代表交易无限制,就调用之前的无限制方法 + */ + public int maxProfit(int k, int[] prices) { + int days = prices.length; + if (days == 0) return 0; + if (k > days / 2) return new LeetCode_122_693().maxProfit(prices); + // 0 没有股票, 1 持有股票 + int[][][] dp = new int[days][k + 1][2]; + for (int i = 0; i < days; i++) { + for (int j = k; j >= 1; j--) { + if (i - 1 == -1) { + dp[0][j][0] = 0; + dp[0][j][1] = -prices[0]; + continue; + } + //当前没有股票,但是我之前有,我卖一股 + dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);//不动,卖 + //当前持有股票,但是我之前没有,我买了一股 + dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);//不动,买 + } + } + return dp[days - 1][k][0]; + } + + public static void main(String[] args) { + // System.out.println(new LeetCode_188_693().maxProfit(2, new int[]{2, 4, 1}) + " " + 2); + System.out.println(new LeetCode_188_693().maxProfit(2, new int[]{3, 2, 6, 5, 0, 3}) + " " + 7); + System.out.println(new LeetCode_188_693().maxProfit(0, new int[]{3, 2, 6, 5, 0, 3}) + " " + 0); + System.out.println(new LeetCode_188_693().maxProfit(2, new int[]{2, 4, 1}) + " " + 2); + System.out.println(new LeetCode_188_693().maxProfit(4, new int[]{2, 4, 1}) + " " + 2); + } +} diff --git a/Week 05/id_693/practise/LeetCode_198_693.java b/Week 05/id_693/practise/LeetCode_198_693.java new file mode 100644 index 000000000..2bb6ba5d4 --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_198_693.java @@ -0,0 +1,59 @@ +package id_693.practise; + +/** + * @Desc 198. 打家劫舍 https://leetcode-cn.com/problems/house-robber/ + * @Auther 李雷(KyLin) + * @Date 2019/11/14 + */ +public class LeetCode_198_693 { + //dp:就是偷和不偷,那么定义一个一维数组存钱,再加1维确定偷和不偷 + /* + 1、分解子问题 a[i] : 0 .... i 能偷到 max value ; a[i -1] + 2、定义状态数组 a[i][0,1] :0 偷,1不偷 + 3、DP方程 + a[i][0] = Math.max(a[i - 1][0],a[i - 1][1]) + a[i][1] = a[i - 1][1] + nums[i]; + */ + public int rob(int[] nums) { + if (nums.length == 0) return 0; + int[][] dp = new int[nums.length][2]; + dp[0][0] = nums[0]; + dp[0][1] = 0; + for (int i = 1; i < nums.length; i++) { + dp[i][0] = dp[i - 1][1] + nums[i]; + dp[i][1] = Math.max(dp[i - 1][0], dp[i - 1][1]);//如果不偷,那么可以直接取前一天的最大值, + } + return Math.max(dp[nums.length - 1][0], dp[nums.length - 1][1]); + } + + //去二维变1维,因为偷和不偷是恒定。 + public int rob2(int[] nums) { + if (nums.length == 0) return 0; + if (nums.length == 1) return nums[0]; + int[] dp = new int[nums.length]; + dp[0] = nums[0]; + dp[1] = Math.max(nums[0], nums[1]); + for (int i = 2; i < nums.length; i++) { + dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]); + } +// return Math.max(dp[nums.length - 1], dp[nums.length - 2]);//反正最后的就是最大值了 + return dp[nums.length - 1]; + } + + //去1维 + public int rob3(int[] nums) { + int prevMax = 0; + int currMax = 0; + for (int num : nums) { + int temp = currMax; + currMax = Math.max(currMax, prevMax + num); + prevMax = temp; + } + return currMax; + } + + public static void main(String[] args) { + new LeetCode_198_693().rob2(new int[]{2, 1, 1, 2}); + assert new LeetCode_198_693().rob2(new int[]{2, 1, 1, 2}) == 4; + } +} diff --git a/Week 05/id_693/practise/LeetCode_213_693.java b/Week 05/id_693/practise/LeetCode_213_693.java new file mode 100644 index 000000000..cd52623f7 --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_213_693.java @@ -0,0 +1,24 @@ +package id_693.practise; + +/** + * @Desc 213. 打家劫舍 II https://leetcode-cn.com/problems/house-robber-ii/ + * @Auther 李雷(KyLin) + * @Date 2019/11/14 + */ +public class LeetCode_213_693 { + //dp 和之前的 一样:区别就是 开始偷,那么最后不偷,只需要控制好范围就好了 + public int rob(int[] nums) { + if (nums.length == 1) return nums[0]; + return Math.max(helper(nums,0,nums.length - 2),helper(nums,1,nums.length -1)); + } + private int helper(int [] nums,int lo ,int len) { + int prevMax = 0; + int currMax = 0; + for (int i = lo; i <= len; i++) { + int temp = currMax; + currMax = Math.max(currMax,prevMax + nums[i]); + prevMax = temp; + } + return currMax; + } +} diff --git a/Week 05/id_693/practise/LeetCode_279_693.java b/Week 05/id_693/practise/LeetCode_279_693.java new file mode 100644 index 000000000..f9a2ac544 --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_279_693.java @@ -0,0 +1,43 @@ +package id_693.practise; + +import java.util.Arrays; + +/** + * @Desc 279. 完全平方数 https://leetcode-cn.com/problems/perfect-squares/ + * @Auther 李雷(KyLin) + * @Date 2019/11/15 + */ +public class LeetCode_279_693 { + //dp方程,和零钱问题类似 + /* + 1、子问题:f[i] = min(f[i],f[i - j*j] + 1) + 2、状态数组:dp[i] + 3、dp方程:dp[i] = Math.min(dp[i], dp[i - j * j] + 1); + */ + public int numSquares(int n) { + int[] dp = new int[n + 1]; + Arrays.fill(dp, n + 1); + dp[0] = 0; + for (int i = 1; i <= n; i++) { + for (int j = 1; j * j <= n; j++) { + if (j * j <= i) { + dp[i] = Math.min(dp[i], dp[i - j * j] + 1); + } + } + } + return dp[n]; + } + + //对上面进行优化 + public int numSquares2(int n) { + int[] dp = new int[n + 1]; + Arrays.fill(dp, n + 1); + dp[0] = 0; + for (int i = 1; i <= n; i++) { + for (int j = 1; i - j * j >= 0; j++) { + dp[i] = Math.min(dp[i], dp[i - j * j] + 1); + } + } + return dp[n]; + } +} \ No newline at end of file diff --git a/Week 05/id_693/practise/LeetCode_309_693.java b/Week 05/id_693/practise/LeetCode_309_693.java new file mode 100644 index 000000000..910083f73 --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_309_693.java @@ -0,0 +1,37 @@ +package id_693.practise; + +/** + * @Desc 309. 最佳买卖股票时机含冷冻期 https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/ + * @Auther 李雷(KyLin) + * @Date 2019/11/14 + */ +public class LeetCode_309_693 { + //dp解法:与122一样,只是需要再买的时候多减1天 + /* + dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]) + dp[i][1] = max(dp[i-1][1], dp[i-2][0] - prices[i]) + 解释:第 i 天选择 buy 的时候,要从 i-2 的状态转移,而不是 i-1 。 + */ + public int maxProfit(int[] prices) { + int days = prices.length; + if (days == 0) return 0; + // 0 没有股票, 1 持有股票 + int[][] dp = new int[days][2]; + for (int i = 0; i < days; i++) { + if (i - 1 == -1) { + //base处理 + dp[0][0] = 0; + dp[0][1] = -prices[0]; + continue; + } else if (i - 1 == 0) { + //处理第一次交易 + 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]);//不动,买 + continue; + } + 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 - 2][0] - prices[i]);//不动,买 + } + return dp[days - 1][0]; + } +} diff --git a/Week 05/id_693/practise/LeetCode_322_693.java b/Week 05/id_693/practise/LeetCode_322_693.java new file mode 100644 index 000000000..eac200b66 --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_322_693.java @@ -0,0 +1,60 @@ +package id_693.practise; + +import org.junit.Assert; + +import java.util.Arrays; + +/** + * @Desc 322. 零钱兑换 https://leetcode-cn.com/problems/coin-change/description/ + * @Auther 李雷(KyLin) + * @Date 2019/11/14 + */ +public class LeetCode_322_693 { + // dp 解法:dp[i] = min(dp[i],dp[i - coin] + 1); + public int coinChange2(int[] coins, int amount) { + int[] dp = new int[amount + 1]; + Arrays.fill(dp, amount + 1); + dp[0] = 0; + for (int i = 1; i <= amount; i++) { + for (int coin : coins) { + if (coin <= i) { + dp[i] = Math.min(dp[i], dp[i - coin] + 1); + } + } + } + return dp[amount] > amount ? -1 : dp[amount]; + } + + //记忆化搜索 + public int coinChange(int[] coins, int amount) { + int[] dp = new int[amount + 1]; + return bfs(coins, amount, dp); + + } + + private int bfs(int[] coins, int amount, int[] dp) { + if (amount == 0) { + return 0; + } + if (dp[amount] != 0) { + return dp[amount]; + } + int ans = Integer.MAX_VALUE; + for (int coin : coins) { + if (amount - coin < 0) { + continue; + } + int a = bfs(coins, amount - coin, dp); + if (a == -1) { + continue; + } + ans = Math.min(ans, a + 1); + } + return dp[amount] = ans == Integer.MAX_VALUE ? -1 : ans; + } + + public static void main(String[] args) { + Assert.assertEquals(3, new LeetCode_322_693().coinChange(new int[]{1, 2, 5}, 11)); + Assert.assertEquals(-1, new LeetCode_322_693().coinChange(new int[]{2}, 3)); + } +} diff --git a/Week 05/id_693/practise/LeetCode_518_693.java b/Week 05/id_693/practise/LeetCode_518_693.java new file mode 100644 index 000000000..4b9c2a54e --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_518_693.java @@ -0,0 +1,20 @@ +package id_693.practise; + +/** + * @Desc 518. 零钱兑换 II https://leetcode-cn.com/problems/coin-change-2/ + * @Auther 李雷(KyLin) + * @Date 2019/11/15 + */ +public class LeetCode_518_693 { + //dp 标准背包问题。 + public int change(int amount, int[] coins) { + int[] dp = new int[amount + 1]; + dp[0] = 1; + for (int coin : coins) { + for (int i = coin; i <= amount; i++) { + dp[i] += dp[i - coin]; + } + } + return dp[amount]; + } +} diff --git a/Week 05/id_693/practise/LeetCode_53_693.java b/Week 05/id_693/practise/LeetCode_53_693.java new file mode 100644 index 000000000..c15af2649 --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_53_693.java @@ -0,0 +1,44 @@ +package id_693.practise; + +import org.junit.Assert; + +/** + * @Desc 53. 最大子序和 https://leetcode-cn.com/problems/maximum-subarray/ + * @Auther 李雷(KyLin) + * @Date 2019/11/12 + */ +public class LeetCode_53_693 { + //理解了。但是没吃透啊。。。说是dp 但是没看出: + // 1、如果累加 是负数,那么直接用当前值比较ans 取出最大值 + // 2、如果累加 不是负数,那么直接继续累加,同时比较ans 取出最大值 + // 他们都是选择最大值 + // 大于0 就继续累加,如果小于0 则就是自身了 + /* + 1、暴力:n^2 + 2、DP: + a. 分治(子问题: max_sum(i) = Max(max_sum(i - 1), 0) + a[i]; + b. 定义状态数组: f[i] + c. DP方程:f[i] = Max(f(i - 1), 0) + a[i]; + + 1. dp问题:公式为:dp[i] = max(nums[i],nums[i] + dp[i - 1]) + 2. 最大子序和 = 当前元素自身最大,或者 包含之前后最大 + */ + public int maxSubArray(int[] nums) { + int ans = nums[0]; + int count = 0; + for (int num : nums) { + if (count > 0) { + count += num; + } else { + count = num; + } + ans = Math.max(ans, count); + } + return ans; + } + + public static void main(String[] args) { + Assert.assertEquals(6, new LeetCode_53_693().maxSubArray(new int[]{-2, 1, -3, 4, -1, 2, 1, -5, 4})); + Assert.assertEquals(-1, new LeetCode_53_693().maxSubArray(new int[]{-2, -1, -3, -4, -1, -2, -1, -5, -4})); + } +} diff --git a/Week 05/id_693/practise/LeetCode_62_693.java b/Week 05/id_693/practise/LeetCode_62_693.java new file mode 100644 index 000000000..c8a43af3e --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_62_693.java @@ -0,0 +1,84 @@ +package id_693.practise; + +import org.junit.Assert; + +import java.util.Arrays; + +/** + * @Desc 62. 不同路径 https://leetcode-cn.com/problems/unique-paths/ + * @Auther 李雷(KyLin) + * @Date 2019/11/11 + */ +public class LeetCode_62_693 { + //dp,第一版,听思路写题解-自低向上-空间复杂度O(N ^ 2) + public int uniquePaths2(int m, int n) { + if (m == 0 || n == 0) { + return 0; + } + if (m == 1 || n == 1) { + return 1; + } + int[][] temp = new int[m][n]; + temp[m - 1][n - 1] = 1; + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + if (i == m - 1 && j == n - 1) { + continue; + } + int ni = i + 1; + int nj = j + 1; + int t = 0; + if (ni < m) { + t += temp[ni][j]; + } + if (nj < n) { + t += temp[i][nj]; + } + temp[i][j] = t; + } + } + return temp[0][0]; + } + + //降低空间复杂度,从题得出,只需要一层的数字即可.切最右边只有1个路线-空间复杂度O(N) + public int uniquePaths(int m, int n) { + int[] temp = new int[n]; + Arrays.fill(temp, 1); + for (int i = m - 2; i >= 0; i--) { + for (int j = n - 2; j >= 0; j--) { + temp[j] += temp[j + 1]; + } + } + return temp[0]; + } + + //leetcode题解,写法比我优美,自上而下-空间复杂度O(N) + public int uniquePaths3(int m, int n) { + int[] temp = new int[n]; + Arrays.fill(temp, 1); + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + temp[j] += temp[j - 1]; + } + } + return temp[n - 1]; + } + + //增加老师题解 -空间复杂度O(N ^ 2) 里面提前把为1的考虑 + public int uniquePaths4(int m, int n) { + int[][] temp = new int[m][n]; + for (int i = 0; i < n; i++) temp[0][i] = 1; + for (int i = 0; i < m; i++) temp[i][0] = 1; + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + temp[i][j] = temp[i - 1][j] + temp[i][j - 1]; + } + } + return temp[m - 1][n - 1]; + } + + public static void main(String[] args) { + Assert.assertEquals(1, new LeetCode_62_693().uniquePaths4(1, 1)); + Assert.assertEquals(28, new LeetCode_62_693().uniquePaths4(7, 3)); + } +} diff --git a/Week 05/id_693/practise/LeetCode_63_693.java b/Week 05/id_693/practise/LeetCode_63_693.java new file mode 100644 index 000000000..5741ed7e0 --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_63_693.java @@ -0,0 +1,98 @@ +package id_693.practise; + +import org.junit.Assert; + +/** + * @Desc 63. 不同路径 II https://leetcode-cn.com/problems/unique-paths-ii/ + * @Auther 李雷(KyLin) + * @Date 2019/11/11 + */ +public class LeetCode_63_693 { + //dp暴力破解,沿用之前暴力破解思路。直接自低向上思路出发,遇到障碍物直接修改为0,(实际场合不赞同用这样玩) + //后面看官方题解也是这个思路,还用了3个for。。就不做优化了,后面复刷看国际站吧 + public int uniquePathsWithObstacles1(int[][] obstacleGrid) { + if (obstacleGrid.length == 0 || obstacleGrid[0].length == 0) { + return 0; + } + int row = obstacleGrid.length; + int column = obstacleGrid[0].length; + obstacleGrid[row - 1][column - 1] = 1; + for (int i = row - 1; i >= 0; i--) { + for (int j = column - 1; j >= 0; j--) { + if (obstacleGrid[i][j] == 0) { + int a = i + 1 < row ? obstacleGrid[i + 1][j] : 0; + int b = j + 1 < column ? obstacleGrid[i][j + 1] : 0; + obstacleGrid[i][j] = a + b; + } else { + if (i == row - 1 && j == column - 1) continue; + obstacleGrid[i][j] = 0; + } + } + } + return obstacleGrid[0][0]; + } + + //dp暴力破解 顺序上升(看了几遍中文官方的,实在写不下去那样的代码。还是自己的看着舒服 + public int uniquePathsWithObstacles2(int[][] obstacleGrid) { + if (obstacleGrid.length == 0 || obstacleGrid[0].length == 0) { + return 0; + } + int row = obstacleGrid.length; + int column = obstacleGrid[0].length; + obstacleGrid[0][0] = 1; + for (int i = 0; i < row; i++) { + for (int j = 0; j < column; j++) { + if (obstacleGrid[i][j] == 0) { + int a = i - 1 >= 0 ? obstacleGrid[i - 1][j] : 0; + int b = j - 1 >= 0 ? obstacleGrid[i][j - 1] : 0; + obstacleGrid[i][j] = a + b; + } else { + if (i == 0 && j == 0) continue; + obstacleGrid[i][j] = 0; + } + } + } + return obstacleGrid[row - 1][column - 1]; + } + + //饿,这个优化。有点复杂哈。还是上面看着好些哈, + public int uniquePathsWithObstacles(int[][] obstacleGrid) { + if (obstacleGrid.length == 0 || obstacleGrid[0].length == 0 || obstacleGrid[0][0] == 1) { + return 0; + } + int row = obstacleGrid.length; + int column = obstacleGrid[0].length; + obstacleGrid[0][0] = 1; + for (int i = 1; i < row; i++) { + if (obstacleGrid[i][0] != 1) { + obstacleGrid[i][0] += obstacleGrid[i - 1][0]; + } else { + obstacleGrid[i][0] = 0; + } + } + for (int i = 1; i < column; i++) { + if (obstacleGrid[0][i] != 1) { + obstacleGrid[0][i] += obstacleGrid[0][i - 1]; + } else { + obstacleGrid[0][i] = 0; + } + } + for (int i = 1; i < row; i++) { + for (int j = 1; j < column; j++) { + if (obstacleGrid[i][j] == 0) { + obstacleGrid[i][j] = obstacleGrid[i - 1][j] + obstacleGrid[i][j - 1]; + } else { + obstacleGrid[i][j] = 0; + } + } + } + return obstacleGrid[row - 1][column - 1]; + } + + public static void main(String[] args) { + Assert.assertEquals(0, new LeetCode_63_693().uniquePathsWithObstacles(new int[][]{{1, 0}})); + Assert.assertEquals(0, new LeetCode_63_693().uniquePathsWithObstacles(new int[][]{{0, 1}})); + Assert.assertEquals(2, new LeetCode_63_693().uniquePathsWithObstacles(new int[][]{{0, 0, 0}, {0, 1, 0}, {0, 0, 0}})); + Assert.assertEquals(0, new LeetCode_63_693().uniquePathsWithObstacles(new int[][]{{0, 0}, {1, 1}, {0, 0}})); + } +} diff --git a/Week 05/id_693/practise/LeetCode_714_693.java b/Week 05/id_693/practise/LeetCode_714_693.java new file mode 100644 index 000000000..d4e3e5726 --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_714_693.java @@ -0,0 +1,35 @@ +package id_693.practise; + +/** + * @Desc 714. 买卖股票的最佳时机含手续费 https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/ + * @Auther 李雷(KyLin) + * @Date 2019/11/14 + */ +public class LeetCode_714_693 { + //dp解法:于122 一样,只是需要卖的时候减去fee + /* + dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]) + dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i] - fee) + 解释:相当于买入股票的价格升高了。 + 在第一个式子里减也是一样的,相当于卖出股票的价格减小了。 + */ + public int maxProfit(int[] prices, int fee) { + int days = prices.length; + if (days == 0) return 0; + // 0 没有股票, 1 持有股票 + int[][] dp = new int[days][2]; + for (int i = 0; i < days; i++) { + if (i - 1 == -1){ + //base处理 + dp[i][0] = 0; + dp[i][1] = -prices[0]; + continue; + } + //当前没有股票,但是我之前有,我卖一股 + dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee);//不动,卖 + //当前持有股票,但是我之前没有,我买了一股 + dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);//不动,买 + } + return dp[days - 1][0]; + } +} diff --git a/Week 05/id_693/practise/LeetCode_980_693.java b/Week 05/id_693/practise/LeetCode_980_693.java new file mode 100644 index 000000000..f50a04027 --- /dev/null +++ b/Week 05/id_693/practise/LeetCode_980_693.java @@ -0,0 +1,129 @@ +package id_693.practise; + +/** + * @Desc 980. 不同路径 III https://leetcode-cn.com/problems/unique-paths-iii/ + * @Auther 李雷(KyLin) + * @Date 2019/11/15 + */ +public class LeetCode_980_693 { + + int num = 0; + + // dfs+backtrack + public int uniquePathsIII2(int[][] grid) { + if (grid.length == 0 || grid[0].length == 0) { + return 0; + } + int x = 0; + int y = 0; + int k = 1;//空白地板+起始地板 + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[i].length; j++) { + if (grid[i][j] == 0) { + k++; + } else if (grid[i][j] == 1) { + x = i; + y = j; + } + } + } + boolean[][] used = new boolean[grid.length][grid[0].length]; + dfs2(grid, used, x, y, k, 0); + return num; + } + + private void dfs2(int[][] grid, boolean[][] used, int x, int y, int k, int len) { + if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length || grid[x][y] == -1 || used[x][y]) { + return; + } + if (grid[x][y] == 2) { + if (len == k) { + num++; + } + return; + } + used[x][y] = true; + dfs2(grid, used, x + 1, y, k, len + 1); + dfs2(grid, used, x - 1, y, k, len + 1); + dfs2(grid, used, x, y + 1, k, len + 1); + dfs2(grid, used, x, y - 1, k, len + 1); + used[x][y] = false; + } + + //针对第一种空间优化,去掉boolean二维数组 + public int uniquePathsIII3(int[][] grid) { + if (grid.length == 0 || grid[0].length == 0) { + return 0; + } + int k = 1;//空白地板+起始地板 + int x = 0; + int y = 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[i].length; j++) { + if (grid[i][j] == 0) { + k++; + } else if (grid[i][j] == 1) { + x = i; + y = j; + } + } + } + dfs3(grid, x, y, k); + return num; + } + + private void dfs3(int[][] grid, int x, int y, int k) { + if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length || grid[x][y] == -1) { + return; + } + if (grid[x][y] == 2) { + if (k == 0) { + num++; + } + return; + } + grid[x][y] = -1; + dfs3(grid, x + 1, y, k - 1); + dfs3(grid, x - 1, y, k - 1); + dfs3(grid, x, y - 1, k - 1); + dfs3(grid, x, y + 1, k - 1); + grid[x][y] = 0; + } + + //针对第一种空间优化 去掉boolean二维数组,并取消全局变量 + public int uniquePathsIII(int[][] grid) { + if (grid.length == 0 || grid[0].length == 0) { + return 0; + } + int k = 1;//空白地板+起始地板 + int x = 0; + int y = 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[i].length; j++) { + if (grid[i][j] == 0) k++; + else if (grid[i][j] == 1) { + x = i; + y = j; + } + } + } + return dfs(grid, x, y, k); + } + + private int dfs(int[][] grid, int x, int y, int k) { + if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length || grid[x][y] == -1) { + return 0; + } + if (grid[x][y] == 2) { + return k == 0 ? 1 : 0; + } + int result = 0; + grid[x][y] = -1; + result += dfs(grid, x + 1, y, k - 1); + result += dfs(grid, x - 1, y, k - 1); + result += dfs(grid, x, y - 1, k - 1); + result += dfs(grid, x, y + 1, k - 1); + grid[x][y] = 0; + return result; + } +} diff --git a/Week 05/id_698/LeetCode_32_698.java b/Week 05/id_698/LeetCode_32_698.java new file mode 100644 index 000000000..c71bfb86a --- /dev/null +++ b/Week 05/id_698/LeetCode_32_698.java @@ -0,0 +1,30 @@ +/** + * 给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。 + * @author gning (id=698) + */ + + public class LeetCode_32_698 { + + public int longestValidParentheses(String s) { + char[] ss = s.toCharArray(); + int[] dp = new int[ss.length]; + + int maxlen = 0; + + for(int i=1; i=2 ? dp[i-2] : 0) + 2; + }else if(i - dp[i-1]>0 && ss[i - dp[i-1] - 1] == '(' ) { + dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + + maxlen = Math.max(maxlen,dp[i]); + + } + } + + return maxlen; + } + + } \ No newline at end of file diff --git a/Week 05/id_698/LeetCode_64_698.java b/Week 05/id_698/LeetCode_64_698.java new file mode 100644 index 000000000..2252964a7 --- /dev/null +++ b/Week 05/id_698/LeetCode_64_698.java @@ -0,0 +1,30 @@ +/** + * 给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 + * @author gning (id=698) + */ + + public class LeetCode_64_698 { + + public int minPathSum(int[][] grid) { + int[][] dp = new int[grid.length][grid[0].length]; + + for(int i=grid.length-1; i>=0; i--) { + for(int j=grid[0].length-1; j>=0; j--) { + if(i==grid.length-1 && j int: + cur, pre = 0, 0 + for num in nums: + cur, pre = max(pre + num, cur), cur + return cur + + + +# @lc code=end + diff --git a/Week 05/id_703/LeetCode_32_703.py b/Week 05/id_703/LeetCode_32_703.py new file mode 100644 index 000000000..2550197ef --- /dev/null +++ b/Week 05/id_703/LeetCode_32_703.py @@ -0,0 +1,26 @@ +# +# @lc app=leetcode.cn id=32 lang=python3 +# +# [32] 最长有效括号 +# + +# @lc code=start +class Solution: + def longestValidParentheses(self, s: str) -> int: + if not s: + return 0 + res = 0 + stack = [-1] + for i in range(len(s)): + if s[i] == "(": + stack.append(i) + else: + stack.pop() + if not stack: + stack.append(i) + else: + res = max(res,i - stack[-1]) + return res + +# @lc code=end + diff --git a/Week 05/id_708/LeetCode_1142_708.java b/Week 05/id_708/LeetCode_1142_708.java new file mode 100644 index 000000000..f27555788 --- /dev/null +++ b/Week 05/id_708/LeetCode_1142_708.java @@ -0,0 +1,26 @@ +class Solution { + public int longestCommonSubsequence(String text1, String text2) { + int row = text1.length(); + int col = text2.length(); + + int[][] dp = new int[row+1][col+1]; + for (int i = 1; i <= row; i++) { + for (int j = 1; j <= col; j++) { + if (text1.charAt(i-1) == text2.charAt(j-1)) { + dp[i][j] = dp[i-1][j-1] + 1; + } else { + // 它并没有指定谁包含谁的最长子序列,因此选择其中一个较大的 + dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]); + } + } + } + // printArrs(dp); + return dp[row][col]; + } + + private void printArrs(int[][] arrs) { + for (int i = 0; i < arrs.length; i++) { + System.out.println(Arrays.toString(arrs[i])); + } + } +} \ No newline at end of file diff --git a/Week 05/id_708/LeetCode_120_708.java b/Week 05/id_708/LeetCode_120_708.java new file mode 100644 index 000000000..a8f335755 --- /dev/null +++ b/Week 05/id_708/LeetCode_120_708.java @@ -0,0 +1,13 @@ +class Solution { + // 分治:当前位置最小值 = 左子树和右子树最小值 + 当前位置值 + // 所以自底向上为:计算左子树和右子树最小值+当前位置值 + public int minimumTotal(List> triangle) { + int[] dp = new int[triangle.size()+1]; // 最大行数量总等于行数 + for (int i = triangle.size()-1; i >= 0; i--) { // 自底向上 + for (int j = 0; j < triangle.get(i).size(); j++) { // 从左往右遍历 + dp[j] = Math.min(dp[j], dp[j+1]) + triangle.get(i).get(j); + } + } + return dp[0]; + } +} \ No newline at end of file diff --git a/Week 05/id_708/LeetCode_62_708.java b/Week 05/id_708/LeetCode_62_708.java new file mode 100644 index 000000000..84a055bb8 --- /dev/null +++ b/Week 05/id_708/LeetCode_62_708.java @@ -0,0 +1,39 @@ +// 动态递推 +class Solution { + // 自底向上,计算 paths[i][j] 的可能走法 + // paths[i][j] = paths[i-1][j] + paths[i][j-1] 即下面和右边走法之和 + public int uniquePaths(int m, int n) { + int[][] dp = new int[m][n]; + + for (int i = 0; i < m; i++) dp[i][n-1] = 1; + for (int i = 0; i < n; i++) dp[m-1][i] = 1; + for (int i = m-2; i >= 0; i--) { + for (int j = n-2; j >= 0; j--) { + dp[i][j] = dp[i+1][j] + dp[i][j+1]; + } + } + return dp[0][0]; + } +} + +// 分治法 +// class Solution { +// public int uniquePaths(int m, int n) { +// return helper(m, n, 0, 0); +// } + +// private int helper(int m, int n, int curRow, int curCol) { +// if (!isValidSquare(m, n, curRow, curCol)) return 0; +// if (isAtEnd(m, n, curRow, curCol)) return 1; +// // 只能往下走或往右走 +// return helper(m, n, curRow+1, curCol) + helper(m, n, curRow, curCol+1); +// } + +// private boolean isValidSquare(int m, int n, int curRow, int curCol) { +// return curRow >= 0 && curRow < m && curCol >= 0 && curCol < n; +// } + +// private boolean isAtEnd(int m, int n, int curRow, int curCol) { +// return curRow == m-1 && curCol == n-1; +// } +// } \ No newline at end of file diff --git a/Week 05/id_708/LeetCode_63_708.java b/Week 05/id_708/LeetCode_63_708.java new file mode 100644 index 000000000..150d82313 --- /dev/null +++ b/Week 05/id_708/LeetCode_63_708.java @@ -0,0 +1,26 @@ +class Solution { + public int uniquePathsWithObstacles(int[][] obstacleGrid) { + // 划分子问题 + // 找出状态数组 + // 找出状态转移方程 + int m = obstacleGrid.length, n = obstacleGrid[0].length; + + int[][] dp = new int[m][n]; + for (int i = m-1; i >= 0; i--) { + for (int j = n-1; j >= 0; j--) { + if (isValidGrid(obstacleGrid, i, j)) { + if (i == m-1 && j == n-1) dp[i][j] = 1; + else if (i == m-1) dp[i][j] = dp[i][j+1]; + else if (j == n-1) dp[i][j] = dp[i+1][j]; + else dp[i][j] = dp[i+1][j] + dp[i][j+1]; + } else dp[i][j] = 0; + // System.out.printf("%d, %d, %d\n", i, j, dp[i][j]); + } + } + return dp[0][0]; + } + + private boolean isValidGrid(int[][] obstacleGrid, int row, int col) { + return obstacleGrid[row][col] == 0; + } +} \ No newline at end of file diff --git a/Week 05/id_708/LeetCode_70_708.java b/Week 05/id_708/LeetCode_70_708.java new file mode 100644 index 000000000..acd0453fa --- /dev/null +++ b/Week 05/id_708/LeetCode_70_708.java @@ -0,0 +1,36 @@ +// 迭代方式 +class Solution { + // f(1) = 1 + // f(2) = 2 + // f(n) = f(n-1) + f(n-2) + public int climbStairs(int n) { + if (n <= 1) return n; + + int i = 1, j = 2; + while (n-- > 2) { + int temp = j; + j += i; + i = temp; + } + return j; + } +} + +// class Solution { +// // f(1) = 1 +// // f(2) = 2 +// // f(n) = f(n-1) + f(n-2) +// public int climbStairs(int n) { +// int[] dp = new int[n]; +// return _climbStairs(n, 0, dp); +// } + +// // @params i 当前阶数 +// private int _climbStairs(int n, int i, int[] dp) { +// if (i > n) return 0; +// if (i == n) return 1; +// if (dp[i] > 0) return dp[i]; +// dp[i] = _climbStairs(n, i+1, dp) + _climbStairs(n, i+2, dp); +// return dp[i]; +// } +// } \ No newline at end of file diff --git a/Week 05/id_713/LeetCode_32_LongestValidParentheses.java b/Week 05/id_713/LeetCode_32_LongestValidParentheses.java new file mode 100644 index 000000000..78d7ec7ca --- /dev/null +++ b/Week 05/id_713/LeetCode_32_LongestValidParentheses.java @@ -0,0 +1,41 @@ +package id_713; + +/** + * 32. 最长有效括号 + * 给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。 + *

+ * 示例 1: + *

+ * 输入: "(()" + * 输出: 2 + * 解释: 最长有效括号子串为 "()" + *

+ * 示例 2: + *

+ * 输入: ")()())" + * 输出: 4 + * 解释: 最长有效括号子串为 "()()" + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/longest-valid-parentheses + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_32_LongestValidParentheses { + + public int longestValidParentheses(String s) { + int max = 0; + int dp[] = new int[s.length()]; + + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) == ')') { + if (s.charAt(i - 1) == '(') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == ')') { + dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + } + max = Math.max(max, dp[i]); + } + return max; + } +} diff --git a/Week 05/id_713/LeetCode_64_MinimumPathSum.java b/Week 05/id_713/LeetCode_64_MinimumPathSum.java new file mode 100644 index 000000000..75a315cac --- /dev/null +++ b/Week 05/id_713/LeetCode_64_MinimumPathSum.java @@ -0,0 +1,62 @@ +package id_713; + +/** + * 64. 最小路径和 + * 给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 + *

+ * 说明:每次只能向下或者向右移动一步。 + *

+ * 示例: + *

+ * 输入: + * [ + * [1,3,1], + * [1,5,1], + * [4,2,1] + * ] + * 输出: 7 + * 解释: 因为路径 1→3→1→1→1 的总和最小。 + */ +public class LeetCode_64_MinimumPathSum { + + /* + 思路: + 1. 套用 路径计数的思路, 从右下角出发, 把左边和上面都加起来, 左边=左边+出发点, 上边=上边+出发点 + 2. 问题在于: 不是计数器了, 需要在 除了最下一行,除了最右一列的 二维坐标中找最小值 + + + 1 3 1 + 1 5 1 + 4 2 1 + 从右下的1出发, + 如果是最右列, 则累加 + 如果是最下行, 则累加 + 如果不是右列 也不是最下行, 则选取 右边 和 左边中最小的 累加 + + + */ + + public int minPathSum(int[][] grid) { + int row = grid.length - 1; + int col = grid[0].length - 1; + + for (int i = row; i >= 0; i--) { + for (int j = col; j >= 0; j--) { + // 最下行 + if (i == row && j != col) + grid[i][j] = grid[i][j] + grid[i][j + 1]; + // 最右列 + else if (i != row && j == col) + grid[i][j] = grid[i][j] + grid[i + 1][j]; + // 中间的坐标 + else if (i != row && j != col) { + grid[i][j] = grid[i][j] + Math.min(grid[i + 1][j], grid[i][j + 1]); + + } + } + } + + return grid[0][0]; + } + +} \ No newline at end of file diff --git a/Week 05/id_713/LeetCode_72_EditDistance.java b/Week 05/id_713/LeetCode_72_EditDistance.java new file mode 100644 index 000000000..26b334586 --- /dev/null +++ b/Week 05/id_713/LeetCode_72_EditDistance.java @@ -0,0 +1,90 @@ +package id_713; + +/** + * 72. 编辑距离 + */ +public class LeetCode_72_EditDistance { + /* + 给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。 + + 你可以对一个单词进行如下三种操作: + + 插入一个字符 + 删除一个字符 + 替换一个字符 + + 示例 1: + + 输入: word1 = "horse", word2 = "ros" + 输出: 3 + 解释: + horse -> rorse (将 'h' 替换为 'r') + rorse -> rose (删除 'r') + rose -> ros (删除 'e') + + 示例 2: + + 输入: word1 = "intention", word2 = "execution" + 输出: 5 + 解释: + intention -> inention (删除 't') + inention -> enention (将 'i' 替换为 'e') + enention -> exention (将 'n' 替换为 'x') + exention -> exection (将 'n' 替换为 'c') + exection -> execution (插入 'u') + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/edit-distance + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + + + + E 5 4 4 3 + S 4 3 3 2 + R 3 2 2 2 R和R相等, 所以 min(左,下,左下)不加1 + O 2 2 1 2 O和O相等, 所以 min(左,下,左下)不加1 + H 1 1 2 3 + # 0 1 2 3 + # R O S + + + */ + + public int minDistance(String word1, String word2) { + int m = word1.length(); + int n = word2.length(); + + if (m == 0 || n == 0) { // 如果其中一个为空串, 则返回另一个串的长度 + return n + m; + } + + int[][] dp = new int[m + 1][n + 1]; + + for (int i = 0; i < m + 1; i++) { + dp[0][i] = i; + } + + for (int j = 0; j < n + 1; j++) { + dp[j][0] = j; + } + + + for (int i = 1; i < m + 1; i++) { + for (int j = 1; j < n + 1; j++) { + int left = dp[i][j - 1] + 1; + int down = dp[i - 1][j] + 1; + int leftDown = dp[i - 1][j - 1]; + + if (word1.charAt(i - 1) != word2.charAt(j - 1)) { + leftDown += 1; + } + + dp[i][j] = Math.min(left, Math.min(down, leftDown)); + + + } + } + + return dp[m][n]; + } +} diff --git a/Week 05/id_713/LeetCode_91_DecodeWays.java b/Week 05/id_713/LeetCode_91_DecodeWays.java new file mode 100644 index 000000000..d2bcd7de2 --- /dev/null +++ b/Week 05/id_713/LeetCode_91_DecodeWays.java @@ -0,0 +1,62 @@ +package id_713; + +/** + * 91. 解码方法 + */ +public class LeetCode_91_DecodeWays { + + /* + 一条包含字母 A-Z 的消息通过以下方式进行了编码: + + 'A' -> 1 + 'B' -> 2 + ... + 'Z' -> 26 + + 给定一个只包含数字的非空字符串,请计算解码方法的总数。 + + 示例 1: + + 输入: "12" + 输出: 2 + 解释: 它可以解码为 "AB"(1 2)或者 "L"(12)。 + + 示例 2: + + 输入: "226" + 输出: 3 + 解释: 它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/decode-ways + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + public int numDecodings(String s) { + if (s == null || s.length() == 0) return 0; + int n = s.length(); + + int[] dp = new int[n + 1]; + dp[n] = 1; + if (s.charAt(n - 1) == '0') { + dp[n - 1] = 0; + } else { + dp[n - 1] = 1; + } + + for (int i = n - 2; i >= 0; i--) { + if (s.charAt(i) == '0') { + dp[i]=0; + continue; + } + if( (s.charAt(i) - '0' * 10 + s.charAt(i+1) - '0') <= 26) { + dp[i] = dp[i + 1] + dp[i + 2]; + } else { + dp[i] = dp[i + 1]; + } + } + + return dp[0]; + } + +} diff --git a/Week 05/id_713/NOTE.md b/Week 05/id_713/NOTE.md index a6321d6e2..5b6f77819 100644 --- a/Week 05/id_713/NOTE.md +++ b/Week 05/id_713/NOTE.md @@ -2,3 +2,236 @@ +# 递归代码模板 + +### Python版本 + +```python +def recursion(level, param1, param2, ...): + # 递归终结者 (终止条件) + if level > MAX_LEVEL: + process_result + return + + # 当前层的逻辑处理 + process(level, data...) + + # 下探 (进入下层) + self.recursion(level + 1, p1, ...) + + # 恢复当前层的状态, 如果需要的话 +``` + +### java版本 + +```java +public void recur(int level, int param) { + // 递归终结者 (终止条件) + if (level > MAX_LEVEL) { + // 逻辑处理 + return; + } + + // 处理当前层的逻辑 + process(level, param); + + // 下探 + recur(level + 1, param); + + // 恢复当前层的状态, 如果需要的话 +} +``` + + + +# 分治代码模板 + +## Python版本 + +```python +def divide_conquer(problem, param1, param2, ...): + # 递归终结者 (终止条件) + if problem is None: + # 终止处理 + return + + # 处理&分割 数据 + data = prepare_data(problem) + subproblems = split_problem(problem, data) + + # 治理数据 + subresult1 = self.divide_conquer(subproblem[0], p1, ...) + subresult2 = self.divide_conquer(subproblem[1], p1, ...) + subresult3 = self.divide_conquer(subproblem[2], p1, ...) + + # 处理&生成 最终结果 + result = process_result(subresult1, subresult2, subresult3) + + # 恢复当前层的状态, 如果需要的话 +``` + +## java版本 + +```java +public void divideConquer(Object problem, int param1, int param2, ...) { + // 递归终结者 (终止条件) + if (problem == null) { + // 终止处理 + return + } + + // 分割问题 + data = this.prepareData(problem); + subProblems = this.splitProblem(problem, data); + + // 治理 + subResult1 = this.divideConquer(subProblem[0], p1, ...); + subResult2 = this.divideConquer(subProblem[0], p1, ...); + subResult3 = this.divideConquer(subProblem[0], p1, ...); + + // 生成最终结果 + result = this.processResult(subResult1, subResult2, subResult3, ...); + + // 恢复当前层的状态, 如果需要的话 +} +``` + + + +# 思考 + +* 人肉递归低效 +* 找最近, 最简方法, 拆分成**可重复**的**子问题** + +* 数学归纳法 + + + +# 动态规划 + +* 动态规划 与 递归/分治 没有根本上的区别 (关键看有无最优子结构) +* 共性: 找到**重复子问题** +* 差异: 最优子结构 中途可以淘汰次优解. 没有**最优解的比较**就是分治 + +* **需要缓存(状态存储)** +* **把次优的状态淘汰掉** + + + +--- + +## 实战例题 + +### Fibonacci数列 + +#### 原始 + +```java +int fib(int n) { + if (n <= 0) { + return 0; + } else if (n == 1) { + return 1; + } else { + return fib(n - 1) + fib(n - 2); + } +} +``` + +#### 记忆化搜索 + +```java +int fib(int n) { + if (n <= 1) return n; + if (memo[n] == 0) memo[n] = fib(n - 1) + fib(n - 2); + return memo[n]; +} +``` + +#### buttom up 自底向上 + +```java +int fib(int n) { + if (n <= 1) return n; + + int[] a = new int[n]; + a[0] = 0; + a[1] = 1; + + for (int i = 2; i <=n; i++) { + a[i] = a[i - 1] + a[i - 2]; + } + + return a[n]; +} +``` + + + +### 路径计数 count the path + +* 二维棋盘, 从左上角走到右下角, 其中有障碍物 +* 只能向右走或者向下走 +* 拆解成子问题: 起始位(0, 0)有多少种走法 = (0, 1)走法总数 + (1, 0)走法总数 +* 子问题是重复的, 符合动态规划思想 + +#### 分治 + +```java +/** + * @param grid 棋盘 true可以走 false不能走(有障碍物) + * @param row 行 + * @param col 列 + */ +public int countPath(boolean[][] grid, int row, int col) { + if (!validate(grid, row, col)) return 0; + if (isAtEnd(grid, row, col)) return 1; + return countPath(grid, row + 1, col) + countPath(grid, row, col + 1); +} +``` + +# 地推 buttom up 自底部向上 + +* 在End位(右下角), End的上面和左面, 只有一种走法 +* 地推公式: + * dp[i, j] = dp[i + 1, j] + dp[i, j + 1] + +| 27 | 17 | 12 | 12 | 7 | 4 | 1 | 1 | +| :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: | +| 10 | 5 | X | 5 | 3 | 3 | X | 1 | +| 5 | 5 | 2 | 2 | X | 3 | 3 | 1 | +| X | 3 | X | 2 | 1 | X | 2 | 1 | +| 7 | 3 | X | 1 | 1 | 1 | 1 | 1 | +| 4 | 3 | 3 | X | X | 0 | X | 1 | +| 1 | X | 3 | 2 | 1 | X | 2 | 1 | +| 1 | 1 | 1 | 1 | 1 | 1 | 1 | End | + + + +```python +if a[i, j] == '空地': + dp[i, j] = dp[i + 1, j] + dp[i, j + 1] +else: + dp[i, j] = 0 +``` + + + +--- + +### 动态规划关键点 + +1. 最优子结构 **dp[n] = best_of(dp[n-1], dp[n-2], ...)** +2. 存储中间状态: **dp[i]** +3. 递归公式 (状态转移方程) + 1. 一维: dp[i] = dp[n-1] + dp[n-2] + 2. 二维: dp[i, j] = dp[i + 1, j] + dp[i+1, j] (且判断a[i,j]是否是空地) + + + +### 动态规划三部曲 + +1. 子问题 +2. 状态定义 +3. DP方程 + diff --git a/Week 05/id_713/example/LeetCode_1143_LongestCommonSubsequence.java b/Week 05/id_713/example/LeetCode_1143_LongestCommonSubsequence.java new file mode 100644 index 000000000..9dd19ff82 --- /dev/null +++ b/Week 05/id_713/example/LeetCode_1143_LongestCommonSubsequence.java @@ -0,0 +1,63 @@ +package id_713.example; + +/** + * 1143. 最长公共子序列 + * 给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列。 + * 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 + * 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。 + * 若这两个字符串没有公共子序列,则返回 0。 + *

+ * 示例 1: + * 输入:text1 = "abcde", text2 = "ace" + * 输出:3 + * 解释:最长公共子序列是 "ace",它的长度为 3。 + *

+ * 示例 2: + * 输入:text1 = "abc", text2 = "abc" + * 输出:3 + * 解释:最长公共子序列是 "abc",它的长度为 3。 + *

+ * 示例 3: + * 输入:text1 = "abc", text2 = "def" + * 输出:0 + * 解释:两个字符串没有公共子序列,返回 0。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/longest-common-subsequence + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_1143_LongestCommonSubsequence { + + /* + 思路: + 1. 子问题划分 + 如果 str1最一位 == str2最后一位, 最大子序列为 str1[1...m-1]与str2[1...n-1]的最大子序列 + 1 + 如果 串1最一位 != 串2最后一位, 最大子序列为 + str1[1...m]与str2[1...n-1] + str1[1...m-1]与str2[1...n] + 中的最大值 + 2. 边界 + + 3. dp方程 + 如果 str[i] == str[j], dp[i][j] = dp[i-1][j-1] + 1 + 否则 max(dp[i-1][j], dp[i][j-1]) + */ + + public int longestCommonSubsequence(String text1, String text2) { + int m = text1.length(); + int n = text2.length(); + + + int[][] dp = new int[m + 1][n + 1]; + for (int i = 1; i < m + 1; i++) { + for (int j = 1; i < n + 1; i++) { + if (text1.charAt(i - 1) == text2.charAt(j - 1)) { + dp[i][j] = dp[i-1][j-1] + 1; + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + } + } + } + return dp[m][n]; + } +} \ No newline at end of file diff --git a/Week 05/id_713/example/LeetCode_120_Triangle.java b/Week 05/id_713/example/LeetCode_120_Triangle.java new file mode 100644 index 000000000..7e25eb4f9 --- /dev/null +++ b/Week 05/id_713/example/LeetCode_120_Triangle.java @@ -0,0 +1,43 @@ +package id_713.example; + +import java.util.List; + +/** + * 120. 三角形最小路径和 + */ +public class LeetCode_120_Triangle { + + /* + 给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。 + + 例如,给定三角形: + + [ + [2], + [3,4], + [6,5,7], + [4,1,8,3] + ] + + 自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/triangle + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + public int minimumTotal(List> triangle) { + + int row = triangle.size(); + int[] dp = new int[row + 1]; + + for (int i = row - 1; i >= 0; i--) { + for (int j = 0; j <= i; j++) { + dp[i] = Math.min(dp[i], dp[i + 1]) + triangle.get(i).get(j); + } + } + + return dp[0]; + } + +} diff --git a/Week 05/id_713/example/LeetCode_198_HouseRobber.java b/Week 05/id_713/example/LeetCode_198_HouseRobber.java new file mode 100644 index 000000000..e69489b4f --- /dev/null +++ b/Week 05/id_713/example/LeetCode_198_HouseRobber.java @@ -0,0 +1,98 @@ +package id_713.example; + +/** + * 198. 打家劫舍 + */ +public class LeetCode_198_HouseRobber { + + /* + 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 + + 给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。 + + 示例 1: + + 输入: [1,2,3,1] + 输出: 4 + 解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 + 偷窃到的最高金额 = 1 + 3 = 4 。 + + 示例 2: + + 输入: [2,7,9,3,1] + 输出: 12 + 解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 + 偷窃到的最高金额 = 2 + 9 + 1 = 12 。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/house-robber + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + + + + 思路: + 1. 二维DP + + 一维记录数组中索引 + 二维记录状态, 抢或不抢 + + + dp[i][0] = Math.max(dp[i - 1][0] + dp[i - 1][1]); // 不抢 + dp[i][1] = dp[i - 1][0] + nums[i]; // 抢 + + */ + + public int rob(int[] nums) { + if (nums == null || nums.length == 0) return 0; + + int[][] dp = new int[nums.length][2]; + + for (int i = 1; i < nums.length; i++) { + dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1]); // 不打劫只能找最大的 + dp[i][1] = dp[i - 1][0] + nums[i]; // 打劫才能累加, 而且是累加前一个没有打劫的(隔一个) + } + + return Math.max(dp[nums.length - 1][0], dp[nums.length - 1][0]); + + } + + /* + 二维降低为一维 + */ + public int rob2(int[] nums) { + if (nums == null || nums.length == 0) return 0; + if (nums.length == 1) return nums[0]; + + int[] dp = new int[Integer.MAX_VALUE]; + dp[0] = nums[0]; + dp[1] = nums[0] > nums[1] ? nums[0] : nums[1]; + + for (int i = 2; i < nums.length; i++) { + dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]); + } + return dp[nums.length - 1]; + } + + /* + 一维降低成 2个变量, 前值, 前前值 + */ + public int rob3(int[] nums) { + if (nums == null || nums.length == 0) return 0; + if (nums.length == 1) return nums[0]; + +// int[] dp = new int[Integer.MAX_VALUE]; +// dp[0] = nums[0]; +// dp[1] = nums[0] > nums[1] ? nums[0] : nums[1]; + + int first = nums[0]; + int second = Math.max(nums[0], nums[1]); + int res = second; + + for (int i = 2; i < nums.length; i++) { + res = Math.max(second, first + nums[i]); + + } + return res; + } + +} \ No newline at end of file diff --git a/Week 05/id_713/example/LeetCode_53_MaximumSubarray.java b/Week 05/id_713/example/LeetCode_53_MaximumSubarray.java new file mode 100644 index 000000000..7036f3559 --- /dev/null +++ b/Week 05/id_713/example/LeetCode_53_MaximumSubarray.java @@ -0,0 +1,53 @@ +package id_713.example; + +/** + * 53. 最大子序和 + */ +public class LeetCode_53_MaximumSubarray { + + /* + 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 + + 示例: + + 输入: [-2,1,-3,4,-1,2,1,-5,4], + 输出: 6 + 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 + + 进阶: + + 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/maximum-subarray + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + + + + + 思路: + + 1. 暴力 O(n ^ 2) + 2. DP + a. 分治(子问题) 通常从后往前找, problem(i) = Max(sub(i - 1), 0) + problem(i) + b. 状态数组定义 + c. DP方程 dp[i] = Max(dp[i - 1], 0]) + dp[i] + + 最大子序列和 = 当前元素最大 或 包含之前以后最大 + + */ + + + public int maxSubArray(int[] nums) { + for (int i = 1; i < nums.length; i++) { + nums[i] = Math.max(nums[i - 1], 0) + nums[i]; + } + + int max = Integer.MIN_VALUE; + for (int i : nums) { + max = (i > max ? i : max); + } + + return max; + } +} diff --git a/Week 05/id_713/example/LeetCode_62_UniquePaths.java b/Week 05/id_713/example/LeetCode_62_UniquePaths.java new file mode 100644 index 000000000..a22350f05 --- /dev/null +++ b/Week 05/id_713/example/LeetCode_62_UniquePaths.java @@ -0,0 +1,74 @@ +package id_713.example; + +import java.util.Arrays; + +/** + * 62. 不同路径 + * 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 + * 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 + * 问总共有多少条不同的路径? + *

+ * 示例 1: + * 输入: m = 3, n = 2 + * 输出: 3 + * 解释: + * 从左上角开始,总共有 3 条路径可以到达右下角。 + * 1. 向右 -> 向右 -> 向下 + * 2. 向右 -> 向下 -> 向右 + * 3. 向下 -> 向右 -> 向右 + *

+ * 示例 2: + * 输入: m = 7, n = 3 + * 输出: 28 + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/unique-paths + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_62_UniquePaths { + + /* + 思路1: + 1. 利用动态规划, 从右下角开始计算 + 2. 方程: dp[i][j] = dp[i + 1][j] + dp[i][j + 1] + + 思路2: + 1. 压缩空间, 把二维数组一层一层向上执行 + 2. 从右至左(列), 从下至上(行), 用一个数组维护, 之前用完的覆盖即可, 不用再保留 + 从而达到压缩空间的目的 + + + */ + + public int uniquePaths(int m, int n) { + int[][] arr = new int[m][n]; + arr[m - 1][n - 1] = 1; + + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + if (i == m - 1 && j != n - 1) { + arr[i][j] = arr[i][j] + arr[i][j + 1]; + } else if (i != m - 1 && j == n - 1) { + arr[i][j] = arr[i][j] + arr[i + 1][j]; + } else if (i != m - 1 && j != n - 1) { + arr[i][j] = arr[i + 1][j] + arr[i][j + 1]; + } + + } + } + + return arr[0][0]; + } + + public int uniquePath2(int m, int n) { + int[] arr = new int[n]; + Arrays.fill(arr, 1); + + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + arr[j] += arr[j - 1]; + } + } + + return arr[n - 1]; + } +} \ No newline at end of file diff --git a/Week 05/id_718/1143.longest-common-subsequence.py b/Week 05/id_718/1143.longest-common-subsequence.py new file mode 100644 index 000000000..147a779ce --- /dev/null +++ b/Week 05/id_718/1143.longest-common-subsequence.py @@ -0,0 +1,20 @@ +# +# @lc app=leetcode id=1143 lang=python3 +# +# [1143] Longest Common Subsequence +# + +# @lc code=start +class Solution: + def longestCommonSubsequence(self, text1: str, text2: str) -> int: + m, n = len(text1), len(text2) + dp = [[0 for _ in range(n+1)] for _ in range(m+1)] + for i in range(1, m+1): + for j in range(1, n+1): + if text1[i-1] == text2[j-1]: + dp[i][j] = 1 + dp[i-1][j-1] + else: + dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + return dp[-1][-1] +# @lc code=end + diff --git a/Week 05/id_718/120.triangle.py b/Week 05/id_718/120.triangle.py new file mode 100644 index 000000000..73135c4f1 --- /dev/null +++ b/Week 05/id_718/120.triangle.py @@ -0,0 +1,24 @@ +# +# @lc app=leetcode id=120 lang=python3 +# +# [120] Triangle +# + +# @lc code=start +class Solution: + def minimumTotal(self, triangle: List[List[int]]) -> int: + dp, n = triangle[-1], len(triangle) + for i in range(n-2, -1, -1): + for j in range(len(triangle[i])): + dp[j] = min(dp[j], dp[j+1]) + triangle[i][j] + return dp[0] +# @lc code=end +''' + def minimumTotal(self, triangle: List[List[int]]) -> int: + dp = triangle + for i in range(len(triangle) -2, -1, -1): + for j in range(len(triangle[i])): + dp[i][j] = min(dp[i+1][j], dp[i+1][j+1]) + triangle[i][j] + return dp[0][0] +''' + diff --git a/Week 05/id_718/121.best-time-to-buy-and-sell-stock.py b/Week 05/id_718/121.best-time-to-buy-and-sell-stock.py new file mode 100644 index 000000000..3b4409a87 --- /dev/null +++ b/Week 05/id_718/121.best-time-to-buy-and-sell-stock.py @@ -0,0 +1,17 @@ +# +# @lc app=leetcode id=121 lang=python3 +# +# [121] Best Time to Buy and Sell Stock +# + +# @lc code=start +class Solution: + def maxProfit(self, prices: List[int]) -> int: + minp = float('inf') + ret = 0 + for price in prices: + minp = min(minp, price) + ret = max(ret, price - minp) + return ret +# @lc code=end + diff --git a/Week 05/id_718/122.best-time-to-buy-and-sell-stock-ii.py b/Week 05/id_718/122.best-time-to-buy-and-sell-stock-ii.py new file mode 100644 index 000000000..b04995166 --- /dev/null +++ b/Week 05/id_718/122.best-time-to-buy-and-sell-stock-ii.py @@ -0,0 +1,16 @@ +# +# @lc app=leetcode id=122 lang=python3 +# +# [122] Best Time to Buy and Sell Stock II +# + +# @lc code=start +class Solution: + def maxProfit(self, prices: List[int]) -> int: + ret = 0 + for i in range(1, len(prices)): + if prices[i] > prices[i-1]: + ret += prices[i] - prices[i-1] + return ret +# @lc code=end + diff --git a/Week 05/id_718/123.best-time-to-buy-and-sell-stock-iii.py b/Week 05/id_718/123.best-time-to-buy-and-sell-stock-iii.py new file mode 100644 index 000000000..3ddef0a83 --- /dev/null +++ b/Week 05/id_718/123.best-time-to-buy-and-sell-stock-iii.py @@ -0,0 +1,27 @@ +# +# @lc app=leetcode id=123 lang=python3 +# +# [123] Best Time to Buy and Sell Stock III +# + +# @lc code=start +class Solution: + def maxProfit(self, prices: List[int]) -> int: + if not prices or not len(prices): + return 0 + n = len(prices) + first, second = [0] * n, [0] * n + minp = prices[0] + for i in range(1, n): + first[i] = max(first[i-1], prices[i] - minp) + minp = min(minp, prices[i]) + maxp = prices[-1] + for i in range(n-2,0,-1): + second[i] = max(second[i+1], maxp - prices[i]) + maxp = max(maxp, prices[i]) + ret = 0 + for i in range(1,n): + ret = max(ret, first[i]+second[i]) + return(ret) +# @lc code=end + diff --git a/Week 05/id_718/188.best-time-to-buy-and-sell-stock-iv.py b/Week 05/id_718/188.best-time-to-buy-and-sell-stock-iv.py new file mode 100644 index 000000000..965cfa38f --- /dev/null +++ b/Week 05/id_718/188.best-time-to-buy-and-sell-stock-iv.py @@ -0,0 +1,51 @@ +# +# @lc app=leetcode id=188 lang=python3 +# +# [188] Best Time to Buy and Sell Stock IV +# + +# @lc code=start +class Solution: + def maxProfit(self, k: int, prices: List[int]) -> int: + n = len(prices) + if not n: return 0 + if k > n / 2: + return self.maxProfitII(prices) + dp = [[0 for _ in prices] for _ in range(k + 1)] + for i in range(1,k+1): + localmax = dp[i-1][0] - prices[0] # the part about jj + for j in range(1,n): + dp[i][j] = max(dp[i][j-1], prices[j] + localmax) + localmax = max(localmax, dp[i-1][j] - prices[j]) + return dp[k][n-1] + + def maxProfitII(self, prices): + price_diff = [x - y for x,y in zip(prices[1:], prices[:-1])] + return sum(filter(lambda x: x > 0, price_diff)) +""" +class Solution: + def maxProfit(self, k: int, prices: List[int]) -> int: + n = len(prices) + if not n: return 0 + if k > n / 2: + return self.maxProfitII(prices) + profit = [[[0 for _ in range(2)] for _ in range(k+1)] for _ in range(n)] + profit[0][0][0], profit[0][0][1] = 0, -prices[0] + for j in range(1,k+1): + profit[0][j][0], profit[0][j][1] = -float('inf'), -float('inf') + # at time i, total trade j times, 0/1 - no stock at hand/one stock at hand + for i in range(1, n): + profit[i][0][0] = profit[i-1][0][0] + profit[i][0][1] = max(profit[i-1][0][1], profit[i-1][0][0] - prices[i]) + for j in range(1, k+1): + profit[i][j][0] = max(profit[i-1][j][0], profit[i-1][j-1][1] + prices[i]) # sell or do nothing, finish one trade + profit[i][j][1] = max(profit[i-1][j][1], profit[i-1][j][0] - prices[i]) # buy or do nothing + max_profit = [profit[n-1][j][0] for j in range(k+1)] + return(max(max_profit)) + + def maxProfitII(self, prices): + price_diff = [x - y for x,y in zip(prices[1:], prices[:-1])] + return sum(filter(lambda x: x > 0, price_diff)) +""" +# @lc code=end + diff --git a/Week 05/id_718/322.coin-change.py b/Week 05/id_718/322.coin-change.py new file mode 100644 index 000000000..1aa6ac30d --- /dev/null +++ b/Week 05/id_718/322.coin-change.py @@ -0,0 +1,24 @@ +# +# @lc app=leetcode id=322 lang=python3 +# +# [322] Coin Change +# + +# @lc code=start +class Solution: + def coinChange(self, coins: List[int], amount: int) -> int: + # i, j means use i coins, total amoung is j + MAX = 100000000000000 + dp = [MAX for _ in range(amount+1)] + dp[0] = 0 + for amt in range(1, amount + 1): + for coin in coins: + if amt - coin < 0: + continue + dp[amt] = min(dp[amt], dp[amt-coin] + 1) + if dp[amount] == MAX: + return - 1 + return dp[amount] + +# @lc code=end + diff --git a/Week 05/id_718/45.jump-game-ii.py b/Week 05/id_718/45.jump-game-ii.py new file mode 100644 index 000000000..8cec81bc4 --- /dev/null +++ b/Week 05/id_718/45.jump-game-ii.py @@ -0,0 +1,20 @@ +# +# @lc app=leetcode id=45 lang=python3 +# +# [45] Jump Game II +# + +# @lc code=start +class Solution: + def jump(self, nums: List[int]) -> int: + n = len(nums) + cur, cnt, pos = 0, 0, 0 + while cur < n - 1: + cnt += 1 + pre = cur + while pos <= pre: + cur = max(cur, pos + nums[pos]) + pos += 1 + return cnt +# @lc code=end + diff --git a/Week 05/id_718/518.coin-change-2.py b/Week 05/id_718/518.coin-change-2.py new file mode 100644 index 000000000..54d3df063 --- /dev/null +++ b/Week 05/id_718/518.coin-change-2.py @@ -0,0 +1,19 @@ +# +# @lc app=leetcode id=518 lang=python3 +# +# [518] Coin Change 2 +# + +# @lc code=start +class Solution: + def change(self, amount: int, coins: List[int]) -> int: + # dp[i][j] is to use first i coins to make amount j + dp = [[0] * (amount+1) for _ in range(len(coins) + 1)] + dp[0][0] = 1 + for i in range(1, len(coins) + 1): + dp[i][0] = 1 + for j in range(1, amount+1): + dp[i][j] = dp[i-1][j] + (dp[i][j-coins[i-1]] if j >= coins[i-1] else 0) + return dp[-1][-1] +# @lc code=end + diff --git a/Week 05/id_718/55.jump-game.py b/Week 05/id_718/55.jump-game.py new file mode 100644 index 000000000..b6f429e8a --- /dev/null +++ b/Week 05/id_718/55.jump-game.py @@ -0,0 +1,18 @@ +# +# @lc app=leetcode id=55 lang=python3 +# +# [55] Jump Game +# + +# @lc code=start +class Solution: + def canJump(self, nums: List[int]) -> bool: + n, reach = len(nums), 0 + for i, jump in enumerate(nums): + if reach >= n - 1: + return True + if i > reach: + return False + reach = max(reach, i + jump) +# @lc code=end + diff --git a/Week 05/id_718/62.unique-paths.py b/Week 05/id_718/62.unique-paths.py new file mode 100644 index 000000000..4540da145 --- /dev/null +++ b/Week 05/id_718/62.unique-paths.py @@ -0,0 +1,19 @@ +# +# @lc app=leetcode id=62 lang=python3 +# +# [62] Unique Paths +# + +# @lc code=start +class Solution: + def uniquePaths(self, m: int, n: int) -> int: + dp = [[0 for _ in range(n)] for _ in range(m)] + for i in range(m): + for j in range(n): + if i == 0 and j == 0: + dp[i][j] = 1 + else: + dp[i][j] = dp[i-1][j] + dp[i][j-1] #out of range in other languages + return dp[-1][-1] +# @lc code=end + diff --git a/Week 05/id_718/63.unique-paths-ii.py b/Week 05/id_718/63.unique-paths-ii.py new file mode 100644 index 000000000..dca412234 --- /dev/null +++ b/Week 05/id_718/63.unique-paths-ii.py @@ -0,0 +1,23 @@ +# +# @lc app=leetcode id=63 lang=python3 +# +# [63] Unique Paths II +# + +# @lc code=start +class Solution: + def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int: + dp = obstacleGrid + for i in range(len(dp)): + for j in range(len(dp[0])): + if i == 0 and j == 0: + dp[i][j] = 1 - dp[i][j] + elif i == 0: + dp[i][j] = dp[i][j-1] if dp[i][j] != 1 else 0 + elif j == 0: + dp[i][j] = dp[i-1][j] if dp[i][j] != 1 else 0 + else: + dp[i][j] = dp[i][j-1] + dp[i-1][j] if dp[i][j] !=1 else 0 + return dp[-1][-1] +# @lc code=end + diff --git a/Week 05/id_718/72.edit-distance.py b/Week 05/id_718/72.edit-distance.py new file mode 100644 index 000000000..3c4175e80 --- /dev/null +++ b/Week 05/id_718/72.edit-distance.py @@ -0,0 +1,26 @@ +# +# @lc app=leetcode id=72 lang=python3 +# +# [72] Edit Distance +# + +# @lc code=start +class Solution: + def minDistance(self, word1: str, word2: str) -> int: + n, m = len(word1), len(word2) + dp = [[0 for _ in range(m+1)] for _ in range(n+1)] + for i in range(n+1): + for j in range(m+1): + if i == 0: + dp[i][j] = j + elif j == 0: + dp[i][j] = i + else: + if word1[i-1] == word2[j-1]: + dp[i][j] = min(dp[i-1][j-1], dp[i][j-1] + 1, dp[i-1][j] + 1) + else: + dp[i][j] =min(dp[i-1][j-1], dp[i][j-1], dp[i-1][j]) + 1 + return dp[n][m] + +# @lc code=end + diff --git a/Week 05/id_718/980.unique-paths-iii.py b/Week 05/id_718/980.unique-paths-iii.py new file mode 100644 index 000000000..1378584d1 --- /dev/null +++ b/Week 05/id_718/980.unique-paths-iii.py @@ -0,0 +1,12 @@ +# +# @lc app=leetcode id=980 lang=python3 +# +# [980] Unique Paths III +# + +# @lc code=start +class Solution: + def uniquePathsIII(self, grid: List[List[int]]) -> int: + +# @lc code=end + diff --git a/Week 05/id_733/LeetCode_1143_733.go b/Week 05/id_733/LeetCode_1143_733.go new file mode 100644 index 000000000..5a9f77659 --- /dev/null +++ b/Week 05/id_733/LeetCode_1143_733.go @@ -0,0 +1,27 @@ +func longestCommonSubsequence(text1 string, text2 string) int { + l1, l2 := len(text1), len(text2) + if l1 == 0 || l2 == 0 { + return 0 + } + + dp := make([][]int, l1+1) + for i := range dp { + dp[i] = make([]int, l2+1) + } + + for i := 1; i <= l1; i++ { + for j := 1; j <= l2; j++ { + if text1[i-1] == text2[j-1] { + dp[i][j] = dp[i-1][j-1] + 1 + } else { + if dp[i-1][j] > dp[i][j-1] { + dp[i][j] = dp[i-1][j] + } else { + dp[i][j] = dp[i][j-1] + } + } + } + } + + return dp[l1][l2] +} diff --git a/Week 05/id_733/LeetCode_120_733.go b/Week 05/id_733/LeetCode_120_733.go new file mode 100644 index 000000000..fb7cc21e8 --- /dev/null +++ b/Week 05/id_733/LeetCode_120_733.go @@ -0,0 +1,23 @@ +func minimumTotal(triangle [][]int) int { + l := len(triangle) + if l == 0 { + return 0 + } + + dp := make([]int, len(triangle[l-1])+1) + for i := l - 1; i >= 0; i-- { + for j := range triangle[i] { + dp[j] = min(dp[j], dp[j+1]) + triangle[i][j] + } + } + + return dp[0] +} + +func min(x, y int) int { + if x > y { + return y + } + + return x +} diff --git a/Week 05/id_733/LeetCode_152_733.go b/Week 05/id_733/LeetCode_152_733.go new file mode 100644 index 000000000..5d157bf0e --- /dev/null +++ b/Week 05/id_733/LeetCode_152_733.go @@ -0,0 +1,34 @@ +func maxProduct(nums []int) int { + if len(nums) == 0 { + return 0 + } + + res := nums[0] + for i, prevMax, prevMin := 1, nums[0], nums[0]; i < len(nums); i++ { + if nums[i] < 0 { + prevMax, prevMin = prevMin, prevMax + } + + prevMax = max(prevMax*nums[i], nums[i]) + prevMin = min(prevMin*nums[i], nums[i]) + res = max(res, prevMax) + } + + return res +} + +func max(x, y int) int { + if x > y { + return x + } + + return y +} + +func min(x, y int) int { + if x < y { + return x + } + + return y +} diff --git a/Week 05/id_733/LeetCode_198_733.go b/Week 05/id_733/LeetCode_198_733.go new file mode 100644 index 000000000..24dc0c8e8 --- /dev/null +++ b/Week 05/id_733/LeetCode_198_733.go @@ -0,0 +1,17 @@ +func rob(nums []int) int { + prev1, prev2 := 0, 0 + for _, n := range nums { + cur := max(prev1, prev2+n) + prev1, prev2 = cur, prev1 + } + + return prev1 +} + +func max(x, y int) int { + if x > y { + return x + } + + return y +} diff --git a/Week 05/id_733/LeetCode_322_733.go b/Week 05/id_733/LeetCode_322_733.go new file mode 100644 index 000000000..1d90779d6 --- /dev/null +++ b/Week 05/id_733/LeetCode_322_733.go @@ -0,0 +1,61 @@ +const IntMax = int(^uint(0) >> 1) + +func coinChange(coins []int, amount int) int { + memo := make([]int, amount+1) + res := doCoinChange(coins, amount, memo) + if res == IntMax { + return -1 + } + + return res +} + +func doCoinChange(coins []int, amount int, memo []int) int { + if amount < 0 { + return IntMax + } + + if amount == 0 { + return 0 + } + + if memo[amount] > 0 { + return memo[amount] + } + + m := IntMax + for _, c := range coins { + r := doCoinChange(coins, amount - c, memo) + if r < m { + m = r + 1 + } + } + + memo[amount] = m + return m +} + +func coinChangeV2(coins []int, amount int) int { + dp := make([]int, amount+1) + for i := 1; i < len(dp); i++ { + dp[i] = amount + 1 + } + + for i := 1; i <= amount; i++ { + for _, c := range coins { + if i < c { + continue + } + + if dp[i-c] < dp[i] { + dp[i] = dp[i-c] + 1 + } + } + } + + if dp[amount] > amount { + return -1 + } + + return dp[amount] +} diff --git a/Week 05/id_733/LeetCode_32_733.go b/Week 05/id_733/LeetCode_32_733.go new file mode 100644 index 000000000..7ebacb347 --- /dev/null +++ b/Week 05/id_733/LeetCode_32_733.go @@ -0,0 +1,23 @@ +func longestValidParentheses(s string) int { + res := 0 + dp := make([]int, len(s)+1) + for i := 1; i < len(s); i++ { + dpIdx := i + 1 + if s[i] == '(' { + dp[dpIdx] = 0 + } else if s[i-1] == '(' { + dp[dpIdx] = dp[dpIdx-2] + 2 + } else { + idx := i - 1 - dp[i] + if idx >= 0 && s[idx] == '(' { + dp[dpIdx] = dp[i] + dp[idx] + 2 + } + } + + if dp[dpIdx] > res { + res = dp[dpIdx] + } + } + + return res +} diff --git a/Week 05/id_733/LeetCode_509_733.go b/Week 05/id_733/LeetCode_509_733.go new file mode 100644 index 000000000..93dfddeae --- /dev/null +++ b/Week 05/id_733/LeetCode_509_733.go @@ -0,0 +1,13 @@ +func fib(N int) int { + if N <= 1 { + return N + } + + prev2, prev1 := 0, 1 + for i := 2; i <= N; i++ { + cur := prev1 + prev2 + prev1, prev2 = cur, prev1 + } + + return prev1 +} diff --git a/Week 05/id_733/LeetCode_53_733.go b/Week 05/id_733/LeetCode_53_733.go new file mode 100644 index 000000000..0a01efc16 --- /dev/null +++ b/Week 05/id_733/LeetCode_53_733.go @@ -0,0 +1,23 @@ +func maxSubArray(nums []int) int { + if len(nums) == 0 { + return 0 + } + + res := nums[0] + for i, prev := 1, nums[0]; i < len(nums); i++ { + var cur int + if prev < 0 { + cur = nums[i] + } else { + cur = prev + nums[i] + } + + if cur > res { + res = cur + } + + prev = cur + } + + return res +} diff --git a/Week 05/id_733/LeetCode_62_733.go b/Week 05/id_733/LeetCode_62_733.go new file mode 100644 index 000000000..7a6095316 --- /dev/null +++ b/Week 05/id_733/LeetCode_62_733.go @@ -0,0 +1,18 @@ +func uniquePaths(m int, n int) int { + if m <= 0 || n <= 0 { + return 0 + } + + dp := make([]int, m) + for j := range dp { + dp[j] = 1 + } + + for i := 1; i < n; i++ { + for j := 1; j < m; j++ { + dp[j] += dp[j-1] + } + } + + return dp[m-1] +} diff --git a/Week 05/id_733/LeetCode_63_733.go b/Week 05/id_733/LeetCode_63_733.go new file mode 100644 index 000000000..56ba0fe01 --- /dev/null +++ b/Week 05/id_733/LeetCode_63_733.go @@ -0,0 +1,29 @@ +func uniquePathsWithObstacles(obstacleGrid [][]int) int { + n := len(obstacleGrid) + if n == 0 { + return 0 + } + + m := len(obstacleGrid[0]) + if m == 0 { + return 0 + } + + dp := make([]int, m) + dp[0] = 1 + for i := 0; i < n; i++ { + if obstacleGrid[i][0] == 1 { + dp[0] = 0 + } + + for j := 1; j < m; j++ { + if obstacleGrid[i][j] == 1 { + dp[j] = 0 + } else { + dp[j] += dp[j-1] + } + } + } + + return dp[m-1] +} diff --git a/Week 05/id_733/LeetCode_64_733.go b/Week 05/id_733/LeetCode_64_733.go new file mode 100644 index 000000000..34038cde6 --- /dev/null +++ b/Week 05/id_733/LeetCode_64_733.go @@ -0,0 +1,34 @@ +func minPathSum(grid [][]int) int { + n := len(grid) + if n == 0 { + return 0 + } + + m := len(grid[0]) + if m == 0 { + return 0 + } + + dp := make([]int, m) + dp[0] = grid[0][0] + for j := 1; j < m; j++ { + dp[j] = dp[j-1] + grid[0][j] + } + + for i := 1; i < n; i++ { + dp[0] += grid[i][0] + for j := 1; j < m; j++ { + dp[j] = min(dp[j-1], dp[j]) + grid[i][j] + } + } + + return dp[m-1] +} + +func min(x, y int) int { + if x > y { + return y + } + + return x +} diff --git a/Week 05/id_733/LeetCode_70_733.go b/Week 05/id_733/LeetCode_70_733.go new file mode 100644 index 000000000..f076da137 --- /dev/null +++ b/Week 05/id_733/LeetCode_70_733.go @@ -0,0 +1,13 @@ +func climbStairs(n int) int { + if n == 1 { + return 1 + } + + prev1, prev2 := 2, 1 + for i := 3; i <= n; i++ { + cur := prev1 + prev2 + prev1, prev2 = cur, prev1 + } + + return prev1 +} diff --git a/Week 05/id_738/LeetCode_152_738.py b/Week 05/id_738/LeetCode_152_738.py new file mode 100644 index 000000000..6f7b8fa13 --- /dev/null +++ b/Week 05/id_738/LeetCode_152_738.py @@ -0,0 +1,57 @@ +class Solution(object): + def maxProduct(self, nums): + """ + 最大乘积子序列:https://leetcode-cn.com/problems/maximum-product-subarray/description/ + + :type nums: List[int] + :rtype: int + """ + # DP + # 1. 子问题分解: + # 因为有负数乘积,所以有2种情况 + # 1.1 nums[i]为负数,因为负数会使最小值变大,最大值变小,所以: + # problem_max(i) = max(problem_min(i - 1) * nums[i], nums[i]) + # 1.2 nums[i]为正数: + # problem_max(i) = max(problem_max(i - 1) * nums[i], nums[i]) + # 1.3 所以这里要定义一个problem_min的问题: + # nums[i]为负数: problem_min(i) = min(problem_max(i - 1) * nums[i], nums[i]) + # nums[i]为正数: problem_min(i) = min(problem_min(i - 1) * nums[i], nums[i]) + # 1. 定义状态: dp_min[i], dp_max[i] + # 2. DP方程: 略,问题分解改成dp_min, dp_max表示即可 + # dp_min = [nums[0]] * len(nums) + # dp_max = [nums[0]] * len(nums) + # for i in range(1, len(nums)): + # if nums[i] < 0: + # dp_max[i] = max(dp_min[i - 1] * nums[i], nums[i]) + # dp_min[i] = min(dp_max[i - 1] * nums[i], nums[i]) + # else: + # dp_max[i] = max(dp_max[i - 1] * nums[i], nums[i]) + # dp_min[i] = min(dp_min[i - 1] * nums[i], nums[i]) + # return max(dp_max) + + # 空间优化 + # 每次dp_min和dp_max都只需要最后一个值就可以推导出下一个值,所以只要2个变量来存储最大最小即可 + # dp_min变量永远比dp_max小,所以在nums[i]< 0 的时候,两者要互换下,才能正确得出dp_min[i]和dp_max[i] + dp_min = nums[0] + dp_max = nums[0] + result = dp_max + for i in range(1, len(nums)): + if nums[i] < 0: + dp_min, dp_max = dp_max, dp_min + dp_max = max(dp_max * nums[i], nums[i]) + dp_min = min(dp_min * nums[i], nums[i]) + result = max(result, dp_max) + return result + + + + + + + + + + + + + diff --git a/Week 05/id_738/LeetCode_32_738.py b/Week 05/id_738/LeetCode_32_738.py new file mode 100644 index 000000000..0aa6ed683 --- /dev/null +++ b/Week 05/id_738/LeetCode_32_738.py @@ -0,0 +1,23 @@ +class Solution(object): + def longestValidParentheses(self, s): + """ + 最长有效括号:https://leetcode-cn.com/problems/longest-valid-parentheses/ + + :type s: str + :rtype: int + """ + n = len(s) + if n == 0: return 0 + dp = [0] * n + res = 0 + for i in range(n): + if i>0 and s[i] == ")": + if s[i - 1] == "(": + dp[i] = dp[i - 2] + 2 + elif s[i - 1] == ")" and i - dp[i - 1] - 1 >= 0 and s[i - dp[i - 1] - 1] == "(": + dp[i] = dp[i - 1] + 2 + dp[i - dp[i - 1] - 2] + if dp[i] > res: + res = dp[i] + return res + + \ No newline at end of file diff --git a/Week 05/id_738/LeetCode_64_738.py b/Week 05/id_738/LeetCode_64_738.py new file mode 100644 index 000000000..9fed0129b --- /dev/null +++ b/Week 05/id_738/LeetCode_64_738.py @@ -0,0 +1,17 @@ +class Solution(object): + def minPathSum(self, grid): + """ + 最小路径和:https://leetcode-cn.com/problems/minimum-path-sum/ + + :type grid: List[List[int]] + :rtype: int + """ + for i in range(len(grid)): + for j in range(len(grid[0])): + if i == j == 0: continue + elif i == 0: grid[i][j] = grid[i][j - 1] + grid[i][j] + elif j == 0: grid[i][j] = grid[i - 1][j] + grid[i][j] + else: grid[i][j] = min(grid[i - 1][j], grid[i][j - 1]) + grid[i][j] + return grid[-1][-1] + + \ No newline at end of file diff --git a/Week 05/id_738/NOTE.md b/Week 05/id_738/NOTE.md index a6321d6e2..9894fadd9 100644 --- a/Week 05/id_738/NOTE.md +++ b/Week 05/id_738/NOTE.md @@ -1,4 +1,53 @@ -# NOTE +# 第五周-动态规划-学习总结 +## 知识点总结 +- 动态规划的本质 + - 寻找重复性 -> 计算机指令集 +- 动态规划的特点 + - 动态递归 + - 将复杂的问题分解成简单的子问题,使用递归方式 + - 本质上解决的问题:分治 + 最优子结构 +- 动态规划和分治的区别 + - 动态规划和递归或者分治没有本质的区别 + - 关键看有无最优子结构 + - 只是动态规划是分治的特殊形式,分治是一切普通解,动态规划是最优解。硬要说动态规划是分治也是可以的,只不过是它的最优解和普通解重合了而已 + - 共性:找到重复子结构 + - 差异性:最优子结构、中途可以淘汰次优解 + - 原分治可能要指数级的计算,经过淘汰次优解,就可以降到常数级或者平方级 +- 动态规划解题关键点 + - 最优子结构(分治):opt[n] = best_of(opt[n - 1], opt[n - 2], ...) + - 存储中间状态(一维数组或者二维数组):opt[i](面试最重要的一步) + - 递推公式(美其名曰:状态转移方程或者DP方程)(竞赛题最难的一步) + - 一维路径,比如:Fib: opt[i] = opt[n - 1] + opt[n - 2] + - 二维路径,比如:opt[i, j] = opt[i + 1][j] + opt[i][j + 1](且判断a[i, j]是否是空地) +- 动态规划解题技巧注意 + - 打破自己的思维惯性,形成机器思维(找重复性) + - 机器只有if else,for loop,递归 + - 理解复杂逻辑的关键,寻求递推公式 + - 不要人肉递归,放权,只要做开始一小段分析即可 + +## 本周DP习题感悟 +本周做了很多DP的系统,无论是老师讲解还是自己完成的习题。 + +从一开始,感觉对DP没思路,到按照老师给的三步骤(分解子问题,定义状态,列出DP方程)进行分步解题的后,渐渐掌握到DP题目的思路和关键。 + +我自己任务,DP解题的关键在于: +1. 将问题划分为子问题 +2. 能够将问题划分为子问题后,那么就要考虑状态是几个维度的,当一维不能满足子问题推导的时候(从感觉上来说,就是好像缺少了点题意的条件表达),就要晋升二维、三维等了 +3. 当子问题和状态维度定义出来后,DP方程自然而然也就出来了 +4. 剩下来就是代码的编写,但是这只是刚刚开始 +5. 代码编写后,虽然能通过,但是必然有空间的优化,毕竟开始定义的数组维度都"比较高",这时候就可以开始在4的代码下,开始画"表格"了。从画出来的表格,必然能够看到一些可以节省的维度空间。这时候就可以在表格的帮助下,完成空间降维优化 +6. 5中完成了一次空间降维优化,那么这个步骤就是再次考虑能够再继续降维:比如:一维数组降为常量,如果能,那么用常量代替一维数组,完成最终优化。 + +## 写在最后 +DP题目万变不离其宗,关键在于找到DP子问题的切分。子问题切分的难易度是DP问题难易度的体现。这方面能力的培养,千万要通过大量DP题练习,归纳和找到切分子问题的灵感! + + + + + + + + diff --git a/Week 05/id_738/pratice/LeetCode_1143_738.py b/Week 05/id_738/pratice/LeetCode_1143_738.py new file mode 100644 index 000000000..a72d2bd47 --- /dev/null +++ b/Week 05/id_738/pratice/LeetCode_1143_738.py @@ -0,0 +1,81 @@ +class Solution(object): + def longestCommonSubsequence(self, text1, text2): + """ + 最长公共子序列:https://leetcode-cn.com/problems/longest-common-subsequence/ + + :type text1: str + :type text2: str + :rtype: int + """ + # DP + # 先初始化第一行和第一列的值 + # 再用DP方程求解其他元素的值 + # 最后一个元素的值就是答案 + # m = len(text1) + # n = len(text2) + # dp = [[0] * n for _ in range(m)] + # # 初始化二维数组第一行和第一列 + # for i in range(m): + # if text1[i] == text2[0]: + # for j in range(i, m): + # dp[j][0] = 1 + # break + # else: + # dp[i][0] = 0 + + # for i in range(n): + # if text2[i] == text1[0]: + # for j in range(i, n): + # dp[0][j] = 1 + # break + # else: + # dp[0][i] = 0 + + # for i in range(1, m): + # for j in range(1, n): + # if text1[i] == text2[j]: + # dp[i][j] = dp[i - 1][j - 1] + 1 + # else: + # dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]) + # return dp[-1][-1] + +############################################################################### + + # 上面的DP,初始化的时候比较冗长, + # 根据DP递推公式,我们可以假设两个字符串最前面都有一个"什么都不是"的字符,和任意字符都不是同一个字符串。 + # 那么dp数组大小为 (m + 1) * (n + 1),其中第0行和第0列全部都为0(因为第一个字符不等于任意一个字符) + # 所以可以简化为: + m = len(text1) + n = len(text2) + dp = [[0] * (n + 1) for _ in range(m + 1)] + for i in range(1, m + 1): + for j in range(1, n + 1): + if text1[i - 1] == text2[j - 1]: + dp[i][j] = dp[i - 1][j - 1] + 1 + else: + dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]) + return dp[-1][-1] + +############################################################################### + + # 暴力法,提交超时,字符串小的时候没问题 + # 找出text1的所有子序列 + # q = [text1] + # while q: + # l = [] + # while q: + # s = q.pop() + # # 如果是子序列则return + # i = 0 + # for j in range(len(text2)): + # if s[i] == text2[j]: + # i += 1 + # if i == len(s): + # return i + # l.append(s) + # for s in l: + # for i in range(len(s)): + # new_s = s[:i] + s[i + 1:] + # if new_s and new_s not in q: + # q.append(new_s) + # return 0 \ No newline at end of file diff --git a/Week 05/id_738/pratice/LeetCode_120_738.py b/Week 05/id_738/pratice/LeetCode_120_738.py new file mode 100644 index 000000000..17257ef50 --- /dev/null +++ b/Week 05/id_738/pratice/LeetCode_120_738.py @@ -0,0 +1,45 @@ +class Solution(object): + def minimumTotal(self, triangle): + """ + 三角形的最小路径和:https://leetcode-cn.com/problems/triangle/description/ + + :type triangle: List[List[int]] + :rtype: int + """ + # 递归,提交超时 + # def dfs(level, i, val): + # if level == len(triangle): + # self.max_result = min(self.max_result, val) + # return + # dfs(level + 1, i, val + triangle[level][i]) + # dfs(level + 1, i + 1, val + triangle[level][i]) + + # self.max_result = float('inf') + # dfs(0, 0, 0) + # return self.max_result + +################################################################ + + # DP + # 1. 找重复性,分解子问题:problem(i, j) = problem(i, j + 1) + problem(i + 1, j + 1) + [i, j] + # 2. 定义状态(数组):f[i][j] + # 3. 寻找DP方程:f[i, j] = min(f[i + 1, j] + f[i + 1, j + 1]) + triangle[i][j] + m = len(triangle) + n = len(triangle[-1]) + f = [[0] * i for i in range(1, m + 1)] + f[-1] = triangle[-1][:] + for i in range(m - 1)[::-1]: + for j in range(len(triangle[i])): + f[i][j] = min(f[i + 1][j], f[i + 1][j + 1]) + triangle[i][j] + return f[0][0] + +################################################################ + + # 三角形为等边三角形,即第n行有n个元素。 + # 求解每一行的值,只需要上一行的值即可 + # 所以可以构造一个n长度的数组,存储上一行的结果,并原地修改作为本行的结果即可 + f = triangle[-1][:] + for i in range(m - 1)[::-1]: + for j in range(i + 1): + f[j] = min(f[j], f[j + 1]) + triangle[i][j] + return f[0] \ No newline at end of file diff --git a/Week 05/id_738/pratice/LeetCode_322_738.py b/Week 05/id_738/pratice/LeetCode_322_738.py new file mode 100644 index 000000000..e407f8149 --- /dev/null +++ b/Week 05/id_738/pratice/LeetCode_322_738.py @@ -0,0 +1,84 @@ +class Solution(object): + def coinChange(self, coins, amount): + """ + 零钱兑换:https://leetcode-cn.com/problems/coin-change/ + + :type coins: List[int] + :type amount: int + :rtype: int + """ + # DP,其实和爬楼梯一致,只是步伐为coins种而已 + # 1. 分治子问题:problem(n) = min(problem(n - k) for k in coins) + 1 + # 2. 状态定义:f[len(coins)] + # 3. DP方程:f(n) = min(f(n - k) for k in coins) + 1 + Max = amount + 1 + dp = [Max for _ in range(Max)] + dp[0] = 0 + for i in range(Max): + for coin in coins: + if i >= coin: + dp[i] = min(dp[i], dp[i - coin] + 1) + return dp[-1] if dp[-1] < Max else -1 + + + # BFS + # queue = [(0, amount)] + # visited = set([]) + # while queue: + # level, cur = queue.pop(0) + # if cur == 0: + # return level + # for coin in coins: + # next_cur = cur - coin + # if next_cur >= 0 and next_cur not in visited: + # queue.append((level + 1, next_cur)) + # visited.add(next_cur) + # return -1 + + + + + # BFS + # queue = [(0, amount)] + # visited = set() + # while queue: + # curs = [] + # while queue: + # curs.append(queue.pop()) + # for cur in curs: + # level, val = cur + # if val < 0: + # continue + # if val == 0: + # return level + # for coin in coins: + # new = val - coin + # if new not in visited: + # queue.append((level + 1, val - coin)) + # visited.add(new) + # return -1 + + + # BFS + # if amount == 0: + # return 0 + # level = 0 + # q = [a for a in coins] + # visited = set(q) + # while q: + # level += 1 + # l = [] + # while q: + # a = q.pop() + # if a == amount: + # return level + # l.append(a) + # for a in l: + # for c in coins: + # new = a + c + # if new == amount: + # return level + 1 + # elif new < amount and new not in visited: + # q.append(new) + # visited.add(new) + # return -1 \ No newline at end of file diff --git a/Week 05/id_738/pratice/LeetCode_53_738.py b/Week 05/id_738/pratice/LeetCode_53_738.py new file mode 100644 index 000000000..4c071c25f --- /dev/null +++ b/Week 05/id_738/pratice/LeetCode_53_738.py @@ -0,0 +1,35 @@ +class Solution(object): + def maxSubArray(self, nums): + """ + 最大子序和:https://leetcode-cn.com/problems/maximum-subarray/ + + :type nums: List[int] + :rtype: int + """ + # DP + # 定义problem(i)表示以nums[i]为结尾的最大子串值 + # 1. 切分子问题:problem(i) = max(problem(i - 1) + nums[i], nums[i]) + # 2. 定义状态数组:a[n],n为nums的长度,存储每个以i为结尾的最大子串值 + # 3. 推导DP方程:a[i] = max(a[i - 1] + nums[i], num[i]) + # 4. 最终结果就是a数组里面的最大值 + + # a = [0] * len(nums) + # a[0] = nums[0] + # for i in range(1, len(nums)): + # a[i] = max(a[i - 1] + nums[i], nums[i]) + # return max(a) + +############################################################ + + # DP存储空间优化: + # 我们只要求最大值,所以只用一个变量存储当前最大值即可 + # 其实不必存储所有的a[i],对于a[i]处理,我们只需要存储最近一个a[i]值即可 + result = nums[0] + nearest_a = nums[0] + for i in range(1, len(nums)): + nearest_a = max(nearest_a + nums[i], nums[i]) + result = max(nearest_a, result) + return result + + + diff --git a/Week 05/id_738/pratice/LeetCode_62_738.py b/Week 05/id_738/pratice/LeetCode_62_738.py new file mode 100644 index 000000000..bc4c67551 --- /dev/null +++ b/Week 05/id_738/pratice/LeetCode_62_738.py @@ -0,0 +1,55 @@ +class Solution(object): + def uniquePaths(self, m, n): + """ + 不同路径:https://leetcode-cn.com/problems/unique-paths/ + + :type m: int + :type n: int + :rtype: int + """ + # 正常逻辑:反序写法 + + # array = [[1] * n for _ in range(m)] + # for i in range(m - 1)[::-1]: + # for j in range(n - 1)[::-1]: + # array[i][j] = array[i][j + 1] + array[i + 1][j] + # return array[0][0] + + # 改写成正序写法 + # a = [[1] * n for _ in range(m)] + # for i in range(1, m): + # for j in range(1, n): + # a[i][j] = a[i - 1][j] + a[i][j - 1] + # return a[m - 1][n - 1] + + # 是否可以优化存储空间 + # 正序写法中:考虑到,二维矩阵a每行的值都是从左往右求解 + # 而且求解行的某个值,肯定是上一个求解值和上一行该位置的值之和。 + # 比如: + # 1 1 1 + # 1 2 3 + # 1 3 6 + # 可以看出,只用一个n的一维数组存储即可,不需要存储全部中间值: + # a[j] += a[j - 1] + a = [1] * n + for i in range(1, m): + for j in range(1, n): + a[j] += a[j - 1] + return a[-1] + + + + + + + + + + + + + + + + + diff --git a/Week 05/id_738/pratice/LeetCode_63_738.py b/Week 05/id_738/pratice/LeetCode_63_738.py new file mode 100644 index 000000000..435265e9a --- /dev/null +++ b/Week 05/id_738/pratice/LeetCode_63_738.py @@ -0,0 +1,26 @@ +class Solution(object): + def uniquePathsWithObstacles(self, obstacleGrid): + """ + 不同路径2:https://leetcode-cn.com/problems/unique-paths-ii/ + + :type obstacleGrid: List[List[int]] + :rtype: int + """ + m = len(obstacleGrid) + n = len(obstacleGrid[0]) + a = [0] * n + a[0] = 1 + for row in obstacleGrid: + for i in range(n): + if row[i] == 1: + a[i] = 0 + elif i > 0: + a[i] += a[i - 1] + return a[-1] + + + + + + + diff --git a/Week 05/id_738/pratice/LeetCode_70_738.py b/Week 05/id_738/pratice/LeetCode_70_738.py new file mode 100644 index 000000000..a9500542e --- /dev/null +++ b/Week 05/id_738/pratice/LeetCode_70_738.py @@ -0,0 +1,103 @@ +class Solution(object): + def climbStairs(self, n): + """ + 爬楼梯问题:https://leetcode-cn.com/problems/climbing-stairs/description/ + + :type n: int + :rtype: int + """ + + # DP初始分析 + # 1.分解子问题:problem(i) = problem(i - 1) + problem(i - 2) + # 2. 定义状态:dp[i] + # 3. DP方程:dp[i] = dp[i - 1] + dp[i - 2] + # dp = [0 for i in range(n)] + # for i in range(n): + # if i >= 1: + # dp[i] += dp[i - 1] + # if i >= 2: + # dp[i] += dp[i - 2] + # return dp[-1] +################################################### + # DP优化空间 + # a, b = 1, 2 + # for i in range(1, n): + # a, b = b, a + b + # return a +################################################### + # 分治DFS + # def dfs(cur): + # if cur == 0: + # self.result += 1 + # elif cur > 0: + # dfs(cur - 1) + # dfs(cur - 2) + + # self.result = 0 + # dfs(n) + # return self.result + +################################################### + # BFS + # q = [0] + # result = 0 + # while q: + # l = [] + # while q: + # cur = q.pop() + # if cur == n: + # result += 1 + # elif cur < n: + # l.append(cur) + # for cur in l: + # q.append(cur + 1) + # q.append(cur + 2) + # return result +################################################### + # 递归 + # if n == 1 or n == 2: + # return n + # return self.climbStairs(n - 1) + self.climbStairs(n - 2) +################################################### + # 从上往下递归 + # def climb(cur): + # if cur == n: + # return 1 + # if cur > n: + # return 0 + # return climb(cur + 1) + climb(cur + 2) + # return climb(0) +################################################### + + # 从上往下递归-优化-存储中间状态 + # visited = [0 for i in range(n)] + # def climb(cur): + # if cur == n: + # return 1 + # if cur > n: + # return 0 + # if visited[cur] > 0: + # return visited[cur] + # visited[cur] = climb(cur + 1) + climb(cur + 2) + # return visited[cur] + # return climb(0) + +################################################### + # 扩展1:如果题目改为爬楼梯,但是:可以有1,2,3步走 (easy) + if n <= 2: + return n + a, b, c = 1, 2, 4 + for i in range(3, n): + tmp = a + b + a = b + b = c + c += tmp + return c + + # 扩展2: + # 1. 可以有1,2,3步走 (easy) + # 2. 相邻两步不能走一样,即:不能走1,1,1 / 1, 2, 2这样的步伐 (meduim) + # 应该如何解答? + + + \ No newline at end of file diff --git "a/Week 05/id_738/\346\211\223\345\256\266\345\212\253\350\210\215\351\227\256\351\242\230/LeetCode_198_738.py" "b/Week 05/id_738/\346\211\223\345\256\266\345\212\253\350\210\215\351\227\256\351\242\230/LeetCode_198_738.py" new file mode 100644 index 000000000..167e8643e --- /dev/null +++ "b/Week 05/id_738/\346\211\223\345\256\266\345\212\253\350\210\215\351\227\256\351\242\230/LeetCode_198_738.py" @@ -0,0 +1,69 @@ +class Solution(object): + def rob(self, nums): + """ + 打家劫舍: https://leetcode-cn.com/problems/house-robber/ + + :type nums: List[int] + :rtype: int + """ + # DP初始解答问题: + # 1. 分解子问题: dp[i]表示第i间房屋偷盗后目前的偷到的最高金额 + # 如果定义: dp[i] = dp[i - 1] + nums[i],会发现题意不允许相邻两个房子被盗窃,用如果用这个方程表示的话,我们不知道i-1个房子是否已经被盗窃了,如果被盗窃了这个方程就不可用,如果没被盗窃这个方程就可用 + # 故而,需要都一个维度来表示i-1房子是否被盗窃,所以我们开启第二个维度来表示第i个房子是否被盗窃 + # 定义: dp[i][0]表示第i个房子没有被盗窃,,dp[i][1]表示第i个房子已经被盗窃 + # 那么子问题变化为: + # dp[i][0] = max(dp[i - 1][0], dp[i - 1][1]) + # dp[i][1] = dp[i - 1][0] + nums[i] + # 2. 定义状态: dp[i][j] {j=0,1, i=0...len(nums) - 1} + # 3. dp方程 + + # if not nums: + # return 0 + # dp = [[0,0] for _ in range(len(nums))] + # dp[0][1] = nums[0] + # for i in range(1, len(nums)): + # dp[i][0] = max(dp[i - 1][0], dp[i - 1][1]) + # dp[i][1] = dp[i - 1][0] + nums[i] + # return max(dp[-1]) +################################################################ + # 上述DP解法需要定义二维数组 + # 这里我们做空间优化,把它简化为一维数组 + # 问题转化:原题求解可以转化为,max(每个房屋必偷的最大金额) + # 所以我们定义状态: dp[i]为第i个房屋必偷的最大金额 + # dp方程: dp[i] = max(dp[i - 1] + 0, dp[i - 2] + nums[i], .... , dp[1] + nums[i], dp[0] + nums[i]) + # 很明显,dp[i] <= dp[i - 1] + # 所以: dp[i] = max(dp[i - 1] + 0, dp[i - 2] + nums[i]) + + # if not nums: + # return 0 + # # 这里做技术优化,设定dp[0]为0,dp[1]为真正对应nums[0]的房屋,这样就不用判断dp长度是否大于等于2了 + # dp = [0 for _ in range(len(nums) + 1)] + # dp[0] = 0 + # dp[1] = nums[0] + # for i in range(2, len(nums) + 1): + # dp[i] = max(dp[i - 1] + 0, dp[i - 2] + nums[i - 1]) + # # 因为a[i - 2]必然是小于a[i]的,所以只需在最后return的时候,取a[i -1]和a[i]的最小值就行了。 + # return max(dp[-1], dp[-2]) +################################################################ + # 由上面的优化,我们看到,其实我们只需2个状态,来存储第 i - 1和第 i - 2个房屋的状态 + # 那么空间变量还可以优化:只定义2个变量即可 + a = b = 0 + for num in nums: + a, b = b, max(b, a + num) + return max(a, b) + + + + + +################################################################ + +################################################################ + + + + + + + + diff --git "a/Week 05/id_738/\346\211\223\345\256\266\345\212\253\350\210\215\351\227\256\351\242\230/LeetCode_213_738.py" "b/Week 05/id_738/\346\211\223\345\256\266\345\212\253\350\210\215\351\227\256\351\242\230/LeetCode_213_738.py" new file mode 100644 index 000000000..36f6d67bd --- /dev/null +++ "b/Week 05/id_738/\346\211\223\345\256\266\345\212\253\350\210\215\351\227\256\351\242\230/LeetCode_213_738.py" @@ -0,0 +1,21 @@ +class Solution(object): + def rob(self, nums): + """ + 打家劫舍2: https://leetcode-cn.com/problems/house-robber-ii/description/ + + :type nums: List[int] + :rtype: int + """ + # 打家劫舍1问题的比较复杂版,第一个房屋和最后一个房屋相连 + # 直接拆分成2个问题,简化成打家劫舍1问题 + # 1. 不偷盗第一个房子,求出最大金额 + # 2. 不偷盗最后一个房子,求出最大金额 + # 1和2的最大者为题解 + def f(rooms): + a = b = 0 + for room in rooms: + a, b = b, max(b, a + room) + return max(a, b) + return max(f(nums[1:]), f(nums[: -1])) if len(nums) != 1 else nums[0] + + diff --git "a/Week 05/id_738/\350\202\241\347\245\250\351\227\256\351\242\230\351\233\206\345\220\210/LeetCode_121_738.py" "b/Week 05/id_738/\350\202\241\347\245\250\351\227\256\351\242\230\351\233\206\345\220\210/LeetCode_121_738.py" new file mode 100644 index 000000000..24dedd811 --- /dev/null +++ "b/Week 05/id_738/\350\202\241\347\245\250\351\227\256\351\242\230\351\233\206\345\220\210/LeetCode_121_738.py" @@ -0,0 +1,18 @@ +class Solution(object): + def maxProfit(self, prices): + """ + 买卖股票的最佳时机:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/submissions/ + + :type prices: List[int] + :rtype: int + """ + # 最多只能完成一笔交易 + max_profit, min_price = 0, float('inf') + for price in prices: + if min_price > price: + min_price = price + else: + max_profit = max(price - min_price, max_profit) + return max_profit + + diff --git "a/Week 05/id_738/\350\202\241\347\245\250\351\227\256\351\242\230\351\233\206\345\220\210/LeetCode_122_738.py" "b/Week 05/id_738/\350\202\241\347\245\250\351\227\256\351\242\230\351\233\206\345\220\210/LeetCode_122_738.py" new file mode 100644 index 000000000..682e935f1 --- /dev/null +++ "b/Week 05/id_738/\350\202\241\347\245\250\351\227\256\351\242\230\351\233\206\345\220\210/LeetCode_122_738.py" @@ -0,0 +1,13 @@ +class Solution(object): + def maxProfit(self, prices): + """ + 买卖股票的最佳时机2: https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/ + + :type prices: List[int] + :rtype: int + """ + result = 0 + for i in range(len(prices) - 1): + if prices[i] < prices[i + 1]: + result += prices[i + 1] - prices[i] + return result diff --git "a/Week 05/id_738/\350\202\241\347\245\250\351\227\256\351\242\230\351\233\206\345\220\210/LeetCode_123_738.py" "b/Week 05/id_738/\350\202\241\347\245\250\351\227\256\351\242\230\351\233\206\345\220\210/LeetCode_123_738.py" new file mode 100644 index 000000000..c058f37d6 --- /dev/null +++ "b/Week 05/id_738/\350\202\241\347\245\250\351\227\256\351\242\230\351\233\206\345\220\210/LeetCode_123_738.py" @@ -0,0 +1,105 @@ +class Solution(object): + def maxProfit(self, prices): + """ + 股票最佳买卖时机3:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/ + + :type prices: List[int] + :rtype: int + """ + # DP + # 1. 子问题分解: + # 如果,定义a[i]为第i天的最大利润,发现问题无法求解,因为这里面涉及到买卖并且限制最大买卖次数问题,故尝试用二维来定义 + # 定义a[i][k]为第i天,买卖k次的最大利润 + # 那么如果第i天不买股票,a[i][k] = a[i - 1][k] + # 如果第i天买进股票,a[i][k] = max(prices[i] - 第一天买进股票价格, prices[i] - 第二天买进股票价格 + 第一天买卖第k - 1次的最大利润, .... , prices[i] - 第 i - 1 天买进股票价格 + 第 i - 2 天买卖第k - 1次的最大利润) + # 上述文字用方程表示即:a[i][k] = max(prices[i] - prices[0], prices[i] - prices[1] + a[0][k - 1], ... , prices[i] - prices[i - 1] + a[i - 2][k - 1]) + # 2. 定义状态:a[i][k] {i = [0, len(prices)), k = [0, K=2]} + # 3. dp方程 a[i][k] = max(a[i - 1][k], prices[i] - prices[0], prices[i] - prices[1] + a[0][k - 1], ... , prices[i] - prices[i - 1] + a[i - 2][k - 1]) + # 初始题解,提交最后一个超大测试用例超时 + # if not prices: + # return 0 + # K = 2 + # a = [ [0] * (K + 1) for _ in range(len(prices)) ] + # for i in range(1, len(prices)): + # for k in range(1, K + 1): + # mi = prices[0] + # for j in range(1, i): + # mi = min(mi, (prices[j] - a[j - 1][k - 1])) + # a[i][k] = max(a[i - 1][k], prices[i] - mi) + # return a[-1][-1] +############################################################################### + # 优化题解:减去重复计算 + # 上面解法其实存在重复计算:最内层循环 for j in range(i) 每次i增大1,都会重复计算j->i-2次循环体内容 + # 所以可以直接优化,从 for j in range(1) 就开始保留前面已经计算的循环体内容结果,这样 for j in range(2)就可以利用for j in range(1)求解出来的min值再继续和a[2][k - 1]比较即可 + # 所以可以去掉最外层循环 for i in range(1, len(prices)),将最内层循环改成 for i in range(1, len(prices)),同时对于每个交易次数k,最初始的mi一定是prices[0] + # 如果画出二维方格,我们可以看到,每次更新都是按列递增更新的。 + # if not prices: + # return 0 + # K = 2 + # a = [ [0] * (K + 1) for _ in range(len(prices)) ] + # for k in range(1, K + 1): + # mi = prices[0] + # for i in range(1, len(prices)): + # mi = min(mi, (prices[i] - a[i - 1][k - 1])) + # a[i][k] = max(a[i - 1][k], prices[i] - mi) + # return a[-1][-1] +############################################################################### + # 继续优化题解:减去所需的数组空间,化解二维为一维数组 + # 由上面可以看到,我们每次更新都是固定K值,然后按二维网格的列更新的 + # 所以,我们无需存储整个网格内容,只需存储一列的内容即可 + # 由于考虑到 行会比列大,我们点到行列,按行更新的话,就只要一个更小的空间,大小为列数即可 + # 我们要实现行列颠倒,就要存储每一行的上一个最小值:mi = [prices[0]] * (K + 1) + + # if not prices: + # return 0 + # K = 2 + # mi = [prices[0]] * (K + 1) + # a = [ [0] * (K + 1) for _ in range(len(prices)) ] + # for i in range(1, len(prices)): + # for k in range(1, K + 1): + # mi[k] = min(mi[k], (prices[i] - a[i - 1][k - 1])) + # a[i][k] = max(a[i - 1][k], prices[i] - mi[k]) + # return a[-1][-1] + +############################################################################### + + # 上面能解答出正确答案,但是a还是二维数组,不过下面我们马上把它变成一维数组: + # 可以看出,按行更新,每次只需要依赖前面一行的数即可,那么我们只需要存储上一行的数 + + # if not prices: + # return 0 + # K = 2 + # mi = [prices[0]] * (K + 1) + # a = [0] * (K + 1) + # for i in range(1, len(prices)): + # aa = a[:] + # for k in range(1, K + 1): + # mi[k] = min(mi[k], (prices[i] - aa[k - 1])) + # a[k] = max(aa[k], prices[i] - mi[k]) + # return a[-1] + +############################################################################### + + # 这里有个很别扭的语句:aa = a[:],我们如果要把它去掉,那么就要让上上的优化这句代码: + # mi[k] = min(mi[k], (prices[i] - a[i - 1][k - 1])) 变成 + # mi[k] = min(mi[k], (prices[i] - a[i][k - 1])) + # 这样,a每次都依赖于当前行的k-1值(当前行只有在本次循环才会被更新,所以不需要保留前一行的副本) + # 其实按照题意,第i天的k-1次买卖,其实和第i - 1天的第k - 1次买卖是相同的 + # 故而mi[k] = min(mi[k], (prices[i] - a[i][k - 1]))是成立的,所以代码可以转化为 + + if not prices: + return 0 + K = 2 + mi = [prices[0]] * (K + 1) + a = [0] * (K + 1) + for i in range(1, len(prices)): + for k in range(1, K + 1): + mi[k] = min(mi[k], (prices[i] - a[k - 1])) + a[k] = max(a[k], prices[i] - mi[k]) + return a[-1] + + + + + + diff --git "a/Week 05/id_738/\350\202\241\347\245\250\351\227\256\351\242\230\351\233\206\345\220\210/LeetCode_309_738.py" "b/Week 05/id_738/\350\202\241\347\245\250\351\227\256\351\242\230\351\233\206\345\220\210/LeetCode_309_738.py" new file mode 100644 index 000000000..5c8d62228 --- /dev/null +++ "b/Week 05/id_738/\350\202\241\347\245\250\351\227\256\351\242\230\351\233\206\345\220\210/LeetCode_309_738.py" @@ -0,0 +1,90 @@ +class Solution(object): + def maxProfit(self, prices): + """ + 最佳买卖股票时期含冷冻期:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/ + + :type prices: List[int] + :rtype: int + """ + # DP + # 1. 子问题分解:设dp[i] 为第i天股票的最大利润 + # 如果第i天没交易: dp[i] = dp[i - 1] + # 如果第i天买入: dp[i]肯定不是最优解,忽略掉 + # 如果第i天卖出: dp[i] = max(dp[i - 1], prices[i] - prices[j] + dp[j - 2], ... , prices[i] - prices[3] + dp[1], prices[i] - prices[2] + dp[0]) { j = i - 1 } + # dp[0] = 0 + # i == 1, dp[1] = max(dp[0], prices[1] - prices[0]) + # i == 2, dp[2] = max(dp[1], prices[2] - prices[1], prices[2] - prices[0]) + # 2. 状态定义: dp[i] + # 3. DP方程: + # i >= 3, dp[i] = max(dp[i - 1], prices[i] - prices[j] + dp[j - 2], ... , prices[i] - prices[3] + dp[1], prices[i] - prices[2] + dp[0]) { j = i - 1 } + # i == 1, dp[1] = max(dp[0], prices[1] - prices[0]) + # i == 2, dp[2] = max(dp[1], prices[2] - prices[j], prices[2] - prices[j - 1]) { j = i - 1 } + + # if not prices: + # return 0 + # dp = [0 for _ in range(len(prices))] + # for i in range(1, len(prices)): + # # DP方程第一个初始化就是dp[i - 1] + # dp[i] = dp[i - 1] + # for j in range(i): + # diff_price = prices[i] - prices[j] + # if j >= 2: + # dp[i] = max(dp[i], diff_price + dp[j - 2]) + # else: + # dp[i] = max(dp[i], diff_price) + # return dp[-1] + +############################################################################### + + # 优化上述DP,在 for j in range(i) 的时候,每次都要重复计算很多 diff_price + dp[j - 2] + # 可以优化成依赖上一次计算结果的max值 + + # if not prices: + # return 0 + # dp = [0 for _ in range(len(prices))] + # mi = float('inf') + # for i in range(1, len(prices)): + # mi = min(mi, prices[i - 1] - dp[i - 3]) + # if i == 1: + # dp[1] = max(dp[0], prices[1] - prices[0]) + # elif i == 2: + # dp[2] = max(dp[1], prices[2] - prices[1], prices[2] - prices[0]) + # else: + # dp[i] = max(dp[i - 1], prices[i] - mi) + # return dp[-1] + +############################################################################### + + # 空间优化:直接用一个长度为4的数组存储即可 + if not prices: + return 0 + dp = [0] * 4 + mi = float('inf') + r_i = -1 if len(prices) > 4 else len(prices) - 1 + for i in range(1, len(prices)): + mi = min(mi, prices[i - 1] - dp[0]) + if i == 1: + dp[1] = max(dp[0], prices[1] - prices[0]) + elif i == 2: + dp[2] = max(dp[1], prices[2] - prices[1], prices[2] - prices[0]) + else: + dp[3] = max(dp[2], prices[i] - mi) + dp[0], dp[1], dp[2] = dp[1], dp[2], dp[3] + return dp[r_i] + + + + + + + + + + + + + + + + + diff --git a/Week 05/id_748/LeetCode_64_748_minPathSum.java b/Week 05/id_748/LeetCode_64_748_minPathSum.java new file mode 100644 index 000000000..483a6c9d9 --- /dev/null +++ b/Week 05/id_748/LeetCode_64_748_minPathSum.java @@ -0,0 +1,46 @@ +package com.code.week5; + + +/** + * 给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 + * + * 说明:每次只能向下或者向右移动一步。 + * + * 示例: + * + * 输入: + * [ + *   [1,3,1], + * [1,5,1], + * [4,2,1] + * ] + * 输出: 7 + * 解释: 因为路径 1→3→1→1→1 的总和最小。 + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/minimum-path-sum + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + +public class LeetCode_64_748_minPathSum { + + public int minPathSum(int[][] grid) { + int[] dp = new int[grid[0].length]; + for (int i = grid.length - 1; i >= 0; i--) { + for (int j = grid[0].length - 1; j >= 0; j--) { + if(i == grid.length - 1 && j != grid[0].length - 1) + dp[j] = grid[i][j] + dp[j + 1]; + else if(j == grid[0].length - 1 && i != grid.length - 1) + dp[j] = grid[i][j] + dp[j]; + else if(j != grid[0].length - 1 && i != grid.length - 1) + dp[j] = grid[i][j] + Math.min(dp[j], dp[j + 1]); + else + dp[j] = grid[i][j]; + } + } + return dp[0]; + } + + +} diff --git a/Week 05/id_748/LeetCode_91_748_numDecodings.java b/Week 05/id_748/LeetCode_91_748_numDecodings.java new file mode 100644 index 000000000..e9831cd7d --- /dev/null +++ b/Week 05/id_748/LeetCode_91_748_numDecodings.java @@ -0,0 +1,24 @@ +package com.code.week5; + + + +public class LeetCode_91_748_numDecodings { + + public int numDecodings(String s) { + + if (s[0] == '0') return 0; + int pre = 1, curr = 1; + for (int i = 1; i < s.size(); i++) { + int tmp = curr; + if (s[i] == '0') + if (s[i - 1] == '1' || s[i - 1] == '2') curr = pre; + else return 0; + else if (s[i - 1] == '1' || (s[i - 1] == '2' && s[i] >= '1' && s[i] <= '6')) + curr = curr + pre; + pre = tmp; + } + return curr; + + } + +} \ No newline at end of file diff --git a/Week 05/id_748/NOTE.md b/Week 05/id_748/NOTE.md index a6321d6e2..13268e367 100644 --- a/Week 05/id_748/NOTE.md +++ b/Week 05/id_748/NOTE.md @@ -1,4 +1,31 @@ -# NOTE +# 动态规划 +* 动态规划(Dynamic programming,简称DP)。 +* 动态规划的核心思想是把原问题分解成子问题进行求解,也就是分治的思想。 +# 动态规划问题,大致可以通过以下四部进行解决。 + +1. 划分状态,即划分子问题,例如上面的例子,我们可以认为每个组下面、每个部门、每个中心下面最优秀的3个人,都是全公司最优秀的3个人的子问题 + +2. 状态表示,即如何让计算机理解子问题。上述例子,我们可以实用f[i][3]表示第i个人,他手下最优秀的3个人是谁。 + +3. 状态转移,即父问题是如何由子问题推导出来的。上述例子,每个人大Leader下面最优秀的人等于他下面的小Leader中最优秀的人中最优秀的几个。 + +4. 确定边界,确定初始状态是什么?最小的子问题?最终状态又是什么。例如上述问题,最小的子问题就是每个小组长下面最优秀的人,最终状态是整个企业,初始状态为每个领导下面都没有最优名单,但是小组长下面拥有每个人的评分。 + + +# 实现方法 + +## 自底向上 +简单来说就是根据初始状态,逐步推导到最终状态,而这个转移的过程,必定是一个拓扑序。如何理解这个拓扑序问题呢,甲总监下面有X,Y,Z两个小组,甲总监不会一拿到X组最优秀的三个人,就立马去跟A经理汇报,而是要等到Y,Z小组也选出来之后,也就是自己下面所有子问题都解决了,才会继续向汇报。如果推导的过程不是一个拓扑序,那么要么得到错误的结果,要么算法就要退化。 + +自底向上一般用来解决什么问题呢?那就是可以轻松确定拓扑序的问题,例如线性模型,都是从左往右进行转移,区间模型,一般都是从小区间推导到大区间。自底向上的一个经典实现是斐波那楔数列的递推实现,即F[i] = F[i - 1] + F[i - 2]。 + + +## 自顶向下 +也就是从最终状态出发,如果遇到一个子问题还未求解,那么就先求解子问题。如果子问题已经求解,那么直接使用子问题的解,所以自顶向下动态规划又有一个形象生动的名字,叫做记忆化搜索,一般我们采用递归的方式进行求解。 + +## 使用场景 +自顶向下,我们一般用在树上面,因为我们根据父亲结点,很容易找到所有的子问题,也就是所有的子结点,而自底向上的话,我们要去统计这个结点的所有兄弟结点是否已经实现。会稍微复杂一点,而且比较难理解。 + diff --git a/Week 06/id_008/LeetCode_127_008.js b/Week 06/id_008/LeetCode_127_008.js new file mode 100644 index 000000000..838c79f57 --- /dev/null +++ b/Week 06/id_008/LeetCode_127_008.js @@ -0,0 +1,75 @@ +/** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ + +// 双向 BFS 优化 + +var ladderLength = function(beginWord, endWord, wordList) { + var dict = new Set(wordList); + + if (!dict.has(endWord)) { + return 0; + } + + var queueA = [beginWord]; + var queueB = [ endWord]; + var levelA = {}; + var levelB = {}; + levelA[beginWord] = 1; + levelB[ endWord] = 1; + + var level; + + while (queueA.length && queueB.length) { + level = check(queueA, levelA, queueB, levelB); + + if (level) { + return level; + } + + level = check(queueB, levelB, queueA, levelA); + + if (level) { + return level; + } + } + + return 0; + + function check(thisQ, thisL, thatQ, thatL){ + var word = thisQ.shift(); + var plus = thisL[word] + 1; + var list = gen(word); + + for (var i = 0; i < list.length; ++i) { + if (thisL[list[i]]) { + continue; + } + + if (thatL[list[i]]) { + return thisL[word] + thatL[list[i]]; + } + + thisQ.push(list[i]); + thisL[list[i]] = plus; + } + + return 0; + } + + function gen(s){ + var result = []; + + for (var i = 0; i < s.length; ++i) { + for (var j = 0; j < 26; ++j) { + var word = s.substring(0, i) + String.fromCharCode(j + 97) + s.substring(i + 1); + dict.has(word) && word !== s && result.push(word); + } + } + + return result; + } +}; diff --git a/Week 06/id_008/LeetCode_130_008.js b/Week 06/id_008/LeetCode_130_008.js new file mode 100644 index 000000000..938ca8f35 --- /dev/null +++ b/Week 06/id_008/LeetCode_130_008.js @@ -0,0 +1,70 @@ +/** + * @param {character[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ +var solve = function(board) { + if (!board || board.length <= 0 || board[0].length <= 0) { + return board; + } + + // 先把与边界连通的 O 全换成 P + for (var i = 0; i < board[0].length; ++i) { + if (board[0][i] === "O") { + fillP(0, i); + } + + if (board[board.length - 1][i] === "O") { + fillP(board.length - 1, i); + } + } + + for (var i = 1; i < board.length - 1; ++i) { + if (board[i][0] === "O") { + fillP(i, 0); + } + + if (board[i][board[0].length - 1] === "O") { + fillP(i, board[0].length - 1); + } + } + + // 然后将剩下的 O 换成 X + for (var i = 0; i < board.length; ++i) { + for (j = 0; j < board[0].length; ++j) { + if (board[i][j] === "O") { + board[i][j] = "X"; + } + } + } + + // 最后将 P 换回 O + for (var i = 0; i < board.length; ++i) { + for (j = 0; j < board[0].length; ++j) { + if (board[i][j] === "P") { + board[i][j] = "O"; + } + } + } + + return board; + + function fillP(x, y){ + if (x < 0 || y < 0) { + return false; + } + + if (x >= board.length || y >= board[0].length) { + return false; + } + + if (board[x][y] !== "O") { + return false; + } + + board[x][y] = "P"; + fillP(x + 1, y); + fillP(x - 1, y); + fillP(x, y + 1); + fillP(x, y - 1); + } +}; diff --git a/Week 06/id_008/LeetCode_200_008.js b/Week 06/id_008/LeetCode_200_008.js new file mode 100644 index 000000000..2d0cebd59 --- /dev/null +++ b/Week 06/id_008/LeetCode_200_008.js @@ -0,0 +1,44 @@ +/** + * @param {character[][]} grid + * @return {number} + */ +var numIslands = function(grid) { + var result = 0; + + while (1) { + var island = hasIsland(); + + if (!~island[0]) { + break; + } + + dropIsland(island[0], island[1]); + ++result; + } + + return result; + + function hasIsland(){ + for (var i = 0; i < grid.length; ++i) { + for (var j = 0; j < grid[i].length; ++j) { + if (grid[i][j] === "1") { + return [i, j]; + } + } + } + + return [-1, -1]; + } + + function dropIsland(i, j){ + if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] === "0") { + return false; + } + + grid[i][j] = "0"; + dropIsland(i + 1, j); + dropIsland(i - 1, j); + dropIsland(i, j + 1); + dropIsland(i, j - 1); + } +}; diff --git a/Week 06/id_008/LeetCode_22_008.js b/Week 06/id_008/LeetCode_22_008.js new file mode 100644 index 000000000..0a06d7129 --- /dev/null +++ b/Week 06/id_008/LeetCode_22_008.js @@ -0,0 +1,75 @@ +/** + * @param {number} n + * @return {string[]} + */ +var generateParenthesis = function(n) { + var cache = [[]]; + + gen(n); + return cache[n]; + + function gen(k){ + if (cache[k]) { + return cache[k]; + } + + if (k <= 0) { + return []; + } + + var next = gen(k - 1); + var _r = []; + + _r.push("()" + (next[0] || "")); + + for (var i = 0; i < next.length; ++i) { + var arr = next[i].split(""); + + for (var j = 0; j < arr.length; ++j) { + var temp = arr.slice(0); + temp[j] += "()"; + _r.push(temp.join("")); + } + } + + _r = distinct(_r); + cache[k] = _r; + return _r; + } + + function distinct(arr){ + var result = []; + + for (var i = 0; i < arr.length; ++i) { + !~result.indexOf(arr[i]) && result.push(arr[i]); + } + + return result; + } +}; + +/** + * @param {number} n + * @return {string[]} + */ +var generateParenthesis = function(n) { + var result = []; + putChar("", 0, 0); + + function putChar(s, L, R){ + if (L === n && R === n) { + result.push(s); + return null; + } + + if (L < n) { + putChar(s + "(", L + 1, R); + } + + if (R < L) { + putChar(s + ")", L, R + 1); + } + } + + return result; +}; diff --git a/Week 06/id_008/LeetCode_433_008.js b/Week 06/id_008/LeetCode_433_008.js new file mode 100644 index 000000000..05355a38a --- /dev/null +++ b/Week 06/id_008/LeetCode_433_008.js @@ -0,0 +1,61 @@ +/** + * @param {string} start + * @param {string} end + * @param {string[]} bank + * @return {number} + */ +var minMutation = function(start, end, bank) { + if (!~bank.indexOf(end)) { + return -1; + } + + var visited = [start]; + var queue = [start]; + var level = {}; + var node; + var str; + + level[start] = 0; + + while (str = queue.shift()) { + var search = gen(str); + + for (var i = 0; i < search.length; ++i) { + if (search[i] === end) { + return level[str] + 1; + } + + queue.push(search[i]); + visited.push(search[i]); + level[search[i]] = level[str] + 1; + } + } + + return -1; + + function gen(s){ + var result = []; + + for (var i = 0; i < bank.length; ++i) { + if (compare(s, bank[i]) === 1 && !~visited.indexOf(bank[i])) { + result.push(bank[i]); + } + } + + return result; + } + + function compare(a, b){ + var total = 0; + + for (var i = 0; i < 8; ++i) { + a.charAt(i) === b.charAt(i) || ++total; + + if (total > 1) { + return 2; + } + } + + return total; + } +}; diff --git a/Week 06/id_008/LeetCode_51_008.js b/Week 06/id_008/LeetCode_51_008.js new file mode 100644 index 000000000..d23c61c91 --- /dev/null +++ b/Week 06/id_008/LeetCode_51_008.js @@ -0,0 +1,108 @@ +/** + * @param {number} n + * @return {string[][]} + */ +var solveNQueens = function(n) { + // 生成地图,外围扩大一圈,用于边界判断 + var map = []; + + for (var i = 0; i < n + 2; ++i) { + map.push(0); + } + + for (var i = 0; i < n; ++i) { + map.push(0); + + for (j = 0; j < n; ++j) { + map.push("."); + } + + map.push(0); + } + + for (var i = 0; i < n + 2; ++i) { + map.push(0); + } + + // 生成步进,只搜索上面,左右及下面都不需要搜索 + var step = [-n - 3, -n - 2, -n - 1]; + + var result = []; + + // 从第一行开始递归 + putPieceToLine(0); + + return result; + + // 指定行放棋子 + function putPieceToLine(line){ + if (line === n) { + pushResult(); + return true; + } + + var blankPoint = findBlankFromLine(line); + + if (blankPoint.length === 0) { + return false; + } + + for (var i = 0; i < blankPoint.length; ++i) { + map[blankPoint[i]] = "Q"; + putPieceToLine(line + 1); + map[blankPoint[i]] = "."; + } + } + + // 搜索指定行可放棋子点位 + function findBlankFromLine(line){ + var start = (line + 1) * (n + 2) + 1; + var end = start + n; + var result = []; + + // 该行空白点都检查一遍 + find: for (var i = start; i < end; ++i) { + // 三个方向都检查一遍 + for (var j = 0; j < step.length; ++j) { + var k = i; + + while (1) { + // 向指定方向步进 + k += step[j]; + + // 已到达边界,跳出该方向 + if (!map[k]) { + break; + } + + // 发现棋子,跳过该空白点 + if (map[k] === "Q") { + continue find; + } + } + } + + // 合格点位,记录 + result.push(i); + } + + return result; + } + + // 将当前 map 输出为题目要求的格式 + function pushResult(){ + var lines = []; + + for (var i = 1; i <= n; ++i) { + var str = ""; + + for (var j = 1; j <= n; ++j) { + str += map[i * (n + 2) + j]; + } + + lines.push(str); + } + + result.push(lines); + } +}; diff --git a/Week 06/id_008/LeetCode_547_008.js b/Week 06/id_008/LeetCode_547_008.js new file mode 100644 index 000000000..b799c1c18 --- /dev/null +++ b/Week 06/id_008/LeetCode_547_008.js @@ -0,0 +1,39 @@ +/** + * @param {number[][]} M + * @return {number} + */ +var findCircleNum = function(M) { + var n = M.length; + + if (n === 0) { + return 0; + } + + var count = 0; + + function bfs(i){ + var queue = [i]; + + while (queue.length) { + var p = queue.pop(); + + for (var j = 0; j < n; ++j){ + if (M[p][j] === 1 && !visited[j]) { + visited[j] = true; + queue.push(j); + } + } + } + } + + var visited = {}; + + for(var i = 0; i < n; ++i){ + if (!visited[i]) { + bfs(i); + ++count; + } + } + + return count; +}; diff --git a/Week 06/id_008/LeetCode_70_008.js b/Week 06/id_008/LeetCode_70_008.js new file mode 100644 index 000000000..5177ebc23 --- /dev/null +++ b/Week 06/id_008/LeetCode_70_008.js @@ -0,0 +1,28 @@ +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function(n) { + var t = Math.sqrt(5); + return Math.round(1/t * (Math.pow((1+t)/2, n+1) - Math.pow((1-t)/2, n+1))); +}; + +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function(n) { + if (n < 2) { + return 1; + } + + var a = 1; + var b = 1; + + for (var i = 1; i < n; ++i) { + b += a; + a = b - a; + } + + return b; +}; diff --git a/Week 06/id_013/LeetCode_01_013.py b/Week 06/id_013/LeetCode_01_013.py new file mode 100644 index 000000000..6ef71d855 --- /dev/null +++ b/Week 06/id_013/LeetCode_01_013.py @@ -0,0 +1,48 @@ +""" +第一题:208. 实现 Trie (前缀树) +""" +class Trie: + + def __init__(self): + """ + Initialize your data structure here. + """ + self.root = {} + self.end_of_word = '#' + + def insert(self, word: str) -> None: + """ + Inserts a word into the trie. + """ + node = self.root + for char in word: + node = node.setdefault(char, {}) + node[self.end_of_word] = self.end_of_word + + def search(self, word: str) -> bool: + """ + Returns if the word is in the trie. + """ + node = self.root + for char in word: + if char not in node: + return False + node = node[char] + return self.end_of_word in node + + def startsWith(self, prefix: str) -> bool: + """ + Returns if there is any word in the trie that starts with the given prefix. + """ + node = self.root + for char in prefix: + if char not in node: + return False + node = node[char] + return True + +# Your Trie object will be instantiated and called as such: +# obj = Trie() +# obj.insert(word) +# param_2 = obj.search(word) +# param_3 = obj.startsWith(prefix) diff --git a/Week 06/id_013/LeetCode_03_013.py b/Week 06/id_013/LeetCode_03_013.py new file mode 100644 index 000000000..198de0583 --- /dev/null +++ b/Week 06/id_013/LeetCode_03_013.py @@ -0,0 +1,51 @@ +""" +第三题:547. 朋友圈 +""" + + +class Solution: + def _union(self, p, i, j): + p1 = self._parent(p, i) + p2 = self._parent(p, j) + p[p2] = p1 + + def _parent(self, p, i): + root = i + while p[root] != root: + root = p[root] + while p[i] != i: + x = i + i = p[i] + p[x] = root + return root + + def findCircleNum(self, M: List[List[int]]) -> int: + if not M: + return 0 + + n = len(M) + p = [i for i in range(n)] + + for i in range(n): + for j in range(n): + if M[i][j] == 1: + self._union(p, i, j) + + return len(set([self._parent(p, i) for i in range(n)])) + + +class Solution: + def findCircleNum(self, M: List[List[int]]) -> int: + visited, ans = set(), 0 + + def dfs(i): + for j in range(len(M[i])): + if M[i][j] and j not in visited: + visited.add(j) + dfs(j) + + for i in range(len(M)): + if i not in visited: + dfs(i) + ans += 1 + return ans diff --git a/Week 06/id_013/LeetCode_07_013.py b/Week 06/id_013/LeetCode_07_013.py new file mode 100644 index 000000000..76065df15 --- /dev/null +++ b/Week 06/id_013/LeetCode_07_013.py @@ -0,0 +1,26 @@ +""" +第七题:36. 有效的数独 +""" + + +class Solution: + def isValidSudoku(self, board: List[List[str]]) -> bool: + rows = [{} for i in range(9)] + colnums = [{} for i in range(9)] + boxes = [{} for i in range(9)] + + for i in range(9): + for j in range(9): + num = board[i][j] + if num != '.': + num = int(num) + box_index = (i // 3) * 3 + j // 3 + + rows[i][num] = rows[i].get(num, 0) + 1 + colnums[j][num] = colnums[j].get(num, 0) + 1 + boxes[box_index][num] = boxes[box_index].get(num, 0) + 1 + + if rows[i][num] > 1 or colnums[j][num] > 1 or boxes[box_index][num] > 1: + return False + + return True \ No newline at end of file diff --git a/Week 06/id_013/NOTE.md b/Week 06/id_013/NOTE.md index a6321d6e2..9b0f1ec9d 100644 --- a/Week 06/id_013/NOTE.md +++ b/Week 06/id_013/NOTE.md @@ -1,4 +1,5 @@ -# NOTE - - - +# NOTE +单向BFS只从起点一端开始搜索,双向BFS则是从起点和终点两边扩展节点,当节点发生重合时即找到最优解。 +实现方法为:维护两个队列,分别保存从起点和终点扩展到的下一层,这样保证了两个队列中的节点处于相同的深度(即:距离起点或者终点深度相同)。则当拓展到时一定发生重合,得到最优解。 + + diff --git a/Week 06/id_023/leetCode_130_023.js b/Week 06/id_023/leetCode_130_023.js new file mode 100644 index 000000000..ea468316c --- /dev/null +++ b/Week 06/id_023/leetCode_130_023.js @@ -0,0 +1,41 @@ +/** +@param {character[][]} board +@return {void} Do not return anything, modify board in-place instead. +*/ + +var solve = function (board) { + let replaceList = []; + let curBoard = board; + for (let i = 0; i < curBoard.length; i++) { + let curRow = curBoard[i]; + for (let j = 0; j < curRow.length; j++) { + if (i === 0 || i === curBoard.length - 1 || j === 0 || j === curRow.length - 1) { + renderOthers(curBoard, i, j) + } + } + } + + // 递归替换四周的O + function renderOthers(board, x, y) { + if (x < board.length && x >= 0 && y >= 0 && y < board[0].length) { + if (board[x][y] === 'O') { + board[x][y] = '#' + renderOthers(board, x - 1, y); + renderOthers(board, x + 1, y); + renderOthers(board, x, y - 1); + renderOthers(board, x, y + 1); + } + } + } + + // 将无关联的O替换成X,再将标记的#还原成不需要替换的O + for (let i = 0; i < curBoard.length; i++) { + for (let j = 0; j < curBoard[i].length; j++) { + if (curBoard[i][j] === 'O') { + curBoard[i][j] = 'X' + } else if (curBoard[i][j] === '#') { + curBoard[i][j] = 'O' + } + } + } +}; \ No newline at end of file diff --git a/Week 06/id_023/leetCode_547_023.js b/Week 06/id_023/leetCode_547_023.js new file mode 100644 index 000000000..76441d36f --- /dev/null +++ b/Week 06/id_023/leetCode_547_023.js @@ -0,0 +1,27 @@ +/** + * @param {number[][]} M + * @return {number} + */ +var findCircleNum = function (M) { + let n = M.length; + if (n == 0) { + return 0; + } + let count = 0; + let dfs = (i) => { + for (let j = 0; j < n; j++) { + if (M[i][j] == 1 && !visited[j]) { + visited[j] = true; + dfs(j); + } + } + } + let visited = {}; + for (let i = 0; i < n; i++) { + if (!visited[i]) { + dfs(i); + count++; + } + } + return count; +}; \ No newline at end of file diff --git a/Week 06/id_038/week-06-038/.gitignore b/Week 06/id_038/week-06-038/.gitignore new file mode 100644 index 000000000..a47486b2b --- /dev/null +++ b/Week 06/id_038/week-06-038/.gitignore @@ -0,0 +1,164 @@ +.vertx +*.ipr +# log files +*.log +*.log.2* +.log +out +output +dist +dist-3rd +*.txt +!agent-shell-banner.txt +!shell-banner.txt +!VERSION.txt + +# For Eclipse + +.metadata +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders +*.iml +.DS_Store + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + + +# For Java + +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# For Maven + +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# For JetBrains + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +#data module ignore +node_modules + +static +public + +BETHUNE_PRO_HOST_PID +BETHUNE_PRO_AGENT_PID +*PID +APP_PID +PID_* +alert-rules-*.yml +altmng-configs.yml +alertmanager.yml +app-info-*.properties +app-info.properties +inspectResult/ + +/file-uploads/ diff --git a/Week 06/id_038/week-06-038/pom.xml b/Week 06/id_038/week-06-038/pom.xml new file mode 100644 index 000000000..e8201f545 --- /dev/null +++ b/Week 06/id_038/week-06-038/pom.xml @@ -0,0 +1,95 @@ + + 4.0.0 + com.github.kylefeng + week-06-038 + jar + 1.0-SNAPSHOT + week-06-038 + http://maven.apache.org + + UTF-8 + 1.8 + 4.12 + 5.0.0 + ${junit.version}.0 + 1.0.0 + + + + + + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + + + + maven-surefire-plugin + 2.19.1 + + + **/Test*.java + **/*Test.java + **/*Tests.java + **/*TestCase.java + + + + slow + + + + + + org.junit.platform + junit-platform-surefire-provider + ${junit.platform.version} + + + + + + + + + com.google.guava + guava + 28.1-jre + + + + org.assertj + assertj-core + 3.13.2 + test + + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test + + + + org.junit.platform + junit-platform-launcher + ${junit.platform.version} + test + + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + diff --git a/Week 06/id_038/week-06-038/src/main/java/com/github/kylefeng/LeetCode_200_038.java b/Week 06/id_038/week-06-038/src/main/java/com/github/kylefeng/LeetCode_200_038.java new file mode 100644 index 000000000..dfc23feb2 --- /dev/null +++ b/Week 06/id_038/week-06-038/src/main/java/com/github/kylefeng/LeetCode_200_038.java @@ -0,0 +1,41 @@ +package com.github.kylefeng; + +/** + * 200. 岛屿数量 + * + * @author kylefeng + * @time 2019/11/24 15:42 + */ +public class LeetCode_200_038 { + private static int n; + private static int m; + + public static int numIslands(char[][] grid) { + int count = 0; + n = grid.length; + if (n == 0) { + return 0; + } + m = grid[0].length; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (grid[i][j] == '1') { + recur(grid, i, j); + ++count; + } + } + } + return count; + } + + private static void recur(char[][] grid, int i, int j) { + if (i < 0 || j < 0 || i >= n || j >= m || grid[i][j] != '1') { + return; + } + grid[i][j] = '0'; + recur(grid, i + 1, j); + recur(grid, i - 1, j); + recur(grid, i, j + 1); + recur(grid, i, j - 1); + } +} diff --git a/Week 06/id_038/week-06-038/src/main/java/com/github/kylefeng/LeetCode_212_038.java b/Week 06/id_038/week-06-038/src/main/java/com/github/kylefeng/LeetCode_212_038.java new file mode 100644 index 000000000..3ec7e159a --- /dev/null +++ b/Week 06/id_038/week-06-038/src/main/java/com/github/kylefeng/LeetCode_212_038.java @@ -0,0 +1,82 @@ +package com.github.kylefeng; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * 212. 单词搜索 II + * + * @author kylefeng + * @time 2019/11/24 15:32 + */ +public class LeetCode_212_038 { + + public static List findWords(char[][] board, String[] words) { + Trie trie = new Trie(); + Node root = trie.root; + for (String s : words) { + trie.insert(s); + } + + Set result = new HashSet<>(); + int m = board.length; + int n = board[0].length; + boolean[][] visited = new boolean[m][n]; + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + find(board, visited, i, j, m, n, result, root); + } + } + return new LinkedList<>(result); + } + + private static void find(char[][] board, boolean[][] visited, int i, int j, int m, int n, + Set result, Node cur) { + if (i < 0 || i >= m || j < 0 || j >= n || visited[i][j]) { + return; + } + + cur = cur.child[board[i][j] - 'a']; + visited[i][j] = true; + if (cur == null) { + visited[i][j] = false; + return; + } + + if (cur.isLeaf) { + result.add(cur.val); + } + find(board, visited, i + 1, j, m, n, result, cur); + find(board, visited, i, j + 1, m, n, result, cur); + find(board, visited, i, j - 1, m, n, result, cur); + find(board, visited, i - 1, j, m, n, result, cur); + visited[i][j] = false; + } + + static class Trie { + public Node root = new Node(); + + public void insert(String s) { + Node cur = root; + for (char c : s.toCharArray()) { + if (cur.child[c - 'a'] == null) { + cur.child[c - 'a'] = new Node(); + cur = cur.child[c - 'a']; + } else { + cur = cur.child[c - 'a']; + } + } + cur.isLeaf = true; + cur.val = s; + } + } + + static class Node { + public String val; + public Node[] child = new Node[26]; + public boolean isLeaf = false; + } + +} diff --git a/Week 06/id_048/048_Week06.md b/Week 06/id_048/048_Week06.md new file mode 100644 index 000000000..9c63428d7 --- /dev/null +++ b/Week 06/id_048/048_Week06.md @@ -0,0 +1,65 @@ +Trie树模板: +class Trie(object): + + def __init__(self): + self.root = {} + self.end_of_word = "#" + + def insert(self, word): + node = self.root + for char in word: + node = node.setdefault(char, {}) + node[self.end_of_word] = self.end_of_word + + def search(self, word): + node = self.root + for char in word: + if char not in node: + return False + node = node[char] + return self.end_of_word in node + + def startsWith(self, prefix): + node = self.root + for char in prefix: + if char not in node: + return False + node = node[char] + return True + +分析单词搜索 2 用 Tire 树方式实现的时间复杂度 +时间复杂度= board的长*宽 * word的最长单词的长度 + + +总结双向 BFS 代码模版: + +1. 构建2个队列Queues for birdirectional双向的 BFS + // BFS starting from beginWord + Queue> Q_begin = new LinkedList>(); + // BFS starting from endWord + Queue> Q_end = new LinkedList>(); + Q_begin.add(new Pair(beginWord, 1)); + Q_end.add(new Pair(endWord, 1)); + +2 Visited to make sure we don't repeat processing same word. +访问开始和结束的地方 + HashMap visitedBegin = new HashMap(); + HashMap visitedEnd = new HashMap(); + visitedBegin.put(beginWord, 1); + visitedEnd.put(endWord, 1); + +3. 循环双向遍历 +while (!Q_begin.isEmpty() && !Q_end.isEmpty()) { + + // One hop from begin word,从开始端访问节点 + int ans = visitWordNode(Q_begin, visitedBegin, visitedEnd); + if (ans > -1) { + return ans; + } + + // One hop from end word,从结束端访问节点 + ans = visitWordNode(Q_end, visitedEnd, visitedBegin); + if (ans > -1) { + return ans; + } + } diff --git a/Week 06/id_048/LeetCode_127_048.java b/Week 06/id_048/LeetCode_127_048.java new file mode 100644 index 000000000..042e1cf9f --- /dev/null +++ b/Week 06/id_048/LeetCode_127_048.java @@ -0,0 +1,161 @@ +package com.leetcode.week06; + +import javafx.util.Pair; + +import java.util.*; + +/** + * Created by tim on 2019/11/23. + * 给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则: + + 每次转换只能改变一个字母。 + 转换过程中的中间单词必须是字典中的单词。 + 说明: + + 如果不存在这样的转换序列,返回 0。 + 所有单词具有相同的长度。 + 所有单词只由小写字母组成。 + 字典中不存在重复的单词。 + 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。 + 示例 1: + + 输入: + beginWord = "hit", + endWord = "cog", + wordList = ["hot","dot","dog","lot","log","cog"] + + 输出: 5 + + 解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", + 返回它的长度 5。 + 示例 2: + + 输入: + beginWord = "hit" + endWord = "cog" + wordList = ["hot","dot","dog","lot","log"] + + 输出: 0 + + 解释: endWord "cog" 不在字典中,所以无法进行转换。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/word-ladder + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +/** + * 双向BFS + */ +public class LeetCode_127_048 { + public static void main(String[] args) { + LeetCode_127_048 leetCode_127_048 = new LeetCode_127_048(); + String beginWord = "hit"; + String endWord = "cog"; + String[] wordList = {"hot","dot","dog","lot","log","cog"}; + + int len = leetCode_127_048.ladderLength(beginWord, endWord, Arrays.asList(wordList)); + System.out.println("len: " + len); + } + + private int L; + private HashMap> allComboDict; + + LeetCode_127_048() { + this.L = 0; + + // Dictionary to hold combination of words that can be formed, + // from any given word. By changing one letter at a time. + this.allComboDict = new HashMap>(); + } + + private int visitWordNode( + Queue> Q, + HashMap visited, + HashMap othersVisited) { + Pair node = Q.remove(); + String word = node.getKey(); + int level = node.getValue(); + + for (int i = 0; i < this.L; i++) { + + // Intermediate words for current word + String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L); + + // Next states are all the words which share the same intermediate state. + for (String adjacentWord : this.allComboDict.getOrDefault(newWord, new ArrayList())) { + // If at any point if we find what we are looking for + // i.e. the end word - we can return with the answer. + if (othersVisited.containsKey(adjacentWord)) { + return level + othersVisited.get(adjacentWord); + } + + if (!visited.containsKey(adjacentWord)) { + + // Save the level as the value of the dictionary, to save number of hops. + visited.put(adjacentWord, level + 1); + Q.add(new Pair(adjacentWord, level + 1)); + } + } + } + return -1; + } + + public int ladderLength(String beginWord, String endWord, List wordList) { + + if (!wordList.contains(endWord)) { + return 0; + } + + // Since all words are of same length. + this.L = beginWord.length(); + + wordList.forEach( + word -> { + for (int i = 0; i < L; i++) { + // Key is the generic word + // Value is a list of words which have the same intermediate generic word. + String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L); + ArrayList transformations = + this.allComboDict.getOrDefault(newWord, new ArrayList()); + transformations.add(word); + this.allComboDict.put(newWord, transformations); + } + }); + + // Queues for birdirectional BFS + // BFS starting from beginWord + Queue> Q_begin = new LinkedList>(); + // BFS starting from endWord + Queue> Q_end = new LinkedList>(); + Q_begin.add(new Pair(beginWord, 1)); + Q_end.add(new Pair(endWord, 1)); + + // Visited to make sure we don't repeat processing same word. + HashMap visitedBegin = new HashMap(); + HashMap visitedEnd = new HashMap(); + visitedBegin.put(beginWord, 1); + visitedEnd.put(endWord, 1); + + while (!Q_begin.isEmpty() && !Q_end.isEmpty()) { + + // One hop from begin word + int ans = visitWordNode(Q_begin, visitedBegin, visitedEnd); + if (ans > -1) { + return ans; + } + + // One hop from end word + ans = visitWordNode(Q_end, visitedEnd, visitedBegin); + if (ans > -1) { + return ans; + } + } + + return 0; + } + +} + + + diff --git a/Week 06/id_048/LeetCode_212_048.java b/Week 06/id_048/LeetCode_212_048.java new file mode 100644 index 000000000..e39964d99 --- /dev/null +++ b/Week 06/id_048/LeetCode_212_048.java @@ -0,0 +1,105 @@ +package com.leetcode.week06; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + + +/** + * Created by tim on 2019/11/23. + * 给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。 + + 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。 + + 示例: + + 输入: + words = ["oath","pea","eat","rain"] and board = + [ + ['o','a','a','n'], + ['e','t','a','e'], + ['i','h','k','r'], + ['i','f','l','v'] + ] + + 输出: ["eat","oath"] + 说明: + 你可以假设所有输入都由小写字母 a-z 组成。 + + 提示: + + 你需要优化回溯算法以通过更大数据量的测试。你能否早点停止回溯? + 如果当前单词不存在于所有单词的前缀中,则可以立即停止回溯。什么样的数据结构可以有效地执行这样的操作?散列表是否可行?为什么? 前缀树如何?如果你想学习如何实现一个基本的前缀树,请先查看这个问题: 实现Trie(前缀树)。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/word-search-ii + */ +public class LeetCode_212_048 { + public static void main(String[] args) { + LeetCode_212_048 leetCode_212_048 = new LeetCode_212_048(); + String[] words = {"oath","pea","eat","rain"}; + char[][] board = { + {'o', 'a', 'a', 'n'}, + {'e', 't', 'a', 'e'}, + {'i', 'h', 'k', 'r'}, + {'i', 'f', 'l', 'v'} + }; + List lst = leetCode_212_048.findWords(board,words); + for (String wod : lst) { + System.out.println(wod); + } + + } + + + public List findWords(char[][] board, String[] words) { + //构建字典树 + WordTrie myTrie=new WordTrie(); + TrieNode root=myTrie.root; + for(String s:words) { + myTrie.insert(s); + } + //使用set防止重复 + Set result =new HashSet(); + int m=board.length; + int n=board[0].length; + boolean [][]visited=new boolean[m][n]; + //遍历整个二维数组 + for(int i=0;i(result); + } + private void find(char [] [] board, boolean [][]visited,int i,int j,int m,int n,Set result,TrieNode cur){ + //边界以及是否已经访问判断 + if(i<0||i>=m||j<0||j>=n||visited[i][j]) + return; + cur=cur.child[board[i][j]-'a']; + visited[i][j]=true; + if(cur==null) + { + //如果单词不匹配,回退 + visited[i][j]=false; + return; + } + //找到单词加入 + if(cur.isLeaf) + { + result.add(cur.val); + //找到单词后不能回退,因为可能是“ad” “addd”这样的单词得继续回溯 +// visited[i][j]=false; +// return; + } + find(board,visited,i+1,j,m,n,result,cur); + find(board,visited,i,j+1,m,n,result,cur); + find(board,visited,i,j-1,m,n,result,cur); + find(board,visited,i-1,j,m,n,result,cur); + //最后要回退,因为下一个起点可能会用到上一个起点的字符 + visited[i][j]=false; + } + +} diff --git a/Week 06/id_048/TrieNode.java b/Week 06/id_048/TrieNode.java new file mode 100644 index 000000000..98e18e1f3 --- /dev/null +++ b/Week 06/id_048/TrieNode.java @@ -0,0 +1,12 @@ +package com.leetcode.week06; + +/** + * Created by tim on 2019/11/23. + */ +public class TrieNode { + public String val; + public TrieNode[] child=new TrieNode[26]; + public boolean isLeaf=false; + TrieNode(){ + } +} diff --git a/Week 06/id_048/WordTrie.java b/Week 06/id_048/WordTrie.java new file mode 100644 index 000000000..b9e40cbbd --- /dev/null +++ b/Week 06/id_048/WordTrie.java @@ -0,0 +1,21 @@ +package com.leetcode.week06; + +/** + * Created by tim on 2019/11/23. + */ +public class WordTrie { + public TrieNode root=new TrieNode(); + public void insert(String s){ + TrieNode cur=root; + for(char c:s.toCharArray()){ + if(cur.child[c-'a']==null){ + cur.child [c-'a'] = new TrieNode(); + cur=cur.child[c-'a']; + }else + cur=cur.child [c-'a']; + } + cur.isLeaf=true; + cur.val=s; + } + +} diff --git "a/Week 06/id_053/[200]\345\262\233\345\261\277\346\225\260\351\207\217.java" "b/Week 06/id_053/[200]\345\262\233\345\261\277\346\225\260\351\207\217.java" new file mode 100644 index 000000000..a7338c8da --- /dev/null +++ "b/Week 06/id_053/[200]\345\262\233\345\261\277\346\225\260\351\207\217.java" @@ -0,0 +1,70 @@ +//给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。 +// +// 示例 1: +// +// 输入: +//11110 +//11010 +//11000 +//00000 +// +//输出: 1 +// +// +// 示例 2: +// +// 输入: +//11000 +//11000 +//00100 +//00011 +// +//输出: 3 +// +// Related Topics 深度优先搜索 广度优先搜索 并查集 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + private int[] dx = new int[]{-1, 1, 0, 0}; + private int[] dy = new int[]{0, 0, -1, 1}; + private char[][] g; + + public int numIslands(char[][] grid) { + if (grid == null) return -1; + g = grid; + + int result = 0; + //对二维数组进行遍历,用沉到办法将1炸成0 + for (int i = 0; i < g.length; i++) { + for (int j = 0; j < g[0].length; j++) { + if (g[i][j] == '0') continue; + //否则调用函数进行炸岛 + result += floodfill(i, j); + } + } + return result; + } + + private int floodfill(int i, int j) { + //terminator + if (g[i][j] == '0') { + return 0; + } + //process current level logic + g[i][j] = '0'; + //这里写错,是在坐标轴里对X,Y轴进行平移,我写成g.length了,这个和二维数组没半毛关系 + for (int k = 0; k < dx.length; k++) { + int x = i + dx[k]; + int y = j + dy[k]; + if (x >= 0 && x < g.length && y >= 0 && y < g[0].length) { + if (g[x][y] == '0') continue; + //drill down + floodfill(x, y); + } + } + //reverse status if need + return 1; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week 06/id_053/[22]\346\213\254\345\217\267\347\224\237\346\210\220.java" "b/Week 06/id_053/[22]\346\213\254\345\217\267\347\224\237\346\210\220.java" new file mode 100644 index 000000000..cf152e63c --- /dev/null +++ "b/Week 06/id_053/[22]\346\213\254\345\217\267\347\224\237\346\210\220.java" @@ -0,0 +1,43 @@ +//给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。 +// +// 例如,给出 n = 3,生成结果为: +// +// [ +// "((()))", +// "(()())", +// "(())()", +// "()(())", +// "()()()" +//] +// +// Related Topics 字符串 回溯算法 + + +import java.util.ArrayList; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + List result = new ArrayList<>(); + public List generateParenthesis(int n) { + process(0,0,n,""); + return result; + } + public void process(int left,int right,int max,String s) { + //terminal condition + if (left == max && right == max) { + result.add(s); + return; + } + + //process + //recursion + if (left startSta = new ArrayList<>(N*N); + List endSta = new ArrayList<>(N*N); + startSta.add(new State(0, 0)); + endSta.add(new State(N-1, N-1)); + + boolean[][] visited = new boolean[N][N]; + visited[0][0] = true; + visited[N-1][N-1] = true; + + boolean[][] startVisited = new boolean[N][N]; + boolean[][] endVisited = new boolean[N][N]; + startVisited[0][0] = true; + endVisited[N-1][N-1] = true; + + int len = 1; + + while (!startSta.isEmpty() && !endSta.isEmpty()) { + + if (startSta.size() > endSta.size()) { + List tmp = startSta; startSta = endSta; endSta = tmp; + boolean[][] tmpA = startVisited; startVisited = endVisited; endVisited = tmpA; + } + + List newStaSet = new ArrayList<>(); + for (State curSta : startSta) { + int i = curSta.i, j = curSta.j; + for (int[] pos : direc) { + int new_i = i + pos[0], new_j = j + pos[1]; + if (new_i >= 0 && new_i < N && new_j >= 0 && new_j < N && grid[new_i][new_j] == 0) { + State newSta = new State(new_i, new_j); + + if (endVisited[newSta.i][newSta.j]) { + return len + 1; + } + + if (visited[newSta.i][newSta.j]) { + continue; + } + + newStaSet.add(newSta); + visited[newSta.i][newSta.j] = true; + startVisited[newSta.i][newSta.j] = true; + } + } + } + + startSta = newStaSet; + len++; + } + + return -1; + } + + public static void main(String[] args) { + System.out.println(new Solution().shortestPathBinaryMatrix(new int[][] + {{0,0,1,0,0,0,0}, + {0,1,0,0,0,0,1}, + {0,0,1,0,1,0,0}, + {0,0,0,1,1,1,0}, + {1,0,0,1,1,0,0}, + {1,1,1,1,1,0,1}, + {0,0,1,0,0,0,0}})); + } +} \ No newline at end of file diff --git a/Week 06/id_063/Leetcode_127_063.java b/Week 06/id_063/Leetcode_127_063.java new file mode 100644 index 000000000..0aa2435f8 --- /dev/null +++ b/Week 06/id_063/Leetcode_127_063.java @@ -0,0 +1,109 @@ +/* +双向BFS实现 + */ + +import java.util.*; + +public class Solution { + + private class State { + String curStr; + int stepNum; + + State(String str) { + curStr = str; + stepNum = 1; + } + + State() { + curStr = null; + stepNum = 0; + } + } + + public int ladderLength(String beginWord, String endWord, List wordList) { + if (beginWord.length() != endWord.length()) { + return 0; + } + + HashMap> link = new HashMap<>(); + + boolean endWordExist = false; + for (int i = 0; i < wordList.size(); i++) { + if (wordList.get(i).length() != beginWord.length()) { + continue; + } + + String si = wordList.get(i); + if (si.equals(endWord)) { + endWordExist = true; + } + + for (int pos = 0; pos < si.length(); pos++) { + String newStr = si.substring(0, pos) + "?" + si.substring(pos+1, si.length()); + List l = link.getOrDefault(newStr, new LinkedList<>()); + l.add(si); + link.put(newStr, l); + } + } + + if (!endWordExist) { + return 0; + } + + + Set startSta = new HashSet<>(); + startSta.add(beginWord); + Set endSta = new HashSet<>(); + endSta.add(endWord); + + Set visitedStr = new HashSet<>(); + visitedStr.add(beginWord); + visitedStr.add(endWord); + + int len = 1; + while (!startSta.isEmpty() && !endSta.isEmpty()) { + if (startSta.size() > endSta.size()) { + Set sta = startSta; startSta = endSta; endSta = sta; + } + + Set newSta = new HashSet<>(); + for (String curStr : startSta) { + for (int pos = 0; pos < curStr.length(); pos++) { + String newStr = curStr.substring(0, pos) + "?" + curStr.substring(pos+1, curStr.length()); + List nextStep = link.get(newStr); + if (nextStep == null) { + continue; + } + + for (String s : nextStep) { + if (endSta.contains(s)) { + return len + 1; + } + + if (visitedStr.contains(s)) { + continue; + } + visitedStr.add(s); + + newSta.add(s); + } + } + } + + startSta = newSta; + len++; + } + + return 0; + } + + public static void main(String[] args) { + List list = new LinkedList<>(); + for (String s : new String[] {"hot","dot","dog","lot","log","cog"}) { + list.add(s); + } + + System.out.println(new Solution().ladderLength("hit", "cog", list)); + } +} \ No newline at end of file diff --git a/Week 06/id_063/Leetcode_130_063.java b/Week 06/id_063/Leetcode_130_063.java new file mode 100644 index 000000000..bf3f136ca --- /dev/null +++ b/Week 06/id_063/Leetcode_130_063.java @@ -0,0 +1,69 @@ +/* + + dfs搜索所有边界上面是O的点的相邻的O点,这些点保留为O,其他的O全部 + 填成X + + */ + +import java.util.HashSet; +import java.util.Set; + +class Solution { + static int[][] direc = new int[][] {{0,1}, {0,-1}, {1,0}, {-1,0}}; + + private boolean isValid(int rows, int cols, int i, int j) { + return (i >= 0 && i < rows) && (j >= 0 && j < cols); + } + + private void dfs(char[][] board, int cur_i, int cur_j) { + int rows = board.length, cols = board[0].length; + + board[cur_i][cur_j] = '#'; + + int new_i = 0, new_j = 0; + for (int i = 0; i < 4; i++) { + new_i = cur_i + direc[i][0]; + new_j = cur_j + direc[i][1]; + + if (isValid(rows, cols, new_i, new_j) && board[new_i][new_j] == 'O') { + dfs(board, new_i, new_j); + } + } + } + + public void solve(char[][] board) { + if ((board.length == 0) || (board[0].length == 0)) { + return; + } + + for (int j = 0; j < board[0].length; j++) { + if (board[0][j] == 'O') { + dfs(board, 0, j); + } + + if (board[board.length-1][j] == 'O') { + dfs(board, board.length - 1, j); + } + } + + for (int i = 1; i < board.length - 1; i++) { + if (board[i][0] == 'O') { + dfs(board, i, 0); + } + + if (board[i][board[0].length-1] == 'O') { + dfs(board, i, board[0].length-1); + } + } + + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (board[i][j] == '#') { + board[i][j] = 'O'; + } else if (board[i][j] == 'O') { + board[i][j] = 'X'; + } + } + } + } +} diff --git a/Week 06/id_063/Leetcode_200_063.java b/Week 06/id_063/Leetcode_200_063.java new file mode 100644 index 000000000..d478d4a9d --- /dev/null +++ b/Week 06/id_063/Leetcode_200_063.java @@ -0,0 +1,109 @@ +/* +构建并查集, 遍历每一个1节点,检查每个1节点下面和右边的节点是不是1, +若是1,则将本节点和下面或者右边的节点并在一个集合中,每并一个新节点 +集合个数减去一个,每发现一个数值为1的新节点,集合数量加上一个,返回 +最后集合个数 +*/ + + +class UnionSet { + private int len; + private int[] buf; + + UnionSet(int len, char[][] grid) { + this.len = len; + buf = new int[len]; + + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[i].length; j++) { + if (grid[i][j] == '1') { + int index = i * grid[0].length + j; + buf[index] = index; + } + } + } + } + + private int findRoot(int n) { + int node; + + node = n; + while (buf[node] != node) { + node = buf[node]; + } + + int root = node; + + // 路径压缩 + node = n; + while (buf[node] != node) { + int tmp = buf[node]; + buf[node] = root; + node = tmp; + } + + return root; + } + + public boolean merge(int p, int q) { + int root_p = findRoot(p); + int root_q = findRoot(q); + + if (root_p != root_q) { + buf[root_p] = root_q; + return true; + } + + return false; + } + +} + +class Solution { + public int numIslands(char[][] grid) { + if ((grid == null) || (grid.length == 0)) { + return 0; + } + + if (grid[0].length == 0) { + return 0; + } + + UnionSet us = new UnionSet(grid.length * grid[0].length, grid); + + int unionCnt = 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[i].length; j++) { + if (grid[i][j] == '0') { + continue; + } + + unionCnt++; + + if ((i+1 < grid.length) && (grid[i+1][j] == '1')) { + if (us.merge( (i+1)*grid[0].length + j, i*grid[0].length + j )) { + unionCnt--; + } + } + + if ( (j+1 < grid[0].length) && (grid[i][j+1] == '1') ) { + if (us.merge( i*grid[0].length + j, i*grid[0].length + j+1 )) { + unionCnt--; + } + } + } + } + + return unionCnt; + } + + public static void main(String[] args) { + new Solution().numIslands(new char[][] { + {'1','1','1','1','0'}, + {'1','1','0','1','0'}, + {'1','1','0','0','0'}, + {'0','0','0','0','0'} + } + ); + } +} \ No newline at end of file diff --git a/Week 06/id_063/Leetcode_208_063.java b/Week 06/id_063/Leetcode_208_063.java new file mode 100644 index 000000000..81569769b --- /dev/null +++ b/Week 06/id_063/Leetcode_208_063.java @@ -0,0 +1,72 @@ +/* +简单字典树实现,没什么特别算法 + + */ + +class Trie { + private class TrieNode { + TrieNode[] edges; + boolean isEnd; + + TrieNode() { + edges = new TrieNode[26]; + isEnd = false; + } + + TrieNode addSubNode(char ch) { + if (edges[ch-'a'] != null) { + return edges[ch - 'a']; + } + + edges[ch-'a'] = new TrieNode(); + return edges[ch - 'a']; + } + + TrieNode nextNode(char ch) { + return edges[ch - 'a']; + } + } + + private TrieNode root; + + /** Initialize your data structure here. */ + public Trie() { + root = new TrieNode(); + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + TrieNode curNode = root; + for (int i = 0; i < word.length(); i++) { + curNode = curNode.addSubNode(word.charAt(i)); + } + + curNode.isEnd = true; + } + + /** Returns if the word is in the trie. */ + public boolean search(String word) { + TrieNode curNode = root; + for (int i = 0; i < word.length(); i++) { + curNode = curNode.nextNode(word.charAt(i)); + if (curNode == null) { + return false; + } + } + + return curNode.isEnd; + } + + /** Returns if there is any word in the trie that starts with the given prefix. */ + public boolean startsWith(String prefix) { + TrieNode curNode = root; + for (int i = 0; i < prefix.length(); i++) { + curNode = curNode.nextNode(prefix.charAt(i)); + if (curNode == null) { + return false; + } + } + + return true; + } +} diff --git a/Week 06/id_063/Leetcode_212_063.java b/Week 06/id_063/Leetcode_212_063.java new file mode 100644 index 000000000..053ec0c3e --- /dev/null +++ b/Week 06/id_063/Leetcode_212_063.java @@ -0,0 +1,164 @@ +/* +思路 +如果每一个单词都去进行一次dfs +那么单词公共的前缀在dfs时候会被查找多次,因此可以先将所有单词构建字典树 +dfs时候往下一层递归的条件是相邻的节点对应的字符在字典树当前节点中有对应的边 +每次往下递归都需要更新字典树中搜索位置,如果当前字典树节点是一个end节点, +表示找到一个单词,用字典树避免了相同前缀的重复搜索 +*/ + +import java.util.*; + +class TrieNode { + TrieNode[] edges; + boolean isEnd; + + TrieNode() { + edges = new TrieNode[26]; + isEnd = false; + } + + TrieNode addSubNode(char ch) { + if (edges[ch-'a'] != null) { + return edges[ch - 'a']; + } + + edges[ch-'a'] = new TrieNode(); + return edges[ch - 'a']; + } + + TrieNode nextNode(char ch) { + return edges[ch - 'a']; + } +} + + +class Trie { + + + private TrieNode root; + + /** Initialize your data structure here. */ + public Trie() { + root = new TrieNode(); + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + TrieNode curNode = root; + for (int i = 0; i < word.length(); i++) { + curNode = curNode.addSubNode(word.charAt(i)); + } + + curNode.isEnd = true; + } + + public TrieNode getRoot() { + return root; + } +} + +class Solution { + private class Pos { + int i; + int j; + + public Pos(int i, int j) { + this.i = i; + this.j = j; + } + } + + + private void dfs(Pos curPos, boolean[][] visited, char[] path, Set result, char[][] board, TrieNode curNode, int curLevel, char curChar) { + visited[curPos.i][curPos.j] = true; + path[curLevel] = curChar; + + if (curNode.isEnd) { + result.add(new String(path, 0, curLevel+1)); + } + + if ((curPos.i + 1 < board.length) && (curNode.edges[board[curPos.i + 1][curPos.j]-'a'] != null) && (!visited[curPos.i + 1][curPos.j])) { + dfs(new Pos(curPos.i + 1, curPos.j), visited, path, result, board, curNode.edges[board[curPos.i + 1][curPos.j]-'a'], curLevel + 1, board[curPos.i + 1][curPos.j]); + } + + if ((curPos.i - 1 >= 0) && (curNode.edges[board[curPos.i - 1][curPos.j]-'a'] != null) && (!visited[curPos.i - 1][curPos.j])) { + dfs(new Pos(curPos.i - 1, curPos.j), visited, path, result, board, curNode.edges[board[curPos.i - 1][curPos.j]-'a'], curLevel + 1, board[curPos.i - 1][curPos.j]); + } + + if ((curPos.j + 1 < board[0].length) && (curNode.edges[board[curPos.i][curPos.j + 1]-'a'] != null) && (!visited[curPos.i][curPos.j + 1])) { + dfs(new Pos(curPos.i, curPos.j + 1), visited, path, result, board, curNode.edges[board[curPos.i][curPos.j + 1]-'a'], curLevel + 1, board[curPos.i][curPos.j + 1]); + } + + if ((curPos.j - 1 >= 0) && (curNode.edges[board[curPos.i][curPos.j - 1]-'a'] != null) && (!visited[curPos.i][curPos.j - 1])) { + dfs(new Pos(curPos.i, curPos.j - 1), visited, path, result, board, curNode.edges[board[curPos.i][curPos.j - 1]-'a'], curLevel + 1, board[curPos.i][curPos.j - 1]); + } + + visited[curPos.i][curPos.j] = false; + } + + + public List findWords(char[][] board, String[] words) { + Trie trie = new Trie(); + + Set result = new HashSet<>(); + int strMaxLen = 0; + + if (words == null) { + return new ArrayList(result); + } + + if ((board == null) || (board[0] == null)) { + return new ArrayList(result); + } + + for (String s : words) { + trie.insert(s); + strMaxLen = Math.max(s.length(), strMaxLen); + } + + TrieNode root = trie.getRoot(); + char[] path = new char[strMaxLen]; + + + boolean[][] visited = new boolean[board.length][board[0].length]; + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + + if (root.edges[board[i][j]-'a'] == null) { + continue; + } + + dfs(new Pos(i, j), visited, path, result, board, root.edges[board[i][j]-'a'], 0, board[i][j]); + if (result.size() == words.length) { + break; + } + } + } + + return new ArrayList(result); + } +/* + public static void main(String[] args) { + char[][] board = new char[][] { + {'o','a','a','n'}, + {'e','t','a','e'}, + {'i','h','k','r'}, + {'i','f','l','v'} + }; + + new Solution().findWords(board, new String[] {"oath","pea","eat","rain"}); + } + */ + +/* + public static void main(String[] args) { + char[][] board = new char[][] { + {'a', 'b'}, + {'a', 'a'}, + }; + + new Solution().findWords(board, new String[] {"aba","baa","bab","aaab","aaa","aaaa","aaba"}); + } +*/ +} diff --git a/Week 06/id_063/Leetcode_22_063.java b/Week 06/id_063/Leetcode_22_063.java new file mode 100644 index 000000000..962389cca --- /dev/null +++ b/Week 06/id_063/Leetcode_22_063.java @@ -0,0 +1,30 @@ +/* +思路 +递归生成字符串,最多递归n层,每层维护左括号和右括号的数量 +往下递归的条件是左括号数量大于右括号,且左右括号数量都不超过n +*/ + +class Solution { + List dfs(List result, int leftCnt, int rightCnt, int maxCnt, char[] path, int curLevel, int maxLevel) { + if (curLevel == maxLevel) { + result.add(new String(path)); + return result; + } + + if (leftCnt < maxCnt) { + path[curLevel] = '('; + dfs(result, leftCnt + 1, rightCnt, maxCnt, path, curLevel + 1, maxLevel); + } + + if ((rightCnt < maxCnt) && (leftCnt > rightCnt)) { + path[curLevel] = ')'; + dfs(result, leftCnt, rightCnt + 1, maxCnt, path, curLevel + 1, maxLevel); + } + + return result; + } + + public List generateParenthesis(int n) { + return dfs(new ArrayList<>(n<<1), 0, 0, n, new char[n<<1], 0, n<<1); + } +} \ No newline at end of file diff --git a/Week 06/id_063/Leetcode_36_063.java b/Week 06/id_063/Leetcode_36_063.java new file mode 100644 index 000000000..bb5277764 --- /dev/null +++ b/Week 06/id_063/Leetcode_36_063.java @@ -0,0 +1,80 @@ +import java.util.HashSet; +import java.util.Set; + + +/* +思路 + +就是循环判断,没有什么特殊的地方 + + */ + + +class Solution { + public boolean isValidSudoku(char[][] board) { + Set set1 = new HashSet<>(); + Set set2 = new HashSet<>(); + + for (int i = 0; i < 9; i++) { + set1.clear(); + set2.clear(); + + for (int j = 0; j < 9 ;j++) { + if (set1.contains(board[i][j])) { + return false; + } + + if (board[i][j] != '.') { + set1.add(board[i][j]); + } + + if (set2.contains(board[j][i])) { + return false; + } + + if (board[j][i] != '.') { + set2.add(board[j][i]); + } + } + } + + for (int ii = 0; ii < 3; ii++) { + for (int jj = 0; jj < 3; jj++) { + int start_i = ii * 3; + int start_j = jj * 3; + + set1.clear(); + for (int i = start_i; i < start_i + 3; i++) { + for (int j = start_j; j < start_j + 3; j++) { + if (board[i][j] == '.') { + continue; + } + + if (set1.contains(board[i][j])) { + return false; + } + + if (board[i][j] != '.') { + set1.add(board[i][j]); + } + } + } + } + } + + return true; + } + + public static void main(String[] args) { + new Solution().isValidSudoku(new char[][] { + {'5','3','.','.','7','.','.','.','.'}, + {'6','.','.','1','9','5','.','.','.'}, + {'.','9','8','.','.','.','.','6','.'}, + {'8','.','.','.','6','.','.','.','3'}, + {'4','.','.','8','.','3','.','.','1'}, + {'7','.','.','.','2','.','.','.','6'}, + {'.','6','.','.','.','.','2','8','.'}, + {'.','.','.','4','1','9','.','.','5'}, + {'.','.','.','.','8','.','.','7','9'}}); + } +} diff --git a/Week 06/id_063/Leetcode_37_063.java b/Week 06/id_063/Leetcode_37_063.java new file mode 100644 index 000000000..5f314d112 --- /dev/null +++ b/Week 06/id_063/Leetcode_37_063.java @@ -0,0 +1,157 @@ + +/* +思路 + +先把棋盘遍历一遍, 把每一个空格可能出现的数值都统计出来, +然后把空格根据能填的数值多少进行排序,从能填的数值少的 +空格开始进行递归遍历,方便进行减枝 +*/ + + + +import java.util.*; + + +class Solution { + private class Node implements Comparable { + int i; + int j; + List possibleVal; + + Node(int i, int j) { + this.i = i; this.j = j; + possibleVal = new ArrayList<>(9); + } + + @Override + public int compareTo(Node node) { + return possibleVal.size() - node.possibleVal.size(); + } + } + + private boolean initPossibleList(List nodes, char[][] board) { + // 统计每一个空格的信息 + Set helper = new HashSet<>(); + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + if (board[i][j] != '.') { + continue; + } + + helper.clear(); + for (int jj = 0; jj < 9; jj++) { + if (board[i][jj] != '.') { + helper.add(board[i][jj]); + } + } + + for (int ii = 0; ii < 9; ii++) { + if (board[ii][j] != '.') { + helper.add(board[ii][j]); + } + } + + int start_i = (i/3) * 3; + int start_j = (j/3) * 3; + for (int ii = start_i; ii < start_i + 3; ii++) { + for (int jj = start_j; jj < start_j + 3; jj++) { + if (board[ii][jj] != '.') { + helper.add(board[ii][jj]); + } + } + } + + Node node = new Node(i, j); + for (int ii = 0; ii < 9; ii++) { + if (!helper.contains((char)('1' + ii))) { + node.possibleVal.add((char)('1' + ii)); + } + } + + if (node.possibleVal.size() == 0) { + return false; + } + nodes.add(node); + + } + } + + nodes.sort(Node::compareTo); + return true; + } + + private boolean isConflict(Node node, char ch, char[][] board) { + // 检查有没有重复的 + + for (int ii = 0; ii < 9; ii++) { + if ((ii != node.i) && (board[ii][node.j] == ch)) { + return true; + } + } + + for (int jj = 0; jj < 9; jj++) { + if ((jj != node.j) && (board[node.i][jj] == ch)) { + return true; + } + } + + int start_i = (node.i / 3) * 3; + int start_j = (node.j / 3) * 3; + + for (int ii = start_i; ii < start_i + 3; ii++) { + for (int jj = start_j; jj < start_j + 3; jj++) { + if ((ii != node.i) && (jj != node.j) && (board[ii][jj] == ch)) { + return true; + } + } + } + + return false; + } + + private boolean dfs(List nodes, char[][] board, int curLevel, int maxLevel) { + if (curLevel == maxLevel) { + return true; + } + + Node node = nodes.get(curLevel); + + for (char ch : node.possibleVal) { + if (isConflict(node, ch, board)) { + continue; + } + + board[node.i][node.j] = ch; + if (dfs(nodes, board, curLevel + 1, maxLevel)) { + return true; + } + } + + board[node.i][node.j] = '.'; + return false; + } + + public void solveSudoku(char[][] board) { + List nodes = new ArrayList<>(81); + if (!initPossibleList(nodes, board)) { + return; + } + + dfs(nodes, board, 0, nodes.size()); + } + + + public static void main(String[] args) { + new Solution().solveSudoku(new char[][] { + {'5','3','.','.','7','.','.','.','.'}, + {'6','.','.','1','9','5','.','.','.'}, + {'.','9','8','.','.','.','.','6','.'}, + {'8','.','.','.','6','.','.','.','3'}, + {'4','.','.','8','.','3','.','.','1'}, + {'7','.','.','.','2','.','.','.','6'}, + {'.','6','.','.','.','.','2','8','.'}, + {'.','.','.','4','1','9','.','.','5'}, + {'.','.','.','.','8','.','.','7','9'}}); + } + +} diff --git a/Week 06/id_063/Leetcode_433_063.java b/Week 06/id_063/Leetcode_433_063.java new file mode 100644 index 000000000..91e01f3fc --- /dev/null +++ b/Week 06/id_063/Leetcode_433_063.java @@ -0,0 +1,131 @@ +/* +双向A* 解法 + */ + + +import java.util.*; + +class Solution { + + private int strDiff(String s1, String s2) { + int minLen = Math.min(s1.length(), s2.length()); + + int cnt = 0; + for (int i = 0; i < minLen; i++) { + if (s1.charAt(i) != s2.charAt(i)) { + cnt++; + } + } + + return cnt; + } + + private class Node implements Comparable { + String curStr; + int steps; + String targetStr; + + public Node(String curStr, int steps, String targetStr) { + this.curStr = curStr; + this.steps = steps; + this.targetStr = targetStr; + } + + @Override + public int compareTo(Object o) { + Node node = (Node)o; + return ((steps + strDiff(curStr, targetStr)) - (node.steps + strDiff(node.curStr, node.targetStr))); + } + } + + + public int minMutation(String start, String end, String[] bank) { + Set bankString = new HashSet<>(Arrays.asList(bank)); + + if (!bankString.contains(end)) { + return -1; + } + + Set visited = new HashSet<>(); + + final String startTargetStr = end; + final String endTargetStr = start; + + PriorityQueue startHeap = new PriorityQueue<>(); + Set startSet = new HashSet<>(); + PriorityQueue endHeap = new PriorityQueue<>(); + Set endSet = new HashSet<>(); + + + startHeap.add(new Node(start, 0, startTargetStr)); + startSet.add(start); + endHeap.add(new Node(end, 0, endTargetStr)); + endSet.add(end); + visited.add(start); + visited.add(end); + + int len = 0; + while ((startHeap.size() != 0) && (endHeap.size() != 0)) { + PriorityQueue expandHeap = null; + Set expandSet = null; + Set targetSet = null; + + if (startHeap.size() < endHeap.size()) { + expandHeap = startHeap; + expandSet = startSet; + + targetSet = endSet; + } else { + expandHeap = endHeap; + expandSet = endSet; + + targetSet = startSet; + } + + // 数值更少的一端进行状态拓展 + Node node = expandHeap.poll(); + String curStr = node.curStr; + expandSet.remove(curStr); + + char[] charArr = curStr.toCharArray(); + for (int i = 0; i < curStr.length(); i++) { + char oldChar = charArr[i]; + + for (char ch : new char[] {'A', 'C', 'G', 'T'}) { + if (ch == oldChar) { + continue; + } + + charArr[i] = ch; + String s = new String(charArr); + + if (targetSet.contains(s)) { + return len + 1; + } + + if (!bankString.contains(s)) { + continue; + } + + if (visited.contains(s)) { + continue; + } + + expandHeap.add(new Node(s, node.steps+1, node.targetStr)); + expandSet.add(s); + visited.add(s); + } + + charArr[i] = oldChar; + } + + len++; + } + + return -1; + } + + public static void main(String[] args) { + System.out.println(new Solution().minMutation("AAAAACCC", "AACCCCCC", new String[] {"AAAACCCC", "AAACCCCC", "AACCCCCC"})); + } +} \ No newline at end of file diff --git a/Week 06/id_063/Leetcode_51_063.java b/Week 06/id_063/Leetcode_51_063.java new file mode 100644 index 000000000..1b0b696c8 --- /dev/null +++ b/Week 06/id_063/Leetcode_51_063.java @@ -0,0 +1,63 @@ +/* +思路 + +递归求解,没什么可说的,注意横竖和斜线上是否有皇后,进行减枝回溯即可 + */ + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +public class Solution { + + private List choice2str_list(int[] choice) { + List retList = new ArrayList<>(choice.length); + String initString = ""; + for (int i = 0; i < choice.length; i++) initString = initString + "."; + + for (int queuePos : choice) { + StringBuilder s = new StringBuilder(initString); + s.setCharAt(queuePos, 'Q'); + retList.add(s.toString()); + } + + return retList; + } + + private List> dfs(List> result, int[] choice, int curLevel, int maxLevel) { + if (curLevel == maxLevel) { + result.add(choice2str_list(choice)); + return result; + } + + for (int cur_j = 0; cur_j < maxLevel; cur_j++) { + boolean conflict = false; + for (int i = 0; i < curLevel; i++) { + // 列冲突 + if (choice[i] == cur_j) { + conflict = true; + break; + } + + // 对角线冲突 + if (Math.abs(curLevel -i) == Math.abs(cur_j-choice[i])) { + conflict = true; + break; + } + } + + if (conflict) { + continue; + } + + choice[curLevel] = cur_j; + dfs(result, choice, curLevel + 1, maxLevel); + } + + return result; + } + + public List> solveNQueens(int n) { + return dfs(new LinkedList>(), new int[n], 0, n); + } +} diff --git a/Week 06/id_063/Leetcode_547_063.java b/Week 06/id_063/Leetcode_547_063.java new file mode 100644 index 000000000..e22c26b3e --- /dev/null +++ b/Week 06/id_063/Leetcode_547_063.java @@ -0,0 +1,91 @@ +/* +思路 +并查集维护总共的集合数,总结和书就是最后答案 + */ + + + +import java.util.Arrays; + +class UnionSet { + private int len; + private int[] buf; + + UnionSet(int len) { + this.len = len; + buf = new int[len]; + Arrays.setAll(buf, (index)->index); + } + + private int findRoot(int n) { + int node; + + node = n; + while (buf[node] != node) { + node = buf[node]; + } + + int root = node; + + // 路径压缩 + node = n; + while (buf[node] != node) { + int tmp = buf[node]; + buf[node] = root; + node = tmp; + } + + return root; + } + + // 判断两个节点是否在一个集合中 + public boolean isInSameSet(int p, int q) { + return (findRoot(p) == findRoot(q)); + } + + public void merge(int p, int q) { + int root_p = findRoot(p); + int root_q = findRoot(q); + + buf[root_p] = root_q; + } + +} + +public class Solution { + public int findCircleNum(int[][] M) { + if ((M == null) || (M.length == 0)) { + return 0; + } + + if (M[0].length == 0) { + return 0; + } + + UnionSet us = new UnionSet(M.length); + + int unionCnt = M.length; + for (int i = 0; i < M.length; i++) { + for (int j = i + 1; j < M.length; j++) { + if (M[i][j] == 0) { + continue; + } + + if (!us.isInSameSet(i, j)) { + us.merge(i, j); + unionCnt--; + } + } + } + + return unionCnt; + } + + public static void main(String[] args) { + new Solution().findCircleNum(new int[][] { + {1,1,0}, + {1,1,0}, + {0,0,1} + }); + } +} diff --git a/Week 06/id_063/Leetcode_70_063.java b/Week 06/id_063/Leetcode_70_063.java new file mode 100644 index 000000000..962a8c84e --- /dev/null +++ b/Week 06/id_063/Leetcode_70_063.java @@ -0,0 +1,44 @@ +/* +记忆化搜索,递归减枝 +dp[i][j] 表示从i位置走到j位置有多少种走法 + +dp[i][j] = dp[i+1][j] + dp[i+2][j] + + */ + +class Solution { + private int[] memo; + + private int solve(int start, int end) { + if (memo[end - start] != -1) { + return memo[end - start]; + } + + if (start == end) { + return 0; + } + + if (start == end - 1) { + return 1; + } + + if (start == end - 2) { + return 2; + } + + memo[end - start] = solve(start+1, end) + solve(start+2, end); + return memo[end - start]; + } + + public int climbStairs(int n) { + if (n <= 0) { + return 0; + } + + memo = new int[n+1]; + for (int i = 0; i < n+1; i++) { + memo[i] = -1; + } + return solve(0, n); + } +} diff --git a/Week 06/id_063/Leetcode_773_063.java b/Week 06/id_063/Leetcode_773_063.java new file mode 100644 index 000000000..78caf5f92 --- /dev/null +++ b/Week 06/id_063/Leetcode_773_063.java @@ -0,0 +1,164 @@ +/* +A* 搜索解决 + + */ + +import java.util.*; + +class Solution { + + private class State implements Comparable { + int pos; // 0在的位置 + int steps; // 当前状态经历的步数 + int[][] board; // 当前状态 + + @Override + public int hashCode() { + return (pos << 16) | steps; + } + + @Override + public boolean equals(Object obj) { + State sta = (State)obj; + + if (pos != sta.pos || steps != sta.steps) { + return false; + } + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 3; j++) { + if (board[i][j] != sta.board[i][j]) { + return false; + } + } + } + + return true; + } + + @Override + public int compareTo(State o) { + int num1 = steps + Math.abs(1 - (pos/3)) + Math.abs(2 - (pos%3)); + int num2 = o.steps + Math.abs(1 - (o.pos/3)) + Math.abs(2 - (o.pos%3)); + return num1 - num2; + } + + public State(int pos, int steps, int[][] board) { + this.pos = pos; + this.steps = steps; + this.board = new int[2][3]; + + copyBoard(board, this.board); + } + } + + private boolean isEndState(State sta) { + int[][] ans = new int[][] {{1,2,3},{4,5,0}}; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 3; j++) { + if (ans[i][j] != sta.board[i][j]) { + return false; + } + } + } + + return true; + } + + private void copyBoard(int[][] oldBoard, int[][] newBoard) { + for (int i = 0; i < 2; i++) { + System.arraycopy(oldBoard[i], 0, newBoard[i], 0, 3); + } + } + + private List getNewState(State curSta) { + List result = new ArrayList<>(4); + + int i = curSta.pos / 3, j = curSta.pos % 3; + // 0向四个方向移动 + if (i != 0) { + State newState = new State(j, curSta.steps+1, curSta.board); + int tmp = newState.board[i][j]; newState.board[i][j] = newState.board[i-1][j]; newState.board[i-1][j] = tmp; + result.add(newState); + } + + if (i != 1) { + State newState = new State(3 + j, curSta.steps+1, curSta.board); + int tmp = newState.board[i][j]; newState.board[i][j] = newState.board[i+1][j]; newState.board[i+1][j] = tmp; + result.add(newState); + } + + if (j > 0) { + State newState = new State(3*i + (j-1), curSta.steps+1, curSta.board); + int tmp = newState.board[i][j]; newState.board[i][j] = newState.board[i][j-1]; newState.board[i][j-1] = tmp; + result.add(newState); + } + + if (j < 2) { + State newState = new State(3*i + (j+1), curSta.steps+1, curSta.board); + int tmp = newState.board[i][j]; newState.board[i][j] = newState.board[i][j+1]; newState.board[i][j+1] = tmp; + result.add(newState); + } + + return result; + } + + public int slidingPuzzle(int[][] board) { + PriorityQueue heap = new PriorityQueue<>(); + int zeroPos = 0; + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 3; j++) { + if (board[i][j] == 0) { + zeroPos = i* 3 + j; + } + } + } + + Set visited = new HashSet<>(); + State initState = new State(zeroPos, 0, board); + if (isEndState(initState)) { + return 0; + } + + + heap.add(initState); + visited.add(initState); + while (!heap.isEmpty()) { + State curSta = heap.poll(); + + // 0向四个方向移动 + for (State newState : getNewState(curSta)) { + // 检查如果出现过更好的状态,当前的状态就是无用的 + int steps = newState.steps; + boolean useless = false; + while (newState.steps >= 0) { + if (visited.contains(newState)) { + useless = true; + break; + } + newState.steps--; + } + + if (useless) { + continue; + } + + newState.steps = steps; + + if (isEndState(newState)) { + return newState.steps; + } + + heap.add(newState); + visited.add(newState); + } + } + + return -1; + } + + public static void main(String[] args) { + System.out.println( new Solution().slidingPuzzle(new int[][] {{1,2,3},{5, 4, 0}})); + } +} diff --git a/Week 06/id_078/LeetCode_200_078.java b/Week 06/id_078/LeetCode_200_078.java new file mode 100644 index 000000000..0c34621db --- /dev/null +++ b/Week 06/id_078/LeetCode_200_078.java @@ -0,0 +1,109 @@ +//给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。 +// +// 示例 1: +// +// 输入: +//11110 +//11010 +//11000 +//00000 +// +//输出: 1 +// +// +// 示例 2: +// +// 输入: +//11000 +//11000 +//00100 +//00011 +// +//输出: 3 +// +// Related Topics 深度优先搜索 广度优先搜索 并查集 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + class UnionFind { + int count; // # of connected components + int[] parent; + int[] rank; + + public UnionFind(char[][] grid) { // for problem 200 + count = 0; + int m = grid.length; + int n = grid[0].length; + parent = new int[m * n]; + rank = new int[m * n]; + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + if (grid[i][j] == '1') { + parent[i * n + j] = i * n + j; + ++count; + } + rank[i * n + j] = 0; + } + } + } + + public int find(int i) { // path compression + if (parent[i] != i) parent[i] = find(parent[i]); + return parent[i]; + } + + public void union(int x, int y) { // union with rank + int rootx = find(x); + int rooty = find(y); + if (rootx != rooty) { + if (rank[rootx] > rank[rooty]) { + parent[rooty] = rootx; + } else if (rank[rootx] < rank[rooty]) { + parent[rootx] = rooty; + } else { + parent[rooty] = rootx; rank[rootx] += 1; + } + --count; + } + } + + public int getCount() { + return count; + } + } + + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + + int nr = grid.length; + int nc = grid[0].length; + int num_islands = 0; + UnionFind uf = new UnionFind(grid); + for (int r = 0; r < nr; ++r) { + for (int c = 0; c < nc; ++c) { + if (grid[r][c] == '1') { + grid[r][c] = '0'; + if (r - 1 >= 0 && grid[r-1][c] == '1') { + uf.union(r * nc + c, (r-1) * nc + c); + } + if (r + 1 < nr && grid[r+1][c] == '1') { + uf.union(r * nc + c, (r+1) * nc + c); + } + if (c - 1 >= 0 && grid[r][c-1] == '1') { + uf.union(r * nc + c, r * nc + c - 1); + } + if (c + 1 < nc && grid[r][c+1] == '1') { + uf.union(r * nc + c, r * nc + c + 1); + } + } + } + } + + return uf.getCount(); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 06/id_078/LeetCode_547_078.java b/Week 06/id_078/LeetCode_547_078.java new file mode 100644 index 000000000..851c64f42 --- /dev/null +++ b/Week 06/id_078/LeetCode_547_078.java @@ -0,0 +1,61 @@ +//班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。 +// +// 给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。 +// +// 示例 1: +// +// +//输入: +//[[1,1,0], +// [1,1,0], +// [0,0,1]] +//输出: 2 +//说明:已知学生0和学生1互为朋友,他们在一个朋友圈。 +//第2个学生自己在一个朋友圈。所以返回2。 +// +// +// 示例 2: +// +// +//输入: +//[[1,1,0], +// [1,1,1], +// [0,1,1]] +//输出: 1 +//说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。 +// +// +// 注意: +// +// +// N 在[1,200]的范围内。 +// 对于所有学生,有M[i][i] = 1。 +// 如果有M[i][j] = 1,则有M[j][i] = 1。 +// +// Related Topics 深度优先搜索 并查集 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public void dfs(int[][] M, int[] visited, int i) { + for (int j = 0; j < M.length; j++) { + if (M[i][j] == 1 && visited[j] == 0) { + visited[j] = 1; + dfs(M, visited, j); + } + } + } + public int findCircleNum(int[][] M) { + int[] visited = new int[M.length]; + int count = 0; + for (int i = 0; i < M.length; i++) { + if (visited[i] == 0) { + dfs(M, visited, i); + count++; + } + } + return count; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 06/id_083/LeetCode_130_083.java b/Week 06/id_083/LeetCode_130_083.java new file mode 100644 index 000000000..9ca94ccec --- /dev/null +++ b/Week 06/id_083/LeetCode_130_083.java @@ -0,0 +1,51 @@ +/* + * @lc app=leetcode id=130 lang=java + * + * [130] Surrounded Regions + */ + +// @lc code=start +class Solution { + public void solve(char[][] board) { + if (board == null || board.length == 0) return; + int m = board.length; + int n = board[0].length; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + // 从边缘o开始搜索 + boolean isEdge = i == 0 || j == 0 || i == m - 1 || j == n - 1; + if (isEdge && board[i][j] == 'O') { + dfs(board, i, j); + } + } + } + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (board[i][j] == 'O') { + board[i][j] = 'X'; + } + if (board[i][j] == '#') { + board[i][j] = 'O'; + } + } + } + } + + public void dfs(char[][] board, int i, int j) { + if (i < 0 || j < 0 || i >= board.length || j >= board[0].length || board[i][j] == 'X' || board[i][j] == '#') { + // board[i][j] == '#' 说明已经搜索过了. + return; + } + board[i][j] = '#'; + dfs(board, i - 1, j); // 上 + dfs(board, i + 1, j); // 下 + dfs(board, i, j - 1); // 左 + dfs(board, i, j + 1); // 右 + } + +} + + +// @lc code=end + diff --git a/Week 06/id_083/LeetCode_200_083.java b/Week 06/id_083/LeetCode_200_083.java new file mode 100644 index 000000000..213d03109 --- /dev/null +++ b/Week 06/id_083/LeetCode_200_083.java @@ -0,0 +1,58 @@ +import java.util.LinkedList; + +/* + * @lc app=leetcode id=200 lang=java + * + * [200] Number of Islands + */ + +// @lc code=start +class Solution { + public int numIslands(char[][] grid) { + int count = 0; + for(int i=0;i list = new LinkedList<>(); + list.add(new int[]{i,j}); + while(!list.isEmpty()){ + int []cur = list.remove(); + i = cur[0]; + j = cur[1]; + if(0<=i && i=grid.length || j>=grid[0].length || grid[i][j] == '0') + return; + + grid[i][j] = '0'; + dfs(grid, i+1, j); + dfs(grid, i, j+1); + dfs(grid, i-1, j); + dfs(grid, i, j-1); + } + +} +// @lc code=end + diff --git a/Week 06/id_083/LeetCode_208_083.java b/Week 06/id_083/LeetCode_208_083.java new file mode 100644 index 000000000..87b25d2a4 --- /dev/null +++ b/Week 06/id_083/LeetCode_208_083.java @@ -0,0 +1,89 @@ +/* + * @lc app=leetcode id=208 lang=java + * + * [208] Implement Trie (Prefix Tree) + */ + +// @lc code=start + +class TrieNode { + // R links to node children + private TrieNode[] links; + private final int R = 26; + private boolean isEnd; + + public TrieNode() { + links = new TrieNode[R]; + } + public boolean containsKey(char ch) { + return links[ch -'a'] != null; + } + public TrieNode get(char ch) { + return links[ch -'a']; + } + public void put(char ch, TrieNode node) { + links[ch -'a'] = node; + } + public void setEnd() { + isEnd = true; + } + public boolean isEnd() { + return isEnd; + } +} + +class Trie { + + private TrieNode root; + /** Initialize your data structure here. */ + public Trie() { + root = new TrieNode(); + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + TrieNode node = root; + for(int i=0;i findWords(char[][] board, String[] words) { + Trie trie = new Trie(); + for(String word:words) + trie.insert(word); + + int m = board.length,n = board[0].length; + boolean[][] visited = new boolean[m][n]; + Set resultSet = new HashSet<>(); + + for(int i=0;i(resultSet); + } + + private void search(char[][] board,boolean[][]visited,int i,int j,int m,int n,TrieNode node,Setresult){ + if(i<0 || j<0 ||i>=m ||j>=n ||visited[i][j]) + return; + node = node.children[board[i][j] - 'a']; + if(node ==null) + return; + if(node.word != null) + result.add(node.word); + + visited[i][j] = true; + search(board, visited, i-1, j, m, n, node, result); + search(board, visited, i+1, j, m, n, node, result); + search(board, visited, i, j-1, m, n, node, result); + search(board, visited, i, j+1, m, n, node, result); + visited[i][j] = false; + } +} + +class Trie{ + public TrieNode root = new TrieNode(); + public void insert(String word){ + TrieNode node = root; + int n = word.length(); + int charNo; + for(int i=0;i queue = new LinkedList<>(); + // for(int i=0;i findWords(char[][] board, String[] words) { + List res = new ArrayList<>(); + TrieNode root = buildTrie(words); + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + dfs (board, i, j, root, res); + } + } + return res; + } + + //深度优先便利字符数组 + public void dfs(char[][] board, int i, int j, TrieNode p, List res) { + char c = board[i][j]; + if (c == '#' || p.next[c - 'a'] == null) return; //当该字母遍历过,或者不在Trie树中 退出递归 + p = p.next[c - 'a']; + // 当找到该节点单词加入结果集,同时清掉该节点的单词防止 le 找到后 找不到 leet 这样的单词 + if (p.word != null) { + res.add(p.word); + p.word = null; + } + + board[i][j] = '#'; //遍历过的字母设置标识 + if (i > 0) dfs(board, i - 1, j ,p, res); // 左 + if (j > 0) dfs(board, i, j - 1, p, res); // 上 + if (i < board.length - 1) dfs(board, i + 1, j, p, res); //右 + if (j < board[0].length - 1) dfs(board, i, j + 1, p, res); //下 + board[i][j] = c; //还原 + } + + //Trie树生成 + //链接存在。沿着链接移动到树的下一个子层。算法继续搜索下一个键字符。 + //链接不存在。创建一个新的节点,并将它与父节点的链接相连,该链接与当前的键字符相匹配。 + //当一个单词结束时,该单词存储在单词结尾的TrieNode.word中 + public TrieNode buildTrie(String[] words) { + TrieNode root = new TrieNode(); + for (String w : words) { + TrieNode p = root; + for (char c : w.toCharArray()) { + int i = c - 'a'; + if (p.next[i] == null) p.next[i] = new TrieNode(); + p = p.next[i]; + } + p.word = w; + } + return root; + } + + //Trie树节点定义 + class TrieNode { + TrieNode[] next = new TrieNode[26]; // 指向下个字母的26叉树 + String word; // 当前节点单词 + } + +} \ No newline at end of file diff --git a/Week 06/id_098/LeetCode_773_098.java b/Week 06/id_098/LeetCode_773_098.java new file mode 100644 index 000000000..afe6fe139 --- /dev/null +++ b/Week 06/id_098/LeetCode_773_098.java @@ -0,0 +1,70 @@ +//最优解 +/* +比较难想到的技术处理就是, 将二维矩阵转换为String, 然后根据字符串中0所在位置能交换的所有坐标进行交换 +例如:123405中indexOfzero = 4, 那么这个点能够交换的坐标分别为[0, 1], [1,0], [1,2], 转换为一维后就是1, 3, 5 + +每一次搜索都获取下一层中能出现的所有情况存入队列 +例如:123405 下一层交换后出现的所有情况为:103425 + 123045 + 123450, 每次poll时进行比较是否equals"123450"即可 + */ +class Solution { + public int slidingPuzzle(int[][] board) { + Set visited = new HashSet(); + Queue q = new LinkedList(); + String E = "123450"; + char[] buff = new char[6]; + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[i].length; j++) { + buff[i * 3 + j] = (char)(board[i][j] + (int)'0'); + } + } + String s = new String(buff); + q.offer(s); + visited.add(s); + int res = 0; + while (!q.isEmpty()) { + int size = q.size(); + while (size-- > 0) { + s = q.poll(); + //System.out.println(s); + if (0 == s.compareTo(E)) return res; + this.offer(q, s, visited); + } + ++res; + } + + return -1; + } + + private void offer(Queue q, String s, Set visited) { + char[] buff = s.toCharArray(); + for (int i = 0; i < buff.length; i++) { + if (buff[i] == '0') { + int x = i / 3, y = i % 3; + this.swap(q, buff, x, y, x - 1, y, visited); + this.swap(q, buff, x, y, x + 1, y, visited); + this.swap(q, buff, x, y, x, y - 1, visited); + this.swap(q, buff, x, y, x, y + 1, visited); + return; + } + } + } + + private void swap(Queue q, char[] buff, int x1, int y1, + int x2, int y2, Set visited) { + if ( + x1 < 0 || x1 > 1 || y1 < 0 || y1 > 2 || + x2 < 0 || x2 > 1 || y2 < 0 || y2 > 2 + ) return; + + char t = buff[x1 * 3 + y1]; + buff[x1 * 3 + y1] = buff[x2 * 3 + y2]; + buff[x2 * 3 + y2] = t; + String s = new String(buff); + t = buff[x1 * 3 + y1]; + buff[x1 * 3 + y1] = buff[x2 * 3 + y2]; + buff[x2 * 3 + y2] = t; + if (visited.contains(s)) return; + q.offer(s); + visited.add(s); + } +} \ No newline at end of file diff --git a/Week 06/id_103/LeeCode_130_103.java b/Week 06/id_103/LeeCode_130_103.java new file mode 100644 index 000000000..0c7431a2a --- /dev/null +++ b/Week 06/id_103/LeeCode_130_103.java @@ -0,0 +1,49 @@ +/* + * @lc app=leetcode.cn id=130 lang=java + * + * [130] 被围绕的区域 + */ + +// @lc code=start +class Solution { + public void solve(char[][] board) { + if (board == null || board.length == 0) return; + int m = board.length; + int n = board[0].length; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + // 从边缘o开始搜索 + boolean isEdge = i == 0 || j == 0 || i == m - 1 || j == n - 1; + if (isEdge && board[i][j] == 'O') { + dfs(board, i, j); + } + } + } + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (board[i][j] == 'O') { + board[i][j] = 'X'; + } + if (board[i][j] == '#') { + board[i][j] = 'O'; + } + } + } + } + + public void dfs(char[][] board, int i, int j) { + if (i < 0 || j < 0 || i >= board.length || j >= board[0].length || board[i][j] == 'X' || board[i][j] == '#') { + // board[i][j] == '#' 说明已经搜索过了. + return; + } + board[i][j] = '#'; + dfs(board, i - 1, j); // 上 + dfs(board, i + 1, j); // 下 + dfs(board, i, j - 1); // 左 + dfs(board, i, j + 1); // 右 + } + +} +// @lc code=end + diff --git a/Week 06/id_103/LeeCode_36_103.java b/Week 06/id_103/LeeCode_36_103.java new file mode 100644 index 000000000..c5d1368d0 --- /dev/null +++ b/Week 06/id_103/LeeCode_36_103.java @@ -0,0 +1,30 @@ +/* + * @lc app=leetcode.cn id=36 lang=java + * + * [36] 有效的数独 + */ + +// @lc code=start +class Solution { + public boolean isValidSudoku(char[][] board) { + //最外层循环,每次循环并非只是处理第i行,而是处理第i行、第i列以及第i个3x3的九宫格 + for(int i = 0; i < 9; i++){ + HashSet line = new HashSet<>(); + HashSet col = new HashSet<>(); + HashSet cube = new HashSet<>(); + for(int j = 0; j < 9; j++){ + if('.' != board[i][j] && !line.add(board[i][j])) + return false; + if('.' != board[j][i] && !col.add(board[j][i])) + return false; + int m = i/3*3+j/3; + int n = i%3*3+j%3; + if('.' != board[m][n] && !cube.add(board[m][n])) + return false; + } + } + return true; + } +} +// @lc code=end + diff --git a/Week 06/id_108/LeeCode_208_108.java b/Week 06/id_108/LeeCode_208_108.java new file mode 100644 index 000000000..2386e2c1b --- /dev/null +++ b/Week 06/id_108/LeeCode_208_108.java @@ -0,0 +1,182 @@ +package study; + +import java.util.HashMap; + +/** + * + * 思路 + + */ +public class LeeCode_208_108 { + + private class Node { + private int dumpli_num;////该字串的重复数目, 该属性统计重复次数的时候有用,取值为0、1、2、3、4、5…… + private int prefix_num;///以该字串为前缀的字串数, 应该包括该字串本身!!!!! + private Node childs[];////此处用数组实现,当然也可以map或list实现以节省空间 + private boolean isLeaf;///是否为单词节点 + + public Node() { + dumpli_num = 0; + prefix_num = 0; + isLeaf = false; + childs = new Node[26]; + } + } + + + private Node root;///树根 + + public LeeCode_208_108() { + ///初始化trie 树 + root = new Node(); + } + + + /** + * 插入字串,用循环代替迭代实现 + * + * @param words + */ + public void insert(String words) { + insert(this.root, words); + } + + /** + * 插入字串,用循环代替迭代实现 + * + * @param root + * @param words + */ + private void insert(Node root, String words) { + words = words.toLowerCase();////转化为小写 + char[] chrs = words.toCharArray(); + + for (int i = 0, length = chrs.length; i < length; i++) { + ///用相对于a字母的值作为下标索引,也隐式地记录了该字母的值 + int index = chrs[i] - 'a'; + if (root.childs[index] != null) { + ////已经存在了,该子节点prefix_num++ + root.childs[index].prefix_num++; + } else { + ///如果不存在 + root.childs[index] = new Node(); + root.childs[index].prefix_num++; + } + + ///如果到了字串结尾,则做标记 + if (i == length - 1) { + root.childs[index].isLeaf = true; + root.childs[index].dumpli_num++; + } + ///root指向子节点,继续处理 + root = root.childs[index]; + } + + } + + + public HashMap getAllWords() { + return preTraversal(this.root, ""); + } + + /** + * 前序遍历。。。 + * + * @param root 子树根节点 + * @param prefixs 查询到该节点前所遍历过的前缀 + * @return + */ + private HashMap preTraversal(Node root, String prefixs) { + HashMap map = new HashMap(); + + if (root != null) { + + if (root.isLeaf == true) { + ////当前即为一个单词 + map.put(prefixs, root.dumpli_num); + } + + for (int i = 0, length = root.childs.length; i < length; i++) { + if (root.childs[i] != null) { + char ch = (char) (i + 'a'); + ////递归调用前序遍历 + String tempStr = prefixs + ch; + map.putAll(preTraversal(root.childs[i], tempStr)); + } + } + } + + return map; + } + + /** + * 查询某字串是否在字典树中 + * + * @param word + * @return true if exists ,otherwise false + */ + public boolean search(String word) { + char[] chs = word.toLowerCase().toCharArray(); + Node tmpRoot = root; + for (int i = 0, length = chs.length; i < length; i++) { + int index = chs[i] - 'a'; + if (tmpRoot.childs[index] == null) { + ///如果不存在,则查找失败 + return false; + } + tmpRoot = tmpRoot.childs[index]; + } + + // 不能有孩子了 + return tmpRoot.isLeaf; + } + + public boolean startsWith(String prefix) { + char[] chrs = prefix.toLowerCase().toCharArray(); + Node tmpRoot = root; + for (int i = 0, length = chrs.length; i < length; i++) { + int index = chrs[i] - 'a'; + if (tmpRoot.childs[index] == null) { + return false; + } + tmpRoot = tmpRoot.childs[index]; + } + return true; + } + + /** + * 得到以某字串为前缀的字串集,包括字串本身! 类似单词输入法的联想功能 + * + * @param prefix 字串前缀 + * @return 字串集以及出现次数,如果不存在则返回null + */ + public HashMap getWordsForPrefix(String prefix) { + return getWordsForPrefix(this.root, prefix); + } + + /** + * 得到以某字串为前缀的字串集,包括字串本身! + * + * @param root + * @param prefix + * @return 字串集以及出现次数 + */ + private HashMap getWordsForPrefix(Node root, String prefix) { + HashMap map = new HashMap(); + char[] chrs = prefix.toLowerCase().toCharArray(); + //// + for (int i = 0, length = chrs.length; i < length; i++) { + + int index = chrs[i] - 'a'; + if (root.childs[index] == null) { + return null; + } + + root = root.childs[index]; + + } + ///结果包括该前缀本身 + ///此处利用之前的前序搜索方法进行搜索 + return preTraversal(root, prefix); + } + } \ No newline at end of file diff --git a/Week 06/id_108/LeeCode_547_108.java b/Week 06/id_108/LeeCode_547_108.java new file mode 100644 index 000000000..228a15882 --- /dev/null +++ b/Week 06/id_108/LeeCode_547_108.java @@ -0,0 +1,28 @@ +package study; + +public class LeeCode_547_108 { + public int findCircleNum(int[][] M) { + if (M == null && M.length == 0) + return 0; + int n = M.length; + boolean[] visited = new boolean[n]; + int count = 0; + // 如果dfs大于0,说明有未遍历的结点 + // 只需要遍历所有结点 + for (int i = 0; i < n; i++) + if (dfs(M, i, visited) > 0) + count++; + return count; + } + + private int dfs(int[][] mat, int i, boolean[] visited) { + if (visited[i]) + return 0; + visited[i] = true; + int count = 1; + for (int j = 0; j < visited.length; j++) + if (i != j && mat[i][j] == 1) + count += dfs(mat, j, visited); + return count; + } +} diff --git a/Week 06/id_113/LeetCode200.py b/Week 06/id_113/LeetCode200.py new file mode 100644 index 000000000..45368f3bd --- /dev/null +++ b/Week 06/id_113/LeetCode200.py @@ -0,0 +1,62 @@ +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + class UnionFind: + def __init__(self, n): + self.count = n + self.parent = [i for i in range(n)] + self.rank = [1 for _ in range(n)] + + def get_count(self): + return self.count + + def find(self, p): + while p != self.parent[p]: + self.parent[p] = self.parent[self.parent[p]] + p = self.parent[p] + return p + + def is_connected(self, p, q): + return self.find(p) == self.find(q) + + def union(self, p, q): + p_root = self.find(p) + q_root = self.find(q) + if p_root == q_root: + return + + if self.rank[p_root] > self.rank[q_root]: + self.parent[q_root] = p_root + elif self.rank[p_root] < self.rank[q_root]: + self.parent[p_root] = q_root + else: + self.parent[q_root] = p_root + self.rank[p_root] += 1 + + self.count -= 1 + + row = len(grid) + # 特判 + if row == 0: + return 0 + col = len(grid[0]) + + def get_index(x, y): + return x * col + y + + directions = [(1, 0), (0, 1)] + + dummy_node = row * col + + uf = UnionFind(dummy_node + 1) + for i in range(row): + for j in range(col): + if grid[i][j] == '0': + uf.union(get_index(i, j), dummy_node) + if grid[i][j] == '1': + for direction in directions: + new_x = i + direction[0] + new_y = j + direction[1] + if new_x < row and new_y < col and grid[new_x][new_y] == '1': + uf.union(get_index(i, j), get_index(new_x, new_y)) + + return uf.get_count() - 1 diff --git a/Week 06/id_113/LeetCode208.py b/Week 06/id_113/LeetCode208.py new file mode 100644 index 000000000..fc8e6855d --- /dev/null +++ b/Week 06/id_113/LeetCode208.py @@ -0,0 +1,38 @@ +class Trie: + def __init__(self): + """ + Initialize your data structure here. + """ + self.root = {} + self.end_of_word = "#" + + def insert(self, word: str) -> None: + """ + Inserts a word into the trie. + """ + node = self.root + for char in word: + node = node.setdefault(char, {}) + node[self.end_of_word] = self.end_of_word + + def search(self, word: str) -> bool: + """ + Returns if the word is in the trie. + """ + node = self.root + for char in word: + if char not in node: + return False + node = node[char] + return self.end_of_word in node + + def startsWith(self, prefix: str) -> bool: + """ + Returns if there is any word in the trie that starts with the given prefix. + """ + node = self.root + for char in prefix: + if char not in node: + return False + node = node[char] + return True diff --git a/Week 06/id_113/LeetCode212.py b/Week 06/id_113/LeetCode212.py new file mode 100644 index 000000000..45febf0d5 --- /dev/null +++ b/Week 06/id_113/LeetCode212.py @@ -0,0 +1,39 @@ +class Solution: + def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: + self.dx = [-1, 1, 0, 0] + self.dy = [0, 0, -1, 1] + self.end_of_words = "#" + + if not board or not board[0] or not words: + return [] + + self.result = set() + + root = dict() + for word in words: + node = root + for char in word: + node = node.setdefault(char, dict()) + node[self.end_of_words] = self.end_of_words + + self.m, self.n = len(board), len(board[0]) + for i in range(self.m): + for j in range(self.n): + if board[i][j] in root: + self._dfs(board, i, j, "", root) + + return list(self.result) + + def _dfs(self, board, i, j, cur_word, cur_dict): + cur_word += board[i][j] + cur_dict = cur_dict[board[i][j]] + + if self.end_of_words in cur_dict: + self.result.add(cur_word) + tmp, board[i][j] = board[i][j], "@" + + for k in range(4): + x, y = i + self.dx[k], j + self.dy[k] + if 0 <= x < self.m and 0 <= y < self.n and board[x][y] != "@" and board[x][y] in cur_dict: + self._dfs(board, x, y, cur_word, cur_dict) + board[i][j] = tmp \ No newline at end of file diff --git a/Week 06/id_113/LeetCode547.py b/Week 06/id_113/LeetCode547.py new file mode 100644 index 000000000..446f70b64 --- /dev/null +++ b/Week 06/id_113/LeetCode547.py @@ -0,0 +1,31 @@ +class Solution: + def findCircleNum(self, M: List[List[int]]) -> int: + if not M: + return 0 + n = len(M) + p = [i for i in range(n)] + + for i in range(n): + for j in range(n): + if M[i][j] == 1: + self._union(p, i, j) + + return len(set([self.parent(p, i) for i in range(n)])) + + def _union(self, p, i, j): + p1 = self.parent(p, i) + p2 = self.parent(p, j) + + p[p2] = p1 + + def parent(self, p, i): + root = i + while p[root] != root: + root = p[root] + + while p[i] != i: + x = i + i = p[i] + p[x] = root + + return root \ No newline at end of file diff --git a/Week 06/id_118/LeetCode_1091_118.py b/Week 06/id_118/LeetCode_1091_118.py new file mode 100644 index 000000000..1f1338635 --- /dev/null +++ b/Week 06/id_118/LeetCode_1091_118.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Created by Isaac Zhou at 11/30/19 + +class Solution: + def shortestPathBinaryMatrix(self, grid): + n = len(grid) + if grid[0][0] or grid[n - 1][n - 1]: + return -1 + q = [(0, 0, 1)] + for i, j, d in q: + if i == n - 1 and j == n - 1: + return d + for x, y in ( + (i - 1, j - 1), (i - 1, j), (i - 1, j + 1), (i, j - 1), + (i, j + 1), + (i + 1, j - 1), (i + 1, j), (i + 1, j + 1)): + if 0 <= x < n and 0 <= y < n and not grid[x][y]: + grid[x][y] = 1 + q.append((x, y, d + 1)) + + return -1 diff --git a/Week 06/id_118/LeetCode_773_118.py b/Week 06/id_118/LeetCode_773_118.py new file mode 100644 index 000000000..d5ddf175f --- /dev/null +++ b/Week 06/id_118/LeetCode_773_118.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Created by Isaac Zhou at 11/30/19 + +class Solution: + def slidingPuzzle(self, board): + def swap(s, i, j): + return (s[:i] + s[j] + s[i + 1:j] + s[i] + s[j + 1:] + if i < j else s[:j] + s[i] + s[j + 1:i] + s[j] + s[i + 1:]) + + g = {0: {1, 3}, 1: {0, 2, 4}, 2: {1, 5}, 3: {0, 4}, 4: {1, 3, 5}, + 5: {2, 4}} + s, seen = "".join(map(str, sum(board, []))), set() + q = [(s.index("0"), s, 0)] + + for i, s, k in q: + if s == "123450": + return k + seen.add(s) + q += [(j, ns, k + 1) for j in g[i] for ns in {swap(s, i, j)} if + ns not in seen] + + return -1 diff --git a/Week 06/id_123/LeetCode_127_123.c b/Week 06/id_123/LeetCode_127_123.c new file mode 100644 index 000000000..733c77ea6 --- /dev/null +++ b/Week 06/id_123/LeetCode_127_123.c @@ -0,0 +1,85 @@ +// def BFS(graph, start, end): +// queue = []? +// queue.append([start])? +// while queue:? +// node = queue.pop()? +// visited.add(node) +// process(node)? +// nodes = generate_related_nodes(node)? +// queue.push(nodes) +// # other processing work? +// ... + +/* + * 㷨˼룺 + * + * 1. Ҫһиʹ棬headrearʾβ + * 2. check()ڼǷתһĸͬ + * 3. vst[]ڼ¼indexǷѾʹ + * + */ + +bool check(char *s1, char *s2, int len){ + int i, flag = 0; + + for(i=0; i rear){ + node = &que[rear++]; + + if(check(wds[node->index], first, len)){ + return node->d + 1; + } + + for(i=0; iindex], wds[i], len)){ + que[head].index = i; + que[head++].d = node->d + 1; + vst[i] = 1; + } + } + + } + + return 0; +} \ No newline at end of file diff --git a/Week 06/id_123/LeetCode_36_123.c b/Week 06/id_123/LeetCode_36_123.c new file mode 100644 index 000000000..741fc950a --- /dev/null +++ b/Week 06/id_123/LeetCode_36_123.c @@ -0,0 +1,22 @@ + +bool isValidSudoku(char** board, int boardRowSize, int boardColSize) { + bool row[9][10]={0}; //表示某行某数是否被检索过 + bool col[9][10]={0}; //表示某列某数是否被检索过 + bool block[9][10]={0}; //表示某块某数是否被检索过 + + for(int i=0; i<9; i++){ + for(int j=0; j<9; j++){ + if(board[i][j]=='.') continue; + int num=board[i][j]-'0'; + if(row[i][num] || col[j][num] || block[i/3*3+j/3][num]){ + return false; + }else{ + row[i][num]=true; + col[j][num]=true; + block[i/3*3+j/3][num]=true; + } + } + } + return true; + +} \ No newline at end of file diff --git a/Week 06/id_128/LeetCode_200_128.cs b/Week 06/id_128/LeetCode_200_128.cs new file mode 100644 index 000000000..0ff2957be --- /dev/null +++ b/Week 06/id_128/LeetCode_200_128.cs @@ -0,0 +1,95 @@ +public class Solution +{ + public int NumIslands(char[][] grid) + { + int m = grid.Length; + if (m == 0) + return 0; + int n = grid[0].Length; + if (n == 0) + return 0; + + // Create forest + UFSet unionSet = new UFSet(n * m); + int nonGrouped = 0; + for (int row = 0; row < m; row++) + { + for (int column = 0; column < n; column++) + { + if (grid[row][column] == '0') + { + nonGrouped++; + continue; + } + + // Check Neighbors + /// index into the array for row,column + int currentRowColumn = row * n + column; + // left + if ((column - 1) >= 0 && grid[row][column - 1] == '1') + unionSet.Union(currentRowColumn, currentRowColumn - 1); + // top + if ((row - 1) >= 0 && grid[row - 1][column] == '1') + unionSet.Union(currentRowColumn, (row - 1) * n + column); + } + } + + return unionSet.GroupCount - nonGrouped; + } + + private ref struct UFSet + { + private Span _elementSet; + private Span _rankHeight; + private int _groupCount; + + public UFSet(int size) + { + _elementSet = new Span(new int[size]); + // Init parents to self + for (int i = 0; i < size; ++i) + _elementSet[i] = i; + _rankHeight = new Span(new int[size]); + _groupCount = size; + } + + public int Find(int element) + { + int id = element; + while (id != _elementSet[id]) + { + // Compress path + _elementSet[id] = _elementSet[_elementSet[id]]; + id = _elementSet[id]; + } + return id; + } + + public int GroupCount => _groupCount; + + public void Union(int x, int y) + { + int xRoot = Find(x); + int yRoot = Find(y); + // Already joined + if (xRoot == yRoot) + return; + + // x less than y, so place x under y + if (_rankHeight[xRoot] < _rankHeight[yRoot]) + _elementSet[xRoot] = yRoot; + // y less than x, so place y under x + else if (_rankHeight[yRoot] < _rankHeight[xRoot]) + _elementSet[yRoot] = xRoot; + // heights are equal, doesn't matter which goes under which + else + { + // place x under y, increase height of y + _elementSet[xRoot] = yRoot; + _rankHeight[yRoot]++; + } + // each combination is one less group + _groupCount--; + } + } +} \ No newline at end of file diff --git a/Week 06/id_128/LeetCode_547_128.cs b/Week 06/id_128/LeetCode_547_128.cs new file mode 100644 index 000000000..fd6a2b71a --- /dev/null +++ b/Week 06/id_128/LeetCode_547_128.cs @@ -0,0 +1,72 @@ +public class Solution +{ + + public int FindCircleNum(int[][] M) + { + int n = M.length; + UnionFind uf = new UnionFind(n); + for (int i = 0; i < n - 1; i++) + { + for (int j = i + 1; j < n; j++) + { + if (M[i][j] == 1) uf.Union(i, j); + } + } + return uf.Count(); + } + + class UnionFind + { + private int count = 0; + private int[] parent; + private int[] rank; + + public UnionFind(int n) + { + count = n; + parent = new int[n]; + rank = new int[n]; + for (int i = 0; i < n; i++) + { + parent[i] = i; + } + } + + public int Find(int p) + { + while (p != parent[p]) + { + parent[p] = parent[parent[p]]; // path compression by halving + p = parent[p]; + } + return p; + } + + public void Union(int p, int q) + { + int rootP = Find(p); + int rootQ = Find(q); + if (rootP == rootQ) return; + if (rank[rootQ] > rank[rootP]) + { + parent[rootP] = rootQ; + } + else + { + parent[rootQ] = rootP; + if (rank[rootP] == rank[rootQ]) + { + rank[rootP]++; + } + } + count--; + } + + public int Count() + { + return count; + } + } + + +} \ No newline at end of file diff --git a/Week 06/id_128/NOTE.md b/Week 06/id_128/NOTE.md index a6321d6e2..9cfbc314c 100644 --- a/Week 06/id_128/NOTE.md +++ b/Week 06/id_128/NOTE.md @@ -2,3 +2,85 @@ +avl vs red black tree + +- avl stores balance factor on each node {-1, 0 , 1} +- avl is more strictly balanced than red balck tree that a rebalance is trigger when the difference of height +between two sub-trees of any node is 2 or -2, whereas red black tree performs rebalance when one of the sub-tree's height +is two times of the other one's. + +1. avl provides faster lookup because they are more strictly balanced. +2. red black tree provides faster insertion and removal as fewer rotaions are done due to relatively relaxed balancing. +3. avl trees store balance factors or heights with each node, thus requires storage for an integer per node whereas red black tree requires only 1 bit of information per node. +4. red black trees are used in most of the language libraries like map, multimap, multiset in c++ where as avl trees are used in databases where faster retrivals are required. + +public void BidirectionalBFS(graph, start, end) { + startQueue; + endQueue; + visited; + startQueue.Enqueue(start); + endQueue.Enqueue(end); + + while (!startQueue.IsEmpty() && !endQueue.IsEmpty()) { + // process the shorter queue + if (endQueue.Count < startQueue.Count) { + tempQueue = startQueue; + startQueue = endQueue; + endQueue = tempQueue; + } + temp; + foreach (node in startQueue) { + visited.Add(node); + Process(node); + temp = GetAdjacentNodes(node); + } + startQueue = temp; + //process other stuff + } + } + + +字典树 +典型应用是用于统计和排序大量的字符串(但不仅限于字符串),可最大限度地减少无谓的字符串比较,查询效率比哈希表高 + +基本性质: + +节点本身不存完整的单词 +从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串 +每个节点的所有子节点路径代表的字符都不相同 + +并查集 +适用场景:组团、配对问题 + +基本操作: + +makeSet(s) +unionSet(x, y) +find(x) + + + + +AVL树(即平衡二叉搜索树)的左子树的高度减去右子树的高度(即平衡因子)值的范围为{0, 1, -1} + +通过四种旋转操作,可以让树保持平衡: + +左旋 +右旋 +左右旋 +右左旋 +AVL的不足: + +每个节点需要存储额外的信息(平衡因子) +调整次数比较频繁 + + +红黑树是一种近似平衡的二叉搜索树,它能确保任何一个节点的左右子树的高度差小于两倍。 + +红黑树的特点是: + +每个节点要么是红色,要么是黑色 +根节点是黑色 +每个叶子节点是黑色的 +不能有相邻接的两个红色节点 +在任一节点到其每个叶子的所有路径都包含相同数目的黑色节点 \ No newline at end of file diff --git a/Week 06/id_133/leetcode_208_133.java b/Week 06/id_133/leetcode_208_133.java new file mode 100644 index 000000000..821d8fca4 --- /dev/null +++ b/Week 06/id_133/leetcode_208_133.java @@ -0,0 +1,56 @@ + +/** + * https://leetcode-cn.com/problems/implement-trie-prefix-tree/#/description + * 题号:208 + * 题目:实现Trie(前缀树) + */ + +class Trie { + + private TrieNode root; + + /** Initialize your data structure here. */ + public Trie() { + root = new TrieNode(); + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char currentChar = word.charAt(i); + if (!node.containsKey(currentChar)) { + node.put(currentChar, new TrieNode()); + } + node = node.get(currentChar); + } + node.setEnd(); + } + + // search a prefix or whole key in trie and + // returns the node where search ends + private TrieNode searchPrefix(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char curLetter = word.charAt(i); + if (node.containsKey(curLetter)) { + node = node.get(curLetter); + } else { + return null; + } + } + return node; + } + + /** Returns if the word is in the trie. */ + public boolean search(String word) { + TrieNode node = searchPrefix(word); + return node != null && node.isEnd(); + } + + /** Returns if there is any word in the trie that starts with the given prefix. */ + public boolean startsWith(String prefix) { + TrieNode node = searchPrefix(prefix); + return node != null; + } +} \ No newline at end of file diff --git a/Week 06/id_133/leetcode_547_133.java b/Week 06/id_133/leetcode_547_133.java new file mode 100644 index 000000000..4164b1531 --- /dev/null +++ b/Week 06/id_133/leetcode_547_133.java @@ -0,0 +1,31 @@ + +/** + * https://leetcode-cn.com/problems/friend-circles/ + * 题号:547 + * 题目:朋友圈 + */ + +public class Solution { + + public void dfs(int[][] M, int[] visited, int i) { + for (int j = 0; j < M.length; j++) { + if (M[i][j] == 1 && visited[j] == 0) { + visited[j] = 1; + dfs(M, visited, j); + } + } + } + + public int findCircleNum(int[][] M) { + int[] visited = new int[M.length]; + int count = 0; + for (int i = 0; i < M.length; i++) { + if (visited[i] == 0) { + dfs(M, visited, i); + count++; + } + } + return count; + } + +} \ No newline at end of file diff --git a/Week 06/id_138/LeetCode_212_138.java b/Week 06/id_138/LeetCode_212_138.java new file mode 100644 index 000000000..447b81ce5 --- /dev/null +++ b/Week 06/id_138/LeetCode_212_138.java @@ -0,0 +1,84 @@ +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * 单词搜索 II + * @author L + *给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。 +单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用 + */ +public class LeetCode_212_138 { + //字典树节点 + class TrieNode{ + String val; + TrieNode[] child = new TrieNode[26]; + boolean isLeaf=false; + + } + //字典树定义 + class WordTrie{ + TrieNode root = new TrieNode(); + void insert(String s) { + TrieNode cur = root; + for(char ch:s.toCharArray()) { + if(cur.child[ch-'a'] == null) { + cur.child[ch-'a'] = new TrieNode(); + } + cur = cur.child[ch-'a']; + } + cur.isLeaf = true; + cur.val = s; + } + } + public List findWords(char[][] board, String[] words) { + if(board == null) { + return Collections.EMPTY_LIST; + } + //构造字典树 + WordTrie wt = new WordTrie(); + TrieNode root = new TrieNode(); + //初始化字典树 + for(String s:words) { + wt.insert(s); + } + Set result = new HashSet(); + int m = board.length; + int n = board[0].length; + boolean[][]visited = new boolean[m][n]; + for(int i=0;i(result); + } + private void findHelper(char[][] board, boolean[][] visited, int i, int j, int m, int n, Set result, + TrieNode root) { + // TODO Auto-generated method stub + //边界以及是否已经访问判断 + if(i<0||i>=m||j<0||j>=n||visited[i][j]) + return; + root = root.child[board[i][j]-'a']; + visited[i][j]=true; + if(root==null) + { + //如果单词不匹配,回退 + visited[i][j]=false; + return; + } + //找到单词加入,找到单词后不能回退,因为可能是“ad” “addd”这样的单词得继续回溯 + if(root.isLeaf) + { + result.add(root.val); + } + findHelper(board,visited,i+1,j,m,n,result,root); + findHelper(board,visited,i,j+1,m,n,result,root); + findHelper(board,visited,i,j-1,m,n,result,root); + findHelper(board,visited,i-1,j,m,n,result,root); + //最后要回退,因为下一个起点可能会用到上一个起点的字符 + visited[i][j]=false; + } +} diff --git a/Week 06/id_138/LeetCode_547_138.java b/Week 06/id_138/LeetCode_547_138.java new file mode 100644 index 000000000..651f618c6 --- /dev/null +++ b/Week 06/id_138/LeetCode_547_138.java @@ -0,0 +1,72 @@ +import java.util.LinkedList; +import java.util.Queue; + +/** + * +朋友圈: +班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。 +给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。 + * @author L + * + */ +public class LeetCode_547_138 { + /** + * + * @param M + * @return + */ + public int findCircleNum(int[][] M) { + int[] visited = new int[M.length]; + int count = 0; + int length = M.length; + for(int i=0;iqueue = new LinkedList(); + for(int i=0;i geset = new HashSet(Arrays.asList(bank)); + if (!geset.contains(end)) { + return -1; + } + char[] four = {'A', 'C', 'G', 'T'}; + HashSet positve = new HashSet(); + positve.add(start); + HashSet negative = new HashSet(); + negative.add(end); + HashSet tempNewSet = new HashSet(); + int step = 0; + while (positve.size() > 0 && negative.size() > 0) { + step++; + if(positve.size() > negative.size()){ + HashSet temp = new HashSet(positve); + positve = negative; + negative = temp; + } + for(String item:positve){ + String str; + char[] tempStringChars = item.toCharArray(); + for (int i= tempStringChars.length-1;i >= 0;--i){ + char oldChar = tempStringChars[i]; + for(int j=0;j<4;++j){ + tempStringChars[i] = four[j]; + String newString = new String(tempStringChars); + if(negative.contains(newString)){ + return step; + } else if(geset.contains(newString)){ + geset.remove(newString); + tempNewSet.add(newString); + } + } + tempStringChars[i] = oldChar; + } + } + positve = new HashSet(tempNewSet); + tempNewSet.clear(); + } + return -1; + } +} +// @lc code=end + diff --git a/Week 06/id_143/NOTE.md b/Week 06/id_143/NOTE.md index a6321d6e2..8d2882c74 100644 --- a/Week 06/id_143/NOTE.md +++ b/Week 06/id_143/NOTE.md @@ -1,4 +1,213 @@ -# NOTE +# 算法训练营学习 +# 第五周 +## 第十三课 +### 字典树 - +1. 应用场景: + - 统计和排序大量的字符串(可以不仅限于字符串); + - 经常被用在搜索引擎系统用于文本词频统计。 +2. 基本性质结构 + - 树形结构:节点本身不存完整单词,从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串(图中节点里面的字符并不是代表节点会存这么一个或者多个字符) +每个节点的所有子节点路径代表的字符都不同,如果涉及到统计频次,那么节点存储额外的数字,代表频次. +3. 优点: + - 最大限度地减少无谓的字符串比较 + - 查询效率比哈希表高 +4. 核心思想 + - 空间换时间 + - 利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的 +5. 代码模板: + ``` + class Trie(object): + + def __init__(self): + self.root = {} + self.end_of_word = "#" + + def insert(self, word): + node = self.root + for char in word: + node = node.setdefault(char, {}) + node[self.end_of_word] = self.end_of_word + + def search(self, word): + node = self.root + for char in word: + if char not in node: + return False + node = node[char] + return self.end_of_word in node + + def startsWith(self, prefix): + node = self.root + for char in prefix: + if char not in node: + return False + node = node[char] + return True + ``` +### 并查集 + +1. 适用场景 + - 组团、配对问题: + - 判断两个东西是不是一个集合中 +2. 并查集基本操作 + - makeSet(s): 建立一个新的并查集,其中包含s个单元素集合。 + - unionSet(x, y): 把元素x和元素y所在的集合合并,要求x和y所在的集合不相交,如果相交则不合并。 + - find(x): 找到元素x所在的集合的代表,该操作也可以用于判断两个元素是否位于同一个集合,只要它们各自的代表比较一下就可以了。 +并查集和BFS、DFS的运用场景差异一些做题总结 + +3. 特点: + - 对静态的集体数据,一次性解决问题,用并查集方法并不是最优的。 + - 对于变化的集体数据,并查集可以基于已经构建好的数据空间,进行调整和改动,并能在O(1)下随时查出两者是否同组问题,这是BFS,DFS做不到的。 + - 在动态变化的情况下,并查集还是空间换时间的思想。 +4. 实现代码 + ``` + class UnionFind { + private int count = 0; + private int[] parent; + public UnionFind(int n) { + count = n; + parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + public int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) return; + parent[rootP] = rootQ; + count--; + } + } + ```` +## 第十三课 +### 高级搜索 +1. 优化方式:不重复,剪枝 +2. 搜索方向:DFS、BFS、双向搜索、启发式搜索、 +3. 双向BFS代码模板 +``` + # 如果搜索变换结果有限制在一个列表limit里面,那么先转化为set方便后面查找 + limit = set(limit) + # 特殊值过滤,如果题解要求搜索目标一定要在limit里面,则要过滤掉哪些没有在里面的情况返回 + if target not in limit: + return + # 头尾BFS,定义头尾集合,存储头尾每一层到达的值 + head = {start} + tail = {target} + # 遍历步数,按照题解可能为0或者-1 + step = 0 + # 对头尾进行广度优先搜索,这里有个细节处理,始终将head和tail长度小的集合赋值给head + # 这样搜索效率更高 + while head: + # 每一层步数+1 + step += 1 + # 准备下一层到达的值的集合 + next_head = set() + # 处理本层逻辑 + for n in head: + next_n = get_next_level_node(n) # 通过本层的节点获取下一层处理节点 + # 如果替换的值在tail里面找到表示搜索到了,返回步数 或者 结果 + if new_n in tail: + return step (or) value + if new_n in limit: # 如果新节点满足题意限制条件 + next_head.add(new_n) # 加入到一下层处理节点集合中 + # 遍历过的节点从限定列表里面移除,防止重复遍历 + limit.remove(new_n) + # 更新head集合为下一层的值的集合 + head = next_head + # 头尾互换,前面提到的 始终将head和tail长度小的集合赋值给head + if len(head) > len(tail): + head, tail = tail, head + # 没有找到返回异常结果值-1 + return -1 +``` +4. 启发式搜索 + - 启发式搜索:智能搜索、A*搜索 + - 通过优先级去进行搜索 + - 代码模板: + ``` + def AstarSearch(graph, start, end): + pq = collections.priority_queue() #优先级 -> 估价函数 + pq.append([start]) + visited.add(start) + while pq: + node = pq.pop() # can we add more inteligence here + visited.add(node) + + process(node) + nodes = generate_related_nodes(node) + unvisited = [node for node in nodes if node not in visited] + pq.push(unvisited) + ``` + - 估价函数 +> h(n),它用来评价哪些节点是最有希望的是一个我们要找的及诶点,h(n)会返回一个非负实数,也可以认为是从节点n的目标节点路径的估计成本。 +启发式函数式一种告知搜索方向的方法。它提供了一种明智的方法来猜测哪个邻居节点会导向一个目标。 +相似度测量方法 + +## 第十四课 +### 红黑树和AVL树 +1. 平衡树的作用和种类: + - 二叉搜索树在最坏的情况下,可能是一条“棍子”,搜索时间复杂度退化到O(n)。 +2. 保证性能的关键: + - 保证二维维度 -> 左右子树节点平衡 + - Balanced -> 平衡二叉树 + - 平衡树:面试着重掌握 +3. 需要掌握的树 + - 2-3树 + - AVL树 + - B树 + - 红黑树 +4. AVL树 + - 发明者G.M.Adelson-Velsky和Evgenii Landis + - Balance Factor(平衡因子): + - 左子树的高度减去它的右子树的高度(有时相反)。 + - balance factor = {-1, 0, 1} + - 类推平衡二叉搜索树 + - 每个节点存平衡因子balance factor = {-1, 0, 1} + - 不足 + - 节点需要存储额外信息 + - 调整次数频繁 + - 判断是否是AVL树 + - 扫描每个节点,看节点存储的旋转因子是否为{-1, 0, 1} + - 通过旋转操作来进行平衡(四种)(注意旋转节点带有子树的情况) + - 左左型 -> 左旋平衡 + - 右右型 -> 右旋平衡 + - 左右型 -> 先左旋,再右旋平衡 + - 右左型 -> 先右旋,再左旋平衡 +5. 红黑树 + + - 近似平衡二叉树 + - 通俗理解 + - 能够确保任何一个节点的左右子树的高度差小于两倍 + - 比如左子树高度为2,右子树最大高度为4 + - 比如右子树高度为5,左子树最大高度为10 + - 红黑树是满足5条件的二叉搜索树: + - 每个节点要么是红色,要么是黑色 + - 根节点是黑色 + - 每个叶节点是黑色的,且是空节点 + - 不能有相邻接的两个红色节点 + - 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点 + - 特征 + - 从根到叶子节点最长的可能路径不多于最短的可能路径的两倍长 + - 分析单词搜索 2 用 Tire 树方式实现的时间复杂度 +### 单词搜索II 复杂度分析 + +> 设words长度为 n ,每个单词的平均长度为 k , board长宽为 a * b +1. 构建words的字典树时间复杂度为 O(n * k) +2. 对board的每个字母进行扫描,时间复杂度为 O(a * b) +3. 对board的每个字母进行dfs操作 + 3.1 从字典树的根节点,判断到该字母为止的字符串是否为一个单词,时间复杂度为O(1) + 3.2 对该字母的扩散4个方向的字母,进行下探递归dfs + 3.2.1 这里可以理解为一个四叉树,四叉树的平均深度为字典树的平均深度,也就是单词的平均长度 k + 3.2.2 深度为 k的四叉树,节点一共有 4^0 + 4^1 + ... + 4^i {i<=k},略为4^k +综上,该题解的时间复杂度为: +O(n * k) + O(a * b * 4^k) 约大部分情况下,可以看作是a*b*4^k diff --git a/Week 06/id_153/LeetCode_208_153.js b/Week 06/id_153/LeetCode_208_153.js new file mode 100644 index 000000000..abbe08f90 --- /dev/null +++ b/Week 06/id_153/LeetCode_208_153.js @@ -0,0 +1,69 @@ +class TrieNode { + constructor() { + this.END = false; + this.children = {}; + } +} +let root = null; + +/** + * Initialize your data structure here. + */ +var Trie = function() { + root = new TrieNode(); +}; + +/** + * Inserts a word into the trie. + * @param {string} word + * @return {void} + */ +Trie.prototype.insert = function(word) { + let currNode = root; + for (let i = 0; i < word.length; i++) { + if (currNode.children[word[i]] === undefined) { + currNode.children[word[i]] = new TrieNode(); + } + currNode = currNode.children[word[i]]; + } + currNode.END = true; +}; + +const searchPrefix = word => { + let currNode = root; + for (let i = 0; i < word.length; i++) { + if (currNode.children[word[i]]) { + currNode = currNode.children[word[i]]; + } else { + return null; + } + } + return currNode; +}; + +/** + * Returns if the word is in the trie. + * @param {string} word + * @return {boolean} + */ +Trie.prototype.search = function(word) { + let currNode = searchPrefix(word); + return currNode != null && currNode.END; +}; + +/** + * 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 searchPrefix(prefix) != null; +}; + +/** + * 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) + */ diff --git a/Week 06/id_153/LeetCode_212_153.js b/Week 06/id_153/LeetCode_212_153.js new file mode 100644 index 000000000..d17e3270c --- /dev/null +++ b/Week 06/id_153/LeetCode_212_153.js @@ -0,0 +1,73 @@ +/** + * @param {character[][]} board + * @param {string[]} words + * @return {string[]} + */ +var findWords = function(board, words) { + class TrieNode { + constructor() { + this.END = false; + this.children = {}; + } + } + + let root = null; + let Trie = function() { + root = new TrieNode(); + }; + + Trie.prototype.insert = function(word) { + let currNode = root; + for (let i = 0; i < word.length; i++) { + if (currNode.children[word[i]] === undefined) { + currNode.children[word[i]] = new TrieNode(); + } + currNode = currNode.children[word[i]]; + } + currNode.END = true; + }; + + // 初始化变量 + 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]); + } + + // DFS 搜索 + let boardDFS = (i, j, currStr, currNode) => { + if (currNode.END) { + result.push(currStr); + currNode.END = false; + } + + if (i < 0 || j < 0 || i === m || j === n) { + return; + } + + const restore = board[i][j]; + if (restore === "#" || !currNode.children[restore]) { + return; + } + // 前进 + board[i][j] = "#"; + currStr += restore; + boardDFS(i - 1, j, currStr, currNode.children[restore]); + boardDFS(i + 1, j, currStr, currNode.children[restore]); + boardDFS(i, j - 1, currStr, currNode.children[restore]); + boardDFS(i, j + 1, currStr, currNode.children[restore]); + // 回溯 + board[i][j] = restore; + }; + // 寻找结果 + let result = []; + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + boardDFS(i, j, "", root); + } + } + return result; +}; diff --git a/Week 06/id_153/NOTE.md b/Week 06/id_153/NOTE.md index a6321d6e2..4b0b6e79d 100644 --- a/Week 06/id_153/NOTE.md +++ b/Week 06/id_153/NOTE.md @@ -1,4 +1,103 @@ -# NOTE +# 总结 +## 字典树 +`Trie` 树,又称单词查找树或键树,是一种树形结构,典型应用于统计和排序大量的字符串(但不仅限于字符串), +所以经常被搜索引擎系统用于文本词频统计 +优点: 最大限度地减少无谓的字符串比较,查询效率比哈希表高 +*基本性质* +1. 结点本身不存在完整单词 +2. 从根结点到某一结点,路径上经过的字符连接起来,为该结点对应的字符串 +3. 每个结点的所有子节点路径代表的字符都不相同 + +*核心思想* +1. 空间换时间 +2. 利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的 + +## 字典树模板 +```python +class Trie(object): + def __init__(self): + self.root = {} + self.end_of_word = "#" + + def insert(self, word): + node = self.root + for char in word: + node = node.setdefault(char, {}) + node[self.end_of_word] = self.end_of_word + + def search(self, word): + node = self.root + for char in word: + if char not in node: + return False + node = node[char] + return self.end_of_word in node + + def startsWith(self, prefix): + node = self.root + for char in prefix: + if char not in node: + return False + node = node[char] + return True +``` + +## 并查集 +`Disjoint Set` +*适用场景* +组团、配对问题 + +*基本操作* +1. makeSet(s):建立一个新的并查集,其中包含 `s` 个单元素集合 +2. unionSet(x,y):把元素 x 和元素 y 所在的集合合并,要求 x 和 y 所在的集合不相交,如果相交则不合并 +3. find(x):找到元素 x 所在的集合的代表,该操作也可硬用于判断两个元素是否位于同一个集合,只要将它们各自的代表比较一下就可以了。 + +## 并查集代码模板 +```java +class UnionFind { + private int count = 0; + private int[] parent; + public UnionFind(int n) { + count = n; + parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + public int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) return; + parent[rootP] = rootQ; + count--; + } +} +``` +```python +def init(p): + # for i = 0 .. n: p[i] = i; + p = [i for i in range(n)] + +def union(self, p, i, j): + p1 = self.parent(p, i) + p2 = self.parent(p, j) + p[p1] = p2 + +def parent(self, p, i): + root = i + while p[root] != root: + root = p[root] + while p[i] != i: # 路径压缩 ? + x = i; i = p[i]; p[x] = root + return root +``` \ No newline at end of file diff --git "a/Week 06/id_158/127.\345\215\225\350\257\215\346\216\245\351\276\231.cs" "b/Week 06/id_158/127.\345\215\225\350\257\215\346\216\245\351\276\231.cs" new file mode 100644 index 000000000..b4bab63ef --- /dev/null +++ "b/Week 06/id_158/127.\345\215\225\350\257\215\346\216\245\351\276\231.cs" @@ -0,0 +1,70 @@ +/* + * @lc app=leetcode.cn id=127 lang=csharp + * + * [127] 单词接龙 + */ + +// @lc code=start +using System.Collections.Generic; + +public class Solution +{ + public int LadderLength(string beginWord, string endWord, IList wordList) + { + if (!wordList.Contains(endWord)) + return 0; + int n = beginWord.Length; + Dictionary> allCommons = new Dictionary>(); + foreach (var word in wordList) + { + for (int i = 0; i < n; i++) + { + string common = word.Substring(0, i) + "*" + word.Substring(i + 1); + if (!allCommons.ContainsKey(common)) + allCommons.Add(common, new List()); + allCommons[common].Add(word); + } + } + IList begin = new List(); + IList end = new List(); + begin.Add(beginWord); + end.Add(endWord); + + IList visited = new List(); + int len = 1; + while (begin.Count > 0 && end.Count > 0) + { + if (begin.Count > end.Count) + { + IList tmp = begin; + begin = end; + end = tmp; + } + IList neighbor = new List(); + foreach (string cur in begin) + { + for (int i = 0; i < n; i++) + { + string tmp = cur.Substring(0, i) + "*" + cur.Substring(i + 1); + if (!allCommons.ContainsKey(tmp)) + continue; + foreach (string str in allCommons[tmp]) + { + if (end.Contains(str)) + return len + 1; + if (!visited.Contains(str)) + { + visited.Add(str); + neighbor.Add(str); + } + } + } + } + begin = neighbor; + len++; + } + return 0; + } +} +// @lc code=end + diff --git "a/Week 06/id_158/36.\346\234\211\346\225\210\347\232\204\346\225\260\347\213\254.cs" "b/Week 06/id_158/36.\346\234\211\346\225\210\347\232\204\346\225\260\347\213\254.cs" new file mode 100644 index 000000000..8c95216fe --- /dev/null +++ "b/Week 06/id_158/36.\346\234\211\346\225\210\347\232\204\346\225\260\347\213\254.cs" @@ -0,0 +1,39 @@ +/* + * @lc app=leetcode.cn id=36 lang=csharp + * + * [36] 有效的数独 + */ + +// @lc code=start +public class Solution +{ + private int[] rows = new int[9]; + private int[] columns = new int[9]; + private int[] boxes = new int[9]; + public bool IsValidSudoku(char[][] board) + { + for (int row = 0; row < 9; row++) + { + for (int column = 0; column < 9; column++) + { + char c = board[row][column]; + if (c == '.') + { + continue; + } + int val = 1 << c - '1'; + int i = row / 3 * 3 + column / 3; + if (((rows[row] | columns[column] | boxes[i]) & val) > 0) + { + return false; + } + rows[row] |= val; + columns[column] |= val; + boxes[i] |= val; + } + } + return true; + } +} +// @lc code=end + diff --git "a/Week 06/id_158/547.\346\234\213\345\217\213\345\234\210.cs" "b/Week 06/id_158/547.\346\234\213\345\217\213\345\234\210.cs" new file mode 100644 index 000000000..fa29b4825 --- /dev/null +++ "b/Week 06/id_158/547.\346\234\213\345\217\213\345\234\210.cs" @@ -0,0 +1,51 @@ +/* + * @lc app=leetcode.cn id=547 lang=csharp + * + * [547] 朋友圈 + */ + +// @lc code=start +using System; + +public class Solution +{ + public int FindCircleNum(int[][] M) + { + int[] parent = new int[M.Length]; + Array.Fill(parent, -1); + for (int i = 0; i < M.Length; i++) + { + for (int j = 0; j < M.Length; j++) + { + if (M[i][j] == 1 && i != j) + { + union(parent, i, j); + } + } + } + int count = 0; + for (int i = 0; i < parent.Length; i++) + { + if (parent[i] == -1) + count++; + } + return count; + } + + void union(int[] parent, int x, int y) + { + int xset = find(parent, x); + int yset = find(parent, y); + if (xset != yset) + parent[xset] = yset; + } + + int find(int[] parent, int i) + { + if (parent[i] == -1) + return i; + return find(parent, parent[i]); + } +} +// @lc code=end + diff --git a/Week 06/id_173/LeetCode_127_173.cpp b/Week 06/id_173/LeetCode_127_173.cpp new file mode 100644 index 000000000..764b20fa5 --- /dev/null +++ b/Week 06/id_173/LeetCode_127_173.cpp @@ -0,0 +1,43 @@ +/* + * 127. 单词接龙 + */ + +class Solution { +public: + int ladderLength(string beginWord, string endWord, vector& wordList) { + unordered_set wordDict(wordList.begin(), wordList.end()); + if(!wordDict.count(endWord)) + return 0; + + unordered_set curEnd({beginWord}); + unordered_set otherEnd({endWord}); + int length = 1; + + while(!curEnd.empty() && !otherEnd.empty()) { + if(otherEnd.size() < curEnd.size()) + swap(curEnd, otherEnd); + + unordered_set tempSet; + for(string str : curEnd) { + for(int i=0; i parent; + +public: + UnionFind(int n) { + for (int i = 0; i < n; ++i) + parent.push_back(i); + } + + int Find(int p) { + if (p != parent[p]) + parent[p] = Find(parent[p]); + return parent[p]; + } + + void Union(int p, int q) { + int rootP = Find(p); + int rootQ = Find(q); + + if (rootP != rootQ) + parent[rootQ] = rootP; + } +}; + +class Solution { +public: + void solve(vector>& board) { + int rows = board.size(); + if (rows == 0) return; + int cols = board[0].size(); + + UnionFind uf(rows*cols + 1); + + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + if (board[i][j] == 'O') { + if (i == 0 || i == rows - 1 || j == 0 || j == cols - 1) + uf.Union(rows*cols, i*cols + j); + else { + if (i - 1 >= 0 && board[i - 1][j] == 'O') + uf.Union(i*cols + j, (i - 1)*cols + j); + if (i + 1 < rows && board[i + 1][j] == 'O') + uf.Union(i*cols + j, (i + 1)*cols + j); + if (j - 1 >= 0 && board[i][j - 1] == 'O') + uf.Union(i*cols + j, i*cols + j - 1); + if (j + 1 < cols && board[i][j + 1] == 'O') + uf.Union(i*cols + j, i*cols + j + 1); + } + } + } + } + + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + if (board[i][j] == 'O' && (uf.Find(i*cols + j) != uf.Find(rows*cols))) + board[i][j] = 'X'; + } + } + } +}; \ No newline at end of file diff --git a/Week 06/id_173/LeetCode_200_173.cpp b/Week 06/id_173/LeetCode_200_173.cpp new file mode 100644 index 000000000..fe134ec0c --- /dev/null +++ b/Week 06/id_173/LeetCode_200_173.cpp @@ -0,0 +1,88 @@ +/* + * 200. 岛屿数量 + */ + +class UnionFind { +private: + vector parent; + vector rank; + int count; + +public: + UnionFind(vector>& grid) { + count = 0; + int rows = grid.size(); + int cols = grid[0].size(); + + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + if (grid[i][j] == '1') { + parent.push_back(i*cols + j); + count++; + } + else + parent.push_back(-1); + + rank.push_back(0); + } + } + } + + int Find(int p) { + if (p != parent[p]) + parent[p] = Find(parent[p]); + return parent[p]; + } + + void Union(int p, int q) { + int rootP = Find(p); + int rootQ = Find(q); + + if (rootP != rootQ) { + if (rank[rootP] > rank[rootQ]) + parent[rootQ] = rootP; + else if (rank[rootP] < rank[rootQ]) + parent[rootP] = rootQ; + else { + parent[rootQ] = rootP; + rank[rootP]++; + } + + count--; + } + } + + int GetCount() { + return count; + } +}; + +class Solution { +public: + int numIslands(vector>& grid) { + int rows = grid.size(); + if (rows == 0) + return 0; + int cols = grid[0].size(); + + UnionFind uf(grid); + + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + if (grid[i][j] == '1') { + grid[i][j] = '0'; + if (i - 1 >= 0 && grid[i - 1][j] == '1') + uf.Union(i*cols + j, (i - 1)*cols + j); + if (i + 1 < rows && grid[i + 1][j] == '1') + uf.Union(i*cols + j, (i + 1)*cols + j); + if (j - 1 >= 0 && grid[i][j - 1] == '1') + uf.Union(i*cols + j, i*cols + j - 1); + if (j + 1 < cols && grid[i][j + 1] == '1') + uf.Union(i*cols + j, i*cols + j + 1); + } + } + } + + return uf.GetCount(); + } +}; \ No newline at end of file diff --git a/Week 06/id_173/LeetCode_208_173.cpp b/Week 06/id_173/LeetCode_208_173.cpp new file mode 100644 index 000000000..d6e54c23b --- /dev/null +++ b/Week 06/id_173/LeetCode_208_173.cpp @@ -0,0 +1,45 @@ +/* + * 208. 实现 Trie (前缀树) + */ + +class Trie { +private: + Trie* links[26]; + bool end; + +public: + Trie() { + memset(links, NULL, sizeof(links)); + end = false; + } + + void insert(string word) { + Trie* node = this; + for(char ch : word) { + if(node->links[ch-'a'] == NULL) + node->links[ch-'a'] = new Trie(); + node = node->links[ch-'a']; + } + node->end = true; + } + + bool search(string word) { + Trie* node = this; + for(char ch : word) { + if(node->links[ch-'a'] == NULL) + return false; + node = node->links[ch-'a']; + } + return node->end; + } + + bool startsWith(string prefix) { + Trie* node = this; + for(char ch : prefix) { + if(node->links[ch-'a'] == NULL) + return false; + node = node->links[ch-'a']; + } + return true; + } +}; \ No newline at end of file diff --git a/Week 06/id_173/LeetCode_212_173.cpp b/Week 06/id_173/LeetCode_212_173.cpp new file mode 100644 index 000000000..1215a1e28 --- /dev/null +++ b/Week 06/id_173/LeetCode_212_173.cpp @@ -0,0 +1,85 @@ +/* + * 212. 单词搜索 II + */ + +class Trie { +private: + Trie* links[26]; + bool end; + +public: + Trie() { + memset(links, NULL, sizeof(links)); + end = false; + } + + void insert(string word) { + Trie* node = this; + for(char ch : word) { + if(node->links[ch-'a'] == NULL) + node->links[ch-'a'] = new Trie(); + node = node->links[ch-'a']; + } + node->end = true; + } + + Trie* get(char ch) { + return links[ch-'a']; + } + + bool isEnd() { + return end; + } +}; + +int dx[] = {-1, 1, 0, 0}; +int dy[] = {0, 0, -1, 1}; + +class Solution { +public: + vector findWords(vector>& board, vector& words) { + vector res; + + if(board.empty() || words.empty()) + return res; + + Trie trie; + for(string word : words) { + trie.insert(word); + } + + unordered_set hashTable; + for(int i=0; i>& board, int i, int j, string curString, Trie* curTrie, unordered_set& hashTable) { + curString += board[i][j]; + curTrie = curTrie->get(board[i][j]); + if(curTrie == NULL) + return; + if(curTrie->isEnd()) + hashTable.insert(curString); + + char temp = board[i][j]; + board[i][j] = '#'; + + for(int k=0; k<4; ++k) { + int x = i + dx[k]; + int y = j + dy[k]; + if(x>=0 && x=0 && y& bank) { + unordered_set genePool(bank.begin(), bank.end()); + if(!genePool.count(end)) + return -1; + + unordered_set curEnd({start}); + unordered_set otherEnd({end}); + vector character = {'A', 'C', 'G', 'T'}; + int times = 1; + + while(!curEnd.empty() && !otherEnd.empty()) { + if(otherEnd.size() < curEnd.size()) + swap(curEnd, otherEnd); + + unordered_set temp; + for(string str : curEnd) { + for(int i=0; i parent; + int count = 0; + +public: + UnionFind(int n) { + count = n; + parent = vector(n, 0); + for(int i=0; i>& M) { + int count = 0; + UnionFind unionFind(M.size()); + + for(int i=0; ilinks[ch-'a'] == NULL) + node->links[ch-'a'] = new Trie(); + node = node->links[ch-'a']; + } + node->end = true; + } + + bool search(string word) { + Trie* node = this; + for(char ch : word) { + if(node->links[ch-'a'] == NULL) + return false; + node = node->links[ch-'a']; + } + return node->end; + } + + bool startsWith(string prefix) { + Trie* node = this; + for(char ch : prefix) { + if(node->links[ch-'a'] == NULL) + return false; + node = node->links[ch-'a']; + } + return true; + } +}; +``` +## 二、并查集 +#### 1. 基本操作 +- makeSet(s):建立一个新的并查集,其中包含 s 个单元素集合 +- unionSet(x, y):把元素 x 和元素 y 所在的集合合并,要求 x 和 y 所在的集合不相交,如果相交则不合并 +- find(x):找到元素 x 所在的集合的代表,该操作也可以用于判断两个元素是否位于同一个集合,只要将它们各自的代表比较一下就可以了 +#### 2. 使用场景 +  组团、配对问题 +#### 3. 模板 +```C++ +class UnionFind { +private: + vector parent; + int count = 0; + +public: + UnionFind(int n) { + count = n; + parent = vector(n, 0); + for(int i=0; i 右旋 +- 右右型 -> 左旋 +- 左右型 -> 先左旋,再右旋 +- 右左型 -> 先右旋,再左旋 + +## 四、红黑树 +#### 1. 概述 +  红黑树是一种**近似平衡的二叉搜索树**,它能够确保任何一个结点的左右子树高度差小于两倍。 +#### 2. 结构特点 +- 每个结点要么是红色,要么是黑色 +- 根节点是黑色 +- 每个叶节点(NIL节点,空节点)是黑色的 +- 不能有相邻接的两个红色节点 +- 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点 +#### 3. 性质 +  从根到叶子节点最长的可能路径不多于最短的可能路径的两倍长。 diff --git a/Week 06/id_183/Leetcode_130_183.cpp b/Week 06/id_183/Leetcode_130_183.cpp new file mode 100644 index 000000000..818f231e8 --- /dev/null +++ b/Week 06/id_183/Leetcode_130_183.cpp @@ -0,0 +1,66 @@ +/* + * @lc app=leetcode id=130 lang=cpp + * + * [130] Surrounded Regions + */ + +// @lc code=start +class Solution { +public: + + + void solve(vector>& board) { + if (board.size() == 0) return; + int row = board.size(); + int col = board[0].size(); + vector> visited; + visited.resize(row); + for (int j=0; j < row; j++) { + visited[j].resize(col);//每行为c列 + } + + for (int j=0; j> &visited, int i, int j, vector>& board) { + if (i < 0 || i >= board.size() || j < 0 || j >= board[0].size() || visited[i][j] == true || board[i][j] != 'O') { + return; + } + board[i][j] = '*'; + visited[i][j] = true; + dfs(visited, i-1, j, board); + dfs(visited, i+1, j, board); + dfs(visited, i, j - 1, board); + dfs(visited, i, j + 1, board); + } +}; + +// @lc code=end + diff --git a/Week 06/id_183/Leetcode_547_183.cpp b/Week 06/id_183/Leetcode_547_183.cpp new file mode 100644 index 000000000..e7e660466 --- /dev/null +++ b/Week 06/id_183/Leetcode_547_183.cpp @@ -0,0 +1,57 @@ +/* + * @lc app=leetcode id=547 lang=cpp + * + * [547] Friend Circles + */ + +// @lc code=start +class Solution { +public: + + int rank[300]; + int p[300]; + int count; + + int find(int n) { + while (p[n] != n) { + n = p[n]; + } + return p[n]; + } + + void unionN(int n1, int n2) { + int np1 = find(n1); + int np2 = find(n2); + if (np1 == np2) return; + if (rank[np1] > rank[np2]) { + p[np2] = np1; + } else { + p[np1] = np2; + rank[np2]++; + } + count--; + } + + + int findCircleNum(vector>& M) { + int N = M.size(); + count = N; + for (int i=0; i 0 { // 只要队列不为空就继续执行循环 + cur := queue[0] // 取出队首元素顶点 + queue = queue[1:] // 移除队首元素顶点 + curx, cury := cur/C, cur%C // 一维坐标转二维坐标 + for d := 0; d < 8; d++ { // 查看顶点周围8个方向的相邻顶点 + nextx := curx + dirs[d][0] // 周围8个方向坐标差值 + nexty := cury + dirs[d][1] // 相邻顶点坐标(nextx, nexty) + // 合法 && 没有被访问过 && 没有被阻塞 + if inArea(nextx, nexty, R, C) && !visited[nextx][nexty] && grid[nextx][nexty] == 0 { + queue = append(queue, nextx*C+nexty) // 二维坐标转一维坐标入队 + visited[nextx][nexty] = true // 记录这个顶点已经被访问过 + dis[nextx][nexty] = dis[curx][cury] + 1 // 到该顶点的路径是从cur顶点+1 + + if nextx == R-1 && nexty == C-1 { // 如果(nextx,nexty)是终点 + return dis[nextx][nexty] // 返回到该顶点的距离 + } + } + } + } + return -1 +} + +func inArea(x, y, R, C int) bool { + return x >= 0 && x < R && y >= 0 && y < C +} + +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 06/id_188/leetCode-208-188.go b/Week 06/id_188/leetCode-208-188.go new file mode 100644 index 000000000..d5992b8a8 --- /dev/null +++ b/Week 06/id_188/leetCode-208-188.go @@ -0,0 +1,88 @@ +//实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。 +// +// 示例: +// +// Trie trie = new Trie(); +// +//trie.insert("apple"); +//trie.search("apple"); // 返回 true +//trie.search("app"); // 返回 false +//trie.startsWith("app"); // 返回 true +//trie.insert("app"); +//trie.search("app"); // 返回 true +// +// 说明: +// +// +// 你可以假设所有的输入都是由小写字母 a-z 构成的。 +// 保证所有输入均为非空字符串。 +// +// Related Topics 设计 字典树 + +//leetcode submit region begin(Prohibit modification and deletion) +type Trie struct { + value string + children map[string]*Trie + isWord bool +} + +/** Initialize your data structure here. */ +func Constructor() Trie { + return Trie{value: "", children: make(map[string]*Trie), isWord: false} +} + +/** Inserts a word into the trie. */ +func (this *Trie) Insert(word string) { + root := this + for _, w := range word { + n := root.match(string(w)) + if n == nil { + newNode := &Trie{value: string(w), children: make(map[string]*Trie)} + root.children[string(w)] = newNode + root = root.children[string(w)] + } else { + root = n + } + } + root.isWord = true +} + +/** Returns if the word is in the trie. */ +func (this *Trie) Search(word string) bool { + if n := this.match(word); n != nil && n.isWord { + return true + } else { + return false + } +} + +/** Returns if there is any word in the trie that starts with the given prefix. */ +func (this *Trie) StartsWith(prefix string) bool { + if n := this.match(prefix); n != nil { + return true + } else { + return false + } +} + +func (this *Trie) match(word string) *Trie { + root := this + for _, w := range word { + //node := &Trie{value:string(w), children:make(map[string]*Trie)} + if _, ok := root.children[string(w)]; ok { + root = root.children[string(w)] + } else { + return nil + } + } + return root +} + +/** + * Your Trie object will be instantiated and called as such: + * obj := Constructor(); + * obj.Insert(word); + * param_2 := obj.Search(word); + * param_3 := obj.StartsWith(prefix); + */ +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 06/id_188/leetCode-36-188.go b/Week 06/id_188/leetCode-36-188.go new file mode 100644 index 000000000..8a4f506d4 --- /dev/null +++ b/Week 06/id_188/leetCode-36-188.go @@ -0,0 +1,82 @@ +//判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。 +// +// +// 数字 1-9 在每一行只能出现一次。 +// 数字 1-9 在每一列只能出现一次。 +// 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 +// +// +// +// +// 上图是一个部分填充的有效的数独。 +// +// 数独部分空格内已填入了数字,空白格用 '.' 表示。 +// +// 示例 1: +// +// 输入: +//[ +// ["5","3",".",".","7",".",".",".","."], +// ["6",".",".","1","9","5",".",".","."], +// [".","9","8",".",".",".",".","6","."], +// ["8",".",".",".","6",".",".",".","3"], +// ["4",".",".","8",".","3",".",".","1"], +// ["7",".",".",".","2",".",".",".","6"], +// [".","6",".",".",".",".","2","8","."], +// [".",".",".","4","1","9",".",".","5"], +// [".",".",".",".","8",".",".","7","9"] +//] +//输出: true +// +// +// 示例 2: +// +// 输入: +//[ +//  ["8","3",".",".","7",".",".",".","."], +//  ["6",".",".","1","9","5",".",".","."], +//  [".","9","8",".",".",".",".","6","."], +//  ["8",".",".",".","6",".",".",".","3"], +//  ["4",".",".","8",".","3",".",".","1"], +//  ["7",".",".",".","2",".",".",".","6"], +//  [".","6",".",".",".",".","2","8","."], +//  [".",".",".","4","1","9",".",".","5"], +//  [".",".",".",".","8",".",".","7","9"] +//] +//输出: false +//解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。 +// 但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。 +// +// 说明: +// +// +// 一个有效的数独(部分已被填充)不一定是可解的。 +// 只需要根据以上规则,验证已经填入的数字是否有效即可。 +// 给定数独序列只包含数字 1-9 和字符 '.' 。 +// 给定数独永远是 9x9 形式的。 +// +// Related Topics 哈希表 + +//leetcode submit region begin(Prohibit modification and deletion) +func isValidSudoku(board [][]byte) bool { + var row, col, block [9]uint16 + var cur uint16 + for i := 0; i < 9; i++ { + for j := 0; j < 9; j++ { + if board[i][j] == '.' { + continue + } + cur = 1 << (board[i][j] - '1') // 当前数字的 二进制数位 位置 + bi := i/3 + j/3*3 // 3x3的块索引号 + if (row[i]&cur)|(col[j]&cur)|(block[bi]&cur) != 0 { // 使用与运算查重 + return false + } + // 在对应的位图上,加上当前数字 + row[i] |= cur + col[j] |= cur + block[bi] |= cur + } + } + return true +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 06/id_188/leetCode-547-188.go b/Week 06/id_188/leetCode-547-188.go new file mode 100644 index 000000000..34755d340 --- /dev/null +++ b/Week 06/id_188/leetCode-547-188.go @@ -0,0 +1,106 @@ +//班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。 +// +// 给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。 +// +// 示例 1: +// +// +//输入: +//[[1,1,0], +// [1,1,0], +// [0,0,1]] +//输出: 2 +//说明:已知学生0和学生1互为朋友,他们在一个朋友圈。 +//第2个学生自己在一个朋友圈。所以返回2。 +// +// +// 示例 2: +// +// +//输入: +//[[1,1,0], +// [1,1,1], +// [0,1,1]] +//输出: 1 +//说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。 +// +// +// 注意: +// +// +// N 在[1,200]的范围内。 +// 对于所有学生,有M[i][i] = 1。 +// 如果有M[i][j] = 1,则有M[j][i] = 1。 +// +// Related Topics 深度优先搜索 并查集 + +//leetcode submit region begin(Prohibit modification and deletion) +func findCircleNum(M [][]int) int { + // 使用并查集 + u := NewUFS(len(M)) + for i := 0; i < len(M); i++ { + for j := i + 1; j < len(M); j++ { + if M[i][j] == 0 { + continue + } + u.Union(i, j) + } + } + // 查找朋友圈个数 + m := make(map[int]struct{}) + for i := 0; i < len(M); i++ { + m[u.Find(i)] = struct{}{} + } + return len(m) +} + +type UFS struct { + s []int + sz []int +} + +func NewUFS(n int) *UFS { + s := make([]int, n) + sz := make([]int, n) + for i := 0; i < len(s); i++ { + s[i] = i + sz[i] = 1 + } + return &UFS{s: s, sz: sz} +} + +func (u *UFS) Find(n int) int { + if n > len(u.s) || n < 0 { + return -1 + } + r, j := u.s[n], n + for r != j { + j = r + r = u.s[j] + } + // 压缩路径 + p := 0 + for u.s[n] != n { + p = u.s[n] + u.s[n] = r + n = p + } + return r +} + +func (u *UFS) Union(i int, j int) { + p := u.Find(i) + q := u.Find(j) + if p == q { + return + } + if u.sz[p] < u.sz[q] { + u.s[p] = q + u.sz[q] += u.sz[p] + } else { + u.s[q] = p + u.sz[p] += u.sz[q] + } +} + +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 06/id_198/LeetCode_102_198.go b/Week 06/id_198/LeetCode_102_198.go new file mode 100644 index 000000000..e26e45226 --- /dev/null +++ b/Week 06/id_198/LeetCode_102_198.go @@ -0,0 +1,68 @@ +package leetcode + +// Trie Implement a trie +type Trie struct { + val rune + isEnd bool + children []Trie +} + +/** Initialize your data structure here. */ +func Constructor() Trie { + + return Trie{} +} + +/** Inserts a word into the trie. */ +func (this *Trie) Insert(word string) { + + chars := []rune(word) + curr := this + for i := 0; i < len(chars); i++ { + + if curr.children == nil { + + curr.children = make([]Trie, 26) + } + idx := int(chars[i]) - 97 + curr.children[idx].val = chars[i] + curr = &curr.children[idx] + } + curr.isEnd = true +} + +/** Returns if the word is in the trie. */ +func (this *Trie) Search(word string) bool { + + chars := []rune(word) + curr := this + i := 0 + for ; i < len(chars); i++ { + + idx := int(chars[i]) - 97 + if curr.children == nil || curr.children[idx].val != chars[i] { + return false + } + curr = &curr.children[idx] + + } + return curr.isEnd +} + +/** Returns if there is any word in the trie that starts with the given prefix. */ +func (this *Trie) StartsWith(prefix string) bool { + + chars := []rune(prefix) + curr := this + i := 0 + for ; i < len(chars); i++ { + + idx := int(chars[i]) - 97 + if curr.children == nil || curr.children[idx].val != chars[i] { + return false + } + curr = &curr.children[idx] + + } + return true +} diff --git a/Week 06/id_198/LeetCode_102_198_test.go b/Week 06/id_198/LeetCode_102_198_test.go new file mode 100644 index 000000000..e54fe540c --- /dev/null +++ b/Week 06/id_198/LeetCode_102_198_test.go @@ -0,0 +1,112 @@ +package leetcode + +import ( + "reflect" + "testing" +) + +func TestConstructor(t *testing.T) { + tests := []struct { + name string + want Trie + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := Constructor(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Constructor() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestTrie_Insert(t *testing.T) { + type fields struct { + val rune + isEnd bool + children []Trie + } + type args struct { + word string + } + tests := []struct { + name string + fields fields + args args + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + this := &Trie{ + val: tt.fields.val, + isEnd: tt.fields.isEnd, + children: tt.fields.children, + } + this.Insert(tt.args.word) + }) + } +} + +func TestTrie_Search(t *testing.T) { + type fields struct { + val rune + isEnd bool + children []Trie + } + type args struct { + word string + } + tests := []struct { + name string + fields fields + args args + want bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + this := &Trie{ + val: tt.fields.val, + isEnd: tt.fields.isEnd, + children: tt.fields.children, + } + if got := this.Search(tt.args.word); got != tt.want { + t.Errorf("Trie.Search() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestTrie_StartsWith(t *testing.T) { + type fields struct { + val rune + isEnd bool + children []Trie + } + type args struct { + prefix string + } + tests := []struct { + name string + fields fields + args args + want bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + this := &Trie{ + val: tt.fields.val, + isEnd: tt.fields.isEnd, + children: tt.fields.children, + } + if got := this.StartsWith(tt.args.prefix); got != tt.want { + t.Errorf("Trie.StartsWith() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/Week 06/id_198/LeetCode_547_198.go b/Week 06/id_198/LeetCode_547_198.go new file mode 100644 index 000000000..669530b9c --- /dev/null +++ b/Week 06/id_198/LeetCode_547_198.go @@ -0,0 +1,50 @@ +package leetcode + +type UF struct { + parent []int + count int +} + +func NewUF(n int) *UF { + + uf := &UF{parent: make([]int, n), count: n} + for i := 0; i < n; i++ { + + uf.parent[i] = i + } + return uf +} + +func (uf *UF) find(i int) int { + if uf.parent[i] != i { + + uf.parent[i] = uf.find(uf.parent[i]) + } + return uf.parent[i] +} + +func (uf *UF) union(i, j int) { + x := uf.find(i) + y := uf.find(j) + if x != y { + uf.parent[x] = y + uf.count = uf.count - 1 + } +} +func findCircleNum(M [][]int) int { + + if len(M) == 0 { + + return 0 + } + l := len(M) + uf := NewUF(l) + for i := 0; i < l; i++ { + for j := 0; j < l; j++ { + if M[i][j] == 1 && i != j { + uf.union(i, j) + } + } + } + return uf.count +} diff --git a/Week 06/id_198/LeetCode_547_198.simple.go b/Week 06/id_198/LeetCode_547_198.simple.go new file mode 100644 index 000000000..04ec5743c --- /dev/null +++ b/Week 06/id_198/LeetCode_547_198.simple.go @@ -0,0 +1,45 @@ +package leetcode + +func find(parent []int, i int) int { + if parent[i] != i { + + parent[i] = find(parent, parent[i]) + } + + return parent[i] +} + +func union(parent []int, i, j int) { + x := find(parent, i) + y := find(parent, j) + if x != y { + parent[y] = x + } +} +func findCircleNum(M [][]int) int { + + if len(M) == 0 { + + return 0 + } + l := len(M) + parent := make([]int, l) + for i := 0; i < l; i++ { + parent[i] = i + } + for i := 0; i < l; i++ { + for j := 0; j < l; j++ { + if M[i][j] == 1 && i != j { + union(parent, i, j) + } + } + } + count := 0 + for i := 0; i < l; i++ { + if parent[i] == i { + + count++ + } + } + return count +} diff --git a/Week 06/id_198/LeetCode_547_198_test.go b/Week 06/id_198/LeetCode_547_198_test.go new file mode 100644 index 000000000..0b41e6853 --- /dev/null +++ b/Week 06/id_198/LeetCode_547_198_test.go @@ -0,0 +1,25 @@ +package leetcode + +import ( + "testing" +) + +func Test_findCircleNum(t *testing.T) { + type args struct { + M [][]int + } + tests := []struct { + name string + args args + want int + }{ + {name: "s", args: args{M: [][]int{{1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, {0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}}, want: 8}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := findCircleNum(tt.args.M); got != tt.want { + t.Errorf("findCircleNum() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/Week 06/id_198/NOTE.md b/Week 06/id_198/NOTE.md index a6321d6e2..05f7f03d9 100644 --- a/Week 06/id_198/NOTE.md +++ b/Week 06/id_198/NOTE.md @@ -1,4 +1,9 @@ # NOTE - +## 字典树(trie)和并查集() +树=>二叉树=>二叉搜索树 + 字典树 + 多叉树 +树 + 二叉树 diff --git a/Week 06/id_203/LeetCode_208_203.go b/Week 06/id_203/LeetCode_208_203.go new file mode 100644 index 000000000..c7d5e9c1b --- /dev/null +++ b/Week 06/id_203/LeetCode_208_203.go @@ -0,0 +1,90 @@ +package week06 + +/** +实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。 + +示例: + +Trie trie = new Trie(); + +trie.insert("apple"); +trie.search("apple"); // 返回 true +trie.search("app"); // 返回 false +trie.startsWith("app"); // 返回 true +trie.insert("app"); +trie.search("app"); // 返回 true +说明: + +你可以假设所有的输入都是由小写字母 a-z 构成的。 +保证所有输入均为非空字符串。 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/implement-trie-prefix-tree +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +type Trie struct { + val byte + sons [26]*Trie + end int +} + +/** Initialize your data structure here. */ +func Constructor() Trie { + var xx Trie + return xx +} + +/** Inserts a word into the trie. */ +func (tr *Trie) Insert(word string) { + root:=tr + for i:=0;i=len(brd) || j >= len(brd[0]) || visited[k] { return } + nn, ok:= node.next[rune(brd[i][j])] + if !ok{ return } + + if len(nn.word) >0{ *res = append(*res, nn.word); nn.word ="" } + + visited[k]=true + dfs(brd, res, nn, i+1, j, visited) + dfs(brd, res, nn, i-1, j, visited) + dfs(brd, res, nn, i, j+1, visited) + dfs(brd, res, nn, i, j-1, visited) + visited[k]=false +} \ No newline at end of file diff --git a/Week 06/id_213/NumbersOfIsland.java b/Week 06/id_213/NumbersOfIsland.java new file mode 100644 index 000000000..58a73d574 --- /dev/null +++ b/Week 06/id_213/NumbersOfIsland.java @@ -0,0 +1,57 @@ +public class Solution { + // DFS + // 方向数组,它表示了相对于当前位置的 4 个方向的横、纵坐标的偏移量,这是一个常见的技巧 + private static final int[][] directions = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}}; + // 标记数组,标记了 grid 的坐标对应的格子是否被访问过 + private boolean[][] marked; + // grid 的行数 + private int rows; + // grid 的列数 + private int cols; + private char[][] grid; + + public int numIslands(char[][] grid) { + rows = grid.length; + if (rows == 0) { + return 0; + } + cols = grid[0].length; + this.grid = grid; + marked = new boolean[rows][cols]; + int count = 0; + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + // 如果是岛屿中的一个点,并且没有被访问过 + // 就进行深度优先遍历 + if (!marked[i][j] && grid[i][j] == '1') { + count++; + dfs(i, j); + } + } + } + return count; + } + + // 从坐标为 (i,j) 的点开始进行深度优先遍历 + private void dfs(int i, int j) { + marked[i][j] = true; + // 得到 4 个方向的坐标 + for (int k = 0; k < 4; k++) { + int newX = i + directions[k][0]; + int newY = j + directions[k][1]; + // 如果不越界、没有被访问过、并且还要是陆地 + if (inArea(newX, newY) && grid[newX][newY] == '1' && !marked[newX][newY]) { + dfs(newX, newY); + } + } + } + + // 封装成 inArea 方法语义更清晰 + private boolean inArea(int x, int y) { + // 等于号不要忘了 + return x >= 0 && x < rows && y >= 0 && y < cols; + } +} +public class Solution { + //BFS +} \ No newline at end of file diff --git a/Week 06/id_213/RewriteLevelOrder.java b/Week 06/id_213/RewriteLevelOrder.java new file mode 100644 index 000000000..ccd247fc9 --- /dev/null +++ b/Week 06/id_213/RewriteLevelOrder.java @@ -0,0 +1,19 @@ +public class Solution { + List> levels = new ArrayList(); + + public List> levelOrder(TreeNode root) { + if (root == null) return levels; + helper(root, 0); + return levels; + } + private void helper(TreeNode root, int level) { + if (levels.size() == level) + levels.add(new ArrayList()); + levels.get(level).add(root.val); + if(root.left != null) + helper(root.left, level + 1); + if(root.right != null) + helper(root.right, level +1); + + } +} diff --git a/Week 06/id_213/Trie.java b/Week 06/id_213/Trie.java new file mode 100644 index 000000000..e4922a7f9 --- /dev/null +++ b/Week 06/id_213/Trie.java @@ -0,0 +1,73 @@ +class Trie { + + class TrieNode { + private int v; + boolean end; + TrieNode[] next = new TrieNode[26]; + + TrieNode(char ch) { + v = 1 << (ch - 'a'); + } + + TrieNode add(char ch) { + int k = ch - 'a'; + if (next[k] != null) { + next[k].v |= 1 << k; + } else { + next[k] = new TrieNode((ch)); + } + return next[k]; + } + + TrieNode get(char ch) { + return next[ch - 'a']; + } + } + + TrieNode root = new TrieNode('a'); + + /** + * Initialize your data structure here. + */ + public Trie() {} + + /** + * Inserts a word into the trie. + */ + public void insert(String word) { + TrieNode p = root; + for (char c : word.toCharArray()) { + p = p.add(c); + } + p.end = true; + } + + /** + * Returns if the word is in the trie. + */ + public boolean search(String word) { + TrieNode p = root; + for (char c : word.toCharArray()) { + p = p.get(c); + if (p == null) { + return false; + } + } + return p.end; + } + + /** + * Returns if there is any word in the trie that starts with the given prefix. + */ + public boolean startsWith(String prefix) { + TrieNode p = root; + for (char c : prefix.toCharArray()) { + p = p.get(c); + if (p == null) { + return false; + } + } + return true; + } +} + diff --git a/Week 06/id_218/LeetCode_208_218.java b/Week 06/id_218/LeetCode_208_218.java new file mode 100644 index 000000000..d76b95c48 --- /dev/null +++ b/Week 06/id_218/LeetCode_208_218.java @@ -0,0 +1,98 @@ +package leetcode.week6; + +/** + * + */ +public class LeetCode_208_218 { + + private TrieNode root; + + /** Initialize your data structure here. */ + public LeetCode_208_218() { + root = new TrieNode(); + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + TrieNode node = root; + if (word == null || "".equals(word)) { + return; + } + for (int i = 0; i < word.length(); i++) { + Character c = word.charAt(i); + if (!node.containsKey(c)) { + node.put(c, new TrieNode()); + } + node = node.get(c); + } + node.setEnd(); + } + + /** Returns if the word is in the trie. */ + public boolean search(String word) { + TrieNode node = this.prefixCheck(word); + return node != null && node.isEnd; + } + + private TrieNode prefixCheck(String word) { + if (word == null || "".equals(word)) { + return null; + } + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + if (node == null) { + return null; + } + Character c = word.charAt(i); + node = node.get(c); + } + return node; + } + + /** Returns if there is any word in the trie that starts with the given prefix. */ + public boolean startsWith(String prefix) { + return this.prefixCheck(prefix) != null; + } + + class TrieNode { + + private TrieNode[] links; + + private final int R = 26; + + private boolean isEnd; + + public TrieNode() { + links = new TrieNode[R]; + } + + public boolean containsKey(char ch) { + return links[ch - 'a'] != null; + } + + public TrieNode get(char ch) { + return links[ch - 'a']; + } + + public void put(char ch, TrieNode node) { + links[ch - 'a'] = node; + } + + public void setEnd() { + isEnd = true; + } + + public boolean isEnd() { + return isEnd; + } + } +} + + +/** + * Your Trie object will be instantiated and called as such: + * Trie obj = new Trie(); + * obj.insert(word); + * boolean param_2 = obj.search(word); + * boolean param_3 = obj.startsWith(prefix); + */ \ No newline at end of file diff --git a/Week 06/id_218/LeetCode_212_218.java b/Week 06/id_218/LeetCode_212_218.java new file mode 100644 index 000000000..2e9e192c7 --- /dev/null +++ b/Week 06/id_218/LeetCode_212_218.java @@ -0,0 +1,120 @@ +package leetcode.week6; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * @author eason.feng at 2019/11/19/0019 10:49 + **/ +public class LeetCode_212_218 { + + LinkedList linkedList; + + public static void main(String[] args) { + char[][] board = {{'o','a','a','n'}, {'e','t','a','e'}, {'i','h','k','r'}, {'i','f','l','v'}}; + String[] words = {"oath","pea","eat","rain"}; + LeetCode_212_218 leetCode_212_218 = new LeetCode_212_218(); + leetCode_212_218.findWords(board, words); + } + + private List res = new ArrayList<>(); + + public List findWords(char[][] board, String[] words) { + Trie trie = new Trie(); + for (int i = 0; i < words.length; i++) { + trie.insert(words[i]); + } + int m = board.length; + int n = board[0].length; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + this.dfs(board, i, j, m, n, "", trie.root); + } + } + return res; + } + + private void dfs(char[][] board, int i, int j, int m, int n, String currentWord, Trie.TrieNode trie) { + if (i < 0 || i >= m || j < 0 || j >= n) { + return; + } + Character temp = board[i][j]; + currentWord += temp; + Trie.TrieNode node = trie.get(temp); + if (node != null && node.isEnd) { + if (!res.contains(currentWord)) { + res.add(currentWord); + } + } + if (node == null) { + return; + } + board[i][j] = '@'; + int[] dx = {-1, 0, 0, 1}; + int[] dy = {0, -1, 1, 0}; + for (int k = 0; k < 4; k++) { + int x = dx[k] + i, y = dy[k] + j; + if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] != '@' && node.get(board[x][y]) != null) { + this.dfs(board, x, y, m, n, currentWord, node); + } + } + board[i][j] = temp; + } + + class Trie { + TrieNode root; + + public Trie() { + root = new TrieNode(); + } + + public void insert(String word) { + TrieNode node = root; + if (word == null || "".equals(word)) { + return; + } + for (int i = 0; i < word.length(); i++) { + Character c = word.charAt(i); + if (!node.containsKey(c)) { + node.put(c, new TrieNode()); + } + node = node.get(c); + } + node.setEnd(); + } + + class TrieNode { + + private TrieNode[] links; + + private final int R = 26; + + private boolean isEnd; + + public TrieNode() { + links = new TrieNode[R]; + } + + public boolean containsKey(char ch) { + return links[ch - 'a'] != null; + } + + public TrieNode get(char ch) { + return links[ch - 'a']; + } + + public void put(char ch, TrieNode node) { + links[ch - 'a'] = node; + } + + public void setEnd() { + isEnd = true; + } + + public boolean isEnd() { + return isEnd; + } + } + } +} diff --git a/Week 06/id_218/LeetCode_22_218.java b/Week 06/id_218/LeetCode_22_218.java new file mode 100644 index 000000000..8a0feaa79 --- /dev/null +++ b/Week 06/id_218/LeetCode_22_218.java @@ -0,0 +1,38 @@ +package leetcode.week6; + +import java.util.ArrayList; +import java.util.List; + +/** + * https://leetcode-cn.com/problems/generate-parentheses/ + * + * @author eason.feng at 2019/11/25/0025 14:11 + **/ +public class LeetCode_22_218 { + + public List generateParenthesis(int n) { + List list = new ArrayList<>(); + this.generate(0, 0, "", list, n); + System.out.println(list); + return list; + } + + private void generate(int left, int right, String s, List list, int n) { + //terminator + if (left == n && right == n) { + list.add(s); + return; + } + //process + //drill down + if (left < n) { + this.generate(left + 1, right, s + "(", list, n); + } + if (right < left) { + this.generate(left, right + 1, s + ")", list, n); + } + //reverse state + } + + +} diff --git a/Week 06/id_218/LeetCode_22_218_v2.java b/Week 06/id_218/LeetCode_22_218_v2.java new file mode 100644 index 000000000..b72921c86 --- /dev/null +++ b/Week 06/id_218/LeetCode_22_218_v2.java @@ -0,0 +1,32 @@ +package leetcode.week6; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author eason.feng at 2019/11/25/0025 14:54 + **/ +public class LeetCode_22_218_v2 { + + public List generateParenthesis(int n) { + List ans = new ArrayList(); + if (n == 0) { + ans.add(""); + } else { + for (int c = 0; c < n; c++) { + for (String left: generateParenthesis(c)) { + for (String right: generateParenthesis(n-1-c)) { + ans.add("(" + left + ")" + right); + } + } + } + } + return ans; + } + + public static void main(String[] args) { + LeetCode_22_218_v2 leetCode_22_218_v2 = new LeetCode_22_218_v2(); + List res = leetCode_22_218_v2.generateParenthesis(3); + System.out.println(res); + } +} diff --git a/Week 06/id_218/NOTE.md b/Week 06/id_218/NOTE.md index a6321d6e2..cb98f1725 100644 --- a/Week 06/id_218/NOTE.md +++ b/Week 06/id_218/NOTE.md @@ -1,4 +1,46 @@ # NOTE +#### 剪枝的实现和特性 - +* 初级搜索 + * 朴素搜索 + * 优化方式:不重复(fibonacci)、剪枝(生成括号问题) + * 搜索方向: + * DFS + * BFS + + +* 课外阅读 + * 邻接表与邻接矩阵:http://www.jianshu.com/p/ce4109962031 + +#### 数独类型题 + +* 引申出A* 启发式搜索。 + + +#### 双向BFS实现、特性 + + +* Corner cases:各种边界条件、各种可能比较细节的问题 +* + + +#### AVL树(自平衡,平衡二叉树) +* Balance Factor(平衡因子):它的左子树的高度减去它的右子树的高度。(有时相反)balancer factor = {-1, 0, 1} +* 通过旋转操作来进行平衡(四种) + +不足:节点需要存储额外信息、且调整次数频繁,进而产生了新的一类树,近似平衡二叉树 + +#### 红黑树(Red-black Tree 近似平衡二叉树) + + +红黑树是一种近似平衡的二叉树(Binary Search Tree),它能够保任何一个节点的左右子树的高度差小于两倍。具体来说,红黑树是满足如下条件的二叉搜索树: + +* 每个结点要么是红色,要么是黑色 +* 根结点是黑色 +* 每个叶结点(NIL 结点,空结点)是黑色 +* 不能有相邻接的两个红色结点 +* 从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点 + + +![0bb8ce149530a7ea2c20c50e60d3aa52.png](en-resource://database/2536:1) diff --git a/Week 06/id_218/UnionFindTemplate.java b/Week 06/id_218/UnionFindTemplate.java new file mode 100644 index 000000000..c7f6c932f --- /dev/null +++ b/Week 06/id_218/UnionFindTemplate.java @@ -0,0 +1,37 @@ +package leetcode.week6; + +/** + * 并查集实现 + * @author eason.feng at 2019/11/20/0020 19:38 + **/ +public class UnionFindTemplate { + + private int count = 0; + + private int[] parent; + + public UnionFindTemplate(int n) { + count = n; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + public int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) { + return; + } + parent[rootP] = rootQ; + count--; + } +} diff --git a/Week 06/id_223/LeetCode_208_223.java b/Week 06/id_223/LeetCode_208_223.java new file mode 100644 index 000000000..08c1cdea7 --- /dev/null +++ b/Week 06/id_223/LeetCode_208_223.java @@ -0,0 +1,79 @@ +class TrieNode{ + private TrieNode[] links; + private final int R = 26; + private boolean isEnd = false; + + public TrieNode() { + links = new TrieNode[R]; + } + public boolean containsKey(char ch) { + return links[ch - 'a'] != null; + } + public TrieNode get(char ch) { + return links[ch - 'a']; + } + public void put(char ch, TrieNode node) { + links[ch - 'a'] = node; + } + public void setEnd() { + this.isEnd = true; + } + public boolean isEnd() { + return isEnd; + } +} + + +class Trie { + TrieNode root; + + /** Initialize your data structure here. */ + public Trie() { + root = new TrieNode(); + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char currentChar = word.charAt(i); + if (!node.containsKey(currentChar)) { + node.put(currentChar, new TrieNode()); + } + node = node.get(currentChar); + } + node.setEnd(); + } + + + private TrieNode searchPrefix(String prefix) { + TrieNode node = root; + for (int i = 0; i < prefix.length(); i++) { + char currentChar = prefix.charAt(i); + if (node.containsKey(currentChar)) { + node = node.get(currentChar); + }else return null; + } + return node; + } + + /** Returns if the word is in the trie. */ + public boolean search(String word) { + TrieNode node = searchPrefix(word); + return node != null && node.isEnd(); + } + + /** Returns if there is any word in the trie that starts with the given prefix. */ + public boolean startsWith(String prefix) { + TrieNode node = searchPrefix(prefix); + return node != null; + } +} + +/** + * Your Trie object will be instantiated and called as such: + * Trie obj = new Trie(); + * obj.insert(word); + * boolean param_2 = obj.search(word); + * boolean param_3 = obj.startsWith(prefix); + */ \ No newline at end of file diff --git a/Week 06/id_223/LeetCode_547_223.java b/Week 06/id_223/LeetCode_547_223.java new file mode 100644 index 000000000..00c6eeb6c --- /dev/null +++ b/Week 06/id_223/LeetCode_547_223.java @@ -0,0 +1,42 @@ +class Solution { + class UnionFind{ + private int count = 0; + private int[] parent; + public UnionFind(int n) { + count = n; + parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + public int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) return; + parent[rootQ] = rootP; + count--; + } + public int count(){ + return count; + } + } + + + public int findCircleNum(int[][] M) { + int n = M.length; + UnionFind uf = new UnionFind(n); + for (int i = 0; i < n - 1; i++) { + for (int j = i + 1; j < n; j++) { + if (M[i][j] == 1) uf.union(i,j); + } + } + return uf.count(); + } +} \ No newline at end of file diff --git a/Week 06/id_243/LeetCode_102_243.java b/Week 06/id_243/LeetCode_102_243.java new file mode 100644 index 000000000..5bc0ff5f4 --- /dev/null +++ b/Week 06/id_243/LeetCode_102_243.java @@ -0,0 +1,72 @@ +import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; + +/** + * @author eazonshaw + * @date 2019/11/22 10:13 + */ +public class LeetCode_102_243 { + + //BFS Queue + public List> levelOrder(TreeNode root) { + //结果集 + List> rs = new ArrayList<>(); + //边界情况处理 + if(root == null){ + return rs; + } + //维护一个队列 + Deque queue = new LinkedList<>(); + //入队 + queue.add(root); + //层数 + int level = 0; + while (!queue.isEmpty()){ + //添加当前层集合 + rs.add(level,new ArrayList<>()); + //遍历当前层 + int len = queue.size(); + for(int i = 0;i < len;i++){ + //出队 + TreeNode currNode = queue.removeFirst(); + //执行逻辑 + rs.get(level).add(currNode.val); + //子节点入队 + if(currNode.left!=null){ + queue.add(currNode.left); + } + if(currNode.right!=null){ + queue.add(currNode.right); + } + } + level++; + } + return rs; + } + + //递归 + public List> levelOrder1(TreeNode root) { + List> rs = new ArrayList<>(); + fc(root,0, rs); + return rs; + } + //递归函数 + private void fc(TreeNode node,int level,List> rs){ + //终止条件 + if(node == null){ + return; + } + //执行当前层逻辑 + if(rs.size() == level){ + rs.add(level,new ArrayList<>()); + } + rs.get(level).add(node.val); + //下坠 + level++; + fc(node.left,level,rs); + fc(node.right,level,rs); + } + +} diff --git a/Week 06/id_243/LeetCode_208_243.java b/Week 06/id_243/LeetCode_208_243.java new file mode 100644 index 000000000..5b49195c5 --- /dev/null +++ b/Week 06/id_243/LeetCode_208_243.java @@ -0,0 +1,115 @@ +/** + * @author eazonshaw + * @date 2019/11/22 11:28 + * + * 题目:实现Trie树 + */ +public class LeetCode_208_243 { + + class Trie { + + //root + private TrieNode root; + + /** Initialize your data structure here. */ + public Trie() { + //初始化一个空节点 + root = new TrieNode(); + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + char[] wordArray = word.toCharArray(); + int len = wordArray.length; + TrieNode currNode = root; + for(int i = 0;i 其实就是BFS的加强版,为了提高效率,选取左右根节点同时进行BFS,直到相遇为止。 +```java +//普通BFS模板(维护队列) +public void bfs(Node root){ + List visited = new ArrayList<>(); + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()){ + Node currNode = queue.poll(); + if(currNode!=null){ + visited.add(currNode); + queue.addAll(currNode.children); + } + } +} +//双向BFS(维护两个队列) +public void multiBfs(Node leftRoot,Node rightRoot){ + List visited = new ArrayList<>(); + Queue queue1 = new LinkedList<>(); + Queue queue2 = new LinkedList<>(); + queue1.offer(leftRoot); + queue2.offer(rightRoot); + while (!queue1.isEmpty() && !queue2.isEmpty()){ + signVisited(queue1,visited); + signVisited(queue2,visited); + } +} +private void signVisited(Queue queue,List visited){ + Node node = queue.poll(); + if(node!=null){ + if(!visited.contains(node)){ + visited.add(node); + } + for(Node n:node.children){ + queue.offer(n); + } + } +} +``` diff --git a/Week 06/id_253/LeetCode_208_253.java b/Week 06/id_253/LeetCode_208_253.java new file mode 100644 index 000000000..7587e157b --- /dev/null +++ b/Week 06/id_253/LeetCode_208_253.java @@ -0,0 +1,60 @@ +class TrieNode{ + private TrieNode[] linkes; + private final int R = 26; + private boolean isEnd; + public TrieNode(){ + linkes = new TrieNode[R]; + } + public boolean containsKey(char ch){ + return linkes[ch-'a'] != null; + } + public TrieNode get(char ch){ + return linkes[ch-'a']; + } + public void put(char ch , TrieNode node){ + linkes[ch-'a'] = node; + } + public void setEnd(){ + isEnd = true; + } + public boolean isEnd(){ + return isEnd; + } +} +class Trie { + private TrieNode root; + public Trie() { + root = new TrieNode(); + } + public void insert(String word) { + TrieNode node = root; + for(int i =0 ; i findWords(char[][] board, String[] words) { + int m = board.length; + int n = board[0].length; + Trie myTrie = new Trie(); + TrieNode root = myTrie.root; + boolean[][] visited = new boolean[m][n]; + Set ans = new HashSet<>(); + for(int i =0 ; i(ans); + } + public void find(char[][] board , String[] words , int i , int j , int m , int n,boolean[][] visited , Set ans, TrieNode cur){ + if(i < 0 || i >=m || j < 0 || j >= n || visited[i][j]) return ; + cur=cur.child[board[i][j] - 'a']; + visited[i][j] = true; + if(cur == null ) { + visited[i][j] = false; + return; + } + if(cur.isEnd == true) ans.add(cur.val); + find(board,words,i+1,j,m,n,visited,ans,cur); + find(board,words,i,j+1,m,n,visited,ans,cur); + find(board,words,i-1,j,m,n,visited,ans,cur); + find(board,words,i,j-1,m,n,visited,ans,cur); + visited[i][j] = false; + } +} +class Trie{ + public TrieNode root = new TrieNode(); + public void insert(String s){ + TrieNode node = root; + for(int i =0 ; i< s.length() ; i++){ + char c = s.charAt(i); + if(node.child[c - 'a'] == null){ + node.child[c - 'a'] = new TrieNode(); + node = node.child[c - 'a']; + }else{ + node = node.child[c - 'a']; + } + } + node.isEnd = true; + node.val = s; + } +} +class TrieNode{ + public TrieNode[] child = new TrieNode[26]; + public String val; + public boolean isEnd = false; +} diff --git a/Week 06/id_253/LeetCode_36_253.java b/Week 06/id_253/LeetCode_36_253.java new file mode 100644 index 000000000..b52f8de69 --- /dev/null +++ b/Week 06/id_253/LeetCode_36_253.java @@ -0,0 +1,25 @@ +class Solution { + public boolean isValidSudoku(char[][] board) { + HashMap[] row = new HashMap[9]; + HashMap[] col = new HashMap[9]; + HashMap[] boxes = new HashMap[9]; + for(int i =0 ; i < 9 ; i++){ + row[i] = new HashMap<>(); + col[i] = new HashMap<>(); + boxes[i] = new HashMap<>(); + } + for(int i = 0 ;i < 9 ;i++){ + for(int j =0 ; j< 9 ;j++){ + int boxnum = (i/3)*3 + j / 3; + int n = (int)board[i][j] ; + if(board[i][j] != '.'){ + row[i].put(n,row[i].getOrDefault(n,0)+1); + col[j].put(n,col[j].getOrDefault(n,0)+1); + boxes[boxnum].put(n,boxes[boxnum].getOrDefault(n,0)+1); + if(row[i].get(n) > 1 || col[j].get(n) > 1 || boxes[boxnum].get(n) > 1) return false; + } + } + } + return true; + } +} diff --git a/Week 06/id_253/LeetCode_37_253.java b/Week 06/id_253/LeetCode_37_253.java new file mode 100644 index 000000000..676a108aa --- /dev/null +++ b/Week 06/id_253/LeetCode_37_253.java @@ -0,0 +1,60 @@ +class Solution { + int n =3 ; + int N = n* n ; + int[][] row = new int[N][N+1]; + int[][] col = new int[N][N+1]; + int[][] boxes = new int[N][N+1]; + char[][] board; + boolean sudokuSolved = false; + public boolean couldPlace(int d ,int i , int j ){ + int idx = (i/3)*3 + j/3; + return row[i][d]+col[j][d]+boxes[idx][d] == 0 ; + } + public void placeNumber(int d , int i ,int j){ + int idx = (i/3)*3 + j/3; + row[i][d]++; + col[j][d]++; + boxes[idx][d]++; + board[i][j] = (char)(d+'0'); + } + public void removeNumber(int d , int i ,int j){ + int idx = (i/3)*3 + j /3; + row[i][d]--; + col[j][d]--; + boxes[idx][d]--; + board[i][j] = '.'; + } + public void placeNextNumbers(int i , int j){ + if((i == N-1) && (j == N-1)) sudokuSolved = true; + else{ + if(j == N - 1) backtrack(i+1,0); + else{ + backtrack(i , j+1); + } + } + } + public void backtrack(int x , int y){ + if(board[x][y] == '.'){ + for(int d = 1 ; d <= 9 ; d++){ + if(couldPlace(d,x,y)){ + placeNumber(d,x,y); + placeNextNumbers(x,y); + if(!sudokuSolved) removeNumber(d,x,y); + } + } + } + else placeNextNumbers(x,y); + } + public void solveSudoku(char[][] board) { + this.board = board; + for(int i = 0 ; i < 9 ; i++){ + for(int j = 0 ; j < 9 ; j ++){ + if(board[i][j] != '.'){ + int d = Character.getNumericValue(board[i][j]); + placeNumber(d,i,j); + } + } + } + backtrack(0,0); + } +} diff --git a/Week 06/id_253/LeetCode_547_253.java b/Week 06/id_253/LeetCode_547_253.java new file mode 100644 index 000000000..7801a19f4 --- /dev/null +++ b/Week 06/id_253/LeetCode_547_253.java @@ -0,0 +1,27 @@ +class Solution { + public int find(int[] parent ,int i){ + if(parent[i] == -1) return i; + return find(parent , parent[i]); + } + public void union (int i , int j , int[] parent){ + int xfather = find(parent , i); + int yfather = find(parent , j); + if(xfather != yfather) { + parent[xfather] = yfather; + } + } + public int findCircleNum(int[][] M) { + int[] parent = new int[M.length]; + Arrays.fill(parent , -1); + for(int i =0 ; i= board.length || j < 0 || j >= board[0].length) return; + + if (node[board[i][j]] == null) return; + + const c = board[i][j]; + board[i][j] = '#'; + search(node[c], i + 1, j); + search(node[c], i - 1, j); + search(node[c], i, j + 1); + search(node[c], i, j - 1); + board[i][j] = c; + } + + const root = buildTrie(); + + for (let i = 0; i < board.length; i++) { + for (let j = 0; j < board[0].length; j++) { + search(root, i, j); + } + } + return res; +}; +// @lc code=end \ No newline at end of file diff --git a/Week 06/id_258/LeetCode_547_258.js b/Week 06/id_258/LeetCode_547_258.js new file mode 100644 index 000000000..6bda45cee --- /dev/null +++ b/Week 06/id_258/LeetCode_547_258.js @@ -0,0 +1,52 @@ +/* + * @lc app=leetcode.cn id=547 lang=javascript + * + * [547] 朋友圈 + */ + +// @lc code=start +/** + * @param {number[][]} M + * @return {number} + */ +var findCircleNum = function (M) { + if (M.length == 0 || M == null) return 0; + + let p = []; + let N = M.length; + + function union(x, y) { + p[findParent(x)] = findParent(y) + } + + function findParent(node) { + if (p[node] == node) return node; + + while (p[node] != node) { + node = p[node]; + } + + return p[node]; + } + + for (let i = 0; i < N; i++) { + p[i] = i; + } + + for (let i = 0; i < N; i++) { + for (let j = 0; j < N; j++) { + if (M[i][j] == 1) { + union(i, j); + } + } + } + + let s = new Set(); + + for (let i = 0; i < N; i++) { + s.add(findParent(p[i])) + } + + return s.size; +}; +// @lc code=end \ No newline at end of file diff --git a/Week 06/id_273/LeetCode_1091_273.java b/Week 06/id_273/LeetCode_1091_273.java new file mode 100644 index 000000000..6c2e045f2 --- /dev/null +++ b/Week 06/id_273/LeetCode_1091_273.java @@ -0,0 +1,32 @@ +//1091. 二维矩阵中的最短路径 + + +//解法1:BFS +int dir[][] = new int[][]{{0,1},{0,-1},{1,0},{-1,0},{1,-1},{-1,1},{-1,-1},{1,1}}; +public int shortestPathBinaryMatrix(int[][] grid) { + int m = grid.length, n = grid[0].length; + if (grid[0][0] == 1 || grid[m - 1][n - 1] == 1) return -1; + Queue queue = new LinkedList<>(); + queue.offer(new int[]{0, 0}); + boolean[][] visited = new boolean[m][n]; + visited[0][0] = true; + int res = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + while (size -- > 0) { + int[] pop = queue.poll(); + if (pop[0] == m - 1 && pop[1] == n - 1) return res + 1; + for (int i = 0; i < 8; i++) { + int nextX = dir[i][0] + pop[0]; + int nextY = dir[i][1] + pop[1]; + if (nextX >= 0 && nextX < m && nextY >= 0 && nextY < n && !visited[nextX][nextY] && grid[nextX][nextY] != 1) { + queue.offer(new int[]{nextX, nextY}); + visited[nextX][nextY] = true; + } + } + } + res++; + } + return -1; +} + diff --git a/Week 06/id_273/LeetCode_127_273.java b/Week 06/id_273/LeetCode_127_273.java new file mode 100644 index 000000000..b9a9b9620 --- /dev/null +++ b/Week 06/id_273/LeetCode_127_273.java @@ -0,0 +1,83 @@ +//127. 单词接龙 + +//解法1:BFS 执行用时:90ms +//思路:和433. 基因序列的思路一样, 区别仅在于转换单词时需要遍历26个字符 +// 实例: hit(begin) +// ↓ result + 1 +// hot +// ↓ result + 1 +// dot lot +// ↓ result + 1 +// log +// ↓ result + 1 +// cog(end) +//时间复杂度:O(wordList.length * 26 * eachWordLen) +//空间复杂度:O(wordList.len * 3) +public int ladderLength(String beginWord, String endWord, List wordList) { + if (beginWord.equals(endWord)) return 1; + Set visited = new HashSet<>(), wordSet = new HashSet<>(wordList); + visited.add(beginWord); + Queue queue = new LinkedList<>(); + queue.offer(beginWord); + int result = 1; + while (!queue.isEmpty()) { + int size = queue.size(); + while (size-- > 0) { + String str = queue.poll(); + if (str.equals(endWord)) return result; + char[] temp = str.toCharArray(); + for (int i = 0; i < temp.length; i++) { + char old = temp[i]; + for (char j = 'a'; j <= 'z'; j++) { + temp[i] = j; + String next = String.valueOf(temp); + if (visited.add(next) && wordSet.contains(next)) queue.offer(next); + } + temp[i] = old; + } + } + result++; + } + return 0; +} + +//解法2:双向BFS 执行用时:18ms +//思路:start和end两端同时进行BFS遍历, 在中间相遇时return result +// 至于为什么双向BFS的效率要比单端BFS效率更高, 是因为BFS遍历层数越深, 探索的成本就成倍增加 +// 因此, 从两个单端浅遍历的BFS遍历要比一个单端深遍历的BFS成本小很多 +//时间复杂度:O(wordList.length * eachWordLen * 26) +//空间复杂度:O(wordList.len * 3) +public int ladderLength(String beginWord, String endWord, List wordList) { + if (beginWord.equals(endWord)) return 1; + Set visited = new HashSet<>(), start = new HashSet<>(), end = new HashSet<>(), wordListSet = new HashSet<>(wordList); + if (!wordListSet.contains(endWord)) return 0; + start.add(beginWord); end.add(endWord); + visited.add(beginWord); visited.add(endWord); + int len = 1; + while (!start.isEmpty() && !end.isEmpty()) { + if (start.size() > end.size()) { + Set set = start; + start = end; + end = set; + } + Set temp = new HashSet<>(); + for (String s : start) { + char[] charArr = s.toCharArray(); + for (int i = 0; i < charArr.length; i++) { + char old = charArr[i]; + for (char c = 'a'; c <= 'z'; c++) { + charArr[i] = c; + String next = String.valueOf(charArr); + if (end.contains(next)) return len + 1; + if (wordListSet.contains(next) && visited.add(next)) { + temp.add(next); + } + } + charArr[i] = old; + } + } + start = temp; + len++; + } + return 0; +} \ No newline at end of file diff --git a/Week 06/id_273/LeetCode_200_273.java b/Week 06/id_273/LeetCode_200_273.java new file mode 100644 index 000000000..1304a534f --- /dev/null +++ b/Week 06/id_273/LeetCode_200_273.java @@ -0,0 +1,93 @@ +//200. 岛屿数量 + +//解法1:深度优先 执行用时:2ms +//思路:遍历二维数组, 每遍历到一个为‘1’的坐标, 深度优先递归将它自身与周围相邻的‘1’全部转换为‘0’, 这样第二次遍历时就可以排除掉第一次记录的岛屿 +// 相当于每递归一次就是将一个岛屿炸沉, 递归的次数就是岛屿的数量 +int[][] moves = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; +int count = 0; +public int numIslands(char[][] grid) { + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == '1') { + dfs(grid, i, j); + count++; + } + } + } + return count; +} + +private void dfs(char[][] grid, int i, int j) { + if (grid[i][j] == '#' || c != '1') return; + grid[i][j] = '#'; + for (int[] move : moves) { + int x = move[0] + i; + int y = move[1] + j; + if (x < 0 || y < 0 || x >= grid.length || y >= grid[0].length) continue; + dfs(grid, x, y); + } +} + + +//解法2:并查集 执行用时:5ms +//思路:我们把二维矩阵中的每个岛屿都想象成一组联通图, 只需要计算联通图的个数就可以得知二维坐标中有几个岛屿 +// 为此, 可以通过并查集进行实现: +// 1. 将二维矩阵中所有方块作为独立集合存入并查集 +// 2. 依次判断每个方块是否是陆地, 若是陆地, 那么就将该方块与4联通的周围4个方块进行并查集的合并操作 +// 3. 最后计算并查集中不相交的联通图数即可 +public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0 || grid[0].length == 0) return 0; + int[][] dis = new int[][]{{1, 0}, {0, -1}}; + UnionFind uf = new UnionFind(grid); + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == '1') { + for (int[] d : dis) { + int x = i + d[0]; + int y = j + d[1]; + if (x >= 0 && x < grid.length && y >= 0 && y < grid[0].length && grid[x][y] == '1') { + int id1 = i * grid[0].length + j; + int id2 = x * grid[0].length + y; + uf.union(id1, id2); + } + } + } + } + } + return uf.count; +} + +class UnionFind { + int[] parent; + int count; + + public UnionFind(char[][] grid) { + this.parent = new int[grid.length * grid[0].length]; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == '1') { + int id = i * grid[0].length + j; + parent[id] = id; + count++; + } + } + } + } + + public void union(int node1, int node2) { + int x = find(node1); + int y = find(node2); + if (x != y) {//merge x and y + parent[x] = y; + count--; + } + } + + private int find(int node) { + while (node != parent[node]) { + parent[node] = parent[parent[node]]; + node = parent[node]; + } + return node; + } +} \ No newline at end of file diff --git a/Week 06/id_273/LeetCode_208_273.java b/Week 06/id_273/LeetCode_208_273.java new file mode 100644 index 000000000..1005e974b --- /dev/null +++ b/Week 06/id_273/LeetCode_208_273.java @@ -0,0 +1,48 @@ +//208. 实现Trie字典树 + +//解法1:多叉树法 +class Trie { + class TrieNode { + public TrieNode[] next = new TrieNode[26]; + public boolean isEnd; + public TrieNode() { + } + } + + private TrieNode root; + + /** Initialize your data structure here. */ + public Trie() { + this.root = new TrieNode(); + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + TrieNode node = this.root; + for (char c : word.toCharArray()) { + if (node.next[c - 'a'] == null) node.next[c - 'a'] = new TrieNode(); + node = node.next[c - 'a']; + } + node.isEnd = true; + } + + /** Returns if the word is in the trie. */ + public boolean search(String word) { + TrieNode node = root; + for (char c : word.toCharArray()) { + if (node.next[c - 'a'] == null) return false; + node = node.next[c - 'a']; + } + return node.isEnd; + } + + /** Returns if there is any word in the trie that starts with the given prefix. */ + public boolean startsWith(String prefix) { + TrieNode node = root; + for (char c : prefix.toCharArray()) { + if (node.next[c - 'a'] == null) return false; + node = node.next[c - 'a']; + } + return true; + } +} \ No newline at end of file diff --git a/Week 06/id_273/LeetCode_212_273.java b/Week 06/id_273/LeetCode_212_273.java new file mode 100644 index 000000000..2642af7ff --- /dev/null +++ b/Week 06/id_273/LeetCode_212_273.java @@ -0,0 +1,56 @@ +//212. 单词搜索II + +//解法1:字典树+DFS回溯 执行用时:10ms +//思路:1. 创建一个字典树, 并将words存入字典树作为字符前缀 +// 2. 遍历二维数组中的每一个字符, 判断当前字符对应字典树中的节点数组是否已经组成了想要的单词 +// 若已经组成, 添加result字符串 +// 若还未组成, 对其上下左右位置的字符深度优先递归 +//时间复杂度:row = m, col = n, words.length = w, word的平均长度 = l +// O(k*l)构建trie + O(m*n)遍历 + O(l)dfs的平均深度 +//总结:通过字典树的数据结构, 就可以直接排除掉字符前缀不匹配的选项, 枝剪掉了很多不必要的递归 +class Solution { + public List findWords(char[][] board, String[] words) { + List res = new ArrayList<>(); + TrieNode root = buildTrie(words); + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + dfs(board, root, res, i, j); + } + } + return res; + } + + private TrieNode buildTrie(String[] words) { + TrieNode root = new TrieNode(); + for (String s : words) { + TrieNode node = root; + for (char c : s.toCharArray()) { + if (node.next[c - 'a'] == null) node.next[c - 'a'] = new TrieNode(); + node = node.next[c - 'a']; + } + node.word = s; + } + return root; + } + + private void dfs(char[][] board, TrieNode node, List res, int i, int j) { + char c = board[i][j]; + if (c == '#' || node.next[c - 'a'] == null) return; + node = node.next[c - 'a']; + if (node.word != null) { + res.add(node.word); + node.word = null; + } + board[i][j] = '#'; + if (i > 0) dfs(board, node, res, i - 1, j); + if (i < board.length - 1) dfs(board, node, res, i + 1, j); + if (j > 0) dfs(board, node, res, i, j - 1); + if (j < board[0].length - 1) dfs(board, node, res, i, j + 1); + board[i][j] = c; + } +} + +class TrieNode { + TrieNode next[] = new TrieNode[26]; + String word; +} \ No newline at end of file diff --git a/Week 06/id_273/LeetCode_36_273.java b/Week 06/id_273/LeetCode_36_273.java new file mode 100644 index 000000000..3ccffce34 --- /dev/null +++ b/Week 06/id_273/LeetCode_36_273.java @@ -0,0 +1,37 @@ +//36. 有效数独 + +//解法1:遍历 执行用时:2ms +//思路:遍历二维矩阵, 对每一个节点进行判断: +// 1. 同一列下是否存在相同字符 +// 2. 同一行下是否存在相同字符 +// 3. 所在3x3矩阵中是否存在相同字符 +//时间复杂度:O(n^2 * 9) +//空间复杂度:O(1) +public boolean isValidSudoku(char[][] board) { + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board.length; j++) { + if (board[i][j] != '.' && !check(board, i, j)) return false; + } + } + return true; +} + +private boolean check(char[][] board, int i, int j) { + char c = board[i][j]; + //col + for (int m = i + 1; m < board.length; m++) { + if (board[m][j] == c) return false; + } + //row + for (int n = j + 1; n < board.length; n++) { + if (board[i][n] == c) return false; + } + int temp1 = (i / 3) * 3; + int temp2 = (j / 3) * 3; + for (int m = temp1; m < temp1 + 3; m++) { + for (int n = temp2; n < temp2 + 3; n++) { + if (m != i && n != j && board[m][n] == c) return false; + } + } + return true; +} \ No newline at end of file diff --git a/Week 06/id_273/LeetCode_37_273.java b/Week 06/id_273/LeetCode_37_273.java new file mode 100644 index 000000000..9d838d95b --- /dev/null +++ b/Week 06/id_273/LeetCode_37_273.java @@ -0,0 +1,93 @@ +//37. 有效数独 + +//解法1:回溯 + 剪枝 +public void solveSudoku(char[][] board) { + // 三个布尔数组 表明 行, 列, 还有 3*3 的方格的数字是否被使用过 + boolean[][] rowUsed = new boolean[9][10]; + boolean[][] colUsed = new boolean[9][10]; + boolean[][][] boxUsed = new boolean[3][3][10]; + // 初始化 + for(int row = 0; row < board.length; row++){ + for(int col = 0; col < board[0].length; col++) { + int num = board[row][col] - '0'; + if(1 <= num && num <= 9){ + rowUsed[row][num] = true; + colUsed[col][num] = true; + boxUsed[row/3][col/3][num] = true; + } + } + } + // 递归尝试填充数组 + recur(rowUsed, colUsed, boxUsed, board, 0, 0); +} + +private boolean recur(boolean[][] rowsUsed, boolean[][] colsUsed, boolean[][][] boardUsed, char[][] board, int row, int col) { + if (col == board.length) { + col = 0; + row++; + if (row == board.length) { + return true; + } + } + if (board[row][col] == '.') { + for (int num = 1; num <= 9; num++) { + boolean canUsed = !(rowsUsed[row][num] || colsUsed[col][num] || boardUsed[row/3][col/3][num]); + if(canUsed){ + rowsUsed[row][num] = true; + colsUsed[col][num] = true; + boardUsed[row/3][col/3][num] = true; + board[row][col] = (char)(num + '0'); + + if (recur(rowsUsed, colsUsed, boardUsed, board, row, col + 1)) { + return true; + } + board[row][col] = '.'; + + rowsUsed[row][num] = false; + colsUsed[col][num] = false; + boardUsed[row/3][col/3][num] = false; + } + } + } else { + return recur(rowsUsed, colsUsed, boardUsed, board, row, col + 1); + } + return false; +} + +//解法2:回溯 + 剪枝的不同实现 执行用时:18ms +//思路:遍历二维矩阵, 若遍历到一个'.', 可以进行填坑 +// 从字符'1'开始, 向二维矩阵中填入字符, 若填入的字符不符合规则, 回溯之后继续试错字符'2' +public void solveSudoku(char[][] board) { + if (board.length != 0 || board != null) { + solve(board); + } +} + +private boolean solve(char[][] board) { + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board.length; j++) { + if (board[i][j] == '.') { + for (char c = '1'; c <= '9'; c++) { + //判断二维矩阵中, index(i, j) 所在的行/列/3x3矩阵中是否存在字符c + if (isValid(board, i, j, c)) {//如果不存在, 则进行相应的添加 + board[i][j] = c; + if (solve(board)) return true;//递归执行填坑操作, 若到最后能够全部填完return true + board[i][j] = '.';//若填坑失败, 回溯之后填入新的数 + } + } + return false; + } + } + } + return true; +} + +private boolean isValid(char[][] board, int row, int col, char c) { + for(int i = 0; i < board.length; i++) { + if(board[i][col] != '.' && board[i][col] == c) return false; //check row + if(board[row][i] != '.' && board[row][i] == c) return false; //check column + if(board[3 * (row / 3) + i / 3][ 3 * (col / 3) + i % 3] != '.' && + board[3 * (row / 3) + i / 3][3 * (col / 3) + i % 3] == c) return false; //check 3*3 block + } + return true; +} \ No newline at end of file diff --git a/Week 06/id_273/LeetCode_433_273.java b/Week 06/id_273/LeetCode_433_273.java new file mode 100644 index 000000000..51fd8d901 --- /dev/null +++ b/Week 06/id_273/LeetCode_433_273.java @@ -0,0 +1,79 @@ +//433. 最小基因变化 + +//解法1:广度优先遍历 执行用时:1ms +//思路:根据基因库遍历转换start序列的每一个基因字符, 观察每一次基因变化的结果是否存在于基因库bank中: +// 若存在于基因库中, 但并不是目标基因end, 那么就以这个基因为start继续进行基因转换(注意不要重复转换之前转换过的基因--Set) +// 若存在于基因库中, 且是目标基因end, return转换的次数即可 +// 题目的核心就是图的广度优先遍历 +// 例如: AACCGGTT(start) +// ↓ result+1 +// AACCGCTA +// ↓ result+1 +// AACCGCTA AAACGCTA(target) + +public int minMutation(String start, String end, String[] bank) { + Set visited = new HashSet<>(), bankSet = new HashSet<>(Arrays.asList(bank)); + visited.add(start); + if (!bankSet.contains(end)) return -1; + Queue queue = new LinkedList<>(); + queue.offer(start); + char[] genertic = new char[]{'A', 'C', 'G', 'T'}; + int result = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + while (size-- > 0) { + String str = queue.poll(); + if (str.equals(end)) return result; + char[] temp = str.toCharArray(); + for (int i = 0; i < temp.length; i++) { + char old = temp[i]; + for (char c : genertic) { + temp[i] = c; + String next = String.valueOf(temp); + if (visited.add(next) && bankSet.contains(next)) queue.offer(next); + } + temp[i] = old; + } + } + result++; + } + return -1; +} + +//解法2:双向BFS 执行用时:1ms +//思路:同127. 解法2 +public int minMutation(String start, String end, String[] bank) { + if (start.equals(end)) return 0; + List bankList = new ArrayList<>(Arrays.asList(bank)); + Set visited = new HashSet<>(), startSet = new HashSet<>(), + endSet = new HashSet<>(), bankSet = new HashSet<>(bankList); + if (!bankSet.contains(end)) return -1; + startSet.add(start); + endSet.add(end); + char[] genertc = new char[]{'A', 'C', 'G', 'T'}; + int len = 1; + while (!startSet.isEmpty() && !endSet.isEmpty()) { + if(startSet.size() > endSet.size()) { + Set set = startSet; + startSet = endSet; + endSet = set; + } + Set temp = new HashSet<>(); + for (String s : startSet) { + char[] charArr = s.toCharArray(); + for (int i = 0; i < charArr.length; i++) { + char c = charArr[i]; + for (int j = 0; j < genertc.length; j++) { + charArr[i] = genertc[j]; + String next = String.valueOf(charArr); + if (endSet.contains(next)) return len; + if (bankSet.contains(next) && visited.add(next)) temp.add(next); + } + charArr[i] = c; + } + } + startSet = temp; + len++; + } + return -1; +} \ No newline at end of file diff --git a/Week 06/id_273/LeetCode_51_273.java b/Week 06/id_273/LeetCode_51_273.java new file mode 100644 index 000000000..456b634be --- /dev/null +++ b/Week 06/id_273/LeetCode_51_273.java @@ -0,0 +1,98 @@ +//51. N皇后 + + +//解法1:回溯+Set 执行用时:9ms +//思路:由于皇后的攻击范围是所在的行/列/对角线 ,因此在某一行放置了一个皇后, 该行不可能再放置第二个皇后 +// 因此我们只需要对行数作记录, 判断每一列的位置是否available即可 +// 通过3个Set分别存放当前皇后所占据的列,左/右对角线, 下一个皇后在放置之前通过Set判断当前位置是否可以放置 +Set cols = new HashSet<>(), diag1 = new HashSet<>(), diag2 = new HashSet<>(); +public List> solveNQueens(int n) { + List> res = new ArrayList<>(); + recur(n, 0, new ArrayList<>(), res); + return res; +} + +private void recur(int n, int row, List temp, List> res) { + if (row == n) { + res.add(new ArrayList<>(temp)); + return; + } + for (int i = 0; i < n; i++) { + if (cols.contains(i) || diag1.contains(row - i) || diag2.contains(row + i)) continue; + char[] charArr = new char[n]; + Arrays.fill(charArr, '.'); + charArr[i] = 'Q'; + temp.add(String.valueOf(charArr)); + cols.add(i); + diag1.add(row - i); + diag2.add(row + i); + recur(n, row + 1, temp, res); + temp.remove(temp.size() - 1); + cols.remove(i); + diag1.remove(row - i); + diag2.remove(row + i); + } +} + +//解法2:回溯 + boolean[] 执行用时:3ms +boolean[] cols, diag1, diag2; +public List> solveNQueens(int n) { + cols = new boolean[n]; + diag1 = new boolean[n * 2]; + diag2 = new boolean[n * 2]; + List> res = new ArrayList<>(); + recur(n, 0, new ArrayList<>(), res); + return res; +} + +private void recur(int n, int row, List temp, List> res) { + if (row == n) { + res.add(new ArrayList<>(temp)); + return; + } + for (int i = 0; i < n; i++) { + if (cols[i] || diag1[row + i] || diag2[row - i + n - 1]) continue; + char[] charArr = new char[n]; + Arrays.fill(charArr, '.'); + charArr[i] = 'Q'; + temp.add(String.valueOf(charArr)); + cols[i] = true; + diag1[row + i] = true; + diag2[row - i + n - 1] = true; + recur(n, row + 1, temp, res); + temp.remove(temp.size() - 1); + cols[i] = false; + diag1[row + i] = false; + diag2[row - i + n - 1] = false; + } +} + +//解法3:Bitmask 执行用时:2ms +public List> solveNQueens(int n) { + List> res = new ArrayList<>(); + int size = (1 << n) - 1; + recur(n, size, 0, 0, 0, new ArrayList<>(), res); + return res; +} + +private void recur(int n, int size, int cols, int diag1, int diag2, List temp, List> res) { + if (cols == size) { + res.add(new ArrayList<>(temp)); + return; + } + int pos = size & (~ (cols | diag1 | diag2)); + while (pos != 0) { + int p = pos & (-pos); + pos -= p; + char[] charArr = new char[n]; + Arrays.fill(charArr, '.'); + charArr[n - 1 - getPositionOfQueen(p)] = 'Q'; + temp.add(String.valueOf(charArr)); + recur(n, size, p | cols, (p | diag1) << 1, (p | diag2) >> 1, temp, res); + temp.remove(temp.size() - 1); + } +} + +private int getPositionOfQueen(int n) { + return (int)(Math.log(n)/Math.log(2)); +} \ No newline at end of file diff --git a/Week 06/id_273/LeetCode_547_273.java b/Week 06/id_273/LeetCode_547_273.java new file mode 100644 index 000000000..2ba4e4a95 --- /dev/null +++ b/Week 06/id_273/LeetCode_547_273.java @@ -0,0 +1,67 @@ +//547. 朋友圈 + +//解法1:图的深度优先遍历 执行用时:2ms +//思路:把问题转换成图的深度优先遍历, +// 假设:学生0和学生1互为朋友, 那么可以想象节点0和节点1联通 +// 学生2自己在一个朋友圈, 那么节点2不与任何节点联通 +// 深度优先遍历一次图, 那么就可以发现一个朋友圈 +// 我们只需要深度遍历所有的节点, 就能获取所有的朋友圈数(注意判断之前访问过的节点) +//时间复杂度:O(N^2) +//空间复杂度:O(N) +public int findCircleNum(int[][] M) { + boolean[] visited = new boolean[M.length]; + int count = 0; + for (int i = 0; i < M.length; i++) { + if (visited[i]) continue; + dfs(visited, i, M); + count++; + } + return count; +} + +private void dfs(boolean[] visited, int i, int[][] M) { + for (int j = 0; j < visited.length; j++) { + if (M[i][j] == 1 && !visited[j]) { + visited[j] = true; + dfs(visited, j, M); + } + } +} + +//解法2:并查集 执行用时:7ms +//思路:首先明确问题本质是寻找二维坐标下联通图的个数, 那么问题可以通过并查集解决 +// 创建并查集数组, 初始化该数组, 每个节点都指向自己 +// 遍历二维坐标, 如果发现两两节点存在联通的情况, 就在并查集数组中合并两个节点 +// 最后并查集长度 - 并查集合并次数 = result +//时间复杂度:O(M.length^2 * 联通图的最大长度) +public int findCircleNum(int[][] M) { + int[] parent = new int[M.length]; + int count = parent.length; + for (int i = 0; i < M.length; i++) { + parent[i] = i; + } + for (int i = 0; i < M.length; i++) { + for (int j = 0; j < M.length; j++) { + if (i != j && M[i][j] == 1) union(parent, i, j); + } + } + for (int i = 0; i < parent.length; i++) { + if (parent[i] != i) count--; + } + return count; +} + +private void union(int[] parent, int i, int j) { + int x = find(i, parent); + int y = find(j, parent); + if (x == y) return; + parent[x] = y; +} + +private int find(int index, int[] parent) { + while (parent[index] != index) { + index = parent[parent[index]]; + // index = parent[index]; 空间压缩 + } + return index; +} \ No newline at end of file diff --git a/Week 06/id_273/LeetCode_773_273.java b/Week 06/id_273/LeetCode_773_273.java new file mode 100644 index 000000000..3062aee15 --- /dev/null +++ b/Week 06/id_273/LeetCode_773_273.java @@ -0,0 +1,162 @@ +//773. 滑动谜题 + +//解法1:BFS 执行用时:14ms +int[][] des = new int[][]{{1,3,-1}, {0,2,4}, {1,5,-1}, {0,4,-1}, {1,3,5}, {2,4,-1}}; +public int slidingPuzzle(int[][] board) { + String boardStr = boardToStr(board); + String targetStr = "123450"; + if (boardStr.equals(targetStr)) return 0; + Set visited = new HashSet<>(); + visited.add(boardStr); + Queue queue = new LinkedList<>(); + queue.offer(boardStr); + int res = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + while (size-- > 0) { + String str = queue.poll(); + if (str.equals(targetStr)) return res; + char[] charArr = str.toCharArray(); + for (int h = 0; h < charArr.length; h++) { + if (charArr[h] != '0') continue; + for (int j = 0; j < des[0].length; j++) { + if (des[h][j] != -1) { + //change + char[] charArrTemp = charArr.clone(); + char temp1 = charArr[h]; + charArr[h] = charArr[des[h][j]]; + charArr[des[h][j]] = temp1; + if (!visited.contains(String.valueOf(charArr))) { + queue.offer(String.valueOf(charArr)); + } + visited.add(String.valueOf(charArr)); + charArr = charArrTemp; + } + } + } + } + res++; + } + return -1; +} + + +//解法2:A * +//思路:基于普通的广度优先遍历解法, 将普通队列改为使用优先队列, 每次队列poll时都先通过compareTo选择一个"曼哈顿距离最短", +// 也就是距离最终结果需要交换次数最小的一个分支进行计算。像这样每次队列都择优poll, BFS算法就能够更快速的到达终点。 +//关于曼哈顿距离:二维坐标中, 一个点(x1, y1)到另一个点(x2, y2)的曼哈顿距离 = |x1 - x2| + |y1 - y2| +//由于优先队列默认队列头元素是最小元素,故我们可以拿来作为小根堆使用, 故这个A* 算法的主要思想就是每次都poll一个交换次数 + 曼哈顿距离最短的节点, 从而达到枝减的效果 +public int slidingPuzzle(int[][] board) { + //find index of zero in board + int[] index = findIndexOfZero(board); + if (index == null || index.length == 0) return -1; + //create start State + State start = new State(board, 0, index[0], index[1]); + //PQ + PriorityQueue pq = new PriorityQueue<>(); + pq.add(start); + //Set + Set visited = new HashSet<>(); + visited.add(start); + //moves + int[][] moves = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; + + while (!pq.isEmpty()) { + State currState = pq.poll();//poll State by priority + if (currState.isGoal()) return currState.taken; + for (int[] move : moves) { + int x = currState.zeroSetX + move[0]; + int y = currState.zeroSetY + move[1]; + if (x < 0 || y < 0 || x >= board.length || y >= board[0].length) continue; + State newState = currState.swap(x, y); + if (newState != null && visited.add(newState)) pq.offer(newState); + } + } + return -1; +} + +class State implements Comparable{ + int[][] stateBoard; + int taken; + int zeroSetX; + int zeroSetY; + + public State(int[][] stateBoard, int taken, int zeroSetX, int zeroSetY) { + this.stateBoard = new int[2][3]; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 3; j++) { + this.stateBoard[i][j] = stateBoard[i][j]; + } + } + this.taken = taken; + this.zeroSetX = zeroSetX; + this.zeroSetY = zeroSetY; + } + + /** + * swap board where x = zeroX, y = zeroY + * + * @param x + * @param y + * @return State + */ + public State swap(int x, int y) { + State newState = new State(this.stateBoard, this.taken + 1, x, y); + int temp = newState.stateBoard[x][y]; + newState.stateBoard[x][y] = newState.stateBoard[zeroSetX][zeroSetY]; + newState.stateBoard[zeroSetX][zeroSetY] = temp; + return newState; + } + + /** + * priority distance + * @return + */ + public int distance() { + int result = 0; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 3; j++) { + if (stateBoard[i][j] == 0) continue; + //二维坐标曼哈顿距离计算:res = |x - i| + |y - j| + int val = stateBoard[i][j] - 1; + int x = val/3; + int y = val%3; + result += Math.abs(x - i) + Math.abs(y - j); + } + } + return result; + } + + public boolean isGoal() { + return distance() == 0; + } + + /** + * compare all states in PQ while poll or remove + * @param that + * @return + */ + @Override + public int compareTo(State that) { + return this.distance() + this.taken - that.distance() - that.taken; + } + + @Override + public boolean equals(Object obj) { + return Arrays.deepEquals(((State) obj).stateBoard, this.stateBoard); + } + + @Override + public int hashCode() { + return Arrays.deepHashCode(stateBoard); + } +} + +private int[] findIndexOfZero(int[][] board) { + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (board[i][j] == 0) return new int[]{i, j}; + } + } + return null; +} \ No newline at end of file diff --git a/Week 06/id_273/NOTE.md b/Week 06/id_273/NOTE.md index a6321d6e2..6c8c2e3f1 100644 --- a/Week 06/id_273/NOTE.md +++ b/Week 06/id_273/NOTE.md @@ -1,4 +1,56 @@ # NOTE - +### 第十三课 字典树和并查集 +#### Tire树的基本实现和特性 +使用场景: +1. 搜索引擎中, 只输入前缀词就可以根据热度从上至下显示出候选词, 采用了字典树 + 优先队列的数据结构 + +基本性质: +1. 节点数组本身不存完整单词, 如果不区分大小写的话, 节点数组大小为26 +2. 从根节点到某一个节点, 路径上经过的字符连接起来, 为该结点对应的字符串 + +核心思想: +1. 空间换时间 +2. 因为Trie树本身的数据结构性质, 它一般用于解决通过前缀词搜索候选词的问题 + +#### 并查集的基本实现和特性 +使用场景: +1. 一般用于在联通图中, 判断是否联通且合并相互联通的两个图结构 + +实现框架: +1. makeSet(s):初始化一个并查集, 一般通过数组实现, 其中包含s个单元素集合 +2. unionSet(x, y):将元素x, y所在的集合合并, 要求两集合不相交 +3. find(x):找到元素x所在集合的头节点, 该操作也可判断两个元素是否位于同一个集合中, 只需要判断两个元素的头节点是否相同 + +### 第十四课 高级搜索 + +#### 剪枝的实现和特性 +1. 一般对于朴素搜索的优化方式可以通过:"剪枝次优解" 和 "剪枝重复解" 的方式来进行优化 +2. 我们可以大体将搜索的方式分为DFS和BFS +3. 比较形象的理解DFS剪枝的实现可以参考斐波那契问题, 用最简单的暴力递归那么递归的状态树是指数级别的, 其中很多棵状态树都涉及到了重复的计算, 那么为了避免这些不必要的计算, 我们可以将那些重复的状态树进行剪枝, 以达到性能优化的目的 +3. 那么对于BFS的枝减一般可以通过双向BFS和启发式搜索来实现 + +#### 双向BFS的实现和特性 +1. 对于单向的BFS最坏情况下, 要扩散到最深的那一层才能够获取到结果, 而基于BFS的特性, 每扩散一层性能消耗都是成倍增加的, 可以理解为BFS扩散得越深, 开支越大 +2. 那么为了提高BFS的性能, 可以基于上述BFS的特性进行优化, 也就是采用双端BFS。这样两端一起扩散的成本要小于一端扩散的成本 + +#### 启发式搜索的实现和特性 +1. 普通的BFS必须要遍历所有的状态树才能够获取到最后的结果, 那么除了双向BFS能够对其进行优化, 还可以通过启发式搜索 +2. 启发式搜索采用优先队列, 入队的每个状态节点包含了优先级, 也就是距离result节点的距离长短, 那么每当出队操作的时候, 都优先poll距离result节点最短的节点, 就可以剪枝掉其他次优的状态树 + +### 第十五课 红黑树和AVL +#### AVL +1. 由于二叉搜索树在某些极端情况下可能退化到链表结构导致查询时间复杂度退化到O(N), 因此引入的AVL +2. AVL限制了所有节点的左右子树深度不超过2, 因此它的查询时间复杂度始终为O(log2N) +3. 这个限制左右的深度实现主要是通过了"旋转", 旋转主要分为4种情况, 具体就不赘述了, 也就是AVL每次进行插入和删除操作时, 若发现某个节点不平衡, 就会进行一次旋转操作 +4. 因此, 平衡二叉树更加适用于增删操作较少, 查询较多的场景 + + +### 红黑树 +1. 红黑树是一种近似平衡二叉树, 它在AVL的基础之上妥协了一些规定, 以至于红黑树相较于AVL, 插入和删除操作的开支要更加小 +2. 红黑树限制了所有节点的左右子树深度不超过2倍 +3. 红黑树的每个节点要么是红色, 要么是黑色 +4. 根节点与叶子节点为黑色 +5. 不能有邻接的两个红色节点 +6. 从任意节点至其叶子节点的所有路径都包含相同数目的黑色节点 \ No newline at end of file diff --git a/Week 06/id_278/Leetcode_208_278.py b/Week 06/id_278/Leetcode_208_278.py new file mode 100644 index 000000000..233b59662 --- /dev/null +++ b/Week 06/id_278/Leetcode_208_278.py @@ -0,0 +1,58 @@ +class TrieNode: + + def __init__(self): + self.children = {} + self.is_word = False + + +class Trie: + + def __init__(self): + self.root = TrieNode() + + """ + @param: word: a word + @return: nothing + """ + def insert(self, word): + node = self.root + for c in word: + if c not in node.children: + node.children[c] = TrieNode() + node = node.children[c] + + node.is_word = True + + """ + return the node in the trie if exists + """ + def find(self, word): + node = self.root + for c in word: + node = node.children.get(c) + if node is None: + return None + return node + + """ + @param: word: A string + @return: if the word is in the trie. + """ + def search(self, word): + node = self.find(word) + return node is not None and node.is_word + + """ + @param: prefix: A string + @return: if there is any word in the trie that starts with the given prefix. + """ + def startsWith(self, prefix): + return self.find(prefix) is not None + + + +# Your Trie object will be instantiated and called as such: +# obj = Trie() +# obj.insert(word) +# param_2 = obj.search(word) +# param_3 = obj.startsWith(prefix) diff --git a/Week 06/id_278/Leetcode_51_278.py b/Week 06/id_278/Leetcode_51_278.py new file mode 100644 index 000000000..8f47a6475 --- /dev/null +++ b/Week 06/id_278/Leetcode_51_278.py @@ -0,0 +1,51 @@ +class Solution: + """ + @param: n: The number of queens + @return: All distinct solutions + """ + def solveNQueens(self, n): + boards = [] + visited = { + 'col': set(), + 'sum': set(), + 'diff': set(), + } + self.dfs(n, [], visited, boards) + return boards + + def dfs(self, n, permutation, visited, boards): + if n == len(permutation): + boards.append(self.draw(permutation)) + return + + row = len(permutation) + for col in range(n): + if not self.is_valid(permutation, visited, col): + continue + permutation.append(col) + visited['col'].add(col) + visited['sum'].add(row + col) + visited['diff'].add(row - col) + self.dfs(n, permutation, visited, boards) + visited['col'].remove(col) + visited['sum'].remove(row + col) + visited['diff'].remove(row - col) + permutation.pop() + + def is_valid(self, permutation, visited, col): + row = len(permutation) + if col in visited['col']: + return False + if row + col in visited['sum']: + return False + if row - col in visited['diff']: + return False + return True + + def draw(self, permutation): + board = [] + n = len(permutation) + for col in permutation: + row_string = ''.join(['Q' if c == col else '.' for c in range(n)]) + board.append(row_string) + return board diff --git a/Week 06/id_283/Leetcode_127_283.java b/Week 06/id_283/Leetcode_127_283.java new file mode 100644 index 000000000..b8aa9cb70 --- /dev/null +++ b/Week 06/id_283/Leetcode_127_283.java @@ -0,0 +1,42 @@ +/* + * @lc app=leetcode id=127 lang=java + * + * [127] Word Ladder + */ +class Solution { + public int ladderLength(String beginWord, String endWord, List wordList) { + if (!wordList.contains(endWord)) return 0; + int level = 1; + Set set = new HashSet<>(); + for(String word: wordList){ + set.add(word); + } + Queue queue = new LinkedList<>(); + queue.offer(beginWord); + while(!queue.isEmpty()){ + int size = queue.size(); + for(int i = 0; i < size; i++){ + String tmpWord = queue.poll(); + char[] array = tmpWord.toCharArray(); + for(int j = 0; j < array.length; j++){ + char c = array[j]; + for(char k = 'a'; k <= 'z'; k++){ + array[j] = k; + String newWord = String.valueOf(array); + if(k != c &&set.contains(newWord)){ + queue.offer(newWord); + set.remove(newWord); + if(newWord.equals(endWord)){ + return ++level; + } + } + } + array[j] = c; + } + } + level++; + } + return 0; + } +} + diff --git a/Week 06/id_283/Leetcode_200_283.java b/Week 06/id_283/Leetcode_200_283.java new file mode 100644 index 000000000..f863c1c41 --- /dev/null +++ b/Week 06/id_283/Leetcode_200_283.java @@ -0,0 +1,31 @@ +/* + * @lc app=leetcode id=200 lang=java + * + * [200] Number of Islands + */ +class Solution { + public int numIslands(char[][] grid) { + int island = 0; + if (grid.length == 0 || grid[0].length == 0) return 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == '1') { + island++; + dfs(grid, i, j); + } + } + } + return island; + } + + public static void dfs (char[][] grid, int i, int j) { + if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == '0') + return; + grid[i][j] = '0'; + dfs(grid, i - 1, j); + dfs(grid, i, j - 1); + dfs(grid, i, j + 1); + dfs(grid, i + 1, j); + } +} + diff --git a/Week 06/id_298/friend-circles.py b/Week 06/id_298/friend-circles.py new file mode 100644 index 000000000..d76431bdf --- /dev/null +++ b/Week 06/id_298/friend-circles.py @@ -0,0 +1,31 @@ +class Solution: + def findCircleNum(self, M: List[List[int]]) -> int: + if not M: return 0 + n = len(M) + + # 初始化集合每个元素自己单独一个集合 + p = [i for i in range(n)] + # 遍历整个矩阵,如果M[i][j] == 1 合并i, j + for i in range(n): + for j in range(n): + if M[i][j] == 1: + self._union(p, i, j) + # 遍历parent的个数 + return len(set([self._find(p, i) for i in range(n)])) + + def _union(self, p, i, j): + p1 = self._find(p, i) + p2 = self._find(p, j) + p[p2] = p1 + + def _find(self, p, i): + root = i + while p[root] != root: + root = p[root] + + while p[i] != i: + x = i + i = p[i] + p[x] = root + + return root diff --git a/Week 06/id_298/number-of-islands.py b/Week 06/id_298/number-of-islands.py new file mode 100644 index 000000000..343b74f16 --- /dev/null +++ b/Week 06/id_298/number-of-islands.py @@ -0,0 +1,38 @@ +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + self.f = {} + if not grid: + return 0 + + rows = len(grid) + cols = len(grid[0]) + + for i in range(rows): + for j in range(cols): + # 如果当前元素为1,则看左上是否为1, 如果为1则union + if grid[i][j] == '1': + # (i - 1, y):前一个,左 (x, y - 1): 上一个,上 + for x, y in [(-1, 0), (0, -1)]: + tmp_i = i + x + tmp_j = j + y + # 左,上为1则union + if 0 <= tmp_i < rows and 0 <= tmp_j < cols and grid[tmp_i][tmp_j] == '1': + union(tmp_i * cols + tmp_j, i * cols + j) + + result = set() + for i in range(rows): + for j in range(cols): + # 遍历这个网格,如果当前元素为1则,则查找当前元素,并将结果添加到result + if grid[i][j] == '1': + result.add(self.find(i * cols + j)) + + return len(result) + + def union(self, x, y): + self.f[self.find(x)] = self.find(y) + + def find(self, x): + self.f.setdefault(x, x) + if self.f[x] != x: + self.f[x] = self.find(self.f[x]) + return self.f[x] diff --git a/Week 06/id_303/NumbersOfIslands.swift b/Week 06/id_303/NumbersOfIslands.swift new file mode 100644 index 000000000..5096ae7ca --- /dev/null +++ b/Week 06/id_303/NumbersOfIslands.swift @@ -0,0 +1,31 @@ +// +// NumbersOfIslands.swift +// AlgorithmStudy + +import Foundation + +func numIslands(_ grid: [[Character]]) -> Int { + var map = grid + let row = map.count + if row == 0 {return 0} + let col = map[0].count + var count = 0 + for i in 0..= 0 && i < row && j >= 0 && j < col && map[i][j] == "1") { + map[i][j] = "2" + dfs(&map, i, j-1, row, col); + dfs(&map, i-1, j, row, col); + dfs(&map, i, j+1, row, col); + dfs(&map, i+1, j, row, col); + } +} diff --git a/Week 06/id_303/ValidSudoku.swift b/Week 06/id_303/ValidSudoku.swift new file mode 100644 index 000000000..233272ac1 --- /dev/null +++ b/Week 06/id_303/ValidSudoku.swift @@ -0,0 +1,47 @@ +// +// ValidSudoku.swift +// AlgorithmStudy + +import Foundation + +func isValidSudoku(_ board: [[Character]]) -> Bool { + var rowDict: [Int:[Character:Int]] = [:] + var columnDict: [Int:[Character:Int]] = [:] + var boxDict: [Int:[Character:Int]] = [:] + for i in (0..<9) { + for j in (0..<9) { + let c = board[i][j] + if c == "." { + continue + } + if rowDict[i] == nil { + rowDict[i] = [:] + } + if rowDict[i]![c] != nil { + return false + }else{ + rowDict[i]![c] = 1 + } + + if columnDict[j] == nil { + columnDict[j] = [:] + } + if columnDict[j]![c] != nil { + return false + }else{ + columnDict[j]![c] = 1 + } + + let bIndex = i / 3 * 3 + j / 3 + if boxDict[bIndex] == nil { + boxDict[bIndex] = [:] + } + if boxDict[bIndex]![c] != nil { + return false + }else { + boxDict[bIndex]![c] = 1 + } + } + } + return true +} diff --git a/Week 06/id_308/LeedCode_208.js b/Week 06/id_308/LeedCode_208.js new file mode 100644 index 000000000..9e87a931c --- /dev/null +++ b/Week 06/id_308/LeedCode_208.js @@ -0,0 +1,78 @@ +/** + * 题目: 实现 Trie (前缀树) + * 语言: JavaScript + * 执行结果: 打败了87.30%的用户 + * */ + + + +/** + * Initialize your data structure here. + */ +var Trie = function() { + this.isEnd = false; + this.nextWords = {}; +}; + +/** + * Inserts a word into the trie. + * @param {string} word + * @return {void} + */ +Trie.prototype.insert = function(word) { + let curr = this; + const wordArr = word.split(''); + if(wordArr.length === 0) return; + + for(let i=0;i= 0 && row < arr.length && col >=0 && col < arr[0].length && arr[row][col] != ''); +} + +class Tride { + constructor(){ + this.end_word = false; + this.nexts = new Map(); + } + + insert(words){ + let curr = this; + + for(let word of words) { + if(!curr.nexts.has(word)) { + curr.nexts.set(word,new Tride()); + } + + curr = curr.nexts.get(word); + } + + curr.end_word = true; + } +} diff --git a/Week 06/id_308/NOTE.md b/Week 06/id_308/NOTE.md index a6321d6e2..17a28a958 100644 --- a/Week 06/id_308/NOTE.md +++ b/Week 06/id_308/NOTE.md @@ -1,4 +1,40 @@ -# NOTE +## 字典树和并查集 - +#### 字典树 +- 字典树,即Trie树,又称单词查找树或键树,是一种树形结构。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。 +- 它的优点是:最大限度的减少无谓的字符串比较,查询效率比哈希表高 +- 基本性质 + * 结点本身不存完整单词 + * 从根结点到某一结点,路径上经过的字符串连接起来,为该结点对应的字符串 + * 每个结点的所有子结点路径代表的字符都不相同 +- 核心思想 + * Trie树的核心思想是空间换时间 + * 利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。 + +#### 并查集 + +- 组团、配对问题 +- 基本操作 + * makeSet(s): 建立一个新的并查集,其中包含S个单元素集合 + * unionSet(x,y): 把元素x和元素y所在的并查集合并,要求x和y所在的集合不相交,如果相交则不合并 + * find(x): 找到元素x所在的集合的代表,该操作也可以用于判断两个元素是否位于同一集合,只要将它们各自的代表比较一下就可以了 + + +## 高级搜索 + +#### 剪枝的实现和特性 + +- 初级搜索 + * 朴素搜索 + * 优化方式:不重复(fibonacci)/剪枝(生成括号问题) + * 搜索方向 + - DFS: 深度优先搜索 + - BFS: 广度优先搜索 + - 双向搜索/启发式搜索 + + +#### 双向BFS的实现/特性和题解 + + +#### 高级树/AVL/红黑树 diff --git a/Week 06/id_313/LeetCode_127_313.go b/Week 06/id_313/LeetCode_127_313.go new file mode 100644 index 000000000..1dc160154 --- /dev/null +++ b/Week 06/id_313/LeetCode_127_313.go @@ -0,0 +1,50 @@ +package id_313 + +func ladderLength(beginWord string, endWord string, wordList []string) int { + beginSet := make(map[string]bool) + endSet := make(map[string]bool) + wordListSet := make(map[string]bool) + visited := make(map[string]bool) + length := 1 + + for _, v := range wordList { + wordListSet[v] = true + } + + if !wordListSet[endWord] { + return 0 + } + beginSet[beginWord] = true + endSet[endWord] = true + + for len(beginSet) != 0 && len(endSet) != 0 { + + if len(beginSet) > len(endSet) { + beginSet, endSet = endSet, beginSet + } + + temp := make(map[string]bool, 0) + for word := range beginSet { + for i := 0; i < len(word); i++ { + curr := []byte(word) + old := word + for j := 'a'; j <= 'z'; j++ { + curr[i] = byte(j) + word = string(curr) + if endSet[word] { + return length + 1 + } + if !visited[word] && wordListSet[word] { + temp[word] = true + visited[word] = true + } + } + word = old + } + } + + beginSet = temp + length++ + } + return 0 +} diff --git a/Week 06/id_313/LeetCode_208_313.go b/Week 06/id_313/LeetCode_208_313.go new file mode 100644 index 000000000..6fd145a53 --- /dev/null +++ b/Week 06/id_313/LeetCode_208_313.go @@ -0,0 +1,57 @@ +package id_313 + +type Trie struct { + val byte + childs [26]*Trie + end int +} + +/** Initialize your data structure here. */ +func Constructor() Trie { + return Trie{} +} + +/** Inserts a word into the trie. */ +func (this *Trie) Insert(word string) { + node := this + size := len(word) + for i := 0; i < size; i++ { + idx := word[i] - 'a' + if node.childs[idx] == nil { + node.childs[idx] = &Trie{val: word[i]} + } + node = node.childs[idx] + } + node.end++ +} + +/** Returns if the word is in the trie. */ +func (this *Trie) Search(word string) bool { + node := this + size := len(word) + for i := 0; i < size; i++ { + idx := word[i] - 'a' + if node.childs[idx] == nil { + return false + } + node = node.childs[idx] + } + if node.end > 0 { + return true + } + return false +} + +/** Returns if there is any word in the trie that starts with the given prefix. */ +func (this *Trie) StartsWith(prefix string) bool { + node := this + size := len(prefix) + for i := 0; i < size; i++ { + idx := prefix[i] - 'a' + if node.childs[idx] == nil { + return false + } + node = node.childs[idx] + } + return true +} diff --git a/Week 06/id_313/LeetCode_212_313.go b/Week 06/id_313/LeetCode_212_313.go new file mode 100644 index 000000000..86b3ee4a9 --- /dev/null +++ b/Week 06/id_313/LeetCode_212_313.go @@ -0,0 +1,73 @@ +package id_313 + +func findWords(board [][]byte, words []string) []string { + result := make([]string, 0) + + m := len(board) + if m == 0 { + return result + } + + n := len(board[0]) + if n == 0 { + return result + } + + trie := buildTrie(words) + for i := 0; i < m; i++ { + for j := 0; j < n; j++ { + dfs(board, i, j, trie, &result) + } + } + return result +} + +type Trie struct { + childs [26]*Trie + word string +} + +func buildTrie(words []string) *Trie { + root := new(Trie) + for _, word := range words { + cur := root + for _, c := range word { + idx := c - 'a' + if cur.childs[idx] == nil { + cur.childs[idx] = new(Trie) + } + cur = cur.childs[idx] + } + cur.word = word + } + return root +} + +func dfs(board [][]byte, i, j int, trie *Trie, result *[]string) { + c := board[i][j] + if c == '#' || trie.childs[c-'a'] == nil { + return + } + trie = trie.childs[c-'a'] + if len(trie.word) > 0 { + *result = append(*result, trie.word) + trie.word = "" + } + + board[i][j] = '#' + if i > 0 { + dfs(board, i-1, j, trie, result) + } + if i < len(board)-1 { + dfs(board, i+1, j, trie, result) + } + if j > 0 { + dfs(board, i, j-1, trie, result) + } + + if j < len(board[0])-1 { + dfs(board, i, j+1, trie, result) + } + + board[i][j] = c +} diff --git a/Week 06/id_318/LeetCode_212_318.py b/Week 06/id_318/LeetCode_212_318.py new file mode 100644 index 000000000..afb8bab93 --- /dev/null +++ b/Week 06/id_318/LeetCode_212_318.py @@ -0,0 +1,46 @@ +# +# @lc app=leetcode id=212 lang=python3 +# +# [212] Word Search II +# + +# @lc code=start +dx = [-1, 1, 0, 0] +dy = [0, 0, -1, 1] +END_OF_WORD = "#" + +class Solution(object): + def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: + if not board or not board[0]: return [] + if not words: return [] + self.result = set() + + # build a trie + root = collections.defaultdict() + for word in words: + node = root + for char in word: + node = node.setdefault(char, collections.defaultdict()) + node[END_OF_WORD] = END_OF_WORD + + self.m, self.n = len(board), len(board[0]) + for i in range(self.m): + for j in range(self.n): + if board[i][j] in root: + self._dfs(board, i, j, "", root) + return list(self.result) + + def _dfs(self, board, i, j, cur_word, cur_dict): + cur_word += board[i][j] + cur_dict = cur_dict[board[i][j]] + if END_OF_WORD in cur_dict: + self.result.add(cur_word) + tmp, board[i][j] = board[i][j], '@' + for k in range(4): + x, y = i + dx[k], j + dy[k] + if 0 <= x < self.m and 0 <= y < self.n \ + and board[x][y] != '@' and board[x][y] in cur_dict: + self._dfs(board, x, y, cur_word, cur_dict) + board[i][j] = tmp +# @lc code=end + diff --git a/Week 06/id_318/LeetCode_547_318.java b/Week 06/id_318/LeetCode_547_318.java new file mode 100644 index 000000000..632b41474 --- /dev/null +++ b/Week 06/id_318/LeetCode_547_318.java @@ -0,0 +1,65 @@ +/* + * @lc app=leetcode id=547 lang=java + * + * [547] Friend Circles + */ + +// @lc code=start +class Solution { + // implement a disjoint set + class UnionFind { + private int count = 0; + private int[] parent, rank; + + public UnionFind(int n) { + count = n; + parent = new int[n]; + rank = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + public int find(int p) { + while (p != parent[p]) { + // path compression by halving + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) return; + if (rank[rootQ] > rank[rootP]) { + parent[rootP] = rootQ; + } + else { + parent[rootQ] = rootP; + if (rank[rootP] == rank[rootQ]) { + rank[rootP]++; + } + } + count--; + } + + public int count() { + return count; + } + } + + public int findCircleNum(int[][] M) { + int n = M.length; + UnionFind uf = new UnionFind(n); + for (int i = 0; i < n - 1; i++) { + for (int j = i + 1; j < n; j++) { + if (M[i][j] == 1) uf.union(i, j); + } + } + return uf.count(); + } +} +// @lc code=end + diff --git a/Week 06/id_328/ImplementTriePrefixTree.java b/Week 06/id_328/ImplementTriePrefixTree.java new file mode 100644 index 000000000..15fb70722 --- /dev/null +++ b/Week 06/id_328/ImplementTriePrefixTree.java @@ -0,0 +1,67 @@ +class Trie { + private TrieNode root; + public Trie() { + root = new TrieNode(); + } + public void insert(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char currentChar = word.charAt(i); + if (!node.containsKey(currentChar)) { + node.put(currentChar, new TrieNode()); + } + node = node.get(currentChar); + } + node.setEnd(); + } + + public boolean search(String word) { + TrieNode node = searchPrefix(word); + if(node != null && node.isEnd()) { + return true; + }else { + return false; + } + } + + public boolean startsWith(String prefix) { + return searchPrefix(prefix) != null; + } + + private TrieNode searchPrefix(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char curLetter = word.charAt(i); + if (node.containsKey(curLetter)) { + node = node.get(curLetter); + } else { + return null; + } + } + return node; + } + } + +class TrieNode { + private TrieNode[] links; + private final int R = 26; + private boolean isEnd; + public TrieNode() { + links = new TrieNode[R]; + } + public boolean containsKey(char ch) { + return links[ch -'a'] != null; + } + public TrieNode get(char ch) { + return links[ch -'a']; + } + public void put(char ch, TrieNode node) { + links[ch -'a'] = node; + } + public void setEnd() { + isEnd = true; + } + public boolean isEnd() { + return isEnd; + } + } \ No newline at end of file diff --git a/Week 06/id_328/WordLadder.java b/Week 06/id_328/WordLadder.java new file mode 100644 index 000000000..78e0df521 --- /dev/null +++ b/Week 06/id_328/WordLadder.java @@ -0,0 +1,48 @@ +class WordLadder { + public int ladderLength(String beginWord, String endWord, List wordList) { + if(wordList == null ||beginWord == null || endWord == null) return 0; + if(!wordList.contains(endWord)) return 0; + if(beginWord.equals(endWord)) return 2; + + Set meets = new HashSet<>(wordList); + Set beginSet = new HashSet<>(); + beginSet.add(beginWord); + Set endSet = new HashSet<>(); + endSet.add(endWord); + return ladderLength(1,beginSet,endSet,meets); + } + + private int ladderLength(int level,Set beginSet,Set endSet,Set meets){ + if (beginSet.size() == 0 || endSet.size() == 0) + return 0; + + meets.removeAll(beginSet); + level++; + Set nextLevelSet = new HashSet<>(); + for (String beginWord : beginSet) { + char[] chars = beginWord.toCharArray(); + for (int i = 0; i < chars.length; i++) { + char temp = chars[i]; + for (char ch = 'a'; ch < 'z'; ch++) { + chars[i] = ch; + String newWord = String.valueOf(chars); + if (!meets.contains(newWord)) + continue; + if (endSet.contains(newWord)) + return level; + nextLevelSet.add(newWord); + } + chars[i] = temp; + } + } + + if (nextLevelSet.size() <= endSet.size()) { + beginSet = nextLevelSet; + } else { + beginSet = endSet; + endSet = nextLevelSet; + } + + return this.ladderLength(level, beginSet, endSet, meets); + } +} \ No newline at end of file diff --git a/Week 06/id_338/LeetCode_547_338.java b/Week 06/id_338/LeetCode_547_338.java new file mode 100644 index 000000000..5655207d5 --- /dev/null +++ b/Week 06/id_338/LeetCode_547_338.java @@ -0,0 +1,66 @@ +/** + * @author Leesen + * @date 2019/11/24 22:38 + */ +public class LeetCode_547_338 { + //并查集 + class UnionFind { + private int count = 0; + private int[] parent, rank; + + public UnionFind(int n) { + count = n; //****容易遗漏 + parent = new int[n]; + rank = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + public int find(int p) { + while (p != parent[p]) { + // path compression by halving + // 巧妙, 跳过中间一个节点, + // 可以用1,2,3相连的画图试试,当p!=parent[1]时,直接跳到判断3=parent[3] + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) return; + //使用rank可以压缩路径长度, 如果不rank,此处可简单处理为 parent[rootP] = rootQ + if (rank[rootQ] > rank[rootP]) { //****rank[rootQ] + parent[rootP] = rootQ; + } else { + parent[rootQ] = rootP; + if (rank[rootP] == rank[rootQ]) { + rank[rootP]++; + } + } + + count--; + } + + public int count() { + return count; + } + } + + public int findCircleNum(int[][] M) { + int n = M.length; + //step1. 并查集初始化 + UnionFind uf = new UnionFind(n); + //step2. 并查集合并 + for (int i = 0; i < n - 1; i++) { //***n-1 + for (int j = i + 1; j < n; j++) { + if (M[i][j] == 1) uf.union(i, j); + } + } + //step3. 并查集计数 + return uf.count(); + } +} diff --git a/Week 06/id_338/LeetCode_947_338.java b/Week 06/id_338/LeetCode_947_338.java new file mode 100644 index 000000000..df322b011 --- /dev/null +++ b/Week 06/id_338/LeetCode_947_338.java @@ -0,0 +1,46 @@ +/** + * @author Leesen + * @date 2019/11/24 22:39 + */ +public class LeetCode_947_338 { + // If stone a and stone b are in the same column/row, we connect them as a component + // One component can be removed to one stone left at least. + // The largest possible number of moves we can make = Total stones count - count of stones left = Total stones count - count of components. + public int removeStones(int[][] stones) { + int count = 0; + int n = stones.length; + //初始化并查集 + int[] parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + + //合并操作 + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { //****此处细节条件不同 + // check if either row or column is same + // If yes then connect them + // 其它都类似,关键点在这里 + if (stones[i][0] == stones[j][0] || //横坐标行合并 + stones[i][1] == stones[j][1]) { //纵坐标列合并 + int u = find(parent, i); + int v = find(parent, j); + if (u == v) continue; + parent[u] = v; + } + } + } + + // 求出总数。these are the ones that did not connect means clusters count + for (int i = 0; i < n; i++) { + if (parent[i] == i) count++; + } + + return n - count; + } + + private int find(int[] parent, int v) { + return parent[v] == v ? v : find(parent, parent[parent[v]]); + } + +} diff --git a/Week 06/id_343/LeetCode_200.go b/Week 06/id_343/LeetCode_200.go new file mode 100644 index 000000000..141e5dcfa --- /dev/null +++ b/Week 06/id_343/LeetCode_200.go @@ -0,0 +1,29 @@ +func numIslands(grid [][]byte) int { + if grid == nil || len(grid) == 0{ + return 0 + } + N := len(grid) + M := len(grid[0]) + res := 0 + for i := 0;i=N ||j<0 ||j>=M||grid[i][j] != '1'{ + return + } + grid[i][j] ='2' + inflect(grid,i+1,j,N,M) + inflect(grid,i-1,j,N,M) + inflect(grid,i,j+1,N,M) + inflect(grid,i,j-1,N,M) + return +} diff --git a/Week 06/id_343/LeetCode_547.go b/Week 06/id_343/LeetCode_547.go new file mode 100644 index 000000000..7a8b026e7 --- /dev/null +++ b/Week 06/id_343/LeetCode_547.go @@ -0,0 +1,35 @@ +func findCircleNum(M [][]int) int { + if len(M) <= 1 { + return len(M) + } + + count := 0 + visited := make([]bool, len(M)) + + for i, _ := range M { + if visited[i] == false { + BFS(M, visited, i) + count++ + } + } + + return count +} + +func BFS(M [][]int, visited []bool, c int) { + q := make([]int, 0) + q = append(q, c) + visited[c] = true + + for len(q) > 0 { + c = q[0] + q = q[1:] + + for i := 0; i < len(M); i++ { + if M[c][i] == 1 && visited[i] == false { + visited[i] = true + q = append(q, i) + } + } + } +} diff --git a/Week 06/id_353/Leetcode_200_353.cpp b/Week 06/id_353/Leetcode_200_353.cpp new file mode 100644 index 000000000..de4e7f58d --- /dev/null +++ b/Week 06/id_353/Leetcode_200_353.cpp @@ -0,0 +1,75 @@ +/* + * @lc app=leetcode.cn id=200 lang=cpp + * + * [200] 岛屿数量 + */ + +// @lc code=start +class Solution { +private: + int count = 0; + vector parent; + + void init(vector>& grid) { + int r = grid.size(); + int c = grid[0].size(); + parent.resize(r * c); + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + if (grid[i][j] == '1') { + parent[i * c + j] = i * c + j; + count++; + } + } + } + } + + int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + + void _union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP != rootQ) { + parent[rootQ] = rootP; + count--; + } + } + +public: + int numIslands(vector>& grid) { + int r = grid.size(); + if (!r) return 0; + int c = grid[0].size(); + + init(grid); + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + if (grid[i][j] == '1') { + grid[i][j] = '0'; + int old = i * c + j; + if (i - 1 >= 0 && grid[i - 1][j] == '1') { + _union(old, (i - 1) * c + j); + } + if (i + 1 < r && grid[i + 1][j] == '1') { + _union(old, (i + 1) * c + j); + } + if (j - 1 >= 0 && grid[i][j - 1] == '1') { + _union(old, i * c + j - 1); + } + if (j + 1 < c && grid[i][j + 1] == '1') { + _union(old, i * c + j + 1); + } + } + } + } + return count; + } +}; +// @lc code=end + diff --git a/Week 06/id_353/Leetcode_547_353.cpp b/Week 06/id_353/Leetcode_547_353.cpp new file mode 100644 index 000000000..4243b91e6 --- /dev/null +++ b/Week 06/id_353/Leetcode_547_353.cpp @@ -0,0 +1,52 @@ +/* + * @lc app=leetcode.cn id=547 lang=cpp + * + * [547] 朋友圈 + */ + +// @lc code=start +class Solution { +private: + int count = 0; + vector parent; + void init(int n) { + count = n; + parent.resize(count + 1); + for (int i = 0; i < count; i++) { + parent[i] = i; + } + } + + int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + + void _union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP != rootQ) { + parent[rootP] = rootQ; + count--; + } + } + +public: + int findCircleNum(vector>& M) { + parent.clear(); + int n = M.size(); + init(n); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (M[i][j] == 1) + _union(i, j); + } + } + return count; + } +}; +// @lc code=end + diff --git a/Week 06/id_358/NOTE.md b/Week 06/id_358/NOTE.md index a6321d6e2..780beafcd 100644 --- a/Week 06/id_358/NOTE.md +++ b/Week 06/id_358/NOTE.md @@ -1,4 +1,199 @@ -# NOTE +1 Trie树的基本实现和特性 + +Trie树,又叫字典树,单词查找树,键树。 +比如搜索引擎文本词频统计。 + +优点 最大限度减少无谓的字符串比较,查询效率比哈希表高。 + +性质 + +1. 节点本身不存完整单词 +2. 从根节点到某一节点,路径上经过的节点,字符连接起来,为该节点对应的字符串。 +3. 每个节点的所有子节点路径所代表的字符都不相同。 + +核心思想: + +1. 空间换时间 +2. 查找公共前缀 + +节点可以存储额外信息(频次) + +实战题 + +1. 实现前缀树 + +构造字典树的模板代码: + +1. insert() 插入 +2. search()搜索 +3. startsWith() 公共前缀 + +2. word search + +解法: + +1. 暴力法。便利words数组,从board中搜索满足的单词。时间复杂度O(NMM*4^k) +2. Trie 树 + 1. 遍历所有的words构建前缀树 + 2. 对board做DFS,以每个字符开始,看字符串是否在trie中 + +2. 并查集 Disjoint set + +使用场景:组团,配对问题。Group or not? + +基本操作: + +1. makeSet(s): 建立一个新的并查集,其中包含s个单元素集合 +2. unionSet(x,y) 把想,y所在的集合合并,要求x,y所在的集合不想交。如果相交则不合并。 +3. find(x):找到x所在的集合的代表,该操作也可以判断两个元素是否位于同一集合,只要将各自的代表比较一下即可。 + +实战题 + +1. frinds circles + +1. DFS(转换成岛屿问题) +2. BFS(转换成岛屿问题) +3. 并查集 + 1. 遍历N得到各自独立的集合; + 2. 遍历好友关系矩阵,合并集合 + 3. 看有多少孤立的集合 + +实现: + +1. 初始化:每个元素都有一个parent指向自己的数组。 +2. 查询,合并:找到领头元素 +3. 压缩路径,加快查找 + +3. 高级搜索 + +1. 剪枝的实现和特性 + +回顾下初级搜索: + +1. 朴素搜索 +2. 优化方式: 不重复的遍历(Fibonacci), 剪枝(生成括号问题) +3. 搜索方向:DFS(栈)BFS(队列) + +优化方式: + +1. 双向搜索 +2. 启发式搜索(基于优先队列,根据节点的优先级,如A星搜索) + +实战题 + +1. 爬楼梯,硬币兑换问题 + +1. 递归 +2. DP + +2. 括号生成问题 + +1. 递归 + 剪枝(左右括号有限制条件) +2. DP + +3. n皇后问题 + +DFS 判重 --》 剪枝 + +4 数独问题 + +找空格子 --》 优先级搜索 + +2. 双向BFS + +TODO: 记住模板代码 + +3. 实战题 + +1. 单词接龙 + +1. BFS +2. DFS +3. 双向BFS + +2. 基因替换问题 + +类似单词接龙问题 + +3. 启发式搜索 + +如A星算法 + +思想: 基于队列的BFS ---》 基于优先级队列BFS + +关键: 优先级 ---》 估计函数h(n) + +h(n): 用来评价哪些节点是最有希望的是我们要找的节点。会返回一个非负数,也可以认为是从节点n到目标节点路径的估计成本。 + +特性: 会告知搜索方向,是明智的方法猜测。 + +实战题 + +1. 二位矩阵的最短路径 + +1. DP +2. BFS +3. A star + +2. slidng puzzle + +1. DFS +2. BFS +3. A star + +求二维矩阵两点距离, 也就是估价函数:一般用曼哈顿距离 d =abs(x1-x2) + abs(y1-y2) + +4 高级树,AVL树,红黑树 + +二叉搜索树的查找: 时间复杂度O(logN) + +极端情况下退化--》 链表 O(n) + +BST 查询的复杂度 = 树的深度 + +保证性能的关键 + +1. 保证二维维度 ---》 左右字数的结构平衡 +2. balanced + +由此引出平衡二叉树:有多种,如AVL 树,B-tree等 + +如何平衡 + +1. AVL (名字来源于发明者) +2. balance factor(平衡因子) +3. 旋转操作: 左旋,右旋,左右旋,右左旋 + +带字数的旋转需要更换字数的父节点 + +总结 + +1. 平衡二叉搜索树 +2. 每个节点存储balance factor(有三种-1, 0, 1) +3. 四种旋转操作 + +不足 + +节点需要存储额外信息,且调整次数频繁 +由此引出 “近似平和二叉树”,如红黑树 + +红黑树 + + 任何一个节点的左右字数的高度差小于2倍 + +特点: + +1. 每个节点要么是黑色要么是红色 +2. 根节点是黑色 +3. 每个叶节点(NIL节点,空节点)是黑色的 +4. 不能有相邻的两个红色节点 +5. 从任一节点到其每个节点的所有路径都包含相同数目的黑色节点。 + +对比 + +1. AVL 查找更快 +2. 红黑树插入删除更快 +3. AVL 额外存储更多 +4. 红黑树多用在语音的库实现中。AVL多用在数据库中,查找比较多。 - diff --git a/Week 06/id_358/implement_trie.js b/Week 06/id_358/implement_trie.js new file mode 100644 index 000000000..7f14c1af6 --- /dev/null +++ b/Week 06/id_358/implement_trie.js @@ -0,0 +1,41 @@ +/** + * Initialize your data structure here. + */ + var Trie = function() { + +}; + +/** + * Inserts a word into the trie. + * @param {string} word + * @return {void} + */ +Trie.prototype.insert = function(word) { + +}; + +/** + * Returns if the word is in the trie. + * @param {string} word + * @return {boolean} + */ +Trie.prototype.search = function(word) { + +}; + +/** + * 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) { + +}; + +/** + * 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) + */ \ No newline at end of file diff --git a/Week 06/id_358/valid_sodoku.js b/Week 06/id_358/valid_sodoku.js new file mode 100644 index 000000000..af6af6bec --- /dev/null +++ b/Week 06/id_358/valid_sodoku.js @@ -0,0 +1,22 @@ +/** + * @param {character[][]} board + * @return {boolean} + */ + var isValidSudoku = function(board) { + const rows = {}, columns = {}, boxes = {} + for(let i = 0; i < 9; i++) { + for(let j = 0; j < 9; j++) { + let boxIndex = parseInt(i/3) * 3 + parseInt(j/3) + let num = board[i][j] + if(num !== '.') { + if(rows[i+'-'+num] || columns[j+'-'+num] || boxes[boxIndex + '-' + num]) { + return false; + } + rows[i+'-'+num] = true; + columns[j+'-'+num] = true; + boxes[boxIndex+'-'+num] = true; + } + } + } + return true; +}; \ No newline at end of file diff --git a/Week 06/id_358/word_search_2.js b/Week 06/id_358/word_search_2.js new file mode 100644 index 000000000..17da032f9 --- /dev/null +++ b/Week 06/id_358/word_search_2.js @@ -0,0 +1,83 @@ +/** + * @param {character[][]} board + * @param {string[]} words + * @return {string[]} + */ + class TrieNode { + constructor() { + this.isEnd = false; + this.children = {} + } +} + +let root = null; +function Trie() { + root = new TrieNode() +} +Trie.prototype.insert = function(word) { + let node = root; + for(let i = 0; i < word.length; i++) { + if(!node.children[word[i]]) { + node.children[word[i]] = new TrieNode() + } + node = node.children[word[i]] + } + node.isEnd = true; +} + +Trie.prototype.searchPrefix = function(word) { + let node = root; + for(let i = 0; i < word.length; i++) { + if(node.children[word[i]]) { + node = node.children[word[i]] + } else { + return null; + } + } + return node; +} + +Trie.prototype.search = function(word) { + const prefix = this.searchPrefix(word) + return prefix !== null && prefix.isEnd +} +Trie.prototype.startWith = function(word) { + return this.searchPrefix(word) !== null; +} +var findWords = function(board, words) { + const trie = new Trie(); + words.forEach(word => trie.insert(word)) + const m = board.length; + const n = board[0].length; + // 方向向量 + const dx = [-1,1,0,0] + const dy = [0,0,-1,1] + const result = []; + for(let i = 0; i < m; i++) { + for(let j = 0; j < n; j++) { + bfs(i,j,'') + } + } + return result; + + function bfs(i, j, currStr) { + let restore = board[i][j] + currStr += restore; + if(trie.search(currStr) && result.indexOf(currStr) < 0) { + result.push(currStr) + } + if(!trie.startWith(currStr)) { // 剪枝 + return; + } + board[i][j] = '#' // 临时改为特殊字符,用于防止重复访问 + // drill down 四个方向 + for(let k = 0; k < 4; k++) { + let _i = dx[k] + i; + let _j = dy[k] + j; + if(_i >=0 && _j >=0 && _i < m && _j < n && board[_i][_j] !== '#') { + bfs(_i, _j, currStr) + } + } + board[i][j] = restore; + } +}; \ No newline at end of file diff --git a/Week 06/id_363/LeetCode_130_363.java b/Week 06/id_363/LeetCode_130_363.java new file mode 100644 index 000000000..981273d51 --- /dev/null +++ b/Week 06/id_363/LeetCode_130_363.java @@ -0,0 +1,79 @@ +package com.test.leetcode.week06; + +import org.junit.Test; + +/** + * done 20191124 + * todo 20191125 + * todo 20191126 + * todo 20191201 + */ +public class SolutionSurroundRegion130 { + + + @Test + public void test1() { + char[][] board = { + {'X','X','X','X'}, + {'X','O','O','X'}, + {'X','X','O','X'}, + {'X','O','X','X'} + }; + solve(board); + System.out.println(board); + } + + + /** + * 找到被x围绕的区域,并将这些区域里面的x转换成o + * 被围绕的区域不会存在于边界 + * + * @param board + */ + public void solve(char[][] board) { + if (board == null || board.length < 2) { + return; + } + int row = board.length; + int col = board[0].length; + for (int r = 0; r < row; r ++) { + for (int c = 0; c < col; c ++) { + boolean isEdage = r == 0 || r == row - 1 || c == 0 || c == col - 1; + if (isEdage && board[r][c] == 'O') { + helper(board, r, c); + } + } + } + for (int r = 0; r < row; r ++) { + for (int c = 0; c < col; c++) { + if (board[r][c] == 'O') { + board[r][c] = 'X'; + } + if (board[r][c] == '#') { + board[r][c] = 'O'; + } + + } + } + } + + // 把所有和外界联通的o转换成# + int[] dx = {0, 1, 0, -1}; + int[] dy = {1, 0, -1, 0}; + private void helper(char[][] board, int r, int c) { + // termination + if (r < 0 || r >= board.length || c < 0 || c >= board[0].length || board[r][c] != 'O') { + return; + } + // process + board[r][c] = '#'; + // drill down + for (int k = 0; k < 4; k ++) { + helper(board, r + dx[k], c + dy[k]); + } + // reverse + + } + + +} diff --git a/Week 06/id_363/LeetCode_200_363.java b/Week 06/id_363/LeetCode_200_363.java new file mode 100644 index 000000000..a3d743e48 --- /dev/null +++ b/Week 06/id_363/LeetCode_200_363.java @@ -0,0 +1,116 @@ +package com.test.leetcode.week03; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.LinkedList; + + +public class ssolutionNumIsland200 { + + + @Test + public void test1() { + char[][] grid = new char[4][5]; + for (char[] c : grid) { + Arrays.fill(c, '0'); + } + grid[0][0] = '1'; + grid[0][1] = '1'; + grid[0][2] = '1'; + grid[0][3] = '1'; + grid[1][0] = '1'; + grid[1][1] = '1'; + grid[1][3] = '1'; + grid[2][0] = '1'; + grid[2][1] = '1'; + + + char[][] grid2 = new char[4][5]; + for (char[] c : grid2) { + Arrays.fill(c, '0'); + } + grid2[0][0] = '1'; + grid2[0][1] = '1'; + grid2[1][0] = '1'; + grid2[1][1] = '1'; + grid2[2][2] = '1'; + grid2[3][3] = '1'; + grid2[3][4] = '1'; + + char[][] grid3 = {{'1','1','1'},{'0','1','0'},{'1','1','1'}}; + + // 注释掉上面是因为上面的代码会修改grid的值,导致后面的代码输出都是0 + System.out.println(numIslands_20191124_2(grid)); + System.out.println(numIslands_20191124_2(grid2)); + System.out.println(numIslands_20191124_2(grid3)); + + } + + + /** + * DFS + * BFS + * 注意是字符数组 + * + * @param grid + * @return + */ + public int numIslands_20191124_1(char[][] grid) { + int count = 0; + for (int r = 0; r < grid.length; r ++) { + for (int c = 0; c < grid[0].length; c ++) { + if (grid[r][c] == '1') { + count ++; + helper_20191124(grid, r, c); + } + } + } + return count; + } + + int[] dr = {0,1,0,-1}; + int[] dc = {1,0,-1, 0}; + private void helper_20191124(char[][] grid, int r, int c) { + // termination + if (r < 0 || r >= grid.length || c < 0 || c >= grid[0].length || grid[r][c] == '0') { + return; + } + // process + grid[r][c] = '0'; + // drill down + for (int k = 0; k < 4; k ++) { + helper_20191124(grid, r + dr[k], c + dc[k]); + } + // reverse + } + + public int numIslands_20191124_2(char[][] grid) { + int count = 0; + LinkedList queue = new LinkedList<>(); + int[] dx = {0, 1, 0, -1}; + int[] dy = {1, 0, -1, 0}; + for (int r = 0; r < grid.length; r ++) { + for (int c = 0; c < grid[0].length ; c ++) { + if (grid[r][c] == '1') { + count ++; + queue.add(new int[]{r, c}); + while (!queue.isEmpty()) { + int[] cur = queue.poll(); + int x = cur[0]; int y = cur[1]; + grid[x][y] = '0'; + for (int k =0; k < 4; k ++) { + int nr = x + dx[k]; + int nc = y + dy[k]; + if (nr >= 0 && nr <= grid.length - 1 && nc >= 0 && nc < grid[0].length && grid[nr][nc] == '1') { + queue.add(new int[]{nr, nc}); + } + } + } + } + } + } + return count; + } + +} diff --git a/Week 06/id_363/LeetCode_208_363.java b/Week 06/id_363/LeetCode_208_363.java new file mode 100644 index 000000000..db43a6296 --- /dev/null +++ b/Week 06/id_363/LeetCode_208_363.java @@ -0,0 +1,106 @@ +package com.test.leetcode.week06; + +import org.junit.Test; + + +public class SolutionImplementTrie208 { + + + @Test + public void test1() { + Trie trie = new Trie(); + + trie.insert("apple"); + System.out.println(trie.search("apple")); // 返回 true + System.out.println(trie.search("app")); // 返回 false + System.out.println(trie.startsWith("app")); // 返回 true + trie.insert("app"); + System.out.println(trie.search("app")); // 返回 true + } +} + + +class Trie { + + TrieNode root; + + /** Initialize your data structure here. */ + public Trie() { + root = new TrieNode(); + } + + // 时间复杂度: O(m) 空间复杂度O(m) m表示字符串的长度 + /** Inserts a word into the trie. */ + public void insert(String word) { + TrieNode node = root; + for (Character c : word.toCharArray()) { + if (!node.containsKey(c)) { + node.putNode(c, new TrieNode()); + } + node = node.getNode(c); + } + node.setEnd(); + } + + // 时间复杂度: O(m) 空间复杂度O(1) m表示字符串的长度 + /** Returns if the word is in the trie. */ + public boolean search(String word) { + if (word == null || word.length() == 0) { + return false; + } + TrieNode node = searchPrefix(word); + return node != null && node.isEnd(); + } + + /** Returns if there is any word in the trie that starts with the given prefix. */ + public boolean startsWith(String prefix) { + if (prefix == null || prefix.length() == 0) { + return false; + } + return searchPrefix(prefix) != null; + } + + private TrieNode searchPrefix(String prefix) { + TrieNode node = root; + for(char c : prefix.toCharArray()) { + if (node.containsKey(c)) { + node = node.getNode(c); + } else { + return null; + } + } + return node; + } +} + +class TrieNode { + + private TrieNode[] links; + private final int total = 26; + private Boolean isEnd; + + // 注意初始化 + public TrieNode() { + links = new TrieNode[total]; + isEnd = false; + } + + public Boolean containsKey (char c) { + return links[c - 'a'] != null; + } + + public void putNode (char c, TrieNode node) { + links[c - 'a'] = node; + } + + public TrieNode getNode(char c) { + return links[c - 'a']; + } + + public Boolean isEnd () { + return isEnd; + } + public void setEnd() { + this.isEnd = true; + } +} diff --git a/Week 06/id_363/LeetCode_212_363.java b/Week 06/id_363/LeetCode_212_363.java new file mode 100644 index 000000000..606c384cc --- /dev/null +++ b/Week 06/id_363/LeetCode_212_363.java @@ -0,0 +1,173 @@ +package com.test.leetcode.week06; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + + +public class SolutionWordSearchii212 { + + + @Test + public void test1() { + char[][] board = { + {'o','a','a','n'}, + {'e','t','a','e'}, + {'i','h','k','r'}, + {'i','f','l','v'} + }; + String[] words = {"oath","pea","eat","rain"}; + + char[][] board1 = { + {'a'} + }; + + System.out.println(findWords2(board1, new String[]{"a"})); + System.out.println(findWords2(board, words)); + System.out.println(findWords(board, words)); + } + + + // 性能更好 + public List findWords2(char[][] board, String[] words) { + // 1. 创建trie树 + MyTrie myTrie = new MyTrie(); + for (String word : words) { + myTrie.insert(word); + } + MyTrieNode root = myTrie.root; + // 2. 查找字符串 + int row = board.length; + int col = board[0].length; + Set result = new HashSet<>(); + for(int r = 0; r < row; r ++) { + for (int c = 0; c < col ; c ++) { + helper2(board, r, c, root, result); + } + } + return new ArrayList<>(result); + } + + + int[] dr2 = {0, 1, 0, -1}; + int[] dc2 = {1, 0, -1, 0}; + // node 表示父节点的node + private void helper2(char[][] board, int r, int c, MyTrieNode node, Set result) { + // termination + if (r < 0 || r >= board.length || c < 0 || c >= board[0].length || board[r][c] == '@') { + return; + } + node = node.links[board[r][c] - 'a']; + if (node == null) { + return; + } + if (node.isEnd) { + result.add(node.val); + } + // process + char temp = board[r][c]; + board[r][c] = '@'; + for (int k = 0; k < 4; k ++) { + helper2(board, r + dr2[k], c + dc2[k], node, result); + } + board[r][c] = temp; + // drill down + // reverse + } + + /** + * 给定二维网格和一个字典中的单词列表words + * 找出所有同时在二维网格和字典中出现的单词的列表 + * + * 1.循环网格 + * 判断第一个单词是否在列表中是前缀 + * 如果是,那么继续dfs,判断是否存在以这个单词为开始的单词 + * + * // dfs + * 获取当前单词,判断当前单词在trie树中是偶有前缀,如果有那么继续,如果是单词,那么返回这个单词 + * + * @param board + * @param words + * @return + */ + public List findWords(char[][] board, String[] words) { + // 1. 创建trie树 + Trie trie = new Trie(); + for (String word : words) { + trie.insert(word); + } + TrieNode root = trie.root; + // 2. 查找字符串 + int row = board.length; + int col = board[0].length; + Set result = new HashSet<>(); + for(int r = 0; r < row; r ++) { + for (int c = 0; c < col ; c ++) { + if (root.containsKey(board[r][c])) { + helper(board, "", r, c, trie, result); + } + } + } + return new ArrayList<>(result); + } + + int[] dr = {0, 1, 0, -1}; + int[] dc = {1, 0, -1, 0}; + private void helper(char[][] board, String cur, int r, int c, Trie trie, Set result) { + // termination + cur = cur + board[r][c]; + if (!trie.startsWith(cur)) { + return; + } + if (trie.search(cur)) { + result.add(cur); + } + + // process + char temp = board[r][c]; + board[r][c] = '@'; + for (int k = 0; k < 4; k ++) { + int nr = r + dr[k]; + int nc = c + dc[k]; + if (nr >= 0 && nr < board.length && nc >= 0 && nc < board[0].length && board[nr][nc] != '@' ) { + helper(board, cur, nr, nc, trie, result); + } + } + // drill down + // reverse + board[r][c] = temp; + } + +} + + +class MyTrie { + MyTrieNode root; + + public MyTrie () { + root = new MyTrieNode(); + } + + public void insert(String word) { + MyTrieNode node = root; + for (char c : word.toCharArray()) { + if (node.links[c - 'a'] == null) { + node.links[c - 'a'] = new MyTrieNode(); + } + node = node.links[c - 'a']; + } + node.isEnd = true; + node.val = word; + } + +} + +class MyTrieNode { + String val = ""; + MyTrieNode[] links = new MyTrieNode[26]; + boolean isEnd = false; + +} \ No newline at end of file diff --git a/Week 06/id_363/LeetCode_51_363.java b/Week 06/id_363/LeetCode_51_363.java new file mode 100644 index 000000000..1a07149b2 --- /dev/null +++ b/Week 06/id_363/LeetCode_51_363.java @@ -0,0 +1,90 @@ +package com.test.leetcode.week02; + +import org.junit.Test; + +import java.util.LinkedList; +import java.util.List; + + +public class SolutionNQueens51 { + + + @Test + public void test1() { + System.out.println(solveNQueens_20191124(8)); + } + + + /** + * N 皇后 + * pie: + * na: + * queen: + * + * 从第0行开始递归,循环每一列,判断是否能放皇后 + * 如果可以放皇后,那么继续递归下一行 + * 如果不可以放皇后,继续下一列 + * + * @param n + * @return + */ + int[] pie; + int[] na; + int[] cols_20191124; + int[] queen_20191124; + List> result_20191124; + + public List> solveNQueens_20191124(int n) { + // 初始化 + pie = new int[2 * n + 1]; + na = new int[2 * n + 1]; + cols_20191124 = new int[n]; + queen_20191124 = new int[n]; + result_20191124 = new LinkedList<>(); + // 从第0行开始递归 + helper_20191124(0, n); + return result_20191124; + } + + private void helper_20191124(int row, int n) { + // termination + if (row == n) { + result_20191124.add(addResult_20191124(queen_20191124)); + return; + } + // process + for (int c = 0; c < n; c ++) { + if (pie[row + c] == 0 && na[row - c + n] == 0 && cols_20191124[c] == 0) { + pie[row + c] = 1; + na[row - c + n] = 1; + cols_20191124[c] = 1; + queen_20191124[row] = c; + helper_20191124(row + 1, n); + pie[row + c] = 0; + na[row - c + n] = 0; + cols_20191124[c] = 0; + queen_20191124[row] = 0; + } + } + // drill down + // reverse + } + + private List addResult_20191124(int[] queen) { + List result = new LinkedList<>(); + for (int i = 0; i < queen.length; i ++) { + StringBuffer buffer = new StringBuffer(); + for (int j = 0; j < queen[i]; j ++) { + buffer.append("."); + } + buffer.append("Q"); + for (int j = queen[i] + 1;j < queen.length; j ++) { + buffer.append("."); + } + result.add(buffer.toString()); + } + return result; + } + + +} diff --git a/Week 06/id_363/LeetCode_547_363.java b/Week 06/id_363/LeetCode_547_363.java new file mode 100644 index 000000000..c6d25a9b9 --- /dev/null +++ b/Week 06/id_363/LeetCode_547_363.java @@ -0,0 +1,156 @@ +package com.test.leetcode.week06; + +import org.junit.Test; + +import java.util.LinkedList; + +public class SolutionFriendCircle547 { + + + @Test + public void test1() { + int[][] m = { + {1,1,0}, + {1,1,0}, + {0,0,1} + }; + int[][] m2 = { + {1,1,0}, + {1,1,1}, + {0,1,1} + }; + + int[][] m3 = { + {1,0,0,1}, + {0,1,1,0}, + {0,1,1,1}, + {1,0,1,1} + }; + + System.out.println(findCircleNum(m)); + System.out.println(findCircleNum(m2)); + System.out.println(findCircleNum(m3)); + + System.out.println(findCircleNum2(m)); + System.out.println(findCircleNum2(m2)); + System.out.println(findCircleNum2(m3)); + + + System.out.println(findCircleNum3(m)); + System.out.println(findCircleNum3(m2)); + System.out.println(findCircleNum3(m3)); + } + + + /** + * A和B是朋友,B和C是朋友,那么A和C也是朋友 + * + * DFS 使用visited数组表示用户是否被访问过 + * BFS + * 并查集 + * + * @param M + * @return + */ + public int findCircleNum(int[][] M) { + int[] visited = new int[M.length]; // 表示用户是否被访问过 + int count = 0; + for (int i = 0; i < M.length; i ++) { + if (visited[i] == 0) { // 这个用户还没有访问过 + count ++; + helper(M, visited, i); + } + } + return count; + } + + private void helper(int[][] m, int[] visited, int i) { + for (int j = 0; j < m.length; j ++) { + if (m[i][j] == 1 && visited[j] == 0) { // 如果i和j是好朋友,那么就把j标志位1,并且去获取j的好朋友 + visited[j] = 1; + helper(m, visited, j); + } + } + } + + + /** + * 并查集 + * 初始化 + * 如果i和j是好友,那么合并两个集合 + * 求独立的集合的个数 + * @param M + * @return + */ + public int findCircleNum2(int[][] M) { + int n = M.length; + UnionFind unionFind = new UnionFind(n); + for (int i = 0; i < n; i ++) { + for (int j = 0; j < i; j ++) { + if (M[i][j] == 1){ + unionFind.union(i, j); + } + } + } + return unionFind.getCount(); + } + + + + + + + public int findCircleNum3(int[][] M) { + int[] visited = new int[M.length]; // 表示用户是否被访问过 + int count = 0; + LinkedList queue = new LinkedList<>(); + for (int i = 0; i < M.length; i ++) { + if (visited[i] == 0) { // 这个用户还没有访问过 + queue.add(i); + count ++; + while (! queue.isEmpty()) { + Integer j = queue.poll(); + visited[j] = 1; + for (int k = 0; k < M.length; k ++) { + if (M[j][k] == 1 && visited[k] == 0) { + queue.add(k); + } + } + } + } + } + return count; + } + +} + + +class UnionFind { + private int count = 0; + private int[] parent; + public UnionFind(int n) { + count = n; + parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + public int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) return; + parent[rootP] = rootQ; + count--; + } + + public int getCount() { + return count; + } +} \ No newline at end of file diff --git a/Week 06/id_363/NOTE.md b/Week 06/id_363/NOTE.md index a6321d6e2..3410062ba 100644 --- a/Week 06/id_363/NOTE.md +++ b/Week 06/id_363/NOTE.md @@ -1,4 +1 @@ -# NOTE - - - +c \ No newline at end of file diff --git a/Week 06/id_373/week06_373_homework/200.cpp b/Week 06/id_373/week06_373_homework/200.cpp new file mode 100644 index 000000000..c2202df7f --- /dev/null +++ b/Week 06/id_373/week06_373_homework/200.cpp @@ -0,0 +1,39 @@ +class Solution { +public: + int numIslands(vector>& grid) { + int nr = grid.size(); + if (!nr) return 0; + int nc = grid[0].size(); + + int num_islands = 0; + for (int r = 0; r < nr; ++r) { + for (int c = 0; c < nc; ++c) { + if (grid[r][c] == '1') { + ++num_islands; + grid[r][c] = '0'; + queue> nei***ors; + nei***ors.push({r, c}); + while (!nei***ors.empty()) { + auto rc = nei***ors.front(); + nei***ors.pop(); + int row = rc.first, col = rc.second; + if (row - 1 >= 0 && grid[row-1][col] == '1') { + nei***ors.push({row-1, col}); grid[row-1][col] = '0'; + } + if (row + 1 < nr && grid[row+1][col] == '1') { + nei***ors.push({row+1, col}); grid[row+1][col] = '0'; + } + if (col - 1 >= 0 && grid[row][col-1] == '1') { + nei***ors.push({row, col-1}); grid[row][col-1] = '0'; + } + if (col + 1 < nc && grid[row][col+1] == '1') { + nei***ors.push({row, col+1}); grid[row][col+1] = '0'; + } + } + } + } + } + + return num_islands; + } +}; \ No newline at end of file diff --git a/Week 06/id_373/week06_373_homework/547.cpp b/Week 06/id_373/week06_373_homework/547.cpp new file mode 100644 index 000000000..d0a8ba664 --- /dev/null +++ b/Week 06/id_373/week06_373_homework/547.cpp @@ -0,0 +1,22 @@ +public class Solution { + public int findCircleNum(int[][] M) { + int[] visited = new int[M.length]; + int count = 0; + Queue < Integer > queue = new LinkedList < > (); + for (int i = 0; i < M.length; i++) { + if (visited[i] == 0) { + queue.add(i); + while (!queue.isEmpty()) { + int s = queue.remove(); + visited[s] = 1; + for (int j = 0; j < M.length; j++) { + if (M[s][j] == 1 && visited[j] == 0) + queue.add(j); + } + } + count++; + } + } + return count; + } +} diff --git a/Week 06/id_393/LeetCode_208_393.java b/Week 06/id_393/LeetCode_208_393.java new file mode 100644 index 000000000..ab1a5fa23 --- /dev/null +++ b/Week 06/id_393/LeetCode_208_393.java @@ -0,0 +1,85 @@ +package no208; + +/** + * @author boyiren + * @date 2019-11-24 + */ +public class Trie { + public boolean isWord; + public char word; + public Trie[] tries = new Trie[26]; + + /** + * Initialize your data structure here. + */ + public Trie() { + this.isWord = false; + this.word = ' '; + + } + + /** + * Inserts a word into the trie. + */ + public void insert(String word) { + char[] array = word.toCharArray(); + Trie node = this; + for (int i = 0; i < array.length; i++) { + if (node.tries[array[i] - 'a'] == null) { + node.tries[array[i] - 'a'] = new Trie(); + } + node = node.tries[array[i] - 'a']; + node.word = array[i]; + if (i == array.length - 1) { + node.isWord = true; + } + } + + } + + /** + * Returns if the word is in the trie. + */ + public boolean search(String word) { + char[] array = word.toCharArray(); + Trie node = this; + for (int i = 0; i < array.length; i++) { + if (node.tries[array[i] - 'a'] != null) { + node = node.tries[array[i] - 'a']; + if (node.word == array[i]) { + continue; + } else { + return false; + } + } else { + return false; + } + + } + return node.isWord == true ? true : false; + + + } + + /** + * Returns if there is any word in the trie that starts with the given prefix. + */ + public boolean startsWith(String prefix) { + char[] array = prefix.toCharArray(); + Trie node = this; + for (int i = 0; i < array.length; i++) { + if (node.tries[array[i] - 'a'] != null) { + node = node.tries[array[i] - 'a']; + if (node.word == array[i]) { + continue; + } else { + return false; + } + } else { + return false; + } + + } + return true; + } +} \ No newline at end of file diff --git a/Week 06/id_393/LeetCode_36_393.java b/Week 06/id_393/LeetCode_36_393.java new file mode 100644 index 000000000..f08a775cc --- /dev/null +++ b/Week 06/id_393/LeetCode_36_393.java @@ -0,0 +1,44 @@ +package no36; + +import java.util.HashMap; + +/** + * @author boyiren + * @date 2019-11-24 + */ +public class Solution { + public boolean isValidSudoku(char[][] board) { + // init data + HashMap[] rows = new HashMap[9]; + HashMap[] columns = new HashMap[9]; + HashMap[] boxes = new HashMap[9]; + for (int i = 0; i < 9; i++) { + rows[i] = new HashMap(); + columns[i] = new HashMap(); + boxes[i] = new HashMap(); + } + + // validate a board + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + char num = board[i][j]; + if (num != '.') { + int n = (int) num; + int box_index = (i / 3) * 3 + j / 3; + + // keep the current cell value + rows[i].put(n, rows[i].getOrDefault(n, 0) + 1); + columns[j].put(n, columns[j].getOrDefault(n, 0) + 1); + boxes[box_index].put(n, boxes[box_index].getOrDefault(n, 0) + 1); + + // check if this value has been already seen before + if (rows[i].get(n) > 1 || columns[j].get(n) > 1 || boxes[box_index].get(n) > 1) { + return false; + } + } + } + } + + return true; + } +} diff --git a/Week 06/id_408/LeetCode_208_408.c++ b/Week 06/id_408/LeetCode_208_408.c++ new file mode 100644 index 000000000..e8c763469 --- /dev/null +++ b/Week 06/id_408/LeetCode_208_408.c++ @@ -0,0 +1,63 @@ +class Trie { +public: + + Trie() : nexts{ nullptr }, count(0){ + + } + + ~Trie() { + + for (size_t i= 0; i < 27; ++i) { + if (nexts[i]) delete nexts[i]; + } + } + + + void insert(string word) { + Trie* curr = this; + size_t idx = 0; + + for (char ch : word) { + idx = ch - 'a'; + + if (!curr->nexts[idx]) { + curr->nexts[idx] = new Trie(); + } + curr = curr->nexts[idx]; + } + ++curr->count; + } + + + bool search(string word) { + Trie* last = getLast(word); + return last && last->count > 0; + } + + + bool startsWith(string prefix) { + Trie* last = getLast(prefix); + return last; + } + +private: + Trie* getLast(string prefix) { + + Trie* curr = this; + size_t idx = 0; + + for (char ch : prefix) { + idx = ch - 'a'; + + if (!curr->nexts[idx]) { + return nullptr; + } + curr = curr->nexts[idx]; + } + + return curr; + } + + Trie* nexts[27]; + int count; +}; diff --git a/Week 06/id_408/LeetCode_36_408.go b/Week 06/id_408/LeetCode_36_408.go new file mode 100644 index 000000000..a25ce404b --- /dev/null +++ b/Week 06/id_408/LeetCode_36_408.go @@ -0,0 +1,26 @@ +func isValidSudoku(board [][]byte) bool { + var target byte + for i := 0; i < 9; i++ { + for j := 0; j < 9; j++ { + target = board[i][j] + if target != '.' { + for k := 0; k < 9; k++ { + if k != j && board[i][k] != '.' && board[i][k] == target { + return false + } + if i != k && board[k][j] != '.' && board[k][j] == target { + return false + } + } + for m := i / 3 * 3; m < (i/3+1)*3; m++ { + for n := j / 3 * 3; n < (j/3+1)*3; n++ { + if m != i && n != j && board[m][n] != '.' && board[m][n] == target { + return false + } + } + } + } + } + } + return true +} diff --git a/Week 06/id_418/LeetCode_200_418.java b/Week 06/id_418/LeetCode_200_418.java new file mode 100644 index 000000000..dae8fba4c --- /dev/null +++ b/Week 06/id_418/LeetCode_200_418.java @@ -0,0 +1,104 @@ +package com.ljg.leetcode.week06.a03; + +/** + * NumberOfIslands + */ +public class NumberOfIslands { + + public static void main(String[] args) { + NumberOfIslands nIslands = new NumberOfIslands(); + char[][] grid = { { '1', '1', '1', '1', '0' }, { '1', '1', '0', '1', '0' }, { '1', '1', '0', '0', '0' }, + { '0', '0', '0', '0', '0' } }; + int num = nIslands.numIslands(grid); + System.out.println("num:" + num); + } + + int row; + int col; + + private class UnionFind { + int count; + int[] parent; + int[] rank; + + UnionFind(int n) { + this.count = n; + parent = new int[n]; + rank = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + rank[i] = 1; + } + } + + int getIndex(int i, int j) { + return i * col + j; + } + + int getCount() { + return count; + } + + int find(int x) { + while (x != parent[x]) { + x = parent[x]; + } + return x; + } + + void union(int i, int j) { + int rootI = find(i); + int rootJ = find(j); + + if (rootI == rootJ) { + return; + } + + if (rootI < rootJ) { + parent[j] = rootI; + } else { + parent[i] = rootJ; + } + + count--; + } + } + + public int numIslands(char[][] grid) { + if (grid == null || (grid.length == 0 && grid[0].length == 0)) { + return 0; + } + + row = grid.length; + col = grid[0].length; + + UnionFind unionFind = new UnionFind(row * col); + + int waters = 0; + + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + if (grid[i][j] == '0') { + waters++; + continue; + } + + if ((j + 1) < col && grid[i][j + 1] == '1') { + int index1 = unionFind.getIndex(i, j); + int index2 = unionFind.getIndex(i, j + 1); + unionFind.union(index1, index2); + } + + if ((i + 1) < row && grid[i + 1][j] == '1') { + int index1 = unionFind.getIndex(i, j); + int index2 = unionFind.getIndex(i + 1, j); + unionFind.union(index1, index2); + } + + } + } + + return unionFind.getCount() - waters; + + } +} \ No newline at end of file diff --git a/Week 06/id_418/LeetCode_547_418.java b/Week 06/id_418/LeetCode_547_418.java new file mode 100644 index 000000000..1922c8b5c --- /dev/null +++ b/Week 06/id_418/LeetCode_547_418.java @@ -0,0 +1,62 @@ +package com.ljg.leetcode.week06.a02; + +/** + * FendCircles + */ +public class FriendCircles { + + public static void main(String[] args) { + int[][] M = { { 1, 1, 0 }, { 1, 1, 0 }, { 0, 0, 1 } }; + + FriendCircles fCircles = new FriendCircles(); + int num = fCircles.findCircleNum(M); + + System.out.println("num:" + num); + } + + private int count; + private int[] parents; + + private void init(int[][] M) { + this.count = M.length; + parents = new int[this.count]; + for (int i = 0; i < this.count; i++) { + parents[i] = i; + } + } + + private int findRoot(int i) { + int root = parents[i]; + while (root != i) { + i = root; + root = parents[i]; + } + + return root; + } + + private void union(int i, int j) { + int rootI = findRoot(i); + int rootJ = findRoot(j); + + if(rootI == rootJ) { + return; + } + + parents[rootJ] = rootI; + parents[j] = rootI; + count--; + } + + public int findCircleNum(int[][] M) { + init(M); + for (int i = 0; i < M.length; i++) { + for (int j = 0; j < M[0].length; j++) { + if(M[i][j] == 1) { + union(i, j); + } + } + } + return count; + } +} \ No newline at end of file diff --git a/Week 06/id_423/LeetCode_200_423.java b/Week 06/id_423/LeetCode_200_423.java new file mode 100644 index 000000000..dda93c87e --- /dev/null +++ b/Week 06/id_423/LeetCode_200_423.java @@ -0,0 +1,80 @@ +class Solution { + class UnionFind { + int count; // # of connected components + int[] parent; + int[] rank; + + public UnionFind(char[][] grid) { // for problem 200 + count = 0; + int m = grid.length; + int n = grid[0].length; + parent = new int[m * n]; + rank = new int[m * n]; + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + if (grid[i][j] == '1') { + parent[i * n + j] = i * n + j; + ++count; + } + rank[i * n + j] = 0; + } + } + } + + public int find(int i) { // path compression + if (parent[i] != i) parent[i] = find(parent[i]); + return parent[i]; + } + + public void union(int x, int y) { // union with rank + int rootx = find(x); + int rooty = find(y); + if (rootx != rooty) { + if (rank[rootx] > rank[rooty]) { + parent[rooty] = rootx; + } else if (rank[rootx] < rank[rooty]) { + parent[rootx] = rooty; + } else { + parent[rooty] = rootx; rank[rootx] += 1; + } + --count; + } + } + + public int getCount() { + return count; + } + } + + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + + int nr = grid.length; + int nc = grid[0].length; + int num_islands = 0; + UnionFind uf = new UnionFind(grid); + for (int r = 0; r < nr; ++r) { + for (int c = 0; c < nc; ++c) { + if (grid[r][c] == '1') { + grid[r][c] = '0'; + if (r - 1 >= 0 && grid[r-1][c] == '1') { + uf.union(r * nc + c, (r-1) * nc + c); + } + if (r + 1 < nr && grid[r+1][c] == '1') { + uf.union(r * nc + c, (r+1) * nc + c); + } + if (c - 1 >= 0 && grid[r][c-1] == '1') { + uf.union(r * nc + c, r * nc + c - 1); + } + if (c + 1 < nc && grid[r][c+1] == '1') { + uf.union(r * nc + c, r * nc + c + 1); + } + } + } + } + + return uf.getCount(); + } +} \ No newline at end of file diff --git a/Week 06/id_423/LeetCode_433_423.java b/Week 06/id_423/LeetCode_433_423.java new file mode 100644 index 000000000..ce1949720 --- /dev/null +++ b/Week 06/id_423/LeetCode_433_423.java @@ -0,0 +1,29 @@ +class Solution { + public int minMutation(String start, String end, String[] bank) { + int[] ans = {Integer.MAX_VALUE}; + helper(start,end,bank,new boolean[bank.length],ans,0); + return ans[0]==Integer.MAX_VALUE ? -1 : ans[0]; + } + + private void helper(String now,String end,String[] bank,boolean[] flag,int[] ans,int res){ + if(now.equals(end)){ + ans[0] = Math.min(ans[0],res); + }else{ + for(int i =0;i wordList) { + if (!wordList.contains(endWord)) { + return 0; + } + Set dict = new HashSet<>(wordList); + Set beginSet = new HashSet<>(); + Set endSet = new HashSet<>(); + beginSet.add(beginWord); + endSet.add(endWord); + + int step = 1; + Set visited = new HashSet<>(); + while (!beginSet.isEmpty() && !endSet.isEmpty()) { + if (beginSet.size() > endSet.size()) { + Set set = beginSet; + beginSet = endSet; + endSet = set; + } + Set temp = new HashSet<>(); + for (String word : beginSet) { + char[] chs = word.toCharArray(); + for (int i = 0; i < chs.length; i++) { + for (char c = 'a'; c <= 'z'; c++) { + char old = chs[i]; + chs[i] = c; + String target = String.valueOf(chs); + if (endSet.contains(target)) { + return step + 1; + } + if (!visited.contains(target) && dict.contains(target)) { + temp.add(target); + visited.add(target); + } + chs[i] = old; + } + } + } + beginSet = temp; + step++; + } + return 0; + } + +} \ No newline at end of file diff --git a/Week 06/id_428/LeeCode_212_428.java b/Week 06/id_428/LeeCode_212_428.java new file mode 100644 index 000000000..df0c02f72 --- /dev/null +++ b/Week 06/id_428/LeeCode_212_428.java @@ -0,0 +1,79 @@ +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +class Solution { + public List findWords(char[][] board, String[] words) { + //ֵ + WordTrie myTrie=new WordTrie(); + TrieNode root=myTrie.root; + for(String s:words) + myTrie.insert(s); + + //ʹsetֹظ + Set result =new HashSet<>(); + int m=board.length; + int n=board[0].length; + boolean [][]visited=new boolean[m][n]; + //ά + for(int i=0;i(result); + } + private void find(char [] [] board, boolean [][]visited,int i,int j,int m,int n,Set result, TrieNode cur){ + //߽ԼǷѾж + if(i<0||i>=m||j<0||j>=n||visited[i][j]) + return; + cur=cur.child[board[i][j]-'a']; + visited[i][j]=true; + if(cur==null) { + //ʲƥ䣬 + visited[i][j]=false; + return; + } + //ҵʼ + if(cur.isLeaf) + { + result.add(cur.val); + //ҵʺܻˣΪǡad adddĵʵü +// visited[i][j]=false; +// return; + } + find(board,visited,i+1,j,m,n,result,cur); + find(board,visited,i,j+1,m,n,result,cur); + find(board,visited,i,j-1,m,n,result,cur); + find(board,visited,i-1,j,m,n,result,cur); + //ҪˣΪһܻõһַ + visited[i][j]=false; + } +} +class WordTrie{ + public TrieNode root=new TrieNode(); + public void insert(String s){ + TrieNode cur=root; + for(char c:s.toCharArray()){ + if(cur.child[c-'a']==null){ + cur.child [c-'a'] = new TrieNode(); + cur=cur.child[c-'a']; + }else + cur=cur.child [c-'a']; + } + cur.isLeaf=true; + cur.val=s; + } +} +//ֵ +class TrieNode{ + public String val; + public TrieNode[] child=new TrieNode[26]; + public boolean isLeaf=false; + + TrieNode(){ + + } +} \ No newline at end of file diff --git a/Week 06/id_428/NOTE.md b/Week 06/id_428/NOTE.md index a6321d6e2..7945ca75f 100644 --- a/Week 06/id_428/NOTE.md +++ b/Week 06/id_428/NOTE.md @@ -1,4 +1,826 @@ -# NOTE +# Week_06_学习总结 - +## 1、学习过程 +​ 1)字典树构建 2)后续练习题 + +## 2、本周学习内容(待细化) + +​ 1)字典树 + + 主要是递归的应用,其实这里是可以不使用递归的,但是为了简单起见,使用递归可以使得思路更加清晰。需要注意的一点是,使用连接代表字母,而不是使用结点代表字母;结点只是表示从root到该结点的之间组成的单词是否存在。 + +​ 2)并查集 + +​ 3)高级搜索 + +​ 4)红黑树 + +​ 5)AVL树 + +## 3、字典树 + +#### **概述:** + +Trie是个简单但实用的数据结构,是一种树形结构,是一种哈希树的变种,相邻节点间的边代表一个字符,这样树的每条分支代表一则子串,而树的叶节点则代表完整的字符串。和普通树不同的地方是,相同的字符串前缀共享同一条分支。 + +例如:pool,prize,preview,prepare,produce,progress这些关键词的Tire树 + +![Trie Example](http://linux.thai.net/~thep/datrie/trie1.gif) + + + +典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。 + +它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。 + + + +#### **基本性质:** + +- 根节点不包含字符,除根节点外每一个节点都只包含一个字符; +- 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串; +- 每个节点的所有子节点包含的字符都不相同。 + + + +#### **应用场景:** + +字典数查找效率很高,时间复杂度是O(m),m是要查找的单词中包含的字母的个数,但是会浪费大量存放空指针的存储空间,属于以空间换时间的算法。 + +##### **1、串快速检索** + +给出N个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。 + +##### **2、单词自动完成** + +编辑代码时,输入字符,自动提示可能的关键字、变量或函数等信息。 + +##### **3、最长公共前缀** + +对所有串建立字典树,对于两个串的最长公共前缀的长度即他们所在的结点的公共祖先个数,于是,问题就转化为最近公共祖先问题。 + +##### **4、串排序方面的应用** + +给定N个互不相同的仅由一个单词构成的英文名,让你将他们按字典序从小到大输出用字典树进行排序,采用数组的方式创建字典树,这棵树的每个结点的所有儿子很显然地按照其字母大小排序。对这棵树进行先序遍历即可。 + +参考:https://www.cnblogs.com/Quincy/p/4898348.html + +## 4、并查集 + +1 并查集介绍 +1.给定N个元素; +2.开始时每个元素位于独立的集合中(即该集合只包含当前元素),当它们之间具有某种关系的时候,按照一定顺序,将位于同一组的所有元素进行合并。 +3.利用并查集可以查找到某个元素位于哪一个组中,或者可以查找一共有多少个组。 +应用场景:当有一组集合,当这些集合具有某些关系时,就需要对这些集合进行合并。 +4.根据并查集:我们可以知道任意两个元素是否具有某种关系(如1和2是朋友,2和3是朋友,则1,2,3位于同一组,就可以知道1和3是否是朋友)。 +例如:当N=10时, +(1)给定10个元素0,1,2,3,4,5,6,7,8,9; +(2)开始时每个元素位于独立的集合中,则可以用一个数组,这些元素为数组的下标,因为每个元素位于独立的集合,也就表示刚开始时一共有N=10个集合,每个集合有一个元素,我们可以将数组初始化为-1,表示对应组里面只有一个元素。 + +(3)若这些元素具有如下关系,有3个不相交的集合:{0,6,7,8} ,{1,4,9},{2,3,5} + +(4)将这些集合里面的元素两两进行合并。 +例如0和6位于同一组,我们可以将并查集看成树状结构,让一个数作为孩子,让另一个数作为根,孩子里面存放父亲的下标。例如可以让0为根,6去做孩子,并且将下标为6的值加到下标为0的值上去,并让下标为6的位置存放0,则0里面存放的数据就变为-2(-1+(-1)=-2); +并且将两个数进行合并时,先要判断这两个数的根是否相同,如果相同说明已经在同一组里,就可以直接return;如果不相同,则让两个数的根进行合并(让一个数的根作为另一个数的根) +例如:{0,6,7,8}位于同一个集合(或同一组)中,按照从前到后的顺序,先将0和6进行合并,因为0和6对应的值为-1,说明0和6已经是根,此时将两个根进行合并,让0去做6的根,则将6的值+=到0的值上去则0位置处的值为-2,然后6里存放0(也就是6里存放父亲的下标)。将{0,6,7,8}合并完成后数组里面的内容变为: + +将上面所有集合进行合并后,最终数组如下: + + +2 查(查找一个数的根) +1.思路:对于数x,如果下标为x的里面存放的数据为m,若m小于0,说明当前位置x就是根,如果m大于0,再继续判断下标为m的数据是否为根,继续上面这个过程;(v是自定义的一个vector) +2.代码: + +size_t FindRoot(int x) +{ + int index = x; + while (v[index] >= 0) + { + //说明还不是根,则继续查找它的内容存放的是否是根 + index = v[index]; + } + return index; +} +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +3 并(将两个集合进行合并) +1.思路:首先判断这两个数的根是否相同,若相同说明已经在同一组里,就不用在合并,如果不相同,就将一个数的根加等到另一个数的根上去,并将该数存放根。 +2.代码: + +void Union(int x1, int x2) +{ + //先判断两个数的根是否相同 + int root1 = FindRoot(x1); + int root2 = FindRoot(x2); + if (root1 != root2) + { + //两个根不相同,让一个根去做另一个根的根 + v[root1] += v[root2]; + v[root2] = root1; + } +} +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +4 若想知道合并完成后一共有多少个组 +1.思路:遍历一遍数组,负数的个数就是根的个数(有多少个根,就有多少个组)。 +2.代码: + +int GetCount() +{ + int num = 0; + for (size_t i = 0; i < v.size(); ++i) + { + if (v[i] < 0) + { + ++num; + } + } + return num; +} +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +5 并查集的使用场景 +题目1.假设已知有n个人和m对好友关系(存于数组r),如果两个人是直接或间接的好友(好友的好友),则认为他们属于同一个朋友圈。请写程序求出这n个人里一共有多少个朋友圈。例如:n=5,m=3,r={{1,2},{2,3},{4,5}},表示有5个人,1和2是好友,2和3是好友,则1,2,3属于同一个朋友圈,4,5属于另一个朋友圈。结果为两个朋友圈。 + +代码: + +int friends(int n, int m, int r[][2]) +{ + UnionFindSet ufs(n + 1); //因为人的下标是从1开始,所以多开一个空间,将0给留着 + + for (size_t i = 0; i < m; i++) + { + ufs.Union(r[i][0], r[i][1]); //将第i对好友关系放进并查集里,因为好友关系是以两个两个为一对 + } + + return ufs.GetCount() - 1; //因为第一个空间是多开的,而初始化值为-1 +} +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +6 并查集的整体代码 +#include +#include +using namespace std; + +class UnionFindSet +{ +public: + UnionFindSet(size_t n) + { + v.resize(n, -1); + } + + void Union(int x1, int x2) + { + //先判断两个数的根是否相同 + int root1 = FindRoot(x1); + int root2 = FindRoot(x2); + if (root1 != root2) + { + //两个根不相同,让一个根去做另一个根的根 + v[root1] += v[root2]; + v[root2] = root1; + } + } + + //查找一个数的根 + size_t FindRoot(int x) + { + int index = x; + while (v[index] >= 0) + { + //说明还不是根,则继续查找它的内容存放的是否是根 + index = v[index]; + } + return index; + } + + //获得组数,也就是有多少个分组 + int GetCount() + { + int num = 0; + for (size_t i = 0; i < v.size(); ++i) + { + if (v[i] < 0) + { + ++num; + } + } + return num; + } + +private: + vector v; +}; +———————————————— +原文链接:https://blog.csdn.net/xu1105775448/article/details/82077944 + +## 5、红黑树 + +红黑树是一棵自平衡的二叉搜索树,因此在学习红黑树之前,我们需要回顾一下之前所学的知识**二叉搜索树和平衡二叉树**。 + +###### **1.二叉搜索树** + +二叉搜索树又叫二叉查找树或者二叉排序树,它首先是一个二叉树,而且必须满足下面的条件: + +1)若左子树不空,则左子树上所有结点的值均小于它的根节点的值; + +2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值 + +3)左、右子树也分别为二叉搜索树 + + + + + +![img](https:////upload-images.jianshu.io/upload_images/4314397-9588c432805a9570.png?imageMogr2/auto-orient/strip|imageView2/2/w/300/format/webp) + +二叉搜索树示例 + +###### **2.平衡二叉树** + +二叉搜索树解决了许多问题,比如可以快速的查找最大值和最小值,可以快速找到排名第几位的值,快速搜索和排序等等。但普通的二叉搜索树有可能出现极不平衡的情况(斜树),这样我们的时间复杂度就有可能退化成 O(N) 的情况。比如我们现在插入的数据是 [1,2,3,4,5,6,7] 转换为二叉树如下: + + + +![img](https:////upload-images.jianshu.io/upload_images/4314397-2f1facc1bcfc501c.png?imageMogr2/auto-orient/strip|imageView2/2/w/709/format/webp) + +斜树 + +由于普通的二叉搜索树会出现极不平衡的情况,那么我们就必须得想想办法了,这个时候平衡二叉树就能帮到我们了。什么是平衡二叉树?平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或**它的左右两个子树的高度差的绝对值不超过1**,并且左右两个子树都是一棵平衡二叉树。 + +平衡二叉树有一个很重要的性质:左右两个子树的高度差的绝对值不超过1。那么解决方案就是如果二叉树的左右高度超过 1 ,我们就把当前树调整为一棵平衡二叉树。这就涉及到**左旋**、**右旋**、**先右旋再左旋**、**先左旋再右旋**。 + +**2.1 右旋:** + + + + +![img](https:////upload-images.jianshu.io/upload_images/4314397-318ca114772ad3fc.png?imageMogr2/auto-orient/strip|imageView2/2/w/986/format/webp) + +右旋.png + + + +```swift + TreeNode *R_Rotation(TreeNode *pNode) { + TreeNode *left = pNode->left; + TreeNode *right = left->right; + left->right = pNode; + pNode->left = right; + // 重新调整高度 + pNode->height = max(getHeight(pNode->left), getHeight(pNode->right)) + 1; + left->height = max(getHeight(left->left), getHeight(left->right)) + 1; + return left; + } +``` + +**2.2 左旋:** + + + + +![img](https:////upload-images.jianshu.io/upload_images/4314397-f06bb1d69571eaf0.png?imageMogr2/auto-orient/strip|imageView2/2/w/944/format/webp) + +左旋 + + + +```swift + TreeNode *L_Rotation(TreeNode *pNode) { + TreeNode *right = pNode->right; + TreeNode *left = right->left; + right->left = pNode; + pNode->right = left; + // 重新调整高度 + pNode->height = max(getHeight(pNode->left), getHeight(pNode->right)) + 1; + right->height = max(getHeight(right->left), getHeight(right->right)) + 1; + return right; + } +``` + +**2.3 先右旋再左旋:** + + + + +![img](https:////upload-images.jianshu.io/upload_images/4314397-d86683fad715ce89.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp) + +先右旋再左旋 + + + +```php + TreeNode *R_L_Rotation(TreeNode *pNode) { + pNode->right = R_Rotation(pNode->right); + return L_Rotation(pNode); + } +``` + +**2.4 先左旋再右旋:** + + + + +![img](https:////upload-images.jianshu.io/upload_images/4314397-fc689643c778438f.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp) + +先左旋再右旋 + + + +```php + TreeNode *L_R_Rotation(TreeNode *pNode) { + pNode->left = L_Rotation(pNode->left); + return R_Rotation(pNode); + } +``` + +###### **3.红黑树** + +红黑树用法就比较广了,比如 JDK 1.8 的 HashMap,TreeMap,C++ 中的 map 和 multimap 等等。红黑树学习起来还是有一点难度的,这时如果我们心中有 B 树就有助于理解它,如果没有 B 树也没有关系。 + +红黑树的特性: + (1)每个节点或者是黑色,或者是红色。 + (2)根节点是黑色。 + (3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!] + (4)如果一个节点是红色的,则它的子节点必须是黑色的。 + (5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。 + + + + + +![img](https:////upload-images.jianshu.io/upload_images/4314397-a0bff7addb21cfe8.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/268/format/webp) + +红黑树 + +假设我们现在要插入一个新的节点,如过插入的这个新的节点为黑色,那么必然会违反性质(5),所以我们把新插入的点定义为红色的。但是如果插入的新节点为红色,就可能会违反性质(4) ,因此我们需要对其进行调整,使得整棵树依然满足红黑树的性质,也就是双红修正。接下来我们只要分情况分析就可以了: + +1. 如果没有出现双红现象,父亲是黑色的不需要修正; +2. 叔叔是红色的 ,将叔叔和父亲染黑,然后爷爷染红; +3. 叔叔是黑色的,父亲是爷爷的左节点,且当前节点是其父节点的右孩子,将“父节点”作为“新的当前节点”,以“新的当前节点”为支点进行左旋。然后将“父节点”设为“黑色”,将“祖父节点”设为“红色”,以“祖父节点”为支点进行右旋; +4. 叔叔是黑色的,父亲是爷爷的左节点,且当前节点是其父节点的左孩子,将“父节点”设为“黑色”,将“祖父节点”设为“红色”,以“祖父节点”为支点进行右旋; +5. 叔叔是黑色的,父亲是爷爷的右节点,且当前节点是其父节点的左孩子,将“父节点”作为“新的当前节点”,以“新的当前节点”为支点进行右旋。然后将“父节点”设为“黑色”,将“祖父节点”设为“红色”,以“祖父节点”为支点进行左旋; +6. 叔叔是黑色的,父亲是爷爷的右节点,且当前节点是其父节点的右孩子,将“父节点”设为“黑色”,将“祖父节点”设为“红色”,以“祖父节点”为支点进行左旋; + +上面的双红修正现象看似比较复杂,但实际上只有三种情况,一种是没有双红现象,另一种是父亲和叔叔都是红色的,最后一种是叔叔是黑色的。我们来画个实例看下: + + + + + +![img](https:////upload-images.jianshu.io/upload_images/4314397-fae62fda145e9e35.png?imageMogr2/auto-orient/strip|imageView2/2/w/957/format/webp) + + + +```php +void solveDoubleRed(TreeNode *pNode) { + while (pNode->parent && pNode->parent->color == red) {// 情况 1 + TreeNode *uncle = brother(parent(pNode)); + + if (getColor(uncle) == red) {// 情况2 + // 设置双亲和叔叔为黑色 + setColor(parent(pNode), black); + setColor(uncle, black); + // 指针回溯至爷爷 + pNode = parent(parent(pNode)); + } else { + // 父亲是爷爷的左儿子 + if (parent(parent(pNode))->left = parent(pNode)) { // 情况 3 和 4 + // 自己是父亲的右儿子 + if (parent(pNode)->right == pNode) { + pNode = parent(pNode); + L_Rotation(pNode); + } + // 把我自己这边的红色节点挪到隔壁树上,但仍然不能违反性质 4 和 5 + setColor(parent(pNode), black); + setColor(parent(parent(pNode)), red); + R_Rotation(parent(parent(pNode))); + } else { // 情况 5 和 6 + // 自己是父亲的左儿子 + if (parent(pNode)->left == pNode) { + pNode = parent(pNode); + R_Rotation(pNode); + } + // 把我自己这边的红色节点挪到隔壁树上,但仍然不能违反性质 4 和 5 + setColor(parent(pNode), black); + setColor(parent(parent(pNode)), red); + L_Rotation(parent(parent(pNode))); + } + } + } + + // 根结点为黑色 + root->color = black; + } +``` + +哎~好复杂这怎么记得住。如果要记住肯定不太可能而且费劲,接下来我们来分析下为什么要这么操作,还有没有更好的调整方法。我们所有的调整都是为了不违反性质4和性质5,假设我在左边的这个支树上新增了一个红色的节点,违反了性质4 。想法就是我把左支树上的一个红色节点,挪动右支树上去,这样就解决了我有两个连续红色节点的问题。但挪给右支树的过程中不能违反性质4和性质5,所以必须得考虑叔叔节点的颜色。 + + + +![img](https:////upload-images.jianshu.io/upload_images/4314397-1c1da607c0af9450.png?imageMogr2/auto-orient/strip|imageView2/2/w/1089/format/webp) + + + +最后我们来看下红黑树的删除操作,红黑树的删除操作要比新增操作要复杂些,但总体来说都是出现问题就去解决问题。当我们移除的是一个红色节点,那么根本就不会影响我们的性质4和性质5,我们不需要调整,但倘若我们移除的是一个黑色的节点,这时肯定会违反我们的性质5,所以我们只需要调整移除黑色节点的情况。分情况讨论下: + +1. 如果兄弟节点是红色的,把兄弟节点染黑,父节点染红,左/右旋父节点; + +2. 如果兄弟节点是黑色的,并且两个侄子节点都是黑色的,将兄弟节点染红,指针回溯至父亲节点; + +3. 如果兄弟节点是黑色,的并且远侄子是黑色的,近侄子是红色的,将进侄子染黑,兄弟染红,左/右旋兄弟节点,进入下面情况 4 ; + +4. 如果兄弟节点是黑色的,并且远侄子是红色的,近侄子随意,将兄弟节点染成父亲节点的颜色,父亲节点染黑,远侄子染黑,左/右旋父亲节点。 + + + + ![img](https:////upload-images.jianshu.io/upload_images/4314397-a46ca001a1a1a959.png?imageMogr2/auto-orient/strip|imageView2/2/w/1151/format/webp) + + + +```php +void solveLostBlack(TreeNode *pNode) { + while (pNode != root && getColor(pNode) == black) { + if (left(parent(pNode)) == pNode) { + TreeNode *sib = brother(pNode); + if (getColor(sib) == red) { + setColor(sib, black); + setColor(parent(pNode), red); + L_Rotation(parent(pNode)); + sib = brother(pNode); + } + + if (getColor(left(sib)) == black && getColor(right(sib)) == black) { + setColor(sib, red); + pNode = parent(pNode); + } else { + if (getColor(right(sib)) == black) { + setColor(left(sib), black); + setColor(sib, red); + R_Rotation(sib); + sib = brother(pNode); + } + + setColor(sib, getColor(parent(pNode))); + setColor(parent(pNode), black); + setColor(right(sib), black); + L_Rotation(parent(pNode)); + pNode = root; + } + } else { + TreeNode *sib = brother(pNode); + if (getColor(sib) == red) { + setColor(sib, black); + setColor(parent(pNode), red); + R_Rotation(parent(pNode)); + sib = brother(pNode); + } + + if (getColor(left(sib)) == black && getColor(right(sib)) == black) { + setColor(sib, red); + pNode = parent(pNode); + } else { + if (getColor(left(sib)) == black) { + setColor(right(sib), black); + setColor(sib, red); + L_Rotation(sib); + sib = brother(pNode); + } + + setColor(sib, getColor(parent(pNode))); + setColor(parent(pNode), black); + setColor(left(sib), black); + R_Rotation(parent(pNode)); + pNode = root; + } + } + } + pNode->color = black; + } +``` + +## 6、AVL树 + +平衡二叉搜索树 +在二叉搜索树中,已经知道search、insert和remove等主要接口的运行时间均正比于树的高度。但是在最坏的情况下,二叉搜索树可能退化成列表,此时查找的效率会降至O(n)。因此,通常通过控制树高,来控制最坏情况下的时间复杂度。 +对于节点数目固定的BST,越是平衡,最坏情况下的查找速度越快,如下图所示: + + +为了理解平衡二叉树,我们首先要理解几个主要概念,理想平衡与适度平衡以及如何进行等价变换让树更为平衡。 + +理想平衡与适度平衡 +节点数目固定时,兄弟子树高度越接近(平衡),全树的高度也将倾向于更低。包含n个节点的二叉树,高度不可能小于log2(n)(向上取整),当树的高度刚好为log2(n)时,称作理想二叉树。例如完全二叉树和满二叉树。 + +但是大多数情况下,树不能满足完全二叉树的条件,因此要放宽平衡的标准。在渐进意义下,放松标准后的平衡性称为适度平衡(渐进的不超过O(logn))。 我们称可以保持适度平衡的BST称为平衡二叉树(BBST),例如AVL树、红黑树等。 + +等价变换 +若两颗二叉搜索树的中序遍历序列是相同,则称他们是相互等价的,反之亦然。例如: + +总结出来,等价BST的特性即是: +上下可变:联系关系不尽相同,承袭关系可能颠倒。左右不乱:中序遍历完全一致。 + +旋转调整 +实际上任何一组等价BST的相互转换,都可以认为是一系列的基本操作串接而成的。最基本的修复手段就是通过围绕节点的旋转,实现等价前提下的的局部拓扑调整。 +1、zig:顺时针旋转 + +2、zag:逆时针旋转 + +平衡二叉搜索树的适度平衡性,都是通过对树中每一局部增加某种限制条件来保证的。例如:AVL树中,兄弟节点高度相差不过1。除了适度平衡性,还有以下局部性: +1、经过单次动态修改操作之后,至多只有O(log(n))处局部不在满足限制条件。 +2、在O(log(n))时间内,使这O(log(n))处局部(以至全树)重新满足限制条件。 +意味着,刚刚失去平衡的二叉搜索树,可以通过(一系列zig或zag,只在局部操作可以在常数时间内完成)变换,转换成一颗等价的平衡二叉搜索树。 + +AVL——BBST + +首先给出所用的一些宏定义 + + #define Balanced(x) ( stature( (x).lc ) == stature( (x).rc ) ) //理想平衡条件 + #define BalFac(x) ( stature( (x).lc ) - stature( (x).rc ) ) //平衡因子 + #define AvlBalanced(x) ( ( -2 < BalFac(x) ) && ( BalFac(x) < 2 ) ) //AVL平衡条件 + #define stature(p)((p)?(p)->height:-1) + + /****************************************************************************************** + * 在左、右孩子中取更高者 + * 在AVL平衡调整前,借此确定重构方案 + ******************************************************************************************/ + #define tallerChild(x) ( \ + stature( (x)->lc ) > stature( (x)->rc ) ? (x)->lc : ( /*左高*/ \ + stature( (x)->lc ) < stature( (x)->rc ) ? (x)->rc : ( /*右高*/ \ + IsLChild( * (x) ) ? (x)->lc : (x)->rc /*等高:与父亲x同侧者(zIg-zIg或zAg-zAg)优先*/ \ + ) \ + ) \ + ) +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +定义及性质 +AVL树,即平衡因子受限的二叉搜索树_各节点的平衡因子的绝对值均不超过1。 +平衡因子:balFac(v)=height(lc(v))-height(rc(v)); +接口定义如下(其中BST的定义参考二叉搜索树): + #include "BST/BST.h" //基于BST实现AVL树 + template class AVL : public BST { //由BST派生AVL树模板类 + public: + BinNodePosi(T) insert ( const T& e ); //插入(重写) + bool remove ( const T& e ); //删除(重写) + // BST::search()等其余接口可直接沿用 + }; +1 +2 +3 +4 +5 +6 +7 +AVL树的平衡性 +收先要证明高度为h的AVL树至少包含fib(h+3)-1个节点。 +证明: +假设高度为h的AVL树,至少应该包含s(h)个节点。情况如下图所示: + +则,s(h)满足以下递推式: +s(h)=s(h-1)+1+s(h-2) +=>s(h)+1=s(h-1)+1+s(h-2)+1 +=>T(h)=T(h-1)+T(h-2)=fib(h+3) +=>s(h)=fib(h+3)-1 +综上,高度为h的AVL树,至少包含fib(h+3)-1个节点。于是反过来包含n个节点的AVL树高度应为O(log(n))。 + +AVL树的失衡与重平衡 +AVL树经过插入、删除等动态修改操作,节点的高度可能发生变化,使得其不在满足AVL树的平衡条件。例如: + +AVL插入操作 +1、插入单旋 +v是p的右孩子,且p是g的右孩子,朝向一致。 + +上图灰色部分表示插入节点(二选一),插入操作之后,同时可有多个失衡节点,但是最低者g不低于x的祖父。g经过单旋调整之后复衡,子树高度复原,更高的祖先也必然平衡,如下图所示(此处为zag旋转,还有对称情况): + +2、插入双旋 +v是p的左孩子,p是g的右孩子,朝向不一致。 + +与插入情况类似,同时可有多个失衡节点,但是最低者g不低于x的祖父。g经过双旋调整之后复衡,子树高度复原,更高的祖先也必然平衡, +经过一次zig旋转和zag旋转之后结果如下(同样还有对称情况): + +接口实现: + + template BinNodePosi(T) AVL::insert ( const T& e ) { //将关键码e插入AVL树中 + BinNodePosi(T) & x = search ( e ); if ( x ) return x; //确认目标节点不存在 + BinNodePosi(T) xx = x = new BinNode ( e, _hot ); _size++; //创建新节点x + // 此时,x的父亲_hot若增高,则其祖父有可能失衡 + for ( BinNodePosi(T) g = _hot; g; g = g->parent ) { //从x之父出发向上,逐层检查各代祖先g + if ( !AvlBalanced ( *g ) ) { //一旦发现g失衡,则(采用“3 + 4”算法)使之复衡,并将子树 + FromParentTo ( *g ) = rotateAt ( tallerChild ( tallerChild ( g ) ) ); //重新接入原树 + break; //g复衡后,局部子树高度必然复原;其祖先亦必如此,故调整随即结束 + } else //否则(g依然平衡),只需简单地 + updateHeight ( g ); //更新其高度(注意:即便g未失衡,高度亦可能增加) + } //至多只需一次调整;若果真做过调整,则全树高度必然复原 + return xx; //返回新节点位置 + } //无论e是否存在于原树中,总有AVL::insert(e)->data == e +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +效率:首先按照二叉搜索树的常规算法,在O(log(n))时间内插入新的节点x。由于原树是平衡的,最多检查O(log(n))就可以确定失衡节点的位置,为了恢复平衡,最多两次旋转(常数时间)就可以恢复。因此,AVL树的插入操作可以在O(log(n))时间内完成。 + +AVL树删除操作。 +与插入操作不同,在摘除节点之后,以及在之后的调整过程中,失衡的节点集始终至多含有一个节点。而且若该结点存在,则其高度必然与失衡前相同。如下图所示: + +失衡的节点g与之前的高度相同。 +重平衡操作: +与插入操作同理,从_hot节点(删除节点的父节点)出发沿parent指针上行,经过O(log(n))时间就可以确定失衡节点g的位置。作为失衡节点g,在不包含被删除节点x的一侧,必然有一个非空孩子p,且p的高度至少为1。于是根据以下规则从p的两个孩子中选出节点v:若连个孩子不等高,v选择其中的更高者;否则选取v与p的同向者(v与p同为左孩子,或者同为右孩子)。 +1、单旋 +经过单旋调整之后,子树高度未必复原,更高祖先仍可能失衡(例如下图,T2下面的节点不存在时的情况)。 +为了让上图所示的二叉搜索树恢复平衡,我们只需要经过一次zig旋转(对称的情况类似),如下图(此时p的两个孩子同高,p选择同向者)结果: + +2、双旋 + +3、失衡传播 +与插入操作不同,在删除节点之后,尽管可以通过单旋或者双旋调整使局部子树复衡,但是局部子树的高就全局而言,亦然可能再次失衡,例如下图: + +将这种由于底层失衡节点的重平衡而致使更高祖先失衡的情况,称为失衡传播。失衡传播的方向必然自底而上,在此过程的任一时刻,至多只有一个失衡节点;高层的某一个节点由失衡转为平衡,只能发生下层节点复衡之后。因此,最多需要O(log(n))次调整即可恢复。 +算法实现: + + + template bool AVL::remove ( const T& e ) { //从AVL树中删除关键码e + BinNodePosi(T) & x = search ( e ); if ( !x ) return false; //确认目标存在(留意_hot的设置) + removeAt ( x, _hot ); _size--; //先按BST规则删除之(此后,原节点之父_hot及其祖先均可能失衡) + for ( BinNodePosi(T) g = _hot; g; g = g->parent ) { //从_hot出发向上,逐层检查各代祖先g + if ( !AvlBalanced ( *g ) ) //一旦发现g失衡,则(采用“3 + 4”算法)使之复衡,并将该子树联至 + g = FromParentTo ( *g ) = rotateAt ( tallerChild ( tallerChild ( g ) ) ); //原父亲 + updateHeight ( g ); //并更新其高度(注意:即便g未失衡,高度亦可能降低) + } //可能需做Omega(logn)次调整——无论是否做过调整,全树高度均可能降低 + return true; //删除成功 + } //若目标节点存在且被删除,返回true;否则返回false +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +3+4重构算法及其实现 +对于上面所说的重平衡操作,如果采用旋转一步一步的完成将会显得很复杂,其实就如同拼魔方一样,我们可以将魔方打散然后合起来就可以而不是旋转。 + +假设g为最低的失衡节点,考察祖孙三代g~p~v,按照中序遍历次序,将其重命名为a、b、c。并且他们拥有互不相交的四颗(可能为空的)子树,按中序遍历次序命名为:T0、T1、T2、T3。合起来的中序遍历为{T0,a,T1,b,T2,c,T3}。我们最终得到的结果(正如魔方的六面相同一样)都如下图所示: + + +实现如下: + +/******************* +*按照“3+4”结构联结三个节点及其四颗子树,返回重组之后的局部子树根节点的位置 +*子树根节点与上层节点之间的双向联结,均有上层调用者完成 +**********************/ +templateBinNodePosi(T) BST::connect34( +BinNodePosi(T) a,BinNodePosi(T) b,BinNodePosi(T) c, +BinNodePosi(T) T0,BinNodePosi(T) T1,BinNodePosi(T) T2,BinNodePosi(T) T3) +{ + a->lc=T0; if(T0) T0->parent=a; + a->rc=T1; if(T1) T1->parent=a; updateheight(a); + c->lc=T2; if(T2) T2->parent=c; + c->rc=T3; if(T3) T3->parent=c; updateheight(c); + b->lc=a; a->parent=b; + b->rc=c; c->parent=b; updateheight(b); + return b;//子树新的根节点 +} +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +利用上述connect34()算法,可以按照不同的情况,完成重平衡算法: + + + /****************************************************************************************** + * BST节点旋转变换统一算法(3节点 + 4子树),返回调整之后局部子树根节点的位置 + * 注意:尽管子树根会正确指向上层节点(如果存在),但反向的联接须由上层函数完成 + ******************************************************************************************/ + template BinNodePosi(T) BST::rotateAt ( BinNodePosi(T) v ) { //v为非空孙辈节点 + BinNodePosi(T) p = v->parent; BinNodePosi(T) g = p->parent; //视v、p和g相对位置分四种情况 + if ( IsLChild ( *p ) ) /* zig */ + if ( IsLChild ( *v ) ) { /* zig-zig */ + p->parent = g->parent; //向上联接 + return connect34 ( v, p, g, v->lc, v->rc, p->rc, g->rc ); + } else { /* zig-zag */ + v->parent = g->parent; //向上联接 + return connect34 ( p, v, g, p->lc, v->lc, v->rc, g->rc ); + } + else /* zag */ + if ( IsRChild ( *v ) ) { /* zag-zag */ + p->parent = g->parent; //向上联接 + return connect34 ( g, p, v, g->lc, p->lc, v->lc, v->rc ); + } else { /* zag-zig */ + v->parent = g->parent; //向上联接 + return connect34 ( g, v, p, g->lc, v->lc, v->rc, p->rc ); + } + } +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +分析:为了调用connect34(),主要是要确定对应的三个节点四个子树,主要根据中序遍历序列确定。例如上面对应的情况(这里只列举zig-zig和zig-zag,其余情况类似): + + + + +总结 +优点:无论查找、插入或删除,最坏的情况下的复杂度均为O(log(n)),O(n)的存储空间。 +缺点: +1、借助高度或平衡因子,为此需要改造元素结构,或额外的封装(可以通过伸展树splay解决) +2、单词动态调整后,全树的拓扑结构的变化量可能高达log(n)。插入与删除操作的变化量不对等,inert操作只许常数即可满足,remove操作则可能高达log(n)。(红黑树可以使得插入与删除操作的变化量都在常数内,即只需常数的时间就可恢复平衡)。 +———————————————— +原文链接:https://blog.csdn.net/xc13212777631/article/details/80760427 \ No newline at end of file diff --git a/Week 06/id_433/LeetCode_212_433.py b/Week 06/id_433/LeetCode_212_433.py new file mode 100644 index 000000000..7bb508df8 --- /dev/null +++ b/Week 06/id_433/LeetCode_212_433.py @@ -0,0 +1,69 @@ +from typing import List +from collections import defaultdict +from collections import deque + + +class Trie: + + def __init__(self): + self.root = {} + self.end = '#' + + def insert(self, word): + curr = self.root + for c in word: + curr = curr.setdefault(c, {}) + curr[self.end] = True + + def search(self, word): + curr = self.root + for c in word: + curr = curr.get(c) + if not curr: + return False + return curr.get(self.end) + + def startsWith(self, word): + curr = self.root + for c in word: + curr = curr.get(c) + if not curr: + return False + return True + + +class Solution: + def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: + trie = Trie() + for word in words: + trie.insert(word) + + rows = len(board) + cols = len(board[0]) + res = set() + + def dfs(x, y, curr, node): + if x < 0 or x >= cols or y < 0 or y >= rows \ + or board[y][x] == '@' \ + or board[y][x] not in node: + return + + char = board[y][x] + curr += char + node = node[char] + + if trie.end in node: + res.add(curr) + + board[y][x] = '@' + dfs(x+1, y, curr, node) + dfs(x, y+1, curr, node) + dfs(x-1, y, curr, node) + dfs(x, y-1, curr, node) + board[y][x] = char + + for y in range(rows): + for x in range(cols): + dfs(x, y, "", trie.root) + + return list(res) \ No newline at end of file diff --git a/Week 06/id_433/LeetCode_547_433.py b/Week 06/id_433/LeetCode_547_433.py new file mode 100644 index 000000000..d0d5ce0a4 --- /dev/null +++ b/Week 06/id_433/LeetCode_547_433.py @@ -0,0 +1,20 @@ +class Solution: + def findCircleNum(self, M: List[List[int]]) -> int: + n = len(M) + visited = [0]*n + count = 0 + + queue = [] + for i in range(n): + if visited[i] == 0: + count += 1 + queue.append(i) + + while queue: + curr = queue.pop(0) + for j in range(n): + if M[curr][j] == 1 and visited[j] == 0: + visited[curr] = 1 + queue.append(j) + + return count \ No newline at end of file diff --git a/Week 06/id_443/LeetCode_208_443.java b/Week 06/id_443/LeetCode_208_443.java new file mode 100644 index 000000000..49b7caa3f --- /dev/null +++ b/Week 06/id_443/LeetCode_208_443.java @@ -0,0 +1,128 @@ +//实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。 +// +// 示例: +// +// Trie trie = new Trie(); +// +//trie.insert("apple"); +//trie.search("apple"); // 返回 true +//trie.search("app"); // 返回 false +//trie.startsWith("app"); // 返回 true +//trie.insert("app"); +//trie.search("app"); // 返回 true +// +// 说明: +// +// +// 你可以假设所有的输入都是由小写字母 a-z 构成的。 +// 保证所有输入均为非空字符串。 +// +// Related Topics 设计 字典树 + +package com.modds.alltest.leetcode.editor.cn; + +public class LeetCode_208_443_ImplementTriePrefixTree { + public static void main(String[] args) { +// Solution solution = new LeetCode_208_443_ImplementTriePrefixTree().new Solution(); + } + + + //leetcode submit region begin(Prohibit modification and deletion) + class Trie { + private TrieNode root; + + /** + * Initialize your data structure here. + */ + public Trie() { + root = new TrieNode(); + } + + /** + * Inserts a word into the trie. + */ + public void insert(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char c = word.charAt(i); + if (!node.containsKey(c)) { + node.put(c, new TrieNode()); + } + node = node.get(c); + } + node.setEnd(); + } + + private TrieNode searchPrefix(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char c = word.charAt(i); + if (node.containsKey(c)) { + node = node.get(c); + } else { + return null; + } + } + return node; + } + + /** + * Returns if the word is in the trie. + */ + public boolean search(String word) { + TrieNode node = searchPrefix(word); + return node != null && node.isEnd; + } + + /** + * Returns if there is any word in the trie that starts with the given prefix. + */ + public boolean startsWith(String prefix) { + TrieNode node = searchPrefix(prefix); + return node != null; + + } + } + + class TrieNode { + private final int R = 26; + private TrieNode[] links; + private boolean isEnd; + + public TrieNode() { + links = new TrieNode[R]; + } + + public boolean containsKey(char ch) { + return links[ch - 'a'] != null; + } + + public TrieNode get(char ch) { + return links[ch - 'a']; + } + + public void put(char ch, TrieNode trieNode) { + links[ch - 'a'] = trieNode; + } + + public void setEnd() { + isEnd = true; + } + + public boolean isEnd() { + return isEnd; + } + + } + + +/** + * Your Trie object will be instantiated and called as such: + * Trie obj = new Trie(); + * obj.insert(word); + * boolean param_2 = obj.search(word); + * boolean param_3 = obj.startsWith(prefix); + */ +//leetcode submit region end(Prohibit modification and deletion) + +} \ No newline at end of file diff --git a/Week 06/id_443/LeetCode_547_443.java b/Week 06/id_443/LeetCode_547_443.java new file mode 100644 index 000000000..829834755 --- /dev/null +++ b/Week 06/id_443/LeetCode_547_443.java @@ -0,0 +1,80 @@ +//班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。 +// +// 给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。 +// +// 示例 1: +// +// +//输入: +//[[1,1,0], +// [1,1,0], +// [0,0,1]] +//输出: 2 +//说明:已知学生0和学生1互为朋友,他们在一个朋友圈。 +//第2个学生自己在一个朋友圈。所以返回2。 +// +// +// 示例 2: +// +// +//输入: +//[[1,1,0], +// [1,1,1], +// [0,1,1]] +//输出: 1 +//说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。 +// +// +// 注意: +// +// +// N 在[1,200]的范围内。 +// 对于所有学生,有M[i][i] = 1。 +// 如果有M[i][j] = 1,则有M[j][i] = 1。 +// +// Related Topics 深度优先搜索 并查集 + +package com.modds.alltest.leetcode.editor.cn; + +import java.util.LinkedList; +import java.util.Queue; + +public class LeetCode_547_443_FriendCircles { + public static void main(String[] args) { + Solution solution = new LeetCode_547_443_FriendCircles().new Solution(); + } + + + //leetcode submit region begin(Prohibit modification and deletion) + class Solution { + public int findCircleNum(int[][] M) { + if (M == null || M.length == 0) { + return 0; + } + + int l = M.length; + int count = 0; + int[] visited = new int[l]; + Queue queue = new LinkedList<>(); + + for (int i = 0; i < l; i++) { + if (visited[i] == 0) { + queue.add(i); + while (!queue.isEmpty()) { + int s = queue.remove(); + visited[s] = 1; + for (int j = 0; j < l; j++) { + if (M[s][j] == 1 && visited[j] == 0) { + queue.add(j); + } + } + } + count++; + } + } + return count; + } + + } +//leetcode submit region end(Prohibit modification and deletion) +} \ No newline at end of file diff --git a/Week 06/id_468/leetcode127_6_468.java b/Week 06/id_468/leetcode127_6_468.java new file mode 100644 index 000000000..7b8c9cb3e --- /dev/null +++ b/Week 06/id_468/leetcode127_6_468.java @@ -0,0 +1,99 @@ +package week6; + +import java.util.*; + +/** + * @program: leetcode + * @description: 单词接龙 + * @author: 王瑞全 + * @create: 2019-11-2423:28 + **/ + + +public class leetcode127_6_468 { + //单项bfs + public int ladderLength(String beginWord, String endWord, List wordList) { + if(!wordList.contains(endWord)) return 0; + + Queue beginWords = new LinkedList(); + HashSet dictionary = new HashSet(); + beginWords.offer(beginWord); + + for(String word : wordList){ + dictionary.add(word); + } + + int length = 0; + boolean flag = false; + while(!beginWords.isEmpty() ){ + length++; + int size = beginWords.size(); + for(int index = 0; index < size; index++){ + String curBegin = beginWords.poll(); + if(curBegin.equals(endWord)) { + return length; + } + char[] curword = curBegin.toCharArray(); + for(int i = 0; i wordList) { + if(!wordList.contains(endWord)) return 0; + Set begin = new HashSet(); + Set end = new HashSet(); + begin.add(beginWord); + end.add(endWord); + Set dic = new HashSet(); + for(String str : wordList) dic.add(str); + + int length = 1; + while(!begin.isEmpty() && !end.isEmpty()){ + length++; + if(begin.size() > end.size()){ + Set temp = begin; + begin = end; + end = temp; + } + Set tempset = new HashSet(); + + for(String bstr: begin){ + char[] bchars = bstr.toCharArray(); + for(int i = 0; i=n||j>=m||grid[i][j]!=1) return ; + grid[i][j]=0; + DFS(grid,i+1,j); + DFS(grid,i-1,j); + DFS(grid,i,j+1); + DFS(grid,i,j-1); + } +} diff --git a/Week 06/id_468/leetcode208_6_468.java b/Week 06/id_468/leetcode208_6_468.java new file mode 100644 index 000000000..cc7753a69 --- /dev/null +++ b/Week 06/id_468/leetcode208_6_468.java @@ -0,0 +1,62 @@ +package week6; + +/** + * @program: leetcode + * @description: Implement a trie with insert, search, and startsWith methods. + * @author: 王瑞全 + * @create: 2019-11-2317:00 + **/ + + +public class leetcode208_6_468 { + public class Trie { + class TrieNode { + public TrieNode[] children; + public boolean isWord; + + public TrieNode() { + this.children = new TrieNode[26]; + this.isWord = false; + } + } + + private TrieNode root; + + //初始化你的数据结构 + public Trie(){ + root=new TrieNode(); + } + //插入一个节点到字典树中 + public void insert(String word){ + TrieNode p=root; + for(int i=0;i findWords(char[][] board, String[] words) { + List res=new ArrayList<>(); + if(words==null||words.length==0){ + return res; + } + Trie trie=new Trie(words); + this.row=board.length; + this.col=board[0].length; + for(int i=0;i res,int x,int y,char[][] board,TrieNode root){ + if(!root.children.containsKey(board[x][y])){ + return ;//没有在矩阵中找到前缀 + } + TrieNode child=root.children.get(board[x][y]); + if(child.word!=null){ + res.add(child.word); + child.word=null;//置空避免重复 + } + + + //回溯 + char temp=board[x][y]; + board[x][y]='0'; + for(int k=0;k<4;k++){ + int nextX=x+directionX[k]; + int nextY=y+directionY[k]; + if(nextX>=0 && nextX=0 && nextY children; + //这里不需要isWord,如果word!=null 那么这个节点就是一个单词 + + public TrieNode(){ + this.word=null; + this.children=new HashMap<>(); + } + } + class Trie{ + private TrieNode root; + public Trie(String[] words){ + this.root=new TrieNode(); + for(String word:words){ + insert(word); + } + } + private void insert(String word){ + TrieNode cur=root; + Map curChildren=cur.children; + for(int i=0;i S[gj]) { + G[gj] = gi; + S[gi] += S[gj]; + } + else { + G[gi] = gj; + S[gj] += S[gi]; + } + return true; + } + + private int find(int[] G, int i) { + int j = i; + while (G[j] != j) j = G[j]; + G[i] = j; + return j; + } + + // graph - dfs + private int graph(int[][] M) { + int n = M.length, res = 0; + boolean[] visisted = new boolean[n]; + for (int i = 0; i < n; i++) { + if (!visisted[i]) { + dfs(M, visisted, i); + res++; + } + } + return res; + } + + private void dfs(int[][] M, boolean[] visisted, int i) { + visisted[i] = true; + for (int j = 0; j < M.length; j++) { + if (i != j && M[i][j] != 0 && !visisted[j]) { + M[i][j] = M[j][i] = 1; + dfs(M, visisted, j); + } + } + } +} + diff --git a/Week 06/id_468/leetcode70_6_468.java b/Week 06/id_468/leetcode70_6_468.java new file mode 100644 index 000000000..8f8245540 --- /dev/null +++ b/Week 06/id_468/leetcode70_6_468.java @@ -0,0 +1,33 @@ +package week6; + +/** + * @program: leetcode + * @description: 爬楼梯 + * @author: 王瑞全 + * @create: 2019-11-2423:23 + **/ + + +public class leetcode70_6_468 { + public int climbStairs(int n) { + if(n==1||n==0){ + return 1; + } + int total=climbStairs(n-1)+climbStairs(n-2); + return total; + } + public int climbStairs1(int n) { + if(n==1||n==0){ + return n; + } + int count1=1; + int count2=1; + for(int i=2;i<=n;i++){ + int temp=count2; + count2=count2+count1; + count1=temp; + } + return count2; + + } +} diff --git "a/Week 06/id_493/200.\345\262\233\345\261\277\346\225\260\351\207\217.js" "b/Week 06/id_493/200.\345\262\233\345\261\277\346\225\260\351\207\217.js" new file mode 100644 index 000000000..9910fe65c --- /dev/null +++ "b/Week 06/id_493/200.\345\262\233\345\261\277\346\225\260\351\207\217.js" @@ -0,0 +1,75 @@ +/* + * @lc app=leetcode.cn id=200 lang=javascript + * + * [200] 岛屿数量 + */ + +// @lc code=start +/** + * @param {character[][]} grid + * @return {number} + */ +var numIslands = function(grid) { + class UnionFind { + constructor(testGrid) { + this.count = 0; + this.parent = []; + let wlen = testGrid.length; + let hlen = testGrid[0].length; + for (let i = 0; i < wlen; i++) { + for (let j = 0; j < hlen; j++) { + if (testGrid[i][j] == 1) { + this.parent[i * hlen + j] = i * hlen + j; + this.count = this.count + 1; + } else { + this.parent[i * hlen + j] = -1; + } + } + } + } + find(n) { + while (this.parent[n] != n && this.parent[n] != -1) { + n = this.parent[n]; + } + return this.parent[n]; + } + union(p, q) { + let pRoot = this.find(p); + let qRoot = this.find(q); + if (pRoot == -1 || qRoot == -1 || pRoot == qRoot) { + return; + } + this.parent[qRoot] = pRoot; + this.count = this.count - 1; + } + } + + if (grid.length == 0) { + return 0; + } + + let uf = new UnionFind(grid); + let wlen = grid.length; + let hlen = grid[0].length; + for (let i = 0; i < wlen; i++) { + for (let j = 0; j < hlen; j++) { + if (grid[i][j] == 1) { + if (i > 0 && grid[i - 1][j] == 1) { + uf.union((i - 1) * hlen + j, i * hlen + j); + } + if (i < wlen - 1 && grid[i + 1][j] == 1) { + uf.union((i + 1) * hlen + j, i * hlen + j); + } + if (j > 0 && grid[i][j - 1] == 1) { + uf.union(i * hlen + j - 1, i * hlen + j); + } + if (j < hlen - 1 && grid[i][j + 1] == 1) { + uf.union(i * hlen + j + 1, i * hlen + j); + } + } + } + } + return uf.count; +}; +// @lc code=end + diff --git "a/Week 06/id_493/51.n\347\232\207\345\220\216.js" "b/Week 06/id_493/51.n\347\232\207\345\220\216.js" new file mode 100644 index 000000000..9cff03ff3 --- /dev/null +++ "b/Week 06/id_493/51.n\347\232\207\345\220\216.js" @@ -0,0 +1,66 @@ +/* + * @lc app=leetcode.cn id=51 lang=javascript + * + * [51] N皇后 + */ + +// @lc code=start +/** + * @param {number} n + * @return {string[][]} + */ +var solveNQueens = function(n) { + // 皇后的数量 + let queenNum = n; + // 存放皇后和对应列的关系 + let placeMap = new Map(); + // 存放一共有多少种放法 + let count = 0; + let result = []; + + // 放第curRow+1个皇后到curRow行 + function placeQueen(curRow) { + // curRow大于等于queenNum,说明所有皇后都放完了,是一种放法 + if (curRow >= queenNum) { + count++; + let curResult = []; + for (let row = 0; row < queenNum; row++) { + let occupyCol = placeMap.get(row); + curResult.push('.'.repeat(occupyCol) + 'Q' + '.'.repeat(queenNum - occupyCol - 1)); + } + result.push(curResult); + return; + } + // 把第n个皇后试图放到每一列上 + for (let col = 0; col < queenNum; col++) { + // 检查是否可以放到第n列上 + if (checkPosition(curRow, col)) { + // 可以放下的话,就存储皇后对应的列号,再接着放下一个皇后 + placeMap.set(curRow, col); + placeQueen(curRow + 1); + } + } + } + function checkPosition(curRow, curCol) { + for (let i = 0; i < curRow; i++) { + // 检查n行,m列上,是否可以放皇后 + let occupyCol = placeMap.get(i); + let row = i; + /*** + * 1、在同一个行上。所有皇后都在不同的行上,无需判断这种情况 + * 2、在同一个列上。curCol == occupyCol + * 3、在对角线上。判断条件是 列号减行号相同;occupyCol - i == curCol - curRow + * 4、在斜对角线上。判断条件是 列号加行号相同;occupyCol + i == curCol + curRow + ***/ + if (curCol == occupyCol || (occupyCol - row == curCol - curRow) || (occupyCol + row == curCol + curRow)) { + return false; + } + } + return true; + } + // 第一个皇后放第一行 + placeQueen(0); + return result; +}; +// @lc code=end + diff --git a/Week 06/id_498/LeetCode_208_498.py b/Week 06/id_498/LeetCode_208_498.py new file mode 100644 index 000000000..69d0595e6 --- /dev/null +++ b/Week 06/id_498/LeetCode_208_498.py @@ -0,0 +1,59 @@ +class Trie: + """ + 插入,搜索,前缀 + """ + + def __init__(self): + """ + 在这里初始化数据结构。 + Initialize your data structure here. + """ + # 根 + self.root = {} + self.end_of_word = '#' + + def insert(self, word: str) -> None: + """ + Inserts a word into the trie. + """ + # 实例化 + node = self.root + # 把每个新字母都对应一个新字典 + for char in word: + node = node.setdefault(char, {}) + # 最后加入 # + node[self.end_of_word] = self.end_of_word + + def search(self, word: str) -> bool: + """ + Returns if the word is in the trie. + """ + node = self.root + for char in word: + # 如果字符在节点里 + if char not in node: + return False + # 下一层 + node = node[char] + # 返回是否到末尾 + return self.end_of_word in node + + def startsWith(self, prefix: str) -> bool: + """ + Returns if there is any word in the trie that starts with the given prefix. + """ + node = self.root + for char in prefix: + # 如果字符在节点里 + if char not in node: + return False + # 下一层 + node = node[char] + # 没有末尾符号 + return True + +# Your Trie object will be instantiated and called as such: +# obj = Trie() +# obj.insert(word) +# param_2 = obj.search(word) +# param_3 = obj.startsWith(prefix) diff --git a/Week 06/id_498/LeetCode_212_498.py b/Week 06/id_498/LeetCode_212_498.py new file mode 100644 index 000000000..7cd464c51 --- /dev/null +++ b/Week 06/id_498/LeetCode_212_498.py @@ -0,0 +1,64 @@ +from typing import List +from collections import defaultdict + + +class Solution: + def __init__(self): + # 结果集 + self.result = set() + self.end_of_word = '#' + self.grid = [[0, 1], [-1, 0], [0, -1], [1, 0]] + + def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: + # 特判 + if not board or not board[0] or not words or not words[0]: + return [] + + # 构建trie + root = defaultdict() + for word in words: + # 字典树的根 + node = root + for char in word: + # 新的根生成新的字典 + node = node.setdefault(char, defaultdict()) + # 结束标志 + node[self.end_of_word] = self.end_of_word + + # board 代表 + board_xs, board_ys = len(board), len(board[0]) + for board_x in range(board_xs): + for board_y in range(board_ys): + # 如果board的元素在根里 + if board[board_x][board_y] in root: + # 就进行深度优先搜索, + self.DFS(board, board_xs, board_ys, board_x, board_y, '', root[board[board_x][board_y]]) + + return list(self.result) + + def DFS(self, board_p, board_xs_p, board_ys_p, board_x_p, board_y_p, current_word, current_dict): + # 每个字符拼成的单词 + current_word += board_p[board_x_p][board_y_p] + # 如果结尾符在当前字典中 + if self.end_of_word in current_dict: + # 把当前词放入结果集 + self.result.add(current_word) + # 为了不让重复使用,把当前字符换掉。 + tmp, board_p[board_x_p][board_y_p] = board_p[board_x_p][board_y_p], '!' + # 因为是四个方向 + for d in self.grid: + # 前后左右的移动 + move_x, move_y = board_x_p + d[0], board_y_p + d[1] + # 不能越界,不能是非字母,并且要在当前字典里 + if 0 <= move_x < board_xs_p and 0 <= move_y < board_ys_p and board_p[move_x][move_y] != '!' \ + and board_p[move_x][move_y] in current_dict: + self.DFS(board_p, board_xs_p, board_ys_p, move_x, move_y, current_word, + current_dict[board_p[move_x][move_y]]) + # 恢复 + board_p[board_x_p][board_y_p] = tmp + + +b = [['o', 'a', 'a', 'n'], ['e', 't', 'a', 'e'], ['i', 'h', 'k', 'r'], ['i', 'f', 'l', 'v']] +ws = ["oath", "pea", "eat", "rain"] +s = Solution() +print(s.findWords(b, ws)) diff --git a/Week 06/id_503/NOTE.md b/Week 06/id_503/NOTE.md index a6321d6e2..617d8a6fc 100644 --- a/Week 06/id_503/NOTE.md +++ b/Week 06/id_503/NOTE.md @@ -1,4 +1,165 @@ -# NOTE +# 字典数(Trie) +* 又称**单词查找树**或**键树**,是一种树形结构 +* 典型应用是用于**统计**和**排序大量的字符串**(但不仅限于字符串) +* 经常被搜索引擎系统用于文本**词频统计** +* 优点:最大限度地**减少无谓**的字符串比较,查询效率比哈希表高 +* 基本性质 + * 结点本身不存完整单词 + * 从根结点到某一结点,路径上经过的字符连接起来,为该结点对应的字符串 + * 每个结点的所有子结点路径代表的字符都不相同 +* 核心思想 + * 空间换时间 + * 利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目 +* 代码模板 + ```javascript + /** + * Initialize your data structure here. + */ + class Trie { + constructor() { + this.root = new TrieNode(); + } + /** + * Inserts a word into the trie. + * @param {string} word + * @return {void} + */ + insert(word) { + let node = this.root; - + for (let c of word) { + node = node.links[c] = node.links[c] || new TrieNode(); + } + node.isEnd = true; + } + /** + * Returns if the word is in the trie. + * @param {string} word + * @return {boolean} + */ + search(word) { + const node = this.searchNode(word); + + return !!(node && node.isEnd) + } + /** + * Returns if there is any word in the trie that starts with the given prefix. + * @param {string} prefix + * @return {boolean} + */ + startsWith(prefix) { + return !!this.searchNode(prefix); + } + + /** + * Returns a TrieNode object if there is any word search ends in the trie, or returns null. + * @param {string} word + * @returns {TrieNode} + */ + searchNode(word) { + if (!word) { + return null; + } + + let node = this.root; + for (const c of word) { + if (!(node = node.links[c])) { + return null; + } + } + + return node; + } + } + + /** + * Node of a trie + */ + class TrieNode { + + constructor() { + this.links = {}; + this.isEnd = false; + } + } + ``` +# 并查集 (Disjoint Set) +* 适用场景:组团、配对问题 +* 基本操作: + * makeSet(s):建立一个新的并查集,其中包含 s 个单元素集合 + * unionSet(x, y):把元素 x 和元素 y 所在的集合合并,要求 x 和 y 所在的集合不相交,如果相交则不合并 + * find(x):找到元素 x 所在的集合的代表,该操作也可以用于判断两个元素是否位于同一个集合,只要将它们各自的代表比较一下就可以了 +* 代码模板 + ```javascript + class DisjointSet { + count = 0; + parent = []; + + /** + * + * @param {number} n + */ + constructor(n) { + this.count = n; + this.parent = []; + for (let i = 0; i < n; i++) { + this.parent[i] = i; + } + } + + /** + * + * @param {number} p + * @returns {number} + */ + find(p) { + let parent = this.parent; + while (p !== parent[p]) { + p = parent[p] = parent[parent[p]]; + } + return p; + } + + /** + * + * @param {number} p + * @param {number} q + */ + union(p, q) { + let rootP = find(p); + let rootQ = find(q); + if (rootP === rootQ) { + return; + } + + this.parent[rootP] = rootQ; + count--; + } + } + ``` +# AVL 树 +* 一种平衡二叉搜索数 +* 发明者 G. M. **A**delson-**V**elsky 和 Evgenii **L**andis +* Balance Factor(平衡因子): 是它的左子树的高度减去它的右子树的高度(有时相反)。 balance factor = {-1, 0, 1} +* 通过旋转操作来进行平衡(四种) + * 左旋:子树形态:右右子树 —> 左旋 + * 右旋:子树形态:左左子树 —> 右旋 + * 左右旋:子树形态:左右子树 —> 左右旋 + * 右左旋:子树形态:右左子树 —> 右左旋 +* 不足:结点需要存储额外信息且调整次数频繁 + +# 红黑树(Red-Black Tree) +* 是一种**近似平衡**的二叉搜索树(Binary Search Tree),它能够确保任何一 个结点的左右子树的**高度差小于两倍**。具体来说,红黑树是满足如下条件的二叉搜索树: + * 每个结点要么是红色,要么是黑色 + * 根节点是黑色 + * 每个叶节点(NIL节点,空节点)是黑色的 + * 不能有相邻接的两个红色节点 + * 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点 +* 关键性质:从根到叶子的最长的可能路径**不多于**最短的可能路径的**两倍**长 + +# AVL vs 红黑树 +* **AVL trees** provide **faster lookups **than Red Black Trees **because** they are more **strictly balanced**. +* **Red Black Trees** provide **faster insertion and removal** operations than AVL trees as fewer rotations are done due to relatively relaxed balancing. +* **AVL trees** store **balance factors or heights** with each node, thus requires storage for an integer per node whereas Red Black Tree requires only 1 bit of information per node. +* **Red Black Trees** are used in most of the language **libraries** \ No newline at end of file diff --git a/Week 06/id_503/leetcode_208_503.js b/Week 06/id_503/leetcode_208_503.js new file mode 100644 index 000000000..bba9002ea --- /dev/null +++ b/Week 06/id_503/leetcode_208_503.js @@ -0,0 +1,80 @@ +/** + * Initialize your data structure here. + */ +class Trie { + constructor() { + this.root = new TrieNode(); + } + /** + * Inserts a word into the trie. + * @param {string} word + * @return {void} + */ + insert(word) { + let node = this.root; + + for (let c of word) { + node = node.links[c] = node.links[c] || new TrieNode(); + } + + node.isEnd = true; + } + /** + * Returns if the word is in the trie. + * @param {string} word + * @return {boolean} + */ + search(word) { + const node = this.searchNode(word); + + return !!(node && node.isEnd) + } + /** + * Returns if there is any word in the trie that starts with the given prefix. + * @param {string} prefix + * @return {boolean} + */ + startsWith(prefix) { + return !!this.searchNode(prefix); + } + + /** + * Returns a TrieNode object if there is any word search ends in the trie, or returns null. + * @param {string} word + * @returns {TrieNode} + */ + searchNode(word) { + if (!word) { + return null; + } + + let node = this.root; + for (const c of word) { + if (!(node = node.links[c])) { + return null; + } + } + + return node; + } +} + +/** + * Node of a trie + */ +class TrieNode { + + constructor() { + this.links = {}; + this.isEnd = 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) + */ \ No newline at end of file diff --git a/Week 06/id_503/leetcode_212_503.js b/Week 06/id_503/leetcode_212_503.js new file mode 100644 index 000000000..a24769026 --- /dev/null +++ b/Week 06/id_503/leetcode_212_503.js @@ -0,0 +1,67 @@ +/** + * @param {character[][]} board + * @param {string[]} words + * @return {string[]} + */ +var findWords = function (board, words) { + if (!board.length || !board[0].length || !words.length) { + return []; + } + + // 构建单词字典树 + const wordsTrie = {} + for (const w of words) { + let node = wordsTrie; + for (const c of w) { + node = node[c] = node[c] || {}; + } + + node.word = w; + } + + const res = []; + const m = board.length; + const n = board[0].length; + // 搜索方向:右,上、左、下(四联通) + const dx = [1, 0, -1, 0]; + const dy = [0, 1, 0, -1]; + + // DFS + var search = (i, j, trie) => { + if (i < 0 || i >= m || j < 0 || j >= n) { // 边界判断 + return; + } + + const c = board[i][j]; + const node = trie[c]; + if (!node) { // 判断是否包含字符(也包括 “#”) + return; + } + + if (node.word) { // 查找到单词 + res.push(node.word); + + node.word = null; // 防止重复;需要继续搜索,因为有相同前缀的情况,比如:"ab", "abc" + } + + board[i][j] = "#" // 标识已经查找过了 + + for (let d = 0; d < dx.length; d++) { + const ix = i + dx[d]; + const jx = j + dy[d]; + + search(ix, jx, node) + } + + board[i][j] = c; // 还原状态(回溯) + } + + for (let i = 0; i < board.length; i++) { + for (let j = 0; j < board[i].length; j++) { + + search(i, j, wordsTrie); + } + } + + return res; +}; \ No newline at end of file diff --git a/Week 06/id_503/leetcode_36_503.js b/Week 06/id_503/leetcode_36_503.js new file mode 100644 index 000000000..0e425a699 --- /dev/null +++ b/Week 06/id_503/leetcode_36_503.js @@ -0,0 +1,33 @@ +/** + * @param {character[][]} board + * @return {boolean} + */ +var isValidSudoku = function (board) { + if (!board) { + return false; + } + + const exists = {}; // 用来存储行、列、子数独已存在的值 (value -> true 字典) + + for (let i = 0; i < board.length; i++) { + for (let j = 0; j < board[i].length; j++) { + const val = board[i][j]; + if (val === '.') { + continue; + } + + const rowKey = i + "r" + val; + const colKey = j + "c" + val; + const boxKey = (parseInt(i / 3) * 3 + parseInt(j / 3)) + "b" + val + if (exists[rowKey] || exists[colKey] || exists[boxKey]) { + return false; + } + + exists[rowKey] = true; + exists[colKey] = true; + exists[boxKey] = true; + } + } + + return true; +}; \ No newline at end of file diff --git a/Week 06/id_503/leetcode_37_503.js b/Week 06/id_503/leetcode_37_503.js new file mode 100644 index 000000000..7ff0b0cd0 --- /dev/null +++ b/Week 06/id_503/leetcode_37_503.js @@ -0,0 +1,64 @@ +/** + * @param {character[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ +var solveSudoku = function (board) { + if (!board) { + return; + } + + solve(board); +}; + +/** + * @param {character[][]} board + * @return {boolean} + */ +function solve(board) { + for (let i = 0; i < board.length; i++) { + for (let j = 0; j < board[i].length; j++) { + if (board[i][j] === ".") { + for (let n = 1; n < 10; n++) { + const val = n.toString(); + if (isValid(board, i, j, val)) { + + board[i][j] = val; + + if (solve(board)) { + return true; + } + + board[i][j] = "."; + } + } + + return false; + } + } + } + + return true +} + +/** + * @param {character[][]} board + * @param {number} rowIndex + * @param {number} colIndex + * @param {string} val + * @return {boolean} + */ +function isValid(board, rowIndex, colIndex, val) { + + for (let i = 0; i < 9; i++) { + const boxRow = parseInt(rowIndex / 3) * 3 + parseInt(i / 3); + const boxCol = parseInt(colIndex / 3) * 3 + parseInt(i % 3); + + if (board[rowIndex][i] === val || + board[i][colIndex] === val || + board[boxRow][boxCol] === val) { + return false; + } + } + + return true; +} \ No newline at end of file diff --git a/Week 06/id_508/LeetCode_200_508.cpp b/Week 06/id_508/LeetCode_200_508.cpp new file mode 100644 index 000000000..ccf5b4f28 --- /dev/null +++ b/Week 06/id_508/LeetCode_200_508.cpp @@ -0,0 +1,35 @@ +class Solution { +public: + int numIslands(vector>& grid) { + int k = 0; + int res = 0; + + deque> que; + for(int i = 0; i< grid.size() ; i++ ) { + for (int j = 0; j < grid[0].size(); j++) { + if(grid[i][j] == '1') { + k++; + que.push_back(pair(i,j)); + while(!que.empty()) { + int x = que.front().first; + int y = que.front().second; + que.pop_front(); + if(grid[x][y] == '0' ||grid[x][y] =='v') continue; + + grid[x][y] = 'v'; + if (x&&grid[x-1][y]=='1') {que.push_back(pair(x-1,y)); + } + if (x<(grid.size()-1)&&grid[x+1][y]=='1') {que.push_back(pair(x+1,y)); + } + if (y&&grid[x][y-1]=='1') {que.push_back(pair(x,y-1)); + } + if (y<(grid[0].size()-1)&&grid[x][y+1]=='1') { que.push_back(pair(x,y+1)); + } + } + } + + } + } + return k; + } +}; diff --git a/Week 06/id_508/LeetCode_547_508.cpp b/Week 06/id_508/LeetCode_547_508.cpp new file mode 100644 index 000000000..ce57bbb3f --- /dev/null +++ b/Week 06/id_508/LeetCode_547_508.cpp @@ -0,0 +1,36 @@ +class Solution { +public: + int findCircleNum(vector>& M) { + createUnion(M.size()); + for(int i=0;icount = n; + data = new int[n]; + for(int i = 0;i= nr || c >= nc || grid[r][c] == '0') + { + return; + } + + grid[r][c] = '0'; + dfs(grid, r - 1, c); + dfs(grid, r + 1, c); + dfs(grid, r, c - 1); + dfs(grid, r, c + 1); + } +} \ No newline at end of file diff --git a/Week 06/id_523/LeetCode_547_523.cs b/Week 06/id_523/LeetCode_547_523.cs new file mode 100644 index 000000000..2c13c46d8 --- /dev/null +++ b/Week 06/id_523/LeetCode_547_523.cs @@ -0,0 +1,29 @@ +public class Solution +{ + public int FindCircleNum(int[][] M) + { + int[] visited = new int[M.Length]; + int count = 0; + for (int i = 0; i < M.Length; i++) + { + if (visited[i] == 0) + { + dfs(M, visited, i); + count++; + } + } + return count; + } + + public void dfs(int[][] M, int[] visited, int i) + { + for (int j = 0; j < M.Length; j++) + { + if (M[i][j] == 1 && visited[j] == 0) + { + visited[j] = 1; + dfs(M, visited, j); + } + } + } +} diff --git a/Week 06/id_533/LeetCode_130.js b/Week 06/id_533/LeetCode_130.js new file mode 100644 index 000000000..8f1b9fd35 --- /dev/null +++ b/Week 06/id_533/LeetCode_130.js @@ -0,0 +1,59 @@ +// https://leetcode-cn.com/problems/surrounded-regions/ + +/** + * @param {character[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ +var solve = function (board) { + if (!board || !board.length) return board; + + var n = board.length, + m = board[0].length, + dx = [-1, 1, 0, 0], + dy = [0, 0 , -1, 1], + parent = []; + + for (var i = 0; i <= n * m; i++) + parent[i] = i; + + for (var i = 0; i < n; i++) { + for (var j = 0; j < m; j++) { + if (board[i][j] === 'O') { + if (i === 0 || i === (n - 1) || j === 0 || j === (m - 1)) { + unio(i * m + j, n * m) + } else { + for (var k = 0; k < dx.length; k++) { + if (board[i + dx[k]][j + dy[k]] === 'O') + unio(i * m + j, (i + dx[k]) * m + (j + dy[k])) + } + } + } + } + } + + for (var i = 0; i < n; i++) { + for (var j = 0; j < m; j++) { + if (board[i][j] === 'O' && find(i * m + j) !== m * n) + board[i][j] = 'X'; + } + } + + function unio (i, j) { + var rootI = find(i), + rootJ = find(j); + if (rootI === rootJ) return; + parent[rootI] = rootJ; + } + + function find (i) { + return parent[i] === i ? parent[i] : find(parent[i]) + } +}; + +var arr = [ + ["X", "X", "X", "X"], + ["X", "O", "O", "X"], + ["X", "X", "O", "X"], + ["X", "O", "X", "X"]]; +solve(arr); +console.log(arr); \ No newline at end of file diff --git a/Week 06/id_533/LeetCode_200.js b/Week 06/id_533/LeetCode_200.js new file mode 100644 index 000000000..120f205fd --- /dev/null +++ b/Week 06/id_533/LeetCode_200.js @@ -0,0 +1,82 @@ +// https://leetcode-cn.com/problems/number-of-islands/ + +/** + * @param {character[][]} grid + * @return {number} + */ +var numIslands = function (grid) { + if (!grid || !grid.length) return 0; + var n = grid.length, + m = grid[0].length, + union = new UnionFind(grid); + for (var i = 0; i < n; i++) { + for (var j = 0; j < m; j++) { + if (grid[i][j] == 1) { + grid[i][j] = 0; + if (i - 1 >= 0 && grid[i - 1][j] == 1) + union.union(i * m + j, (i - 1) * m + j); + if (i + 1 < n && grid[i + 1][j] == 1) + union.union(i * m + j, (i + 1) * m + j); + if (j - 1 >= 0 && grid[i][j - 1] == 1) + union.union(i * m + j, i * m + (j - 1)); + if (j + 1 < m && grid[i][j + 1] == 1) + union.union(i * m + j, i * m + (j + 1)); + } + } + } + return union.count; +}; + +function UnionFind (grid) { + this.parent = []; + this.count = 0; + this.init(grid); +} + +UnionFind.prototype.init = function (grid) { + var n = grid.length, + m = grid[0].length; + for (var i = 0; i < n; i++) { + for (var j = 0; j < m; j++) { + if (grid[i][j] == 1) { + this.parent[i * m + j] = i * m + j; + this.count++; + } + } + } + +} + +UnionFind.prototype.find = function (i) { + if (this.parent[i] != i) + return this.find(this.parent[i]) + return this.parent[i]; +} + +UnionFind.prototype.union = function (i, j) { + var rootI = this.find(i); + var rootJ = this.find(j); + if (rootI == rootJ) return; + this.parent[rootJ] = this.parent[rootI]; + this.count--; +} + +// var res1 = numIslands([ +// [1, 1, 1], +// [1, 0, 1], +// [1, 1, 1]]) +// console.log(res1); +var res1 = numIslands([ + [1, 1, 1, 0, 0], + [1, 1, 0, 1, 0], + [1, 1, 0, 1, 0], + [0, 0, 0, 0, 0] +]) +console.log(res1); +// var res1 = numIslands([ +// [1, 1, 0, 0, 0], +// [1, 1, 0, 0, 0], +// [0, 0, 1, 0, 0], +// [0, 0, 0, 1, 1] +// ]) +// console.log(res1); \ No newline at end of file diff --git a/Week 06/id_533/LeetCode_208.js b/Week 06/id_533/LeetCode_208.js new file mode 100644 index 000000000..59671cb35 --- /dev/null +++ b/Week 06/id_533/LeetCode_208.js @@ -0,0 +1,56 @@ +// https://leetcode-cn.com/problems/implement-trie-prefix-tree/ + +var Trie = function() { + this.root = {}; +}; + +/** + * @param {string} word + * @return {void} + */ +Trie.prototype.insert = function(word) { + var node = this.root; + for (char of word) { + if (!node[char]) + node[char] = {}; + node = node[char]; + } + node.isEnd = true; +}; + +/** + * @param {string} word + * @return {boolean} + */ +Trie.prototype.search = function(word) { + var node = this.searchNode(word); + return node ? node.isEnd === true : false; +}; + +/** + * @param {string} prefix + * @return {boolean} + */ +Trie.prototype.startsWith = function(prefix) { + var node = this.searchNode(prefix); + return node !== null; +}; + +Trie.prototype.searchNode = function(word) { + var node = this.root; + for (char of word) { + if (node[char]) + node = node[char]; + else + return null; + } + return node; +} + +var trie = new Trie(); +trie.insert("apple"); +console.log(trie.search("apple")); // 返回 true +console.log(trie.search("app")); // 返回 false +console.log(trie.startsWith("app")); // 返回 true +trie.insert("app"); +console.log(trie.search("app")); // 返回 true diff --git a/Week 06/id_533/LeetCode_212.js b/Week 06/id_533/LeetCode_212.js new file mode 100644 index 000000000..585de1e8a --- /dev/null +++ b/Week 06/id_533/LeetCode_212.js @@ -0,0 +1,52 @@ +// https://leetcode-cn.com/problems/word-search-ii/ + +/** + * @param {character[][]} board + * @param {string[]} words + * @return {string[]} + */ +var findWords = function(board, words) { + var n = board.length, + m = board[0].length, + root = {}, + result = []; + for (word of words) { + var node = root; + for (char of word) { + if (!node[char]) + node[char] = {} + node = node[char]; + } + node.word = word; + } + for (var i = 0; i < n; i++) { + for (var j = 0; j < m; j++) { + find(i, j, root); + } + } + return result; + + function find (i, j, node) { + if (node.word) { + result.push(node.word); + node.word = null; + } + if (i < 0 || i >= n || j < 0 || j >=m) return; + if(!node[board[i][j]]) return; + var dx = [-1, 1, 0, 0], + dy = [0, 0, -1, 1]; + var tmp = board[i][j]; + board[i][j] = '#'; + for (var k = 0; k < dx.length; k++) + find(i + dx[k], j + dy[k], node[tmp]) + board[i][j] = tmp; + } +}; + +var result = findWords([ + ['o','a','a','n'], + ['e','t','a','e'], + ['i','h','k','r'], + ['i','f','l','v'] +], ["oath","pea","eat","rain"]); +console.log(result) \ No newline at end of file diff --git a/Week 06/id_533/LeetCode_36.js b/Week 06/id_533/LeetCode_36.js new file mode 100644 index 000000000..5621c721c --- /dev/null +++ b/Week 06/id_533/LeetCode_36.js @@ -0,0 +1,67 @@ +// https://leetcode-cn.com/problems/valid-sudoku/ + +/** + * @param {character[][]} board + * @return {boolean} + */ +var isValidSudoku = function(board) { + var rows = {}, + cols = {}, + boxs = {}; + for (var i = 0; i < 9; i++) { + rows[i] = {}; + cols[i] = {}; + boxs[i] = {}; + } + for (var i = 0; i < 9; i++) { + for (var j = 0; j < 9; j++) { + if (board[i][j] === '.') continue; + var num = board[i][j], + boxIdx = Math.floor(i / 3) * 3 + Math.floor(j / 3); + rows[i][num] = rows[i][num] === undefined ? 1 : ++rows[i][num]; + cols[j][num] = cols[j][num] === undefined ? 1 : ++cols[j][num]; + boxs[boxIdx][num] = boxs[boxIdx][num] === undefined ? 1 : ++boxs[boxIdx][num]; + if (rows[i][num] > 1 || cols[j][num] > 1 || boxs[boxIdx][num] > 1) return false; + } + } + return true; +}; + +var arr1 = [ + ['5', '3', '.', '.', '7', '.', '.', '.', '.'], + ['6', '.', '.', '1', '9', '5', '.', '.', '.'], + ['.', '9', '8', '.', '.', '.', '.', '6', '.'], + ['8', '.', '.', '.', '6', '.', '.', '.', '3'], + ['4', '.', '.', '8', '.', '3', '.', '.', '1'], + ['7', '.', '.', '.', '2', '.', '.', '.', '6'], + ['.', '6', '.', '.', '.', '.', '2', '8', '.'], + ['.', '.', '.', '4', '1', '9', '.', '.', '5'], + ['.', '.', '.', '.', '8', '.', '.', '7', '9'] +] +console.log(isValidSudoku(arr1)) +var arr2 = [ + ['8', '3', '.', '.', '7', '.', '.', '.', '.'], + ['6', '.', '.', '1', '9', '5', '.', '.', '.'], + ['.', '9', '8', '.', '.', '.', '.', '6', '.'], + ['8', '.', '.', '.', '6', '.', '.', '.', '3'], + ['4', '.', '.', '8', '.', '3', '.', '.', '1'], + ['7', '.', '.', '.', '2', '.', '.', '.', '6'], + ['.', '6', '.', '.', '.', '.', '2', '8', '.'], + ['.', '.', '.', '4', '1', '9', '.', '.', '5'], + ['.', '.', '.', '.', '8', '.', '.', '7', '9'] +] +console.log(isValidSudoku(arr2)) + +var arr3 = [ + [ '5', '3', '0', '2', '7', '6', '4', '1', '8'], + [ '6', '2', '4', '1', '9', '5', '3', '0', '7'], + [ '1', '9', '8', '3', '4', '0', '5', '6', '2'], + [ '8', '1', '2', '7', '6', '4', '0', '5', '3'], + [ '4', '0', '6', '8', '5', '3', '7', '2', '1'], + [ '7', '5', '3', '0', '2', '1', '8', '4', '6'], + [ '0', '6', '1', '5', '3', '7', '2', '8', '4'], + [ '2', '8', '7', '4', '1', '9', '6', '3', '5'], + [ '3', '4', '5', '6', '8', '2', '1', '7', '9'] +] + +console.log(isValidSudoku(arr3)) \ No newline at end of file diff --git a/Week 06/id_533/LeetCode_37.js b/Week 06/id_533/LeetCode_37.js new file mode 100644 index 000000000..33d6021de --- /dev/null +++ b/Week 06/id_533/LeetCode_37.js @@ -0,0 +1,49 @@ +// https://leetcode-cn.com/problems/sudoku-solver/ + +/** + * @param {character[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ +var solveSudoku = function (board) { + for (var i = 0; i < 9; i++) { + for (var j = 0; j < 9; j++) { + if (board[i][j] !== '.') continue; + for (var k = 1; k <= 9; k++) { + if (isValid(board, i, j, k.toString())) { + board[i][j] = k.toString(); + if (solveSudoku(board)) return true; + board[i][j] = '.' + } + } + return false; + } + } + return true; +}; + +function isValid (board, row, col, k) { + var boxX = Math.floor(row / 3) * 3; + var boxY = Math.floor(col / 3) * 3; + for (var i = 0; i < 9; i++) { + if (board[row][i] === k || board[i][col] === k) return false; + } + for (var i = 0; i < 3; i++) { + for (var j = 0; j < 3; j++) { + if (board[boxX+i][boxY+j] === k) return false; + } + } + return true; +}; + +var arr = [ + ['5', '3', '.', '.', '7', '.', '.', '.', '.'], + ['6', '.', '.', '1', '9', '5', '.', '.', '.'], + ['.', '9', '8', '.', '.', '.', '.', '6', '.'], + ['8', '.', '.', '.', '6', '.', '.', '.', '3'], + ['4', '.', '.', '8', '.', '3', '.', '.', '1'], + ['7', '.', '.', '.', '2', '.', '.', '.', '6'], + ['.', '6', '.', '.', '.', '.', '2', '8', '.'], + ['.', '.', '.', '4', '1', '9', '.', '.', '5'], + ['.', '.', '.', '.', '8', '.', '.', '7', '9']]; +solveSudoku(arr); +console.log(arr); \ No newline at end of file diff --git a/Week 06/id_533/LeetCode_51.js b/Week 06/id_533/LeetCode_51.js new file mode 100644 index 000000000..cc9f528b2 --- /dev/null +++ b/Week 06/id_533/LeetCode_51.js @@ -0,0 +1,28 @@ +// https://leetcode-cn.com/problems/n-queens/ + +/** + * @param {number} n + * @return {string[][]} + */ +// 回溯+剪枝 +var solveNQueens = function (n) { + var result = []; + placeQueens(result, n); + return result; +}; + +function placeQueens (result, col, board = [], row = 0) { + if (row === col) { + result.push(board.map(b_col => '.'.repeat(b_col) + 'Q' + '.'.repeat(col - b_col - 1))) + } + for (var i = 0; i < col; i++) { + if (!board.some((b_col, b_row) => i === b_col || (i - b_col) === (row - b_row) || (b_col + b_row) === (i + row))) { + board.push(i); + placeQueens(result, col, board, row + 1); + board.pop(); + } + } +} + +console.log(solveNQueens(4)); +console.log(solveNQueens(5)); \ No newline at end of file diff --git a/Week 06/id_533/LeetCode_547.js b/Week 06/id_533/LeetCode_547.js new file mode 100644 index 000000000..aff47443e --- /dev/null +++ b/Week 06/id_533/LeetCode_547.js @@ -0,0 +1,56 @@ +// https://leetcode-cn.com/problems/friend-circles/ + +/** + * @param {number[][]} M + * @return {number} + */ +var findCircleNum = function (M) { + if (!M || !M.length) return 0; + var n = M.length, + parent = [], + count = n; + for (i = 0; i < n; i++) + parent[i] = i; + for (var i = 0; i < n; i++) { + for (var j = i; j < n; j++) { + if (i !== j && M[i][j] === 1) + unio(i, j); + } + } + return count; + function unio(i, j) { + var rootI = find(i); + var rootJ = find(j); + if (rootI === rootJ) return; + if (rootI < rootJ) + parent[rootJ] = rootI; + else + parent[rootI] = rootJ; + count--; + } + function find(i) { + if (parent[i] !== i) + return find(parent[i]); + return parent[i]; + } +}; + + +var res1 = findCircleNum([ + [1, 0, 0, 1], + [0, 1, 1, 0], + [0, 1, 1, 1], + [1, 0, 1, 1]]) +console.log(res1); +var res1 = findCircleNum([ + [1, 1, 0], + [1, 1, 0], + [0, 0, 1] +]) +console.log(res1); +var res1 = findCircleNum([ + [1, 1, 0], + [1, 1, 1], + [0, 1, 1] +]) +console.log(res1); \ No newline at end of file diff --git a/Week 06/id_538/LeetCode_200_538.java b/Week 06/id_538/LeetCode_200_538.java new file mode 100644 index 000000000..d73000479 --- /dev/null +++ b/Week 06/id_538/LeetCode_200_538.java @@ -0,0 +1,21 @@ +public class LeetCode_200_538 { + public void dfs(int[][] M, int[] visited, int i) { + for (int j = 0; j < M.length; j++) { + if (M[i][j] == 1 && visited[j] == 0) { + visited[j] = 1; + dfs(M, visited, j); + } + } + } + public int findCircleNum(int[][] M) { + int[] visited = new int[M.length]; + int count = 0; + for (int i = 0; i < M.length; i++) { + if (visited[i] == 0) { + dfs(M, visited, i); + count++; + } + } + return count; + } +} diff --git a/Week 06/id_538/LeetCode_773_538.java b/Week 06/id_538/LeetCode_773_538.java new file mode 100644 index 000000000..e49b7a1f4 --- /dev/null +++ b/Week 06/id_538/LeetCode_773_538.java @@ -0,0 +1,47 @@ +public int slidingPuzzle(int[][] board) { + String target = "123450"; + String start = ""; + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + start += board[i][j]; + } + } + HashSet visited = new HashSet<>(); + // all the positions 0 can be swapped to + int[][] dirs = new int[][] { { 1, 3 }, { 0, 2, 4 }, + { 1, 5 }, { 0, 4 }, { 1, 3, 5 }, { 2, 4 } }; + Queue queue = new LinkedList<>(); + queue.offer(start); + visited.add(start); + int res = 0; + while (!queue.isEmpty()) { + // level count, has to use size control here, otherwise not needed + int size = queue.size(); + for (int i = 0; i < size; i++) { + String cur = queue.poll(); + if (cur.equals(target)) { + return res; + } + int zero = cur.indexOf('0'); + // swap if possible + for (int dir : dirs[zero]) { + String next = swap(cur, zero, dir); + if (visited.contains(next)) { + continue; + } + visited.add(next); + queue.offer(next); + + } + } + res++; + } + return -1; + } + + private String swap(String str, int i, int j) { + StringBuilder sb = new StringBuilder(str); + sb.setCharAt(i, str.charAt(j)); + sb.setCharAt(j, str.charAt(i)); + return sb.toString(); + } diff --git a/Week 06/id_543/LeetCode_200_543.java b/Week 06/id_543/LeetCode_200_543.java new file mode 100644 index 000000000..9685ee445 --- /dev/null +++ b/Week 06/id_543/LeetCode_200_543.java @@ -0,0 +1,92 @@ + +//给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。 +// +// 示例 1: +// +// 输入: +//11110 +//11010 +//11000 +//00000 +// +//输出: 1 +// +// +// 示例 2: +// +// 输入: +//11000 +//11000 +//00100 +//00011 +// +//输出: 3 +// +// Related Topics 深度优先搜索 广度优先搜索 并查集 + +class Solution { + public int numIslands(char[][] grid) { + if(grid.length == 0 || grid[0].length == 0) return 0; + int m = grid.length, n = grid[0].length; + UnionFind uf = new UnionFind(grid); + + for(int i = 0; i < m; i++) { + for(int j = 0; j < n; j++) { + if(grid[i][j] == '0') continue; + int p = i * n + j; + int q; + if(i > 0 && grid[i - 1][j] == '1') { + q = p - n; + uf.union(p, q); + } + if(i < m - 1 && grid[i + 1][j] == '1') { + q = p + n; + uf.union(p, q); + } + if(j > 0 && grid[i][j - 1] == '1') { + q = p - 1; + uf.union(p, q); + } + if(j < n - 1 && grid[i][j + 1] == '1') { + q = p + 1; + uf.union(p, q); + } + } + } + return uf.count; + } + + class UnionFind { + private int[] parent; + int m, n; + int count = 0; + UnionFind(char[][] grid) { + m = grid.length; + n = grid[0].length; + parent = new int[m*n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == '1') { + int id = i * n + j; + parent[id] = id; + count++; + } + } + } + } + public int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) return; + parent[rootP] = rootQ; + count--; + } + } +} \ No newline at end of file diff --git a/Week 06/id_543/LeetCode_547_543.java b/Week 06/id_543/LeetCode_547_543.java new file mode 100644 index 000000000..64ba4266e --- /dev/null +++ b/Week 06/id_543/LeetCode_547_543.java @@ -0,0 +1,78 @@ +//班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。 +// +// 给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。 +// +// 示例 1: +// +// +//输入: +//[[1,1,0], +// [1,1,0], +// [0,0,1]] +//输出: 2 +//说明:已知学生0和学生1互为朋友,他们在一个朋友圈。 +//第2个学生自己在一个朋友圈。所以返回2。 +// +// +// 示例 2: +// +// +//输入: +//[[1,1,0], +// [1,1,1], +// [0,1,1]] +//输出: 1 +//说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。 +// +// +// 注意: +// +// +// N 在[1,200]的范围内。 +// 对于所有学生,有M[i][i] = 1。 +// 如果有M[i][j] = 1,则有M[j][i] = 1。 +// +// Related Topics 深度优先搜索 并查集 + +class Solution { + public int findCircleNum(int[][] M) { + int n = M.length; + UnionFind uf = new UnionFind(n); + for (int i = 0; i < n - 1; i++) { + for (int j = i + 1; j < n; j++) { + if (M[i][j] == 1) uf.union(i, j); + } + } + return uf.count; + } + + class UnionFind { + private int[] parent; + int count = 0; + + public UnionFind(int n) { + count = n; + parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + public int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) return; + parent[rootP] = rootQ; + count--; + } + + } +} \ No newline at end of file diff --git a/Week 06/id_558/LeetCode_200_558.java b/Week 06/id_558/LeetCode_200_558.java new file mode 100644 index 000000000..025c1b8e2 --- /dev/null +++ b/Week 06/id_558/LeetCode_200_558.java @@ -0,0 +1,46 @@ +package Week05; + +/** + * @see https://leetcode-cn.com/problems/number-of-islands/ + * 岛屿数量 + */ +public class LeetCode_200_558 { + + private int[] dx = {1, -1, 0, 0}; + private int[] dy = {0, 0, 1, -1}; + private int count; + + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0) return 0; + int row = grid.length; + int col = grid[0].length; + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + dfs(i, j, row, col, grid, true); + } + } + return count; + } + + private void dfs(int i, int j, int row, int col, char[][] grid, boolean first) { + if (i < 0 || i >= row || j < 0 || j >= col || grid[i][j] == '0') return; + if (first && grid[i][j] == '1') { + count++; + } + grid[i][j] = '0'; + for (int k = 0; k < 4; k++) { + dfs(i + dx[k], j + dy[k], row, col, grid, false); + } + } + + public static void main(String[] args) { + char[][] grid = { + "11110".toCharArray(), + "11010".toCharArray(), + "11000".toCharArray(), + "00101".toCharArray() + }; + LeetCode_200_558 test = new LeetCode_200_558(); + System.out.println(test.numIslands(grid)); + } +} diff --git a/Week 06/id_558/LeetCode_200_II_558.java b/Week 06/id_558/LeetCode_200_II_558.java new file mode 100644 index 000000000..b8aa5aea9 --- /dev/null +++ b/Week 06/id_558/LeetCode_200_II_558.java @@ -0,0 +1,81 @@ +package Week05; + +/** + * @see https://leetcode-cn.com/problems/number-of-islands + * 岛屿数量 + */ +public class LeetCode_200_II_558 { + + private int[] dx = {1, -1, 0, 0}; + private int[] dy = {0, 0, 1, -1}; + + public void union(int i, int j, int dx, int dy, int row, int col, char[][] M, UnionFind unionFind) { + if (dx < 0 || dx >= row || dy < 0 || dy >= col || M[dx][dy] == '0') return; + unionFind.union(i * col + j, dx * col + dy); + } + + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0) return 0; + int row = grid.length; + int col = grid[0].length; + UnionFind unionFind = new UnionFind(grid); + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + if (grid[i][j] == '1') { + for (int k = 0; k < 4; k++) { + union(i, j, i + dx[k], j + dy[k], row, col, grid, unionFind); + } + } + } + } + return unionFind.getCount(); + } + + + static class UnionFind { + private int[] roots; + private int[] rank; + private int count; + + public UnionFind(char[][] M) { + int row = M.length; + int col = M[0].length; + roots = new int[row * col]; + rank = new int[row * col]; + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + if (M[i][j] == '1') { + roots[i * col + j] = i * col + j; + count++; + } + } + } + } + + public int find(int i) { + if (roots[i] != i) roots[i] = find(roots[i]); + return roots[i]; + } + + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP != rootQ) { + if (rank[rootP] > rank[rootQ]) { + roots[rootQ] = rootP; + } else if (rank[rootQ] > rank[rootP]) { + roots[rootP] = rootQ; + } else { + roots[rootQ] = rootP; + rank[rootP] += 1; + } + count--; + } + } + + public int getCount() { + return count; + } + } + +} diff --git a/Week 06/id_558/LeetCode_208_558.java b/Week 06/id_558/LeetCode_208_558.java new file mode 100644 index 000000000..50abc51c6 --- /dev/null +++ b/Week 06/id_558/LeetCode_208_558.java @@ -0,0 +1,93 @@ +package Week05; + +/** + * @see https://leetcode-cn.com/problems/implement-trie-prefix-tree/submissions/ + * 实现 Trie (前缀树) + */ +public class LeetCode_208_558 { + private TrieNode root; + + /** + * Initialize your data structure here. + */ + public LeetCode_208_558() { + root = new TrieNode(); + } + + /** + * Inserts a word into the trie. + */ + public void insert(String word) { + int len = word.length(); + TrieNode node = root; + for (int i = 0; i < len; i++) { + char c = word.charAt(i); + if (!node.containsKey(c)) { + node.put(c, new TrieNode()); + } + node = node.get(c); + } + node.setEnd(); + } + + /** + * Returns if the word is in the trie. + */ + public boolean search(String word) { + TrieNode node = getTrieNode(word); + return node != null && node.isEnd(); + } + + /** + * Returns if there is any word in the trie that starts with the given prefix. + */ + public boolean startsWith(String prefix) { + TrieNode node = getTrieNode(prefix); + return node != null; + } + + private TrieNode getTrieNode(String word) { + int len = word.length(); + TrieNode node = root; + for (int i = 0; i < len; i++) { + char c = word.charAt(i); + if (!node.containsKey(c)) { + return null; + } + node = node.get(c); + } + return node; + } + + static class TrieNode { + private final static int LEN = 26; + private TrieNode[] nodes = new TrieNode[LEN]; + private boolean isEnd; + + public void put(char c, TrieNode node) { + nodes[c - 'a'] = node; + } + + public TrieNode get(char key) { + return nodes[key - 'a']; + } + + public boolean containsKey(char key) { + return nodes[key - 'a'] != null; + } + + public void setEnd() { + this.isEnd = true; + } + + public boolean isEnd() { + return isEnd; + } + } + + public static void main(String[] args) { + int num = 12345678; + + System.out.println(num & 123 & (~123)); + } +} diff --git a/Week 06/id_558/LeetCode_212_558.java b/Week 06/id_558/LeetCode_212_558.java new file mode 100644 index 000000000..4e122d4d9 --- /dev/null +++ b/Week 06/id_558/LeetCode_212_558.java @@ -0,0 +1,149 @@ +package Week05; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @see https://leetcode-cn.com/problems/word-search-ii/ + * 单词搜索 II + */ +public class LeetCode_212_558 { + private TrieNode root = new TrieNode(); + private int[] dx = {1, -1, 0, 0}; + private int[] dy = {0, 0, 1, -1}; + + public List findWords(char[][] board, String[] words) { + for (String s : words) { + insert(s); + } + int rowLen = board.length; + int colLen = board[0].length; + Set result = new HashSet(); + boolean[][] visited = new boolean[rowLen][colLen]; + for (int i = 0; i < rowLen; i++) { + for (int j = 0; j < colLen; j++) { + search(i, j, rowLen, colLen, root, board, visited, result); + } + } + return new ArrayList(result); + } + + private void search(int i, int j, int rowLen, int colLen, TrieNode node, char[][] board, boolean[][] visited, Set result) { + if (i < 0 || i >= rowLen || j < 0 || j >= colLen || visited[i][j]) return; + node = node.get(board[i][j]); + if (node == null) return; + if (node.isEnd()) { + result.add(node.getWord()); + // return; + } + visited[i][j] = true; + for (int k = 0; k < 4; k++) { + search(dx[k] + i, dy[k] + j, rowLen, colLen, node, board, visited, result); + } + visited[i][j] = false; + } + + /** + * Inserts a word into the trie. + */ + public void insert(String word) { + int len = word.length(); + TrieNode node = root; + for (int i = 0; i < len; i++) { + char c = word.charAt(i); + if (!node.containsKey(c)) { + node.put(c, new TrieNode()); + } + node = node.get(c); + } + node.setEnd(); + node.setWord(word); + } + + /** + * Returns if the word is in the trie. + */ + public boolean search(String word) { + TrieNode node = getTrieNode(word); + return node != null && node.isEnd(); + } + + /** + * Returns if there is any word in the trie that starts with the given prefix. + */ + public boolean startsWith(String prefix) { + TrieNode node = getTrieNode(prefix); + return node != null; + } + + private TrieNode getTrieNode(String word) { + int len = word.length(); + TrieNode node = root; + for (int i = 0; i < len; i++) { + char c = word.charAt(i); + if (!node.containsKey(c)) { + return null; + } + node = node.get(c); + } + return node; + } + + static class TrieNode { + private final static int LEN = 26; + private TrieNode[] nodes = new TrieNode[LEN]; + private String word; + private boolean isEnd; + + public void put(char c, TrieNode node) { + nodes[c - 'a'] = node; + } + + public TrieNode get(char key) { + return nodes[key - 'a']; + } + + public boolean containsKey(char key) { + return nodes[key - 'a'] != null; + } + + public void setEnd() { + this.isEnd = true; + } + + public boolean isEnd() { + return isEnd; + } + + public void setWord(String word) { + this.word = word; + } + + public String getWord() { + return this.word; + } + } + + public static void main(String[] args) { + + /* String[] words = {"oath", "pea", "eat", "rain"}; + char[][] board = + { + {'o', 'a', 'a', 'n'}, + {'e', 't', 'a', 'e'}, + {'i', 'h', 'k', 'r'}, + {'i', 'f', 'l', 'v'} + };*/ + String[] words = {"aba","baa","bab","aaab","aaa","aaaa","aaba"}; + char[][] board = + { + {'a', 'b'}, + {'a', 'a'} + }; + + LeetCode_212_558 test = new LeetCode_212_558(); + System.out.println(test.findWords(board, words)); + } +} diff --git a/Week 06/id_558/LeetCode_36_558.java b/Week 06/id_558/LeetCode_36_558.java new file mode 100644 index 000000000..ca195bc10 --- /dev/null +++ b/Week 06/id_558/LeetCode_36_558.java @@ -0,0 +1,88 @@ +package Week05; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * @see https://leetcode-cn.com/problems/valid-sudoku/submissions/ + * 有效的数独 + */ +public class LeetCode_36_558 { + public boolean isValidSudoku(char[][] board) { + if (board == null || board.length == 0) return false; + int row = board.length; + int col = board[0].length; + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + if (!isValid(i, j, row, col, board)) { + return false; + } + } + } + return true; + } + + public boolean isValid(int i, int j, int row, int col, char[][] board) { + if (board[i][j] == '.') return true; + Set set = new HashSet(); + for (int k = 0; k < row; k++) { + if (board[i][k] != '.') { + if (set.contains(board[i][k])) { + return false; + } else { + set.add(board[i][k]); + } + } + } + set.clear(); + for (int k = 0; k < col; k++) { + if (board[k][j] != '.') { + if (set.contains(board[k][j])) { + return false; + } else { + set.add(board[k][j]); + } + } + } + Map> map = new HashMap>(); + for (int m = 0; m < row; m++) { + for (int n = 0; n < col; n++) { + if (board[m][n] != '.') { + char num = board[m][n]; + int box_index = (m / 3) * 3 + n / 3; + Set s = map.get(box_index); + if (s != null) { + if (s.contains(num)) { + return false; + } else { + s.add(num); + } + } else { + s = new HashSet(); + s.add(num); + map.put(box_index, s); + } + } + } + } + + return true; + } + + public static void main(String[] args) { + char[][] board = { + {'5', '3', '.', '.', '7', '.', '.', '.', '.'}, + {'6', '.', '.', '1', '9', '5', '.', '.', '.'}, + {'.', '9', '8', '.', '.', '.', '.', '6', '.'}, + {'8', '.', '.', '.', '6', '.', '.', '.', '3'}, + {'4', '.', '.', '8', '.', '3', '.', '.', '1'}, + {'7', '.', '.', '.', '2', '.', '.', '.', '6'}, + {'.', '6', '.', '.', '.', '.', '2', '8', '.'}, + {'.', '.', '.', '4', '1', '9', '.', '.', '5'}, + {'.', '.', '.', '.', '8', '.', '.', '7', '9'} + }; + System.out.println(new LeetCode_36_558().isValidSudoku(board)); + } +} diff --git a/Week 06/id_558/NOTE.md b/Week 06/id_558/NOTE.md index a6321d6e2..42901e1a3 100644 --- a/Week 06/id_558/NOTE.md +++ b/Week 06/id_558/NOTE.md @@ -1,4 +1,38 @@ # NOTE +#### 知识回顾 +* 字典树 + * 字典树,即 Trie 树,又称单词 查找树或键树,是一种树形结构。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。 + * 优点:最大限度地减少无谓的字符串比较,查询效率 比哈希表高。 + * Trie树的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。 +* 并查集 + * 基本操作 + * makeSet + * union + * find + * 使用场景 + * 组团、配对 + * 是否属于同一个组 + +* 搜索 + * 初级搜索 + * 优化:不重复(fibonacci)、剪枝(生成括号问题) + * 搜索方向:DFS(Stack)、BFS(Queue) + * 高级搜索 + * 双向搜索 + * 启发式搜索 +#### 处理输出 + +* [实现 Trie (前缀树)](https://leetcode-cn.com/problems/implement-trie-prefix-tree/submissions/) +* [单词搜索 II](https://leetcode-cn.com/problems/word-search-ii/) +* [岛屿数量](https://leetcode-cn.com/problems/number-of-islands/) +* [有效的数独](https://leetcode-cn.com/problems/valid-sudoku/submissions/) + +#### 遗留问题 + * 并查集理解实现 + * 红黑树 + +#### 总结改进 +* 增加练习时间 diff --git a/Week 06/id_563/Leecode_127_563.go b/Week 06/id_563/Leecode_127_563.go new file mode 100644 index 000000000..69f96decb --- /dev/null +++ b/Week 06/id_563/Leecode_127_563.go @@ -0,0 +1,32 @@ +func ladderLength(beginWord string, endWord string, wordList []string) int { + m := make(map[string]bool) + for _, v := range wordList { + m[v] = true + } + list := []string{} + list = append(list, beginWord) + rst := 1 + for len(list) != 0 { + l := len(list) + for i := 0; i < l; i ++ { + word := list[0] + list = list[1:] + if word == endWord { + return rst + } + m[word] = false + for i, _ := range word { + for j := 0; j < 26; j++ { + tmp := []rune(word) + tmp[i] = rune('a' + j) + ts := string(tmp) + if v, ok := m[ts]; ok && v && ((len(list) != 0 && ts != list[len(list) - 1]) || len(list) == 0) { + list = append(list, ts) + } + } + } + } + rst++ + } + return 0 +} \ No newline at end of file diff --git a/Week 06/id_563/Leecode_547_563.go b/Week 06/id_563/Leecode_547_563.go new file mode 100644 index 000000000..ddc89e063 --- /dev/null +++ b/Week 06/id_563/Leecode_547_563.go @@ -0,0 +1,33 @@ +/** 可以优化一下 */ +func findCircleNum(M [][]int) int { + N := len(M) + res := N + + friend := make([]int, res) + for i := 0; i < res; i++ { + friend[i] = i + } + + /** s 和 d 是朋友关系 + * 所以,s 的所有朋友都是 d 的朋友 */ + union := func(s, d int) { + for i := range friend { + if friend[i] == s { + friend[i] = d + } + } + } + + for i := 0; i < N; i++ { + for j := i + 1; j < N; j++ { + if M[i][j] == 1 { + if friend[i] != friend[j] { + res-- + union(friend[i], friend[j]) + } + } + } + } + + return res +} \ No newline at end of file diff --git a/Week 06/id_568/LeetCode_208_568.java b/Week 06/id_568/LeetCode_208_568.java new file mode 100644 index 000000000..e81ed54ba --- /dev/null +++ b/Week 06/id_568/LeetCode_208_568.java @@ -0,0 +1,92 @@ +/** + * @author kelvin + * @date 2019/11/21 6:52 AM + */ +public class LeetCode_208_568 { +} + +class TrieNode { + private TrieNode[] links; + private final int R = 26; + private boolean isEnd; + + public TrieNode() { + this.links = new TrieNode[R]; + } + + public boolean containsKey(char ch) { + return this.links[ch - 'a'] != null; + } + + public TrieNode get(char ch) { + return this.links[ch - 'a']; + } + + public void put(char ch, TrieNode node) { + this.links[ch - 'a'] = node; + } + + public void setEnd() { + isEnd = true; + } + + public boolean isEnd() { + return isEnd; + } +} + +class Trie { + private TrieNode root; + + /** + * Initialize your data structure here. + */ + public Trie() { + root = new TrieNode(); + } + + /** + * Inserts a word into the trie. + */ + public void insert(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char currentChar = word.charAt(i); + if (!node.containsKey(currentChar)) { + node.put(currentChar, new TrieNode()); + } + node = node.get(currentChar); + } + node.setEnd(); + + } + + private TrieNode searchPrefix(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char curLetter = word.charAt(i); + if (node.containsKey(curLetter)) { + node = node.get(curLetter); + } else { + return null; + } + } + return node; + } + + /** + * Returns if the word is in the trie. + */ + public boolean search(String word) { + TrieNode node = searchPrefix(word); + return node !=null && node.isEnd(); + } + + /** + * Returns if there is any word in the trie that starts with the given prefix. + */ + public boolean startsWith(String prefix) { + TrieNode node = searchPrefix(prefix); + return node != null; + } +} \ No newline at end of file diff --git a/Week 06/id_568/LeetCode_433_568.java b/Week 06/id_568/LeetCode_433_568.java new file mode 100644 index 000000000..d5ddc74ee --- /dev/null +++ b/Week 06/id_568/LeetCode_433_568.java @@ -0,0 +1,52 @@ +import java.util.Arrays; +import java.util.HashSet; + +/** + * @author kelvin + * @date 2019/11/24 9:55 PM + */ +public class LeetCode_433_568 { + public int minMutation(String start, String end, String[] bank) { + HashSet set = new HashSet(Arrays.asList(bank)); + if (!set.contains(end)) { + return -1; + } + char[] four = {'A', 'C', 'G', 'T'}; + HashSet positve = new HashSet(); + positve.add(start); + HashSet negative = new HashSet(); + negative.add(end); + HashSet tempNewSet = new HashSet(); + int step = 0; + while (positve.size() > 0 && negative.size() > 0) { + step++; + if(positve.size()>negative.size()){ + HashSet temp = new HashSet(positve); + positve = negative; + negative = temp; + } + for(String item:positve){ + String str; + char[] tempStringChars = item.toCharArray(); + for (int i= tempStringChars.length-1;i>=0;--i){ + char oldChar = tempStringChars[i]; + for(int j=0;j<4;++j){ + tempStringChars[i] = four[j]; + String newString = new String(tempStringChars); + if(negative.contains(newString)){ + return step; + } else if(set.contains(newString)){ + set.remove(newString); + tempNewSet.add(newString); + } + } + tempStringChars[i] = oldChar; + } + } + positve = new HashSet(tempNewSet); + tempNewSet.clear(); + } + return -1; + + } +} diff --git a/Week 06/id_573/NOTE.md b/Week 06/id_573/NOTE.md index a6321d6e2..8bfb40bc4 100644 --- a/Week 06/id_573/NOTE.md +++ b/Week 06/id_573/NOTE.md @@ -1,4 +1,89 @@ # NOTE +### Trie +Trie 树优于哈希表的另一个理由是,随着哈希表大小增加,会出现大量的冲突,时间复杂度可能增加到 O(n)O(n),其中 nn 是插入的键的数量。与哈希表相比,Trie 树在存储多个具有相同前缀的键时可以使用较少的空间。此时 Trie 树只需要 O(m)O(m) 的时间复杂度,其中 mm 为键长。而在平衡树中查找键值需要 O(m \log n)O(mlogn) 时间复杂度。 +### 作用 +Trie (发音为 "try") 或前缀树是一种树数据结构,用于检索字符串数据集中的键。这一高效的数据结构有多种应用: + + 1. 自动补全 + 2. 拼写检查 + 3. IP 路由 (最长前缀匹配) + 4. T9 (九宫格) 打字预测 + 5. 单词游戏 + 6. 还有其他的数据结构,如平衡树和哈希表,使我们能够在字符串数据集中搜索单词。为什么我们还需要 Trie 树呢?尽管哈希表可以在 O(1)O(1) 时间内寻找键值,却无法高效的完成以下操作: + 找到具有同一前缀的全部键值。 + 按词典序枚举字符串的数据集。 + + +### Trie 树的结点结构 + + Trie 树是一个有根的树,其结点具有以下字段: + +```` + 1、最多 RR 个指向子结点的链接,其中每个链接对应字母表数据集中的一个字母。本文中假定 RR 为 26,小写拉丁字母的数量。 + 2、布尔字段,以指定节点是对应键的结尾还是只是键前缀。 +```` + +### Trie 树中最常见的两个操作是键的插入和查找。 + + 向 Trie 树中插入键 + 我们通过搜索 Trie 树来插入一个键。我们从根开始搜索它对应于第一个键字符的链接。有两种情况: + + 链接存在。沿着链接移动到树的下一个子层。算法继续搜索下一个键字符。 + 链接不存在。创建一个新的节点,并将它与父节点的链接相连,该链接与当前的键字符相匹配。 + 重复以上步骤,直到到达键的最后一个字符,然后将当前节点标记为结束节点,算法完成。 + +### 复杂度分析 + + 时间复杂度:O(m)O(m),其中 mm 为键长。在算法的每次迭代中,我们要么检查要么创建一个节点,直到到达键尾。只需要 mm 次操作。 + 空间复杂度:O(m)O(m)。最坏的情况下,新插入的键和 Trie 树中已有的键没有公共前缀。此时需要添加 mm 个结点,使用 O(m)O(m) 空间。 + +#### 在 Trie 树中查找键 + 每个键在 trie 中表示为从根到内部节点或叶的路径。我们用第一个键字符从根开始,。检查当前节点中与键字符对应的链接。有两种情况: + + 存在链接。我们移动到该链接后面路径中的下一个节点,并继续搜索下一个键字符。 + 不存在链接。若已无键字符,且当前结点标记为 isEnd,则返回 true。否则有两种可能,均返回 false : + 还有键字符剩余,但无法跟随 Trie 树的键路径,找不到键。 + 没有键字符剩余,但当前结点没有标记为 isEnd。也就是说,待查找键只是Trie树中另一个键的前缀。 + +#### 查找 Trie 树中的键前缀 + 该方法与在 Trie 树中搜索键时使用的方法非常相似。我们从根遍历 Trie 树,直到键前缀中没有字符,或者无法用当前的键字符继续 Trie 中的路径。与上面提到的“搜索键”算法唯一的区别是,到达键前缀的末尾时,总是返回 true。我们不需要考虑当前 Trie 节点是否用 “isend” 标记,因为我们搜索的是键的前缀,而不是整个键。 + + +### DFS\BFS + + DFS即深度优先搜索,俗称不撞南墙不回头,实现方式是用栈来保存选择,然后每次取栈顶元素,并且依据栈顶元素遍历,将符合条件的元素压栈,然后如此往复。 + + BFS即广度优先搜索,特征是层次遍历,利用队列来实现。每次取队列队首元素,遍历所有节点,将所有符合条件的元素入队。循环往复。 + + 不管是DFS还是BFS,都需要一个状态标记来表示元素已经被选取,避免出现重复选择以及死循环。 + + 从上面的分析,我们可以知道DFS是很适合去做连通性搜索测试,并且如果不需要找到最短路径的话,可以直接退出,不需要存储大量路径信息(C语言也可以用递归来做);而BFS则是很适合去搜索==最短路径==,因为其会进行层次遍历,在任何一层发现了目标,那都是最短的路径上发现的。但是BFS会要求记录每一层的信息,会导致信息记录量大。(而且C语言没有队列,需要额外实现) + + 但是很明显,我们这道题就是需要用到BFS。也即根据beginWord来搜索所有与其相差仅为1个词语的单词,将其放入队列中,然后后循环搜索。不过要注意的是,存粹的BFS会超时,所以需要双端BFS,也就是从beginWord和endWord两端搜索。 + + +### 双端BFS + + +依据名字,我们要从两端都搜索,也就需要两个队列来保存各自的搜索信息。我们记为begin_word_queue和end_word_queue,并且记为A和B。双端搜索的规则如下: + +1、每次选取A和B中最少元素的来进行出队操作。如果size相等,取A中元素。 + +2、每次遍历到符合要求的元素,则进行入队操作,A搜索到的记为1, B搜索到的记为2。但是赋值方式为: + + + int selected_flag; //0 means A; 1 means B + int flag = 0; + flag |= selected_flag; + +3、当flag为3的时候代表A、B同时访问了一个节点,退出。 + +4、记录访问层次的次数,返回此次数。层次次数初始值为1 + + + +为什么这么做呢?因为BFS是层次遍历,也就是金字塔型遍历,越往后,搜索到的节点越多,信息越庞大,导致搜索时间越长。==但是结束点又只有一个,所以数据量大就会超时==。 + \ No newline at end of file diff --git a/Week 06/id_573/leetcode_127_573.java b/Week 06/id_573/leetcode_127_573.java new file mode 100644 index 000000000..d6a3a4e0d --- /dev/null +++ b/Week 06/id_573/leetcode_127_573.java @@ -0,0 +1 @@ +class Solution { private int L; private HashMap> allComboDict; Solution() { this.L = 0; // Dictionary to hold combination of words that can be formed, // from any given word. By changing one letter at a time. this.allComboDict = new HashMap>(); } private int visitWordNode( Queue> Q, HashMap visited, HashMap othersVisited) { Pair node = Q.remove(); String word = node.getKey(); int level = node.getValue(); for (int i = 0; i < this.L; i++) { // Intermediate words for current word String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L); // Next states are all the words which share the same intermediate state. for (String adjacentWord : this.allComboDict.getOrDefault(newWord, new ArrayList())) { // If at any point if we find what we are looking for // i.e. the end word - we can return with the answer. if (othersVisited.containsKey(adjacentWord)) { return level + othersVisited.get(adjacentWord); } if (!visited.containsKey(adjacentWord)) { // Save the level as the value of the dictionary, to save number of hops. visited.put(adjacentWord, level + 1); Q.add(new Pair(adjacentWord, level + 1)); } } } return -1; } public int ladderLength(String beginWord, String endWord, List wordList) { if (!wordList.contains(endWord)) { return 0; } // Since all words are of same length. this.L = beginWord.length(); wordList.forEach( word -> { for (int i = 0; i < L; i++) { // Key is the generic word // Value is a list of words which have the same intermediate generic word. String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L); ArrayList transformations = this.allComboDict.getOrDefault(newWord, new ArrayList()); transformations.add(word); this.allComboDict.put(newWord, transformations); } }); // Queues for birdirectional BFS // BFS starting from beginWord Queue> Q_begin = new LinkedList>(); // BFS starting from endWord Queue> Q_end = new LinkedList>(); Q_begin.add(new Pair(beginWord, 1)); Q_end.add(new Pair(endWord, 1)); // Visited to make sure we don't repeat processing same word. HashMap visitedBegin = new HashMap(); HashMap visitedEnd = new HashMap(); visitedBegin.put(beginWord, 1); visitedEnd.put(endWord, 1); while (!Q_begin.isEmpty() && !Q_end.isEmpty()) { // One hop from begin word int ans = visitWordNode(Q_begin, visitedBegin, visitedEnd); if (ans > -1) { return ans; } // One hop from end word ans = visitWordNode(Q_end, visitedEnd, visitedBegin); if (ans > -1) { return ans; } } return 0; } } \ No newline at end of file diff --git a/Week 06/id_573/leetcode_208_573.java b/Week 06/id_573/leetcode_208_573.java new file mode 100644 index 000000000..8a059c155 --- /dev/null +++ b/Week 06/id_573/leetcode_208_573.java @@ -0,0 +1 @@ +class Trie { private TrieNode root; /** * Initialize your data structure here. */ public Trie() { root = new TrieNode(); } /** * Inserts a word into the trie. */ public void insert(String word) { TrieNode node = root; for (int i = 0; i < word.length(); i++) { char currentChar = word.charAt(i); if (!node.containsKey(currentChar)) { node.put(currentChar, new TrieNode()); } node = node.get(currentChar); } node.setEnd(); } // search a prefix or whole key in trie and // returns the node where search ends private TrieNode searchPrefix(String word) { TrieNode node = root; for (int i = 0; i < word.length(); i++) { char curLetter = word.charAt(i); if (node.containsKey(curLetter)) { node = node.get(curLetter); } else { return null; } } return node; } /** * Returns if the word is in the trie. */ public boolean search(String word) { TrieNode node = searchPrefix(word); return node != null && node.isEnd(); } /** * Returns if there is any word in the trie that starts with the given prefix. */ public boolean startsWith(String prefix) { TrieNode node = searchPrefix(prefix); return node != null; } } \ No newline at end of file diff --git a/Week 06/id_573/leetcode_547_573.java b/Week 06/id_573/leetcode_547_573.java new file mode 100644 index 000000000..02501b392 --- /dev/null +++ b/Week 06/id_573/leetcode_547_573.java @@ -0,0 +1 @@ +public class Solution { public int findCircleNum(int[][] M) { int[] visited = new int[M.length]; int count = 0; Queue queue = new LinkedList<>(); for (int i = 0; i < M.length; i++) { if (visited[i] == 0) { queue.add(i); while (!queue.isEmpty()) { int s = queue.remove(); visited[s] = 1; for (int j = 0; j < M.length; j++) { if (M[s][j] == 1 && visited[j] == 0) { queue.add(j); } } } count++; } } return count; } } \ No newline at end of file diff --git a/Week 06/id_578/LeetCode_130_578.java b/Week 06/id_578/LeetCode_130_578.java new file mode 100644 index 000000000..cd5684e5e --- /dev/null +++ b/Week 06/id_578/LeetCode_130_578.java @@ -0,0 +1,78 @@ +package com.hand.week6; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/11/24 + */ +public class LeetCode_130_578 { + int rows; + int cols; + + public void solve(char[][] board) { + if (board == null || board.length == 0) return; + rows = board.length; + cols = board[0].length; + //多申请一个空间 + UnionFind uf = new UnionFind(rows * cols + 1); + //所有边界的O节点都和dummy节点合并 + int dummyNode = rows * cols; + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + if (board[i][j] != 'O') continue; + //当前节点在边界就和dummy合并 + if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1) uf.union(node(i, j), dummyNode); + else { + //将上下左右的O节点和当前节点合并 + if (board[i - 1][j] == 'O') uf.union(node(i, j), node(i - 1, j)); + if (board[i + 1][j] == 'O') uf.union(node(i, j), node(i + 1, j)); + if (board[i][j - 1] == 'O') uf.union(node(i, j), node(i, j - 1)); + if (board[i][j + 1] == 'O') uf.union(node(i, j), node(i, j + 1)); + } + } + } + + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + //判断是否和dummy节点是一类 + if (uf.isConnected(dummyNode, node(i, j))) board[i][j] = 'O'; + else board[i][j] = 'X'; + } + } + } + + public int node(int i, int j) { + return cols * i + j; + } + + class UnionFind { + int[] parents; + + public UnionFind(int totalNodes) { + parents = new int[totalNodes]; + for (int i = 0; i < totalNodes; i++) { + parents[i] = i; + } + } + + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP != rootQ) { + parents[rootQ] = rootP; + } + } + + public int find(int p) { + if (p == parents[p]) + return p; + parents[p] = find(parents[p]); + return parents[p]; + } + + public boolean isConnected(int p, int q) { + return find(p) == find(q); + } + } +} diff --git a/Week 06/id_578/LeetCode_200_578.java b/Week 06/id_578/LeetCode_200_578.java new file mode 100644 index 000000000..ac0129102 --- /dev/null +++ b/Week 06/id_578/LeetCode_200_578.java @@ -0,0 +1,66 @@ +package com.hand.week6; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/11/24 + */ +public class LeetCode_200_578 { + int rows; + int cols; + + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0) return 0; + rows = grid.length; + cols = grid[0].length; + UnionFind uf = new UnionFind(rows * cols + 1); + int dummyNode = rows * cols; + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + if (grid[i][j] == '0') uf.union(node(i, j), dummyNode); + else { + if (i + 1 < rows && grid[i + 1][j] == '1') uf.union(node(i + 1, j), node(i, j)); + if (j + 1 < cols && grid[i][j + 1] == '1') uf.union(node(i, j + 1), node(i, j)); + } + } + } + return uf.getCount() - 1; + } + + public int node(int i, int j) { + return cols * i + j; + } + + class UnionFind { + private int[] parents; + private int count; + + public UnionFind(int totalNodes) { + parents = new int[totalNodes]; + count = totalNodes; + for (int i = 0; i < totalNodes; i++) { + parents[i] = i; + } + } + + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) return; + parents[rootQ] = rootP; + count--; + } + + public int find(int p) { + if (p == parents[p]) + return p; + parents[p] = find(parents[p]); + return parents[p]; + } + + public int getCount() { + return count; + } + } +} diff --git a/Week 06/id_578/LeetCode_208_578.java b/Week 06/id_578/LeetCode_208_578.java new file mode 100644 index 000000000..2dbb492c7 --- /dev/null +++ b/Week 06/id_578/LeetCode_208_578.java @@ -0,0 +1,80 @@ +package com.hand.week6; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/11/24 + */ +public class LeetCode_208_578 { + class Trie { + + class TrieNode { + char data; + TrieNode[] children = new TrieNode[26]; + boolean isEnd; + + public TrieNode(char data) { + this.data = data; + } + + public boolean hasNode(char node) { + return children[node - 'a'] != null; + } + + public TrieNode findNode(char node) { + return children[node - 'a']; + } + + public TrieNode createNode(char node) { + children[node - 'a'] = new TrieNode(node); + return children[node - 'a']; + } + } + + TrieNode head; + + /** + * Initialize your data structure here. + */ + public Trie() { + head = new TrieNode('/'); + } + + /** + * Inserts a word into the trie. + */ + public void insert(String word) { + TrieNode currentNode = head; + for (char c : word.toCharArray()) { + if (currentNode.hasNode(c)) currentNode = currentNode.findNode(c); + else currentNode = currentNode.createNode(c); + } + currentNode.isEnd = true; + } + + /** + * Returns if the word is in the trie. + */ + public boolean search(String word) { + TrieNode currentNode = head; + for (char c : word.toCharArray()) { + if (currentNode.hasNode(c)) currentNode = currentNode.findNode(c); + else return false; + } + return currentNode.isEnd; + } + + /** + * Returns if there is any word in the trie that starts with the given prefix. + */ + public boolean startsWith(String prefix) { + TrieNode currentNode = head; + for (char c : prefix.toCharArray()) { + if (currentNode.hasNode(c)) currentNode = currentNode.findNode(c); + else return false; + } + return true; + } + } +} diff --git a/Week 06/id_578/LeetCode_212_578.java b/Week 06/id_578/LeetCode_212_578.java new file mode 100644 index 000000000..15e1f5cd7 --- /dev/null +++ b/Week 06/id_578/LeetCode_212_578.java @@ -0,0 +1,63 @@ +package com.hand.week6; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/11/24 + */ +public class LeetCode_212_578 { + public List findWords(char[][] board, String[] words) { + Trie trie = new Trie(); + for (String word : words) { + trie.insert(word); + } + int m = board.length; + int n = board[0].length; + boolean[][] visited = new boolean[m][n]; + Set resultSet = new HashSet<>(); + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + helper(board, trie.head, i, j, m, n, visited, resultSet); + } + } + return new ArrayList<>(resultSet); + } + + private void helper(char[][] board, Trie.TrieNode node, int i, int j, int m, int n, boolean[][] visited, Set resultSet) { + if (i < 0 || j < 0 || i >= m || j >= n || visited[i][j]) return; + node = node.children[board[i][j] - 'a']; + if (node == null) return; + if (node.word != null) + resultSet.add(node.word); + visited[i][j] = true; + helper(board, node, i + 1, j, m, n, visited, resultSet); + helper(board, node, i - 1, j, m, n, visited, resultSet); + helper(board, node, i, j + 1, m, n, visited, resultSet); + helper(board, node, i, j - 1, m, n, visited, resultSet); + visited[i][j] = false; + } + + class Trie { + TrieNode head = new TrieNode(); + + public void insert(String word) { + TrieNode currentNode = head; + for (char c : word.toCharArray()) { + if (currentNode.children[c - 'a'] == null) currentNode.children[c - 'a'] = new TrieNode(); + currentNode = currentNode.children[c - 'a']; + } + currentNode.word = word; + } + + class TrieNode { + TrieNode[] children = new TrieNode[26]; + String word; + } + } +} diff --git a/Week 06/id_578/LeetCode_547_578.java b/Week 06/id_578/LeetCode_547_578.java new file mode 100644 index 000000000..06ec72881 --- /dev/null +++ b/Week 06/id_578/LeetCode_547_578.java @@ -0,0 +1,42 @@ +package com.hand.week6; + +import java.util.Arrays; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/11/24 + */ +public class LeetCode_547_578 { + public int findCircleNum(int[][] M) { + int[] parent = new int[M.length]; + Arrays.fill(parent, -1); + for (int i = 0; i < M.length; i++) { + for (int j = 0; j < M.length; j++) { + if (M[i][j] == 1 && i != j) { + union(parent, i, j); + } + } + } + int count = 0; + for (int i = 0; i < parent.length; i++) { + if (parent[i] == -1) + count++; + } + return count; + } + + public int find(int parent[], int i) { + if (parent[i] == -1) + return i; + return find(parent, parent[i]); + } + + public void union(int parent[], int p, int q) { + int rootP = find(parent, p); + int rootQ = find(parent, q); + if (rootP != rootQ) + parent[rootP] = rootQ; + } +} diff --git a/Week 06/id_588/LeetCode_208_588.java b/Week 06/id_588/LeetCode_208_588.java new file mode 100644 index 000000000..8683a2c06 --- /dev/null +++ b/Week 06/id_588/LeetCode_208_588.java @@ -0,0 +1,80 @@ +/** + * 208.实现Trie(前缀树) + * https://leetcode-cn.com/problems/implement-trie-prefix-tree/ + */ +public class LeetCode_208_588 { + + private TrieNode root; + + public LeetCode_208_588() { + + root = new TrieNode(); + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i ++) { + char ch = word.charAt(i); + if ( ! node.containsKey(ch)) { + node.put(ch, new TrieNode()); + } + node = node.get(ch); + } + node.setEnd(); + } + + private TrieNode searchPrefix(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i ++) { + char ch = word.charAt(i); + if (node.containsKey(ch)) { + node = node.get(ch); + } else { + return null; + } + } + return node; + } + + /** Returns if the word is in the trie. */ + public boolean search(String word) { + TrieNode node = searchPrefix(word); + return (null != node && node.isEnd()); + } + + /** Returns if there is any word in the trie that starts with the given prefix. */ + public boolean startsWith(String prefix) { + TrieNode node = searchPrefix(prefix); + return null != node; + } + + class TrieNode { + + private TrieNode[] links; + + private final int R = 26; + private boolean isEnd; + + public TrieNode() { + links = new TrieNode[R]; + } + + public boolean containsKey(char ch) { + return links[ch -'a'] != null; + } + public TrieNode get(char ch) { + return links[ch -'a']; + } + public void put(char ch, TrieNode node) { + links[ch -'a'] = node; + } + public void setEnd() { + isEnd = true; + } + public boolean isEnd() { + return isEnd; + } + } + +} diff --git a/Week 06/id_588/LeetCode_37_588.java b/Week 06/id_588/LeetCode_37_588.java new file mode 100644 index 000000000..8ce233efb --- /dev/null +++ b/Week 06/id_588/LeetCode_37_588.java @@ -0,0 +1,80 @@ +/** + * 37.解数独 + * https://leetcode-cn.com/problems/sudoku-solver/ + */ +public class LeetCode_37_588 { + + int n = 3; + int N = n * n; + + int [][] rows = new int[N][N + 1]; + int [][] columns = new int[N][N + 1]; + int [][] boxes = new int[N][N + 1]; + + char[][] board; + boolean sudokuSolved = false; + + public boolean couldPlace(int d, int row, int col) { + int idx = (row / n) * n + col / n; + return rows[row][d] + columns[col][d] + boxes[idx][d] == 0; + } + + public void placeNumber(int d, int row, int col) { + int idx = (row / n) * n + col / n; + rows[row][d] ++; + columns[col][d] ++; + boxes[idx][d] ++; + board[row][col] = (char)(d + '0'); + } + + public void removeNumber(int d, int row, int col) { + int idx = (row / n) * n + col / n; + rows[row][d] --; + columns[col][d] --; + boxes[idx][d] --; + board[row][col] = '.'; + } + + public void placeNextNumbers(int row, int col) { + + if ((col == N-1) && (row == N-1)) { + sudokuSolved = true; + } else { + if (col == N-1) { + backtrack(row + 1, 0); + } else { + backtrack(row, col + 1); + } + } + } + + public void backtrack(int row, int col) { + if ('.' == board[row][col]) { + for (int d = 1; d < 10; d ++) { + if (couldPlace(d, row, col)) { + placeNumber(d, row, col); + placeNextNumbers(row, col); + if ( !sudokuSolved) { + removeNumber(d, row, col); + } + } + } + } else { + placeNextNumbers(row, col); + } + } + + public void solveSudoku(char[][] board) { + this.board = board; + for (int i = 0; i < N; i ++) { + for (int j = 0; j < N; j ++) { + char num = board[i][j]; + if ('.' != num) { + int d = Character.getNumericValue(num); + placeNumber(d, i ,j); + } + } + } + backtrack(0, 0); + } +} diff --git a/Week 06/id_588/NOTE.md b/Week 06/id_588/NOTE.md index a6321d6e2..ffcaf61d0 100644 --- a/Week 06/id_588/NOTE.md +++ b/Week 06/id_588/NOTE.md @@ -1,4 +1,59 @@ -# NOTE +# 第六周学习总结 + +## 字典树(Trie树) + +典型应用是用于统计和排序大量的字符串(但不仅限于字符串),可最大限度地减少无谓的字符串比较,查询效率比哈希表高 + +基本性质: + +- 节点本身不存完整的单词 +- 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串 +- 每个节点的所有子节点路径代表的字符都不相同 + +## 并查集 + +适用场景:组团、配对问题 + +基本操作: + +- makeSet(s) +- unionSet(x, y) +- find(x) + + +------------------ + + + +在二叉搜索树中,查询、删除、插入等主要操作消耗的时间与树的高度有关。在最坏的情况下,二叉搜索树可能退化成链表,此时查找的效率会降至O(n)。所以,二叉树越是平衡,查找的效率越高。 + +## AVL树 +AVL树(即平衡二叉搜索树)的左子树的高度减去右子树的高度(即平衡因子)值的范围为{0, 1, -1} + +通过四种旋转操作,可以让树保持平衡: + +- 左旋 +- 右旋 +- 左右旋 +- 右左旋 + +AVL的不足: + +- 每个节点需要存储额外的信息(平衡因子) +- 调整次数比较频繁 + +## 红黑树 + +红黑树是一种近似平衡的二叉搜索树,它能确保任何一个节点的左右子树的高度差小于两倍。 + +红黑树的特点是: + +- 每个节点要么是红色,要么是黑色 +- 根节点是黑色 +- 每个叶子节点是黑色的 +- 不能有相邻接的两个红色节点 +- 在任一节点到其每个叶子的所有路径都包含相同数目的黑色节点 + diff --git a/Week 06/id_593/LeetCode_200_593.java b/Week 06/id_593/LeetCode_200_593.java new file mode 100644 index 000000000..e74f9029b --- /dev/null +++ b/Week 06/id_593/LeetCode_200_593.java @@ -0,0 +1,105 @@ +import java.util.Arrays; + +/** + * 200. 岛屿数量 + * 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围, + * 并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。 + *

+ * 示例 1: + *

+ * 输入: + * 11110 + * 11010 + * 11000 + * 00000 + *

+ * 输出: 1 + * 示例 2: + *

+ * 输入: + * 11000 + * 11000 + * 00100 + * 00011 + *

+ * 输出: 3 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/number-of-islands + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + */ +public class LeetCode_200_593 { + private final static int[] DX = new int[]{-1, 1, 0, 0}; + private final static int[] DY = new int[]{0, 0, -1, 1}; + + public int numIslands(char[][] grid) { + if (grid == null) { + return 0; + } + + UnionFind unionFind = new UnionFind(grid); + int row = grid.length; + int col = grid[0].length; + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + if (grid[i][j] == '1') { + for (int k = 0; k < 4; k++) { + int x = i + DX[k]; + int y = j + DY[k]; + if (x >= 0 && x < row && y >= 0 && y < col && grid[x][y] == '1') { + System.out.println(x * col + y + 1); + unionFind.union(i * col + j, x * col + y); + } + } + } + } + } + return unionFind.count; + } + + static class UnionFind { + private int count = 0; + private int[] parent; + + public int[] getParent() { + return parent; + } + + public UnionFind(char[][] grid) { + int row = grid.length; + int col = grid[0].length; + parent = new int[row * col]; + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + if (grid[i][j] == '1') { + count++; + parent[i * col + j] = i * col + j; + } + } + } + System.out.println(Arrays.toString(parent)); + } + + public int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + + public void union(int p, int q) { + int pRoot = find(p); + int qRoot = find(q); + if (pRoot == qRoot) { + return; + } + parent[qRoot] = pRoot; + count--; + } + + + } +} diff --git a/Week 06/id_593/LeetCode_208_593.java b/Week 06/id_593/LeetCode_208_593.java new file mode 100644 index 000000000..c34ddc3c6 --- /dev/null +++ b/Week 06/id_593/LeetCode_208_593.java @@ -0,0 +1,114 @@ +/** + * 208. 实现 LeetCode_208_593 (前缀树) + * 实现一个 LeetCode_208_593 (前缀树),包含 insert, search, 和 startsWith 这三个操作。 + *

+ * 示例: + *

+ * LeetCode_208_593 trie = new LeetCode_208_593(); + *

+ * trie.insert("apple"); + * trie.search("apple"); // 返回 true + * trie.search("app"); // 返回 false + * trie.startsWith("app"); // 返回 true + * trie.insert("app"); + * trie.search("app"); // 返回 true + * 说明: + *

+ * 你可以假设所有的输入都是由小写字母 a-z 构成的。 + * 保证所有输入均为非空字符串。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/implement-trie-prefix-tree + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + * @version 1.0 + */ +public class LeetCode_208_593 { + private TrieNode root; + + public LeetCode_208_593() { + this.root = new TrieNode(); + } + + public static TrieNode buildTrie(String[] words) { + LeetCode_208_593 trie = new LeetCode_208_593(); + for (String word : words) { + trie.insert(word); + } + return trie.root; + } + + public void insert(String word) { + TrieNode current = root; + for (int i = 0; i < word.length(); i++) { + char c = word.charAt(i); + if (current.get(c) == null) { + current.put(c, new TrieNode()); + } + current = current.get(c); + } + current.setEnd(); + current.setWord(word); + } + + public boolean search(String word) { + TrieNode node = searchPrefix(word); + return node != null && node.isEnd(); + } + + public boolean startsWith(String prefix) { + return searchPrefix(prefix) != null; + } + + private TrieNode searchPrefix(String word) { + TrieNode current = root; + for (int i = 0; i < word.length(); i++) { + char c = word.charAt(i); + if (current.containsKey(c)) { + current = current.get(c); + } else { + return null; + } + } + return current; + } +} + +class TrieNode { + private TrieNode[] nodes; + private String word; + private boolean isEnd; + + public TrieNode() { + this.nodes = new TrieNode[26]; + } + + public boolean containsKey(char c) { + return nodes[c - 'a'] != null; + } + + public TrieNode get(char c) { + return nodes[c - 'a']; + } + + public void put(char c, TrieNode node) { + nodes[c - 'a'] = node; + } + + public boolean isEnd() { + return isEnd; + } + + public void setEnd() { + this.isEnd = true; + } + + public void setWord(String word) { + this.word = word; + } + + public String getWord() { + return word; + } +} diff --git a/Week 06/id_593/LeetCode_212_593.java b/Week 06/id_593/LeetCode_212_593.java new file mode 100644 index 000000000..09aae0ae6 --- /dev/null +++ b/Week 06/id_593/LeetCode_212_593.java @@ -0,0 +1,79 @@ + +import java.util.ArrayList; +import java.util.List; + +/** + * 212. 单词搜索 II + *

+ * 给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。 + *

+ * 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。 + *

+ * 示例: + *

+ * 输入: + * words = ["oath","pea","eat","rain"] and board = + *

[ + *

['o','a','a','n'], + *

['e','t','a','e'], + *

['i','h','k','r'], + *

['i','f','l','v'] + *

] + *

+ * 输出: ["eat","oath"] + * 说明: + * 你可以假设所有输入都由小写字母 a-z 组成。 + *

+ * 提示: + *

+ * 你需要优化回溯算法以通过更大数据量的测试。你能否早点停止回溯? + * 如果当前单词不存在于所有单词的前缀中,则可以立即停止回溯。什么样的数据结构可以有效地执行这样的操作?散列表是否可行?为什么? 前缀树如何?如果你想学习如何实现一个基本的前缀树,请先查看这个问题: 实现Trie(前缀树)。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/word-search-ii + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + *

+ * 分析单词搜索 2 用 Tire 树方式实现的时间复杂度,请同学们提交在第 6 周的学习总结中。 + 平均时间的时间复杂度:O(m*n) + * + * @author jaryoung + */ +public class LeetCode_212_593 { + private static final char CHAR = '#'; + private int[] dx = new int[]{-1, 1, 0, 0}; + private int[] dy = new int[]{0, 0, -1, 1}; + + public List findWords(char[][] board, String[] words) { + TrieNode root = Trie.buildTrie(words); + List result = new ArrayList<>(words.length); + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + dfs(board, i, j, root, result); + } + } + return result; + } + + private void dfs(char[][] board, int i, int j, TrieNode node, List result) { + char c = board[i][j]; + if (c == CHAR || node.get(c) == null) { + return; + } + node = node.get(c); + String word = node.getWord(); + if (word != null) { + result.add(word); + // 避免重复数据 + node.setWord(null); + } + board[i][j] = CHAR; + for (int k = 0; k < 4; k++) { + int x = dx[k] + i; + int y = dy[k] + j; + if (x >= 0 && x < board.length && y >= 0 && y < board[0].length && board[x][y] != '#') { + dfs(board, x, y, node, result); + } + } + board[i][j] = c; + } +} diff --git a/Week 06/id_593/LeetCode_429_593.java b/Week 06/id_593/LeetCode_429_593.java new file mode 100644 index 000000000..6517c3379 --- /dev/null +++ b/Week 06/id_593/LeetCode_429_593.java @@ -0,0 +1,58 @@ +import java.util.*; + +/** + * 429. N叉树的层序遍历 + * 给定一个 N 叉树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)。 + *

+ * 例如,给定一个 3叉树 : + *

1 + *

/ / \ + *

3 2 4 + *

/\ + *

5 6 + * 返回其层序遍历: + *

+ * [ + * [1], + * [3,2,4], + * [5,6] + * ] + *   + *

+ * 说明: + *

+ * 树的深度不会超过 1000。 + * 树的节点总数不会超过 5000。 + *

+ *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + */ +public class LeetCode_429_593 { + + public List> levelOrder(Node root) { + if (root == null) { + return Collections.emptyList(); + } + Queue deque = new LinkedList<>(); + List> result = new ArrayList<>(); + deque.add(root); + while (!deque.isEmpty()) { + int size = deque.size(); + List nodeList = new ArrayList<>(); + for (int i = 0; i < size; i++) { + Node node = deque.poll(); + assert node != null; + nodeList.add(node.val); + if (node.children != null) { + deque.addAll(node.children); + } + } + result.add(nodeList); + } + return result; + } +} diff --git a/Week 06/id_593/LeetCode_547_593.java b/Week 06/id_593/LeetCode_547_593.java new file mode 100644 index 000000000..6c12914a4 --- /dev/null +++ b/Week 06/id_593/LeetCode_547_593.java @@ -0,0 +1,100 @@ +import java.util.Arrays; + +/** + * 547. 朋友圈 + *

+ * 班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。 + *

+ * 给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。 + *

+ * 示例 1: + *

+ * 输入: + * [[1,1,0], + * [1,1,0], + * [0,0,1]] + * 输出: 2 + * 说明:已知学生0和学生1互为朋友,他们在一个朋友圈。 + * 第2个学生自己在一个朋友圈。所以返回2。 + * 示例 2: + *

+ * 输入: + * [[1,1,0], + * [1,1,1], + * [0,1,1]] + * 输出: 1 + * 说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。 + * 注意: + *

+ * N 在[1,200]的范围内。 + * 对于所有学生,有M[i][i] = 1。 + * 如果有M[i][j] = 1,则有M[j][i] = 1。 + *

+ *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/friend-circles + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + */ +public class FriendCircles { + + public int findCircleNum(int[][] M) { + if (M == null) { + return 0; + } + int length = M.length; + UnionFind unionFind = new UnionFind(length); + for (int i = 0; i < length; i++) { + for (int j = 0; j < length; j++) { + if (M[i][j] == 1 && i != j) { + unionFind.union(i, j); + } + } + } + return unionFind.getCount(); + } + + static class UnionFind { + private int count = 0; + private int[] parent; + + UnionFind(int n) { + this.count = n; + this.parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + + void union(int p, int q) { + int pRoot = find(p); + int qRoot = find(q); + if (pRoot == qRoot) { + return; + } + parent[pRoot] = qRoot; + count--; + } + + int getCount() { + return count; + } + + @Override + public String toString() { + return "UnionFind{" + + "count=" + count + + ", parent=" + Arrays.toString(parent) + + '}'; + } + } +} diff --git a/Week 06/id_593/NOTE.md b/Week 06/id_593/NOTE.md index a6321d6e2..cae42bbda 100644 --- a/Week 06/id_593/NOTE.md +++ b/Week 06/id_593/NOTE.md @@ -1,4 +1,34 @@ -# NOTE +# + +## 总结一下 双向bfs的代码模版 Java 版 + +```java + public void ladderLength(graph, start, end) { + startQueue; + endQueue; + visited; + startQueue.add(start); + endQueue.add(end); + // 双端队列,从这里开始 + while (!startQueue.isEmpty() && !endQueue.isEmpty()) { + // 看那个队列的长度短,就处理那个队列 + if (endQueue.size() < startQueue.size()) { + tempQueue = startQueue; + startQueue = endQueue; + endQueue = tempQueue; + } + temp; + // 处理 开始队列 或者 结束队列 + for (node: startQueue) { + visited.add(node); + process(node); + temp = getRelatedNodes(node); + } + startQueue = temp; + // 处理其他逻辑 + } + } +``` diff --git a/Week 06/id_598/LeetCode_102_598.java b/Week 06/id_598/LeetCode_102_598.java new file mode 100644 index 000000000..3bfb822ab --- /dev/null +++ b/Week 06/id_598/LeetCode_102_598.java @@ -0,0 +1,88 @@ +import java.util.*; + +/** + * @author northleaf + * @create 2019年11月19日 + */ +public class LeetCode_102_598 { + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + + /** + * 使用BFS的方式进行遍历,借助于队列 + * @param root + * @return + */ + public List> levelOrder1(TreeNode root) { + List> ans = new ArrayList>(); + if (root == null) { + return ans; + } + + Queue queue = new LinkedList(); + queue.add(root); + while (!queue.isEmpty()){ + + List tmp = new ArrayList(); + //遍历queue + int size = queue.size(); + for(int i = 0;i> levelOrder(TreeNode root) { + Map> map = new HashMap>(); + helper(1,root,map); + return new ArrayList(map.values()); + } + + /** + * DFS遍历 + * @param level + * @param node + * @param map + */ + private void helper(int level, TreeNode node, Map> map) { + if(node == null){ + return; + } + List tmp = map.get(level); + if (tmp == null) { + tmp = new ArrayList(); + map.put(level,tmp); + } + tmp.add(node.val); + //下一层 + if(node.left!=null){ + helper(level+1,node.left,map); + } + if(node.right!=null){ + helper(level+1,node.right,map); + } + } +} diff --git a/Week 06/id_598/LeetCode_111_598.java b/Week 06/id_598/LeetCode_111_598.java new file mode 100644 index 000000000..575bab2ec --- /dev/null +++ b/Week 06/id_598/LeetCode_111_598.java @@ -0,0 +1,39 @@ +/** + * @author northleaf + * @create 2019年11月21日 + */ +public class LeetCode_111_598 { + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } + } + + /** + * 使用递归的方式处理 + * + * @param root + * @return + */ + public int minDepth(TreeNode root) { + + if (root == null) { + return 0; + } + if(root.left == null && root.right == null){ + return 1; + } + + int left = minDepth(root.left); + int right = minDepth(root.right); + if(root.left == null || root.right == null){ + return Math.max(left,right) +1; + } + return Math.min(left,right) + 1; + + } +} diff --git a/Week 06/id_598/LeetCode_200_598.java b/Week 06/id_598/LeetCode_200_598.java new file mode 100644 index 000000000..d8f9d57f7 --- /dev/null +++ b/Week 06/id_598/LeetCode_200_598.java @@ -0,0 +1,160 @@ +/** + * 岛屿数量 + * + * @author northleaf + * @create 2019年11月20日 + */ +public class LeetCode_200_598 { + /** + * 并查集的方法 + * @param grid + * @return + */ + public int numIslands1(char[][] grid) { + + + if (grid == null || grid.length < 1) { + return 0; + } + + UnionField unionField = new UnionField(grid); + //二维数组遍历 + int r = grid.length; + int c = grid[0].length; + + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + if(grid[i][j] == '1'){ + //此步的目的是为了路径压缩 + grid[i][j] = '0'; + if(i - 1 >= 0 && grid[i-1][j] == '1'){ + unionField.union(i*c +j ,(i -1 )* c +j ); + } + if(i+1 < r && grid[i+1][j] == '1' ){ + unionField.union(i*c +j ,(i+1) * c +j ); + } + if(j -1 >= 0 && grid[i][j-1] == '1'){ + unionField.union(i*c + j ,i*c +j -1); + } + if(j +1 < c && grid[i][j+1] == '1'){ + unionField.union(i*c + j ,i*c +j +1); + } + } + } + } + + return unionField.getSize(); + } + + + class UnionField { + int count = 0; + + int[] parent = null; + + /** + * 通过二维数组构造并查集 + * + * @param grid 二维方阵 + */ + public UnionField(char[][] grid) { + //行数 + int r = grid.length; + //列数 + int c = grid[0].length; + //构造数组 + parent = new int[r * c]; + //初始化时,只是交界处为1,才能数量加1 + for(int i = 0;i=r||j>=c||grid[i][j] == '0'){ + return; + } + //当前层处理 + //将1变为0 + grid[i][j] = '0'; + //处理四连通 + dfs(grid,i+1,j,r,c); + dfs(grid,i-1,j,r,c); + dfs(grid,i,j+1,r,c); + dfs(grid,i,j-1,r,c); + + } + +} diff --git a/Week 06/id_598/LeetCode_208_598.java b/Week 06/id_598/LeetCode_208_598.java new file mode 100644 index 000000000..edf18deb7 --- /dev/null +++ b/Week 06/id_598/LeetCode_208_598.java @@ -0,0 +1,107 @@ +/** + * @author northleaf + * @create 2019年11月19日 + */ +public class LeetCode_208_598 { + /** + * Your Trie object will be instantiated and called as such: + * Trie obj = new Trie(); + * obj.insert(word); + * boolean param_2 = obj.search(word); + * boolean param_3 = obj.startsWith(prefix); + */ + + + + + class Trie { + + class TrieNode{ + //是否为结尾,默认为false + boolean isEnd = false; + //默认定义26叉 + int R = 26; + //定义一个数组,用于存储 + TrieNode[] trieNodes ; + + /** + * 构造方法 + */ + public TrieNode() { + trieNodes = new TrieNode[26]; + } + + //插入字符 + public void put(char ch,TrieNode trieNode){ + trieNodes[ch - 'a'] = trieNode; + } + + public TrieNode get(char ch){ + return trieNodes[ch - 'a']; + } + + public boolean contains(char ch){ + return trieNodes[ch - 'a'] != null; + } + + /** + * 设置结尾 + */ + public void setEnd(){ + isEnd = true; + } + } + + + TrieNode root ; + + /** Initialize your data structure here. */ + public Trie() { + root = new TrieNode(); + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + TrieNode node = root; + + for(int i = 0; i findWords(char[][] board, String[] words) { + + List ans = new ArrayList(); + + //构造字典树 + WordTrie wordTrie = new WordTrie(); + for(int i = 0;i set = new HashSet(); + for(int i =0;i(set); + + } + + private void find(int i, int j, int m, int n, char[][] board, boolean[][] visited, Set set, TrieNode cur) { + //终止条件 + // i,j的值不能出数组的边界 + if(i<0 || j < 0 || i>=m||j >=n || visited[i][j] ){ + return; + } + //查找字符 + cur = cur.get(board[i][j]); + visited[i][j] =true; + //是否存在 + if (cur == null) { + //不存在,标记为未访问过 + visited[i][j] = false; + return; + } + //是否找到了单词 + if(cur.isEnd){ + set.add(cur.var); + } + //四通路遍历 + find(i+1,j,m,n,board,visited,set,cur); + find(i-1,j,m,n,board,visited,set,cur); + find(i,j+1,m,n,board,visited,set,cur); + find(i,j-1,m,n,board,visited,set,cur); + //将访问标记重置,下个节点可能会用到 + visited[i][j] =false; + } + + /** + * 字典树 + */ + class WordTrie{ + TrieNode root ; + + public WordTrie(){ + this.root = new TrieNode(); + } + + public void insertWord(String word){ + TrieNode node = root; + for(int i = 0;i generateParenthesis(int n) { + List ans = new ArrayList(); + heler(0,0,n,"",ans); + return ans; + } + + private void heler(int left, int right, int n,String s, List ans) { + if(left ==n && right == n){ + ans.add(s); + } + + if(left[] rows = new HashMap[9]; + Map[] cols= new HashMap[9]; + Map[] boxs= new HashMap[9]; + for(int i = 0;i<9;i++){ + rows[i] = new HashMap(); + cols[i] = new HashMap(); + boxs[i] = new HashMap(); + } + + //以二维数组的形式遍历 + for(int i = 0;i<9;i++){ + for(int j = 0;j<9;j++){ + char ch = board[i][j]; + int num = (int)ch; + if(ch != '.'){ + int boxNum = (i / 3) * 3 + j / 3; + rows[i].put(num,rows[i].getOrDefault(num,0)+1); + cols[j].put(num,cols[j].getOrDefault(num,0)+1); + boxs[boxNum].put(num, boxs[boxNum].getOrDefault(num,0)+1); + + if(rows[i].get(num) > 1 || cols[j].get(num)>1 || boxs[boxNum].get(num)>1 ){ + return false; + } + } + + } + } + return true; + } +} diff --git a/Week 06/id_598/LeetCode_547_598.java b/Week 06/id_598/LeetCode_547_598.java new file mode 100644 index 000000000..761161f58 --- /dev/null +++ b/Week 06/id_598/LeetCode_547_598.java @@ -0,0 +1,26 @@ +/** + * @author northleaf + * @create 2019年11月20日 + */ +public class LeetCode_547_598 { + public void dfs(int[][] M, int[] visited, int i) { + for(int j=0;j 左右子树结点平衡(recursively) +2、Banlanced + +## AVL树 +AVL树 +1、发明者G.M.Adelson-Velsky和Evgenii Landis +2、Balanced Factor(平衡因子) +是它的左子树的高度减去它的右子树的高度(有时相反) +balance factor = {-1, 0, 1} +3、通过旋转操作来进行平衡(四种) +4、https://en.wikipedia.org/wiki/Self-balancing_binary_search_tree + +### AVL的四种旋转操作 +1、左旋 +2、右旋 +3、左右旋 +4、右左旋 + +### AVL的旋转 +AVL的旋转 +1、右右子树 --> 左旋 +2、左左子树 --> 右旋 +3、先左子树,后右子树 --> 左右旋 +4、先右子树,后左子树 --> 右左旋 + +参考动画 +https://zhuanlan.zhihu.com/p/63272157https://zhuanlan.zhihu.com/p/63272157 + +旋转的图示 +https://en.wikipedia.org/wiki/Tree_rotation#/media/File:Tree_Rebalancing.gif + +### AVL总结 +AVL总结 +1、平衡二叉搜索树 +2、每个结点存balance factor = {-1, 0, 1} +3、四种旋转操作 + +不足:结点需要存储额外信息,且调整次数频繁 +因此需要引入其他的"近似平衡二叉树",不需要每次都非常严格的保持-1, 0, 1的平衡因子,从而提高整个树维护的效率 + + +## 红黑树 +Red-black Tree +红黑树是一种近似平衡的二叉搜索树,它能够确保任何一个结点的左右子树的高度差小于两倍。满足如下条件: +1)每个结点要么是红色,要么是黑色 +2)根节点是黑色 +3)每个叶结点 (NIL结点,空结点)是黑色,都是空结点 +4)不能有相邻的两个红色结点 +5)从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点 + +## AVL和红黑树对比 +AVL和红黑树对比 +1、AVL Trees provide faster lookup than Red-Black Trees because they are more strictly balanced. +2、Red-Black Trees provide faster insertion and removal operations than AVL Trees as fewer rotations are done due to relatively relaxed balancing. +3、AVL Trees store balance factors or heights with each node, thus requires storage for an integer per node whereas Red-Black Tree requires only 1bit (red or black) of information per node. +4、Red-Black Trees are used in most of the language libraries like map, multimap, multiset in C++ whereas AVL Trees are used in databases where faster retrievals are required. + diff --git a/Week 06/id_613/java/src/main/Trie.java b/Week 06/id_613/java/src/main/Trie.java new file mode 100644 index 000000000..31c8f67b6 --- /dev/null +++ b/Week 06/id_613/java/src/main/Trie.java @@ -0,0 +1,85 @@ +/** + * 实现Trie树 + * + * 时间复杂度O(N), 空间复杂度O(1) + */ +class TrieNode { + // R links to node children + private TrieNode[] links; + + private final int R = 26; + + private boolean isEnd; + + public TrieNode() { + links = new TrieNode[R]; + } + + public boolean containsKey(char ch) { + return links[ch -'a'] != null; + } + public TrieNode get(char ch) { + return links[ch -'a']; + } + public void put(char ch, TrieNode node) { + links[ch -'a'] = node; + } + public void setEnd() { + isEnd = true; + } + public boolean isEnd() { + return isEnd; + } +} + +public class Trie { + private TrieNode root; + + public Trie() { + root = new TrieNode(); + } + + /** + * Inserts a word into the trie. + */ + public void insert(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char currentChar = word.charAt(i); + if (!node.containsKey(currentChar)) { + node.put(currentChar, new TrieNode()); + } + node = node.get(currentChar); + } + node.setEnd(); + } + + private TrieNode searchPrefix(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char curLetter = word.charAt(i); + if (node.containsKey(curLetter)) { + node = node.get(curLetter); + } else { + return null; + } + } + return node; + } + + /** + * Returns if the word is in the trie. + */ + public boolean search(String word) { + TrieNode node = searchPrefix(word); + return node != null && node.isEnd(); + } + + /** + * Returns if there is any word in the trie that starts with the given prefix. + */ + public boolean startsWith(String prefix) { + TrieNode node = searchPrefix(prefix); + return node != null; + } +} diff --git a/Week 06/id_613/java/src/test/TrieTest.java b/Week 06/id_613/java/src/test/TrieTest.java new file mode 100644 index 000000000..078f6b039 --- /dev/null +++ b/Week 06/id_613/java/src/test/TrieTest.java @@ -0,0 +1,20 @@ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +/** + * + */ +public class TrieTest { + @Test + public void testSolutionOne1() { + Trie trie = new Trie(); + + trie.insert("apple"); + assertTrue(trie.search("apple")); + assertFalse(trie.search("app")); + assertTrue(trie.startsWith("app")); + trie.insert("app"); + assertTrue(trie.search("app")); + } +} + diff --git a/Week 06/id_618/LeetCode_127_618.java b/Week 06/id_618/LeetCode_127_618.java new file mode 100644 index 000000000..81590699d --- /dev/null +++ b/Week 06/id_618/LeetCode_127_618.java @@ -0,0 +1,76 @@ +/** + * 双向BFS + */ +class Solution { + public int ladderLength(String beginWord, String endWord, List wordList) { + // endWord也是transformed word, 所以必须存在于wordList中, 否则返回0, 表示无法从beginWord变成endWord + if (!wordList.contains(endWord)) { + return 0; + } + + HashMap> patterns = buildPatterns(beginWord, wordList); + + HashSet begin = new HashSet<>(); + HashSet end = new HashSet<>(); + begin.add(beginWord); + end.add(endWord); + + HashSet visited = new HashSet<>(); + int len = 1; + + while (!begin.isEmpty() && !end.isEmpty()) { + // 当前循环从哪个方向进行bfs; 让begin指向size更小的集合, 这样就不会一直从一个方向bfs了 + if (begin.size() > end.size()) { + HashSet tmp = begin; + begin = end; + end = tmp; + } + + HashSet nextWords = new HashSet<>(); + for (String word : begin) { + for (int i = 0; i < word.length(); i++) { + String pattern = word.substring(0, i) + "*" + word.substring(i + 1); + + ArrayList matchWords = patterns.get(pattern); + + if (matchWords == null) { + continue; + } + + for (String matchWord : matchWords) { + if (end.contains(matchWord)) { + return len + 1; + } + + if (!visited.contains(matchWord)) { + visited.add(matchWord); + nextWords.add(matchWord); + } + } + } + } + + begin = nextWords; + len++; + } + + return 0; + } + + private HashMap> buildPatterns(String beginWord, List wordList) { + HashMap> patterns = new HashMap<>(); + + for (String word : wordList) { + for (int i = 0; i < word.length(); i++) { + String pattern = word.substring(0, i) + "*" + word.substring(i + 1); + if (!patterns.containsKey(pattern)) { + patterns.put(pattern, new ArrayList()); + } + + patterns.get(pattern).add(word); + } + } + + return patterns; + } +} \ No newline at end of file diff --git a/Week 06/id_618/LeetCode_200_618.java b/Week 06/id_618/LeetCode_200_618.java new file mode 100644 index 000000000..490c1e0a4 --- /dev/null +++ b/Week 06/id_618/LeetCode_200_618.java @@ -0,0 +1,305 @@ +/** + * 深度搜索 + */ +class Solution1 { + + // 方向向量 + private int[][] directions = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } }; + + private int rows; + private int cols; + + private char[][] grid; + private boolean[][] visited; + + public int numIslands(char[][] grid) { + this.grid = grid; + this.rows = grid.length; + + if (rows == 0) { + return 0; + } + + this.cols = grid[0].length; + this.visited = new boolean[rows][cols]; + + int islandCount = 0; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (!this.isIsland(i, j)) { + continue; + } + + if (this.isVisited(i, j)) { + continue; + } + + islandCount++; + + this.visit(i, j); + } + } + + return islandCount; + + } + + private boolean inGrid(int i, int j) { + return i > -1 && j > -1 && i < this.rows && j < this.cols; + } + + private boolean isIsland(int i, int j) { + return this.grid[i][j] == '1'; + } + + private boolean isVisited(int i, int j) { + return this.visited[i][j]; + } + + private void visit(int i, int j) { + this.visited[i][j] = true; + + for (int k = 0; k < this.directions.length; k++) { + int nextI = i + directions[k][0]; + int newxtJ = j + directions[k][1]; + + if (!this.inGrid(nextI, newxtJ)) { + continue; + } + + if (!this.isIsland(nextI, newxtJ)) { + continue; + } + + if (this.isVisited(nextI, newxtJ)) { + continue; + } + + this.visit(nextI, newxtJ); + } + + } +} + +/** + * 广度搜索 + */ +class Solution2 { + + private int rows; + private int cols; + + private char[][] grid; + private boolean[][] visited; + + private Deque queue = new LinkedList<>(); + + // 方向向量 + private int[][] directions = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } }; + + private boolean inGrid(int i, int j) { + return i > -1 && j > -1 && i < this.rows && j < this.cols; + } + + private boolean isIsland(int i, int j) { + return this.grid[i][j] == '1'; + } + + private boolean isVisited(int i, int j) { + return this.visited[i][j]; + } + + public int numIslands(char[][] grid) { + this.grid = grid; + this.rows = grid.length; + + if (rows == 0) { + return 0; + } + + this.cols = grid[0].length; + this.visited = new boolean[rows][cols]; + + int islandCount = 0; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (!this.isIsland(i, j)) { + continue; + } + + if (this.isVisited(i, j)) { + continue; + } + + islandCount++; + + LinkedList queue = new LinkedList<>(); + queue.addLast(new Location(i, j)); + + this.visited[i][j] = true; + + while (!queue.isEmpty()) { + Location location = queue.removeFirst(); + int x = location.x; + int y = location.y; + + for (int k = 0; k < 4; k++) { + int nextX = x + directions[k][0]; + int nextY = y + directions[k][1]; + + if (!this.inGrid(nextX, nextY)) { + continue; + } + + if (!this.isIsland(nextX, nextY)) { + continue; + } + + if (this.isVisited(nextX, nextY)) { + continue; + } + + queue.addLast(new Location(nextX, nextY)); + this.visited[nextX][nextY] = true; + } + } + } + } + + return islandCount; + } + + private class Location { + int x; + int y; + + Location(int x, int y) { + this.x = x; + this.y = y; + } + } +} + +/** + * 并查集 + * + * @author hangwen + * + */ +class Solution3 { + // 方向向量 + private int[][] directions = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } }; + + private int rows; + private int cols; + + private char[][] grid; + private boolean[][] visited; + + private boolean inGrid(int i, int j) { + return i > -1 && j > -1 && i < this.rows && j < this.cols; + } + + public int numIslands(char[][] grid) { + this.grid = grid; + + if (grid.length == 0) { + return 0; + } + + this.rows = grid.length; + this.cols = grid[0].length; + + UnionFind uf = new UnionFind(grid); + + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + if (isIand(i, j)) { + grid[i][j] = '0'; + + for (int k = 0; k < 4; k++) { + int nextI = i + directions[k][0]; + int nextJ = j + directions[k][1]; + + if (!this.inGrid(nextI, nextJ)) { + continue; + } + + if (!this.isIand(nextI, nextJ)) { + continue; + } + + uf.union(i * cols + j, nextI * cols + nextJ); + + } + } + } + } + + return uf.getCount(); + } + + private boolean isIand(int i, int j) { + return grid[i][j] == '1'; + } + + class UnionFind { + int count; + int[] parent; + int[] rank; + + public UnionFind(char[][] grid) { + this.count = 0; + int rows = grid.length; + int cols = grid[0].length; + + this.parent = new int[rows * cols]; + this.rank = new int[rows * cols]; + + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + int location = i * cols + j; + + if (grid[i][j] == '1') { + parent[location] = location; + ++count; + } + + rank[location] = 0; + } + } + } + + public int find(int i) { // path compression + if (parent[i] != i) + parent[i] = find(parent[i]); + return parent[i]; + } + + public int getCount() { + return count; + } + + public void union(int x, int y) { // union with rank + int rootX = find(x); + int rootY = find(y); + + if (rootX == rootY) { + return; + } + + if (rank[rootX] > rank[rootY]) { + parent[rootY] = rootX; + } else if (rank[rootX] < rank[rootY]) { + parent[rootX] = rootY; + } else { + parent[rootY] = rootX; + rank[rootX] += 1; + } + + --count; + } + } +} \ No newline at end of file diff --git a/Week 06/id_628/LeetCode_208_628.java b/Week 06/id_628/LeetCode_208_628.java new file mode 100644 index 000000000..7bcb6e47d --- /dev/null +++ b/Week 06/id_628/LeetCode_208_628.java @@ -0,0 +1,88 @@ +//ʵһ Trie (ǰ׺) insert, search, startsWith +// +// ʾ: +// +// Trie trie = new Trie(); +// +//trie.insert("apple"); +//trie.search("apple"); // true +//trie.search("app"); // false +//trie.startsWith("app"); // true +//trie.insert("app"); +//trie.search("app"); // true +// +// ˵: +// +// +// Լе붼Сдĸ a-z ɵġ +// ֤Ϊǿַ +// +// Related Topics ֵ + + + +//leetcode submit region begin(Prohibit modification and deletion) +class LeetCode_208_628 { + private TrieNode root; + /** Initialize your data structure here. */ + public LeetCode_208_628() { + root = new TrieNode(); + root.val = ' '; + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + TrieNode ws = root; + for(int i = 0; i < word.length(); i++){ + char c = word.charAt(i); + if(ws.children[c - 'a'] == null){ + ws.children[c - 'a'] = new TrieNode(c); + } + ws = ws.children[c - 'a']; + } + ws.isWord = true; + } + + /** Returns if the word is in the trie. */ + public boolean search(String word) { + TrieNode ws = root; + for(int i = 0; i < word.length(); i++){ + char c = word.charAt(i); + if(ws.children[c - 'a'] == null) return false; + ws = ws.children[c - 'a']; + } + return ws.isWord; + } + + /** Returns if there is any word in the trie that starts with the given prefix. */ + public boolean startsWith(String prefix) { + TrieNode ws = root; + for(int i = 0; i < prefix.length(); i++){ + char c = prefix.charAt(i); + if(ws.children[c - 'a'] == null) return false; + ws = ws.children[c - 'a']; + } + return true; + } +} + + +class TrieNode { + public char val; + public boolean isWord; + public TrieNode[] children = new TrieNode[26]; + public TrieNode() {} + TrieNode(char c){ + TrieNode node = new TrieNode(); + node.val = c; + } +} + +/** + * Your Trie object will be instantiated and called as such: + * Trie obj = new Trie(); + * obj.insert(word); + * boolean param_2 = obj.search(word); + * boolean param_3 = obj.startsWith(prefix); + */ +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 06/id_628/LeetCode_547_628.java b/Week 06/id_628/LeetCode_547_628.java new file mode 100644 index 000000000..12d44f087 --- /dev/null +++ b/Week 06/id_628/LeetCode_547_628.java @@ -0,0 +1,94 @@ +// N ѧЩѣЩǡǵǴԡ֪ A B ѣB C ѣôǿΪ A Ҳ C ѡνȦָѵļϡ +// +// һ N * N ľ Mʾ༶ѧ֮ѹϵM[i][j] = 1ʾ֪ i j ѧΪѹϵΪ֪ѧе֪Ȧ +// +// ʾ 1: +// +// +//: +//[[1,1,0], +// [1,1,0], +// [0,0,1]] +//: 2 +//˵֪ѧ0ѧ1ΪѣһȦ +//2ѧԼһȦԷ2 +// +// +// ʾ 2: +// +// +//: +//[[1,1,0], +// [1,1,1], +// [0,1,1]] +//: 1 +//˵֪ѧ0ѧ1Ϊѣѧ1ѧ2Ϊѣѧ0ѧ2ҲѣһȦ1 +// +// +// ע⣺ +// +// +// N [1,200]ķΧڡ +// ѧM[i][i] = 1 +// M[i][j] = 1M[j][i] = 1 +// +// Related Topics 鼯 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class UnionFind { + private int count = 0; + private int[] parent; + public UnionFind(int n) { + count = n; + parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + public int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) return; + parent[rootP] = rootQ; + count--; + } + + public int getCount() { + return count; + } + public void setCount(int count) { + this.count = count; + } + public int[] getParent() { + return parent; + } + public void setParent(int[] parent) { + this.parent = parent; + } +} +class LeetCode_547_628 { + public int findCircleNum(int[][] M) { + int len = M.length; + UnionFind uf = new UnionFind(len); + for (int i = 0; i < len; i++) { + for (int j = 0; j < i; j++) { + if (M[i][j] == 1) { + uf.union(i, j); + } + } + } + return uf.getCount(); + } +} + + +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 06/id_628/NOTE.md b/Week 06/id_628/NOTE.md index a6321d6e2..6c688fd74 100644 --- a/Week 06/id_628/NOTE.md +++ b/Week 06/id_628/NOTE.md @@ -1,4 +1,132 @@ -# NOTE +# ܽ - +## ֪ʶ㡿 + +1ֵ + +2鼯 + +3߼-֦ + +4߼-˫bfs + +5߼-ʽA* + +6AVL + +## ѧϰܽ᡿ + +ģ塿 + +ֵ + +```java +class TrieNode { + public char val; + public boolean isWord; + public TrieNode[] children = new TrieNode[26]; + public TrieNode() {} + TrieNode(char c){ + TrieNode node = new TrieNode(); + node.val = c; + } +} + +public class Trie { + private TrieNode root; + public Trie() { + root = new TrieNode(); + root.val = ' '; + } + + public void insert(String word) { + TrieNode ws = root; + for(int i = 0; i < word.length(); i++){ + char c = word.charAt(i); + if(ws.children[c - 'a'] == null){ + ws.children[c - 'a'] = new TrieNode(c); + } + ws = ws.children[c - 'a']; + } + ws.isWord = true; + } + + public boolean search(String word) { + TrieNode ws = root; + for(int i = 0; i < word.length(); i++){ + char c = word.charAt(i); + if(ws.children[c - 'a'] == null) return false; + ws = ws.children[c - 'a']; + } + return ws.isWord; + } + + public boolean startsWith(String prefix) { + TrieNode ws = root; + for(int i = 0; i < prefix.length(); i++){ + char c = prefix.charAt(i); + if(ws.children[c - 'a'] == null) return false; + ws = ws.children[c - 'a']; + } + return true; + } +} +``` + +鼯 + +```java +class UnionFind { + private int count = 0; + private int[] parent; + public UnionFind(int n) { + count = n; + parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + public int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) return; + parent[rootP] = rootQ; + count--; + } +} +``` + +˫BFS + +```java +public void bfs(String start, String end) { + Set s1 = new HashSet(); + Set s2 = new HashSet(); + s1.add(start); + s2.add(end); + Set temp = new HashSet(); + while (!s1.isEmpty() && !s2.isEmpty()) { + for (String word : s1) { + if (s2.contains(word)) { + //TODO + process(word); + temp.add(word); + } + } + s1 = temp; + if (s1.size() > s2.size()) { + Set tmp = s1; + s1 = s2; + s2 = tmp; + } + } + } +``` diff --git a/Week 06/id_633/LeetCode_127_633.java b/Week 06/id_633/LeetCode_127_633.java new file mode 100644 index 000000000..f140b8850 --- /dev/null +++ b/Week 06/id_633/LeetCode_127_633.java @@ -0,0 +1,61 @@ +package lesson_14; + + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class LeetCode_127_633 { + + /** + * 双向bfs 每次遍历节点少的队列(set) 可能是交换遍历 + * @param beginWord + * @param endWord + * @param wordList + * @return + */ + public int ladderLength(String beginWord, String endWord, List wordList) { + Set _wordList = new HashSet<>(wordList); + if (!_wordList.contains(endWord)) { + return 0; + } + Set beginSet = new HashSet<>(); + Set endSet = new HashSet<>(); + int len = 1; + int strLen = beginWord.length(); + HashSet visited = new HashSet<>(); + beginSet.add(beginWord); + endSet.add(endWord); + while (!beginSet.isEmpty() && !endSet.isEmpty()) { + if (beginSet.size() > endSet.size()) { + Set set = new HashSet<>(); + set = beginSet; + beginSet = endSet; + endSet = set; + } + Set temp = new HashSet<>(); + for (String word: beginSet) { + char[] chs = word.toCharArray(); + for (int i = 0; i < chs.length; i++) { + for (char ch = 'a'; ch <= 'z'; ch++) { + char old = chs[i]; + chs[i] = ch; + String target = String.valueOf(chs); + if (endSet.contains(target)) { + return len + 1; + } + if (!visited.contains(target) && _wordList.contains(target)) { + temp.add(target); + visited.add(target); + } + chs[i] = old; + } + } + } + beginSet = temp; + len++; + } + return 0; + } + +} \ No newline at end of file diff --git a/Week 06/id_633/LeetCode_212_633.java b/Week 06/id_633/LeetCode_212_633.java new file mode 100644 index 000000000..ad2baa02c --- /dev/null +++ b/Week 06/id_633/LeetCode_212_633.java @@ -0,0 +1,73 @@ +package lesson_14; + +import java.util.ArrayList; +import java.util.List; + +/** + * 单词拼接 字典树实现时间复杂度分析 + * time: max(m * n + words.length * word.length * 4 ^ word.length) + */ +public class LeetCode_212_633 { + + public List findWords(char[][] board, String[] words) { + List res = new ArrayList<>(); + TrieNode root = buildTrie(words); + for (int i = 0; i < board.length; i++) { // m + for (int j = 0; j < board[0].length; j++) { // n + dfs(board, i, j, root, res); + } + } + return res; + } + + public void dfs(char[][] board, int i, int j, TrieNode node, List res) { + char ch = board[i][j]; + if (ch == '#' || node.children[ch - 'a'] == null) { + return; + } + node = node.children[ch - 'a']; + if (node.word != null) { + res.add(node.word); + node.word = null; + } + + board[i][j] = '#'; + if (i > 0) + dfs(board, i - 1, j, node, res); + if (j > 0) + dfs(board, i, j - 1, node, res); + if (i < board.length - 1) + dfs(board, i + 1, j, node, res); + if (j < board[0].length - 1) + dfs(board, i, j + 1, node, res); + board[i][j] = ch; + } + + /** + * O(words.length * word.length) + * @param words + * @return + */ + public TrieNode buildTrie(String[] words) { + TrieNode root = new TrieNode(); + for (String w: words) { // words.length + TrieNode node = root; + for (char ch: w.toCharArray()) { // word.length + int index = ch - 'a'; + if (node.children[index] == null) { + node.children[index] = new TrieNode(); + } + node = node.children[index]; + } + node.word = w; + } + return root; + } + + + class TrieNode { + TrieNode[] children = new TrieNode[26]; + String word; + } + +} diff --git a/Week 06/id_638/LeetCode_212_638.java b/Week 06/id_638/LeetCode_212_638.java new file mode 100644 index 000000000..71015d7c7 --- /dev/null +++ b/Week 06/id_638/LeetCode_212_638.java @@ -0,0 +1,89 @@ +package test1.Week6; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +public class LeetCode_212_638 { + int[] dx = {-1,1,0,0}; + int[] dy = {0,0,-1,1}; + /** + * 单词搜索 + * @param board + * @param words + * @return + */ + public List findWords(char[][] board, String[] words) { + wordTrie myTrie=new wordTrie(); + trieNode root=myTrie.root; + for(String s:words) + myTrie.insert(s); + Set result =new HashSet<>(); + int m=board.length; + int n=board[0].length; + boolean [][]visited=new boolean[m][n]; + for(int i=0;i(result); + } + private void find(char [] [] board, boolean [][]visited,int i,int j,int m,int n,Set result,trieNode cur){ + if(i<0||i>=m||j<0||j>=n||visited[i][j]) + return; + cur=cur.child[board[i][j]-'a']; + visited[i][j]=true; + if(cur==null) + { + visited[i][j]=false; + return; + } + if(cur.isLeaf) + { + result.add(cur.val); + } + for(int f = 0;f < 4;f++){ + find(board,visited,i+dx[f],j+dy[f],m,n,result,cur); + } + visited[i][j]=false; + } + + + /** + * 字典树 + */ + public class wordTrie{ + public trieNode root=new trieNode(); + public void insert(String s){ + trieNode cur=root; + for(char c:s.toCharArray()){ + if(cur.child[c-'a']==null){ + cur.child [c-'a'] = new trieNode(); + cur=cur.child[c-'a']; + }else + cur=cur.child [c-'a']; + } + cur.isLeaf=true; + cur.val=s; + } + } + + /** + * 字典树结点 + */ + public class trieNode{ + public String val; + public trieNode[] child=new trieNode[26]; + public boolean isLeaf=false; + + trieNode(){ + + } + + } + +} + + diff --git a/Week 06/id_638/LeetCode_547_638.java b/Week 06/id_638/LeetCode_547_638.java new file mode 100644 index 000000000..a673bc58a --- /dev/null +++ b/Week 06/id_638/LeetCode_547_638.java @@ -0,0 +1,48 @@ +package test1.Week6; + +public class LeetCode_547_638 { + + public int findCircleNum(int[][] M) { + int count = M.length; + UnionFind unionFind = new UnionFind(count); + for (int i = 0;i < count - 1;i++){ + for (int j = i + 1;j < count;j++){ + if (M[i][j] == 1){ + unionFind.union(i,j); + } + } + } + return unionFind.count; + } + + + + public class UnionFind{ + + private int count; + private int[] p; + public UnionFind(int count) { + this.p = new int[count]; + this.count = count; + for (int i = 0;i < count;i++){ + p[i] = i; + } + } + + public int findRoot(int i){ + while (p[i] != i){ + p[i] = p[p[i]]; + i = p[i]; + } + return p[i]; + } + + public void union(int m,int n){ + int rootM= findRoot(m); + int rootN= findRoot(n); + if (rootM == rootN) return; + p[rootN] = rootM; + count--; + } + } +} diff --git a/Week 06/id_643/LeetCode_208_643.java b/Week 06/id_643/LeetCode_208_643.java new file mode 100644 index 000000000..67a5ced5b --- /dev/null +++ b/Week 06/id_643/LeetCode_208_643.java @@ -0,0 +1,50 @@ +class TrieNode { + public char val; + public boolean isWord; + public TrieNode[] children = new TrieNode[26]; + public TrieNode() {} + TrieNode(char c){ + TrieNode node = new TrieNode(); + node.val = c; + } +} + +public class Trie { + private TrieNode root; + public Trie() { + root = new TrieNode(); + root.val = ' '; + } + + public void insert(String word) { + TrieNode ws = root; + for(int i = 0; i < word.length(); i++){ + char c = word.charAt(i); + if(ws.children[c - 'a'] == null){ + ws.children[c - 'a'] = new TrieNode(c); + } + ws = ws.children[c - 'a']; + } + ws.isWord = true; + } + + public boolean search(String word) { + TrieNode ws = root; + for(int i = 0; i < word.length(); i++){ + char c = word.charAt(i); + if(ws.children[c - 'a'] == null) return false; + ws = ws.children[c - 'a']; + } + return ws.isWord; + } + + public boolean startsWith(String prefix) { + TrieNode ws = root; + for(int i = 0; i < prefix.length(); i++){ + char c = prefix.charAt(i); + if(ws.children[c - 'a'] == null) return false; + ws = ws.children[c - 'a']; + } + return true; + } +} diff --git a/Week 06/id_643/LeetCode_212_643.java b/Week 06/id_643/LeetCode_212_643.java new file mode 100644 index 000000000..ae21c24a6 --- /dev/null +++ b/Week 06/id_643/LeetCode_212_643.java @@ -0,0 +1,46 @@ +public List findWords(char[][] board, String[] words) { + List res = new ArrayList<>(); + TrieNode root = buildTrie(words); + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + dfs (board, i, j, root, res); + } + } + return res; +} + +public void dfs(char[][] board, int i, int j, TrieNode p, List res) { + char c = board[i][j]; + if (c == '#' || p.next[c - 'a'] == null) return; + p = p.next[c - 'a']; + if (p.word != null) { // found one + res.add(p.word); + p.word = null; // de-duplicate + } + + board[i][j] = '#'; + if (i > 0) dfs(board, i - 1, j ,p, res); + if (j > 0) dfs(board, i, j - 1, p, res); + if (i < board.length - 1) dfs(board, i + 1, j, p, res); + if (j < board[0].length - 1) dfs(board, i, j + 1, p, res); + board[i][j] = c; +} + +public TrieNode buildTrie(String[] words) { + TrieNode root = new TrieNode(); + for (String w : words) { + TrieNode p = root; + for (char c : w.toCharArray()) { + int i = c - 'a'; + if (p.next[i] == null) p.next[i] = new TrieNode(); + p = p.next[i]; + } + p.word = w; + } + return root; +} + +class TrieNode { + TrieNode[] next = new TrieNode[26]; + String word; +} diff --git a/Week 06/id_648/LeetCode_200_648.java b/Week 06/id_648/LeetCode_200_648.java new file mode 100644 index 000000000..d95b91dfb --- /dev/null +++ b/Week 06/id_648/LeetCode_200_648.java @@ -0,0 +1,82 @@ +/** + * @Date 2019/11/24 21:48 + **/ +public class LeetCode_200_648 { + class UnionFind { + int count; + int[] parent; + int[] rank; + + public UnionFind(char[][] grid) { + count = 0; + int m = grid.length; + int n = grid[0].length; + parent = new int[m * n]; + rank = new int[m * n]; + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + if (grid[i][j] == '1') { + parent[i * n + j] = i * n + j; + ++count; + } + rank[i * n + j] = 0; + } + } + } + + public int find(int i) { + if (parent[i] != i) parent[i] = find(parent[i]); + return parent[i]; + } + + public void union(int x, int y) { + int rootx = find(x); + int rooty = find(y); + if (rootx != rooty) { + if (rank[rootx] > rank[rooty]) { + parent[rooty] = rootx; + } else if (rank[rootx] < rank[rooty]) { + parent[rootx] = rooty; + } else { + parent[rooty] = rootx; rank[rootx] += 1; + } + --count; + } + } + + public int getCount() { + return count; + } + } + + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + int nr = grid.length; + int nc = grid[0].length; + int num_islands = 0; + UnionFind uf = new UnionFind(grid); + for (int r = 0; r < nr; ++r) { + for (int c = 0; c < nc; ++c) { + if (grid[r][c] == '1') { + grid[r][c] = '0'; + if (r - 1 >= 0 && grid[r-1][c] == '1') { + uf.union(r * nc + c, (r-1) * nc + c); + } + if (r + 1 < nr && grid[r+1][c] == '1') { + uf.union(r * nc + c, (r+1) * nc + c); + } + if (c - 1 >= 0 && grid[r][c-1] == '1') { + uf.union(r * nc + c, r * nc + c - 1); + } + if (c + 1 < nc && grid[r][c+1] == '1') { + uf.union(r * nc + c, r * nc + c + 1); + } + } + } + } + return uf.getCount(); + } + +} diff --git a/Week 06/id_648/LeetCode_22_648.java b/Week 06/id_648/LeetCode_22_648.java new file mode 100644 index 000000000..6f016983a --- /dev/null +++ b/Week 06/id_648/LeetCode_22_648.java @@ -0,0 +1,26 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * @Date 2019/11/24 21:49 + **/ +public class LeetCode_22_648 { + public List generateParenthesis(int n) { + List ans = new ArrayList<>(); + backtrack(ans,"",0,0,n); + return ans; + } + public void backtrack(List ans,String cur,int open,int close,int max){ + if(cur.length()==2*max){ + ans.add(cur); + return; + } + if(open wordList) { + int count = 0; + + if (!wordList.contains(endWord)) { + return 0; + } + count++; + + HashMap commonMap = new HashMap(); + for (int i = 0; i < wordList.size(); i++) { + String word = wordList.get(i); + for (int j = 0; j < word.length(); j++) { + String temp = word.substring(0, j) + "*" + word.substring(j + 1); + ArrayList mapWords = commonMap.getOrDefault(temp, new ArrayList()); + mapWords.add(word); + commonMap.put(temp, mapWords); + } + } + + HashSet visitSet = new HashSet(); + HashSet begin = new HashSet(); + HashSet end = new HashSet(); + begin.add(beginWord); + end.add(endWord); + while (!end.isEmpty() && !begin.isEmpty()) { + System.out.println("while"); + + if (begin.size() > end.size()) { + HashSet temp = begin; + begin = end; + end = temp; + } + + HashSet nextSet = new HashSet(); + for (String word : begin) { + + if (end.contains(word)) { + return ++count; + } + + for (int i = 0; i < word.length(); i++) { + String temp = word.substring(0, i) + "*" + word.substring(i + 1); + + if (commonMap.containsKey(temp)) { + ArrayList list = commonMap.getOrDefault(temp, new ArrayList()); + + for (int j = 0; j < list.size()&&list.size()>0; j++) { + String next = (String) list.get(j); + if (end.contains(next)) { + return ++count; + } + if (!visitSet.contains(next)) { + nextSet.add(next); + visitSet.add(next); + } + } + } + + } + + + + } + if (nextSet.size() > 0) { + count++; + } + begin = nextSet; + + } + return 0; + } + +} \ No newline at end of file diff --git a/Week 06/id_653/LeetCode_547_653.java b/Week 06/id_653/LeetCode_547_653.java new file mode 100644 index 000000000..e27185cba --- /dev/null +++ b/Week 06/id_653/LeetCode_547_653.java @@ -0,0 +1,75 @@ +class Solution { + + public int findCircleNum(int[][] M) { + int len =M.length; + UnionFind unionFind = new UnionFind(len); + for (int i = 0; i < len; i++) { + for (int j = 0; j < M[i].length; j++) { + if (M[i][j]==1) { + unionFind.union(i,j); + } + } + } + return unionFind.getCount(); + } + + class UnionFind{ + + private int [] rank; + + public int getCount() { + return count; + } + + private int count; + private int [] parent; + + private int findP(int p) { + while (parent[p]!=p) { + p = parent[p]=parent[parent[p]]; + } + return p; + } + + public UnionFind(int count) { + this.count = count; + parent = new int[count]; + rank = new int[count]; + for (int i = 0; i < count; i++) { + parent[i] = i; + rank[i] = i; + } + } + + public void union(int p, int q) { + int pRoot = findP(p); + int qRoot = findP(q); + if (pRoot==qRoot) { + return; + } + if (rank[pRoot]>rank[qRoot]) { + parent[qRoot] = pRoot; + } else if (rank[qRoot]>rank[pRoot]) { + parent[pRoot] = qRoot; + } else { + parent[qRoot] = pRoot; + rank[p]++; + } + count--; + } + + + public int findCircleNum(int[][] M) { + int len =M.length; + UnionFind unionFind = new UnionFind(len); + for (int i = 0; i < len; i++) { + for (int j = 0; j < M[i].length; j++) { + if (M[i][j]==1) { + unionFind.union(i,j); + } + } + } + return unionFind.getCount(); + } + } +} \ No newline at end of file diff --git a/Week 06/id_658/LeetCode_127_658.java b/Week 06/id_658/LeetCode_127_658.java new file mode 100644 index 000000000..4443f4660 --- /dev/null +++ b/Week 06/id_658/LeetCode_127_658.java @@ -0,0 +1,51 @@ +/* + * @lc app=leetcode.cn id=127 lang=java + * + * [127] 单词接龙 + */ + +// @lc code=start +class Solution { + public int ladderLength(String beginWord, String endWord, List wordList) { + Map marked = new HashMap<>(); + Queue queue = new LinkedList(); + marked.put(beginWord, true); + queue.add(beginWord); + int len = 0; + while(!queue.isEmpty()){ + int size = queue.size(); + len++; + while(size-- > 0){ + String cur = queue.poll(); + for(String next: wordList){ + if(marked.get(next) != null){ + continue; + } + if(!canTransfer(cur, next)){ + continue; + } + if(endWord.equals(next)){ + return ++len; + } + queue.add(next); + marked.put(next, true); + } + } + } + + return 0; + } + + private boolean canTransfer(String s1, String s2){ + int index = 0; + int cnt = 0; + while(index < s1.length()){ + if(s1.charAt(index) != s2.charAt(index)) + cnt++; + index++; + } + + return cnt == 1; + } +} +// @lc code=end diff --git a/Week 06/id_658/LeetCode_208_658.java b/Week 06/id_658/LeetCode_208_658.java new file mode 100644 index 000000000..dfe467bcf --- /dev/null +++ b/Week 06/id_658/LeetCode_208_658.java @@ -0,0 +1,96 @@ +/* + * @lc app=leetcode.cn id=208 lang=java + * + * [208] 实现 Trie (前缀树) + */ + +// @lc code=start +class Trie { + + private TrieNode root; + + /** Initialize your data structure here. */ + public Trie() { + root = new TrieNode(); + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char currentChar = word.charAt(i); + if (!node.containsKey(currentChar)) { + node.put(currentChar, new TrieNode()); + } + node = node.get(currentChar); + } + node.setEnd(); + } + + /** Returns if the word is in the trie. */ + public boolean search(String word) { + TrieNode node = searchPrefix(word); + return node != null && node.isEnd(); + } + + /** + * Returns if there is any word in the trie that starts with the given prefix. + */ + public boolean startsWith(String prefix) { + TrieNode node = searchPrefix(prefix); + return node != null; + } + + private TrieNode searchPrefix(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char curLetter = word.charAt(i); + if (node.containsKey(curLetter)) { + node = node.get(curLetter); + } else { + return null; + } + } + return node; + } + + static class TrieNode { + + private TrieNode[] links; + + private final int R = 26; + + private boolean isEnd; + + public TrieNode() { + links = new TrieNode[R]; + } + + public boolean containsKey(char ch) { + return links[ch - 'a'] != null; + } + + public TrieNode get(char ch) { + return links[ch - 'a']; + } + + public void put(char ch, TrieNode node) { + links[ch - 'a'] = node; + } + + public void setEnd() { + isEnd = true; + } + + public boolean isEnd() { + return isEnd; + } + } +} + +/** + * Your Trie object will be instantiated and called as such: Trie obj = new + * Trie(); obj.insert(word); boolean param_2 = obj.search(word); boolean param_3 + * = obj.startsWith(prefix); + */ +// @lc code=end diff --git a/Week 06/id_658/LeetCode_36_658.java b/Week 06/id_658/LeetCode_36_658.java new file mode 100644 index 000000000..c04a9a620 --- /dev/null +++ b/Week 06/id_658/LeetCode_36_658.java @@ -0,0 +1,48 @@ +/* + * @lc app=leetcode.cn id=36 lang=java + * + * [36] 有效的数独 + */ + +// @lc code=start +class Solution { + public boolean isValidSudoku(char[][] board) { + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[i].length; j++) { + if (board[i][j] != '.' && !check(board, i, j)) { + return false; + } + } + } + return true; + } + + private boolean check(char[][] board, int x, int y) { + char num = board[x][y]; + for (int i = 0; i != y && i < board.length; i++) { + if (board[x][i] == num) { + return false; + } + } + + for (int i = 0; i != x && i < board.length; i++) { + if (board[i][y] == num) { + return false; + } + } + + int beginX = 3 * (x / 3); + int beginY = 3 * (y / 3); + for (int i = beginX; i < beginX + 3; i++) { + for (int j = beginY; j < beginY + 3; j++) { + if (i != x && j != y && board[i][j] == num) { + return false; + } + } + } + + return true; + + } +} +// @lc code=end diff --git a/Week 06/id_658/LeetCode_547_658.java b/Week 06/id_658/LeetCode_547_658.java new file mode 100644 index 000000000..38ca75f27 --- /dev/null +++ b/Week 06/id_658/LeetCode_547_658.java @@ -0,0 +1,51 @@ +/* + * @lc app=leetcode.cn id=547 lang=java + * + * [547] 朋友圈 + */ + +// @lc code=start +class Solution { + // public int findCircleNum(int[][] M) { + // int[] visited = new int[M.length]; + // int count = 0; + // Queue queue = new LinkedList<>(); + // for (int i = 0; i < M.length; i++) { + // if (visited[i] == 0) { + // queue.add(i); + // while (!queue.isEmpty()) { + // int s = queue.remove(); + // visited[s] = 1; + // for (int j = 0; j < M.length; j++) { + // if (M[s][j] == 1 && visited[j] == 0) + // queue.add(j); + // } + // } + // count++; + // } + // } + // return count; + // } + + public int findCircleNum(int[][] M) { + int[] visited = new int[M.length]; + int count = 0; + for (int i = 0; i < M.length; i++) { + if (visited[i] == 0) { + dfs(M, visited, i); + count++; + } + } + return count; + } + + public void dfs(int[][] M, int[] visited, int i) { + for (int j = 0; j < M.length; j++) { + if (M[i][j] == 1 && visited[j] == 0) { + visited[j] = 1; + dfs(M, visited, j); + } + } + } +} +// @lc code=end diff --git a/Week 06/id_658/NOTE.md b/Week 06/id_658/NOTE.md index a6321d6e2..e1132fbac 100644 --- a/Week 06/id_658/NOTE.md +++ b/Week 06/id_658/NOTE.md @@ -1,4 +1,126 @@ -# NOTE +# 第六周学习总结 - +## 字典树 Trie +### 基本结构 + +- 又称单词查找树或键树,是一种树形结构,多叉树 +- 典型的应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计 +- 最大限度地减少无谓的字符串比较,查询效率比哈希表高 + +### 基本性质 + +- 结点本身不存完整单词 +- 从根结点到某一结点,路径上经过的字符连接起来即为该结点对应的字符串 +- 每个结点的所有子结点路径代表的字符都不相同 + +### 核心思想 + +- Trie 树的核心思想是空间换时间 +- 利用字符串的公共前缀来降低查询时间的开销已达到提高效率的目的 + +## 并查集 Disjoint Set + +- 解决 `组团、配对` 场景的问题 +- 判断两个个体是否在一个集合中 / 两个群组是不是一个群组,合并群组 + +### 基本操作 + +- 解决问题主要要实现的三个函数 +- makeSet(s): 建立一个新的并查集,其中包含 s 个单元素集合 +- unionSet(x,y): 把元素 x 和元素 y 所在的集合合并,要求 x 和 y 所在的集合不相交,如果相交则不合并 +- find(x): 找到元素 x 所在的集合的代表,该操作也可以用于判断两个元素是否位于同一个集合,只要将他们各自的代表比较一下就可以了 + +### 实现 + +```java +class UnionFind { + private int count = 0; + private int[] parent; + + public UnionFind(int n) { + count = n; + parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + public int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) return; + parent[rootP] = rootQ; + count--; + } +} +``` + +## 高级搜索 + +### 初级搜索 + +- 朴素搜索 +- 深度优先搜索 `DFS: depth first search` +- 广度优先搜索 `BFS: breadth first search` + +#### 优化方式 + +- 不重复(fibonacci)、剪枝(生成括号问题) + - 当状态树的一个分支没有必要的时候就去掉不进行搜索 + - 不必要性来自重复或者分支为次优(找最优解时) +- 双向搜索、启发式搜索 + +### 双向 BFS + +- 从两端开始 BFS 中间重合的结点就是两端之间的最短路径 + +### 启发式搜索 Heuristic Search (A*) + +- 本质上通过优先级不断地去搜索 + +#### 估价函数 + +- 启发式函数:h(n) 用来评价哪些结点最有希望是一个我们要找的结点,h(n) 会返回一个非负实数,也可以认为是从结点 n 到目标结点路径的估计成本 +- 启发式函数是一种告知搜索方向的方法,它提供了一种明智的方法来猜测哪个邻居结点会导向一个目标 + +## 红黑树和 AVL 树 + +- 近似平衡二叉树,左右子树高度尽量是平衡的,并且左右子树以此类推下去都尽量是平衡的 + +### AVL树 + +- 平衡因子 Balance Factor : 左子树的**高度**减去右子树的**高度**(有时相反) + - balance factor 取值 {-1, 0, -1} + - 高度:查询二叉搜索树的效率只与高度有关 +- 通过旋转操作来进行平衡(四种) + - 左旋 右右子树 + - 右旋 左左子树 + - 左右旋 左右子树 先左旋再右旋 + - 右左旋 右左子树 先右旋再左旋 +- 不足:结点需要存储额外信息、且调整次数频繁 + +### 红黑树 + +- 是一种**近似平衡**的二叉搜索树,它能够确保任何一个结点的左右子树的**高度差小于两倍**(大的高度是小的高度的两倍) +- 每个结点要么是红色,要么是黑色 +- 根结点是黑色 +- 每个 叶结点(NIL 结点,空结点)是黑色的 +- 不能有相邻接的两个红色结点 +- 从任一结点到其每个叶子结点的所有路径都包含相同数目的黑色结点 + +### 对比 + +- AVL 树因为更加严格的平衡,所以比红黑树提供了更快的查找 +- 红黑树提供了更快的插入和删除操作,因为 AVL 树的旋转操作比较多 +- 因为 AVL 树需要存储的额外信息(factors or heights)更多一些,所以需要更多的内存,而红黑树只需要 `1 bit` 来存储0或1表示黑或者红 +- 读操作比较多、写操作比较少的场景下用 AVL 树,例如 databases +- 红黑树用在一些语言库中的 map set 中 diff --git a/Week 06/id_668/leetcode_127_668.py b/Week 06/id_668/leetcode_127_668.py new file mode 100644 index 000000000..3ce6a3efd --- /dev/null +++ b/Week 06/id_668/leetcode_127_668.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: word_ladder.py + @time: 2019/11/21 15:14 +""" +from collections import defaultdict + + +class Solution(object): + """ + 给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。 + 转换需遵循如下规则: + 每次转换只能改变一个字母。 + 转换过程中的中间单词必须是字典中的单词。 + + 说明: + 如果不存在这样的转换序列,返回 0。 + 所有单词具有相同的长度。 + 所有单词只由小写字母组成。 + 字典中不存在重复的单词。 + 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。 + + 示例 1: + 输入: + beginWord = "hit", + endWord = "cog", + wordList = ["hot","dot","dog","lot","log","cog"] + 输出: 5 + 解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", + 返回它的长度 5。 + + 示例 2: + 输入: + beginWord = "hit" + endWord = "cog" + wordList = ["hot","dot","dog","lot","log"] + 输出: 0 + 解释: endWord "cog" 不在字典中,所以无法进行转换。 + """ + + def ladder_length(self, begin_word, end_word, word_list): + """ + :type begin_word: str + :type end_word: str + :type word_list: List[str] + :rtype: int + + 从beginword开始,寻找wordlist中与beginword单词相差一个字母的单词,获取到这些单词,然后再往下分别 + 寻找与之相差一个字母的单词,直到首先找到endword。 + """ + if end_word not in word_list: + return 0 + + length = len(begin_word) + all_combo_dict = defaultdict(list) + + for word in word_list: + ''' + 对每个单词,以通配符对应该单词的方式记录下来,比如hot单词, + 记录方式为{'*ot':['hot'], 'h*t':['hot'], 'ho*':['hot']} + 把所有的单词分别都统计出来 + ''' + for i in range(length): + wildcard_word = '{0}*{1}'.format(word[:i], word[i + 1:]) + all_combo_dict[wildcard_word].append(word) + + ''' + 构造的all_combo_dict类型如下 + all_combo_dict = { + u'do*': [u'dot', u'dog'], + u'h*t': [u'hot'], + u'*ot': [u'hot', u'dot', u'lot'], + u'd*t': [u'dot'], + u'lo*': [u'lot', u'log'], + u'ho*': [u'hot'], + u'c*g': [u'cog'], + u'l*g': [u'log'], + u'd*g': [u'dog'], + u'*og': [u'dog', u'log', u'cog'], + u'co*': [u'cog'], + u'l*t': [u'lot'] + } + ''' + + queue = [(begin_word, 1)] + visited = {begin_word: True} + + while queue: + current_word, level = queue.pop(0) + + # 对每个单词可能的通配符单词进行全部对比,比如hit,会分别对比*it, h*t, hi* + for i in range(length): + intermediate_word = current_word[:i] + "*" + current_word[i + 1:] + + for word in all_combo_dict[intermediate_word]: + if word == end_word: + return level + 1 + + # Otherwise, add it to the BFS Queue. Also mark it visited + if word not in visited: + visited[word] = True + queue.append((word, level + 1)) + + return 0 + + def ladder_length2(self, begin_word, end_word, word_list): + """ + :type begin_word: str + :type end_word: str + :type word_list: List[str] + :rtype: int + + 国际站双向BFS + """ + import string + + if begin_word == end_word: + return 1 + + if end_word not in word_list: + return 0 + + q1, q2 = {begin_word}, {end_word} # Note: set not dict + d = {b: 1 for b in word_list} # either remove visted or hash + steps = 1 + + while q1 and q2: + if len(q1) > len(q2): + q1, q2 = q2, q1 # balance + + nq = set() + + for x in q1: + for i in range(len(x)): + for t in string.ascii_lowercase: + if x[i] == t: + continue + + y = x[:i] + t + x[i + 1:] + + if y in q2: + return steps + 1 + + if d.get(y, 0): + d[y] = 0 + nq.add(y) + + q1 = nq + steps += 1 + + return 0 + diff --git a/Week 06/id_668/leetcode_200_668.py b/Week 06/id_668/leetcode_200_668.py new file mode 100644 index 000000000..a4f3960c9 --- /dev/null +++ b/Week 06/id_668/leetcode_200_668.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: number_of_islands.py + @time: 2019/11/23 17:47 +""" + + +class Solution(object): + """ + 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围, + 并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。 + + 示例 1: + 输入: + 11110 + 11010 + 11000 + 00000 + 输出: 1 + + 示例 2: + 输入: + 11000 + 11000 + 00100 + 00011 + 输出: 3 + + """ + + def num_is_lands(self, grid): + """ + :type grid: List[List[str]] + :rtype: int + + 分析: + dfs的实现思路是以一个陆地为起点,将与之相连的陆地全部遍历,遍历完成,岛屿计数加1 + """ + if not grid: + return 0 + + def dfs(x, y): + # 显示的terminator + if x < 0 or y < 0 or x >= m or y >= n or grid[x][y] != '1': + return + + grid[x][y] = '#' # 标记当前的(x, y)位置已被处理,标记为#或者是水域都可以,因为已完成计数 + + for (dx, dy) in ((-1, 0), (1, 0), (0, -1), (0, 1)): + dfs(x + dx, y + dy) # 注意这里的x, y = x + dx, y + dy; dfs(x, y)这样书写的方式不可以 + + m, n, cnt = len(grid), len(grid[0]), 0 + + for i in range(m): + for j in range(n): + if grid[i][j] == '1': + dfs(i, j) # 将与(i,j)陆地相关联的陆地都完成遍历,并标示完成遍历的陆地已被处理,然后岛屿数加1 + cnt += 1 + + return cnt + + def num_is_lands2(self, grid): + """ + :type grid: List[List[str]] + :rtype: int + + 分析: + dfs的实现思路是以一个陆地为起点,将与之相连的陆地全部遍历,遍历完成,岛屿计数加1 + """ + if not grid: + return 0 + + def dfs(x, y): + # 不显示的terminator + grid[x][y] = '#' + + for (dx, dy) in ((-1, 0), (1, 0), (0, -1), (0, 1)): + next_x, next_y = x + dx, y + dy + + if 0 <= next_x < m and 0 <= next_y < n and grid[next_x][next_y] == '1': + dfs(next_x, next_y) + + m, n, cnt = len(grid), len(grid[0]), 0 + + for i in range(m): + for j in range(n): + if grid[i][j] == '1': + dfs(i, j) # 将与(i,j)陆地相关联的陆地都完成遍历,并标示完成遍历的陆地已被处理,然后岛屿数加1 + cnt += 1 + + return cnt + + def num_is_lands3(self, grid): + """ + :type grid: List[List[str]] + :rtype: int + + bfs思想,没有使用递归,将满足条件的陆地放入队列中,先进先出的处理。当把所有连在一起的陆地处理完成后,则一个岛屿处理完成; + 然后再处理下一个岛屿 + """ + from collections import deque + + if not grid: + return 0 + + m, n, cnt, visited = len(grid), len(grid[0]), 0, set() + + def bfs(x, y): + queue = deque() + queue.append((x, y)) + visited.add((x, y)) # 标示当前位置已被处理 + + while queue: + x, y = queue.popleft() + + for (dx, dy) in ((0, 1), (1, 0), (0, -1), (-1, 0)): + next_x, next_y = x + dx, y + dy + + if 0 <= next_x < m and 0 <= next_y < n \ + and grid[next_x][next_y] == '1' and (next_x, next_y) not in visited: + visited.add((next_x, next_y)) + queue.append((next_x, next_y)) + + for i in range(m): + for j in range(n): + if grid[i][j] == '1' and (i, j) not in visited: + bfs(i, j) + cnt += 1 + + return cnt + + def num_is_lands4(self, grid): + """ + :type grid: List[List[str]] + :rtype: int + + bfs思想,没有使用递归,将满足条件的陆地放入队列中,先进先出的处理。当把所有连在一起的陆地处理完成后,则一个岛屿处理完成; + 然后再处理下一个岛屿 + """ + from collections import deque + + if not grid: + return 0 + + m, n, cnt = len(grid), len(grid[0]), 0 + + def bfs(x, y): + queue = deque() + queue.append((x, y)) + grid[x][y] = '#' # 标示当前位置已被处理,修改的全局grid + + while queue: + x, y = queue.popleft() + + for (dx, dy) in ((0, 1), (1, 0), (0, -1), (-1, 0)): + next_x, next_y = x + dx, y + dy + + if 0 <= next_x < m and 0 <= next_y < n and grid[next_x][next_y] == '1': + grid[next_x][next_y] = '#' + queue.append((next_x, next_y)) + + for i in range(m): + for j in range(n): + if grid[i][j] == '1': + bfs(i, j) + cnt += 1 + + return cnt + + def num_is_lands5(self, grid): + """ + :type grid: List[List[str]] + :rtype: int + + 国际站上并查集的解法 + """ + if len(grid) == 0: + return 0 + row, col = len(grid), len(grid[0]) + self.count = sum(grid[i][j] == '1' for i in range(row) for j in range(col)) + parent = [i for i in range(row * col)] + + def find(x): + if parent[x] != x: + return find(parent[x]) + + return parent[x] + + def union(x, y): + xroot, yroot = find(x), find(y) + + if xroot == yroot: + return + + parent[xroot] = yroot + + self.count -= 1 + + for i in range(row): + for j in range(col): + if grid[i][j] == '0': + continue + + index = i * col + j + + if j < col - 1 and grid[i][j + 1] == '1': + union(index, index + 1) + + if i < row - 1 and grid[i + 1][j] == '1': + union(index, index + col) + + return self.count + diff --git a/Week 06/id_668/leetcode_212_668.py b/Week 06/id_668/leetcode_212_668.py new file mode 100644 index 000000000..87cec47f6 --- /dev/null +++ b/Week 06/id_668/leetcode_212_668.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: word_search_22.py + @time: 2019/11/23 14:44 +""" + + +class Solution(object): + def find_words(self, board, words): + """ + :type board: List[List[str]] + :type words: List[str] + :rtype: List[str] + + 复杂度分析: + 1. 对words数组中的单词构建Trie树的操作,假定words数组长度为N,单词word的平均长度为k,则构建Trie树的时间复杂度为O(N * k) + 2. 假定board为m * n的网格,在其中寻找所有单词首字母的操作,其时间复杂度为O(m * n) + 3. 一个单词的dfs操作,除了word的首字母之外,其余剩下的k-1个字母都有上下左右四个位置的寻找操作,因此这里最坏的时间复杂度为O(4 ^ (k-1)) + 即O(4 ^ k) + + 最好情况时间复杂度: + 最好情况是board中不存在words数组中任何一个单词word的首字母,即dfs操作没有进去,则此时整个操作的时间复杂度为max(O(N * k), O(m * n)) + + 最坏情况时间复杂度: + 最坏情况是每个单词都满足Trie树查找且每个联通方向都比较到了最后,因此dfs的时间复杂度为O(4 ^ k),所以整个操作的时间复杂度为 + max(O(N * k), O(m * n * 4 ^ k)) + + 分析: + 解决本体的宏观思路是: + 1. 对words中的所有word构建Trie树 + 2. 对Trie树的第一层key,即所有word的首字母,在board中寻找起始点;若存在则进一步往下验证 + """ + # 构建Trie树,并将words数组中所有的word都装进Trie中 + root, end_of_word = {}, '#' + + for word in words: + node = root + + for c in word: + node = node.setdefault(c, {}) + + node[end_of_word] = end_of_word + + # dfs + def dfs(board, i, j, cur_word, cur_node): + """ + board:二维网格,因为在dfs过程中有些位置已经被访问,所以board形态一直在变,因此需要当参数传递 + i, j:board的坐标 + cur_word:在四联通判断的过程中,当前已经形成的临时word + cur_node:当前访问到的Trie树的结点 + + 思考: + 这类递归写法很特别,没有显示的terminator语句(return),而且通常都是用一个数据容器比如list、tuple + 来添加结果 + """ + cur_word += board[i][j] # 这块是字符串的拼接,如果考虑优化,可使用join方法 + cur_node = cur_node[board[i][j]] + + if end_of_word in cur_node: + result.add(cur_word) + + tmp, board[i][j] = board[i][j], '@' # '@'标示已被访问过的位置 + + for (dx, dy) in ((0, -1), (0, 1), (-1, 0), (1, 0)): + x, y = i + dx, j + dy + + if 0 <= x < m and 0 <= y < n and board[x][y] != '@' and board[x][y] in cur_node: + # 二维坐标(x,y)合法,且该位置字符没有被访问过,且当前字符存在Trie树中 + # 满足上述条件,则可继续往下查找 + dfs(board, x, y, cur_word, cur_node) + + board[i][j] = tmp + + # 开始对二维网格board进行查找,root是一个字典对象,words数组中单词word的首字母均是root的key, + # 因此这里从满足条件的首字母开始判断 + result, m, n = set(), len(board), len(board[0]) # result用set存储,去除在board找到的重复的word + + for i in range(m): + for j in range(n): + if board[i][j] in root: + dfs(board, i, j, '', root) + + return list(result) + diff --git a/Week 06/id_673/friend-circles.cpp b/Week 06/id_673/friend-circles.cpp new file mode 100644 index 000000000..7b67f58cb --- /dev/null +++ b/Week 06/id_673/friend-circles.cpp @@ -0,0 +1,56 @@ +class Solution { +public: + int father[210]; + //查找祖先节点,当节点记录的祖先是自己,则表示查找到祖先了 + int findFather(int x) + { + while(x!=father[x]) + { + x = father[x]; + } + return x; + } + //合并节点:设置共同祖先 + void Union(int a,int b) + { + int fa = findFather(a); + int fb = findFather(b); + if(fa!=fb) + { + father[fa] = fb; + } + } + //最开始的时候,每个节点时分散的,都是自己的祖先 + void init() + { + for(int i=0;i<210;i++) + { + father[i] = i; + } + } + //主函数 + int findCircleNum(vector>& M) { + init(); + //对N个学生两两做判断 + for(int i=0;i>& board) { + //初始化 + memset(row,false,sizeof(row)); + memset(col,false,sizeof(col)); + memset(box,false,sizeof(box)); + + for(int i=0;i<9;i++){ + for(int j=0;j<9;j++){ + if(board[i][j]=='.') + continue; + int index=3*(i/3)+j/3; + int num=board[i][j]-'0'; + row[i][num]=col[j][num]=box[index][num]=true; + } + } + DFS(0,0,board); + } + + void DFS(int i,int j,vector>& board){ + if(solved==true) + return; + if(i>=9){ + solved=true; + return; + } + + //board[i][j]非空,考虑下一个位置 + if(board[i][j]!='.'){ + if(j<8) + DFS(i,j+1,board); + else if(j==8) + DFS(i+1,0,board); + if(solved==true) + return; + } + + //board[i][j]为空,可以填数 + else{ + int index=3*(i/3)+j/3; + for(int num=1;num<=9;num++){ + if(!row[i][num]&&!col[j][num]&&!box[index][num]) //num是否符合规则 + { board[i][j]=num+'0'; //填数 + row[i][num]=col[j][num]=box[index][num]=true; + + if(j<8) //递归 + DFS(i,j+1,board); + else if(j==8) + DFS(i+1,0,board); + + if(!solved){ //回溯 + row[i][num]=col[j][num]=box[index][num]=false; + board[i][j]='.'; + } + + } + } + } + } +}; diff --git a/Week 06/id_683/LeetCode_127_683.java b/Week 06/id_683/LeetCode_127_683.java new file mode 100644 index 000000000..bbe4c4d2a --- /dev/null +++ b/Week 06/id_683/LeetCode_127_683.java @@ -0,0 +1,97 @@ +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.Set; + +class Solution { + + + public int ladderLength(String beginWord, String endWord, List wordList) { + + Set wordSet = new HashSet<>(wordList); + if (!wordSet.contains(endWord)) return 0; + Set beginSet = new HashSet<>(); + Set endSet = new HashSet<>(); + + Set visited = new HashSet<>(); + beginSet.add(beginWord); + endSet.add(endWord); + int len = 1; + + while (!beginSet.isEmpty() && !endSet.isEmpty()) { + if (beginSet.size() > endSet.size()) { + Set tmp = beginSet; + beginSet = endSet; + endSet = tmp; + } + + Set next = new HashSet<>(); + for (String word : beginSet) { + char[] chs = word.toCharArray(); + for (int i = 0; i < chs.length; ++i) { + for (char ch = 'a'; ch <= 'z'; ++ch) { + char old = chs[i]; + chs[i] = ch; + String cur = new String(chs); + if (endSet.contains(cur)) { + return len + 1; + } + if (!visited.contains(cur) && wordSet.contains(cur)) { + next.add(cur); + visited.add(cur); + } + chs[i] = old; + } + } + } + beginSet = next; + len++; + } + return 0; + } + + + public int ladderLengthBFS(String beginWord, String endWord, List wordList) { + + Set visited = new HashSet<>(); + Queue queue = new LinkedList<>(); + visited.add(beginWord); + queue.offer(beginWord); + int len = 0; + + while (!queue.isEmpty()) { + len++; + int size = queue.size(); + while (size-- > 0) { + String cur = queue.poll(); + for (String word : wordList) { + if (visited.contains(word)) { + continue; + } + if (!canTransfer(cur, word)) { + continue; + } + if (endWord.equals(word)) { + return len + 1; + } + queue.add(word); + visited.add(cur); + } + } + } + return 0; + } + + private boolean canTransfer(String s1, String s2) { + int i = 0; + int count = 0; + while (i < s1.length()) { + if (s1.charAt(i) != s2.charAt(i)) { + count++; + } + i++; + } + return count == 1; + } +} \ No newline at end of file diff --git a/Week 06/id_683/LeetCode_208_683.java b/Week 06/id_683/LeetCode_208_683.java new file mode 100644 index 000000000..d13157cd5 --- /dev/null +++ b/Week 06/id_683/LeetCode_208_683.java @@ -0,0 +1,87 @@ +class TrieNode { + + private TrieNode[] links; + + private final int R = 26; + + private boolean end; + + + public TrieNode() { + links = new TrieNode[R]; + } + + public boolean containsKey(char ch) { + return links[ch - 'a'] != null; + } + + public TrieNode get(char ch) { + return links[ch - 'a']; + } + + public void put(char ch, TrieNode node) { + links[ch - 'a'] = node; + } + + public void setEnd() { + end = true; + } + + public boolean isEnd() { + return end; + } +} + +class Trie { + + private TrieNode root; + + /** Initialize your data structure here. */ + public Trie() { + root = new TrieNode(); + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); ++i) { + char ch = word.charAt(i); + if (!node.containsKey(ch)) { + node.put(ch, new TrieNode()); + } + node = node.get(ch); + } + node.setEnd(); + } + + /** Returns if the word is in the trie. */ + public boolean search(String word) { + TrieNode node = searchPrefix(word); + return node != null && node.isEnd(); + } + + private TrieNode searchPrefix(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); ++i) { + char ch = word.charAt(i); + if (!node.containsKey(ch)) { + return null; + } + node = node.get(ch); + } + return node; + } + + /** Returns if there is any word in the trie that starts with the given prefix. */ + public boolean startsWith(String prefix) { + return searchPrefix(prefix) != null; + } +} + +/** + * Your Trie object will be instantiated and called as such: + * Trie obj = new Trie(); + * obj.insert(word); + * boolean param_2 = obj.search(word); + * boolean param_3 = obj.startsWith(prefix); + */ \ No newline at end of file diff --git a/Week 06/id_683/NOTE.md b/Week 06/id_683/NOTE.md index a6321d6e2..0c917226e 100644 --- a/Week 06/id_683/NOTE.md +++ b/Week 06/id_683/NOTE.md @@ -1,4 +1,67 @@ -# NOTE +# 第六周总结 +## 字典树 + +字典树,即Trie树,又称单词查找树或键树,是一种树形结构。典型应用是用于统计和排序大量的字符串,所以经常被搜索引擎系统用于文本词频统计。 + +它的优点:最大限度的减少无谓的字符串比较,查询效率比哈希表高。 + +### 基本性质 + +1. 节点本身不存完整单词。 +2. 从根节点到某一节点,路径上经过的字符串连接起来,为该节点对应的字符串。 +3. 每个节点的所有子节点路径代表的字符都不相同。 + +### 核心思想 + +Trie树的核心思想是空间换时间 + +利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的 + +## 并查集 + +使用场景 + +- 组团、配对问题 +- Group or Not +## 双向BFS模板 + + public T doubleSideBFS(args) { + + Set<> beginSet, endSet; + Set<> visited; + + while (!beginSet.isEmpty() && !endSet.isEmpty()) { + + // 扩散步数小的边 + if (beginSet.size() > endSet.size()) { + Set tmp = beginSet; + beginSet = endSet; + endSet = tmp; + } + + Set nextSpreadSet; + while () { + + // process + // ... + visited.add(node); + nodes = generateRelatedNodes(); + nextSpreadSet.add(nodes); + + } + beginSet = nextSpreadSet; + } + + } + +## 红黑树 + +红黑树是一种近似平衡二叉树,它能确保任何一个节点的左右子树的高度差小于2倍。具体来说,红黑树是满足如下条件的二叉树: +- 每个节点要么是红色,要么使黑色 +- 根节点是黑色 +- 每个叶节点(NIL节点、空节点)是黑色的。 +- 不能有相邻的两个红色节点。 +- 从任一节点到其每个叶子节点的所有路径都包含相同数目的黑色节点 \ No newline at end of file diff --git a/Week 06/id_693/LeetCode_1091_693.java b/Week 06/id_693/LeetCode_1091_693.java new file mode 100644 index 000000000..8a78ad70f --- /dev/null +++ b/Week 06/id_693/LeetCode_1091_693.java @@ -0,0 +1,161 @@ +package id_693; + +import java.util.*; + +/** + * @Author 李雷(KyLin) + * @Desc 1091. 二进制矩阵中的最短路径 https://leetcode-cn.com/problems/shortest-path-in-binary-matrix/ + * @Date 2019/11/25 + */ +public class LeetCode_1091_693 { + /* + 1、DP解法 82 / 84 个通过测试用例 (因为只考虑了4个方向,方向没考虑齐全) TODO + 分解子问题:如果是0 j=n: f(i,j) = min(f(i - 1,j),f(j,j - 1),f(i - 1,j - 1)) + 1 + j!=n:f(i,j) = min(f(i - 1,j),f(j,j - 1),f(i - 1,j + 1),f(i - 1,j - 1)) + 1 + 否则是max (大于100 * 100) 题目说了长度最大为100 + 定义状态数组:dp[m][n] + DP方程: + */ + + class Solution { + public int shortestPathBinaryMatrix(int[][] grid) { + if (grid.length == 0 || grid[0].length == 0 || grid[0][0] == 1) { + return -1; + } + int m = grid.length; + int n = grid[0].length; + int[][] dp = new int[m + 1][n + 1]; + int max = 100 * 100; + dp[0][0] = 0;//可以不用 + for (int i = 1; i <= m; i++) { + dp[i][0] = max; + } + for (int i = 1; i <= n; i++) { + dp[0][i] = max; + } + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + if (grid[i - 1][j - 1] == 0) { + int min = Math.min(dp[i - 1][j - 1],Math.min(dp[i - 1][j],dp[i][j - 1])); + //处理边界 + if (j == n) { + dp[i][j] = min + 1; + } else { + dp[i][j] = Math.min(min,dp[i - 1][j + 1]) + 1; + } + } else { + dp[i][j] = max; + } + } + } + for (int[] nums : dp) { + System.out.println(Arrays.toString(nums)); + } + return dp[m][n] >= max ? -1 : dp[m][n]; + } + } + + public static void main(String[] args) { + /* + 0 0 0 + 1 1 0 + 1 1 0 + 0 max max max + max 1 2 3 + max max max 3 + max max max 4 + + + 0 1 + 1 0 + + 0 max max + max 1 max + max max 0 + + [[0,0,1], + [1,0,0], + [0,0,0]] + 0 max max max + max 1 2 max + max max 2 3 + max max+1 3 3 + + [[0,0,1,0,0,0,0], + [0,1,0,0,0,0,1], + [0,0,1,0,1,0,0], + [0,0,0,1,1,1,0], + [1,0,0,1,1,0,0], + [1,1,1,1,1,0,1], + [0,0,1,0,0,0,0]] + */ + +// System.out.println(new LeetCode_1091_693().new Solution2().shortestPathBinaryMatrix(new int[][]{{0,1},{1,0}})); + System.out.println(new LeetCode_1091_693().new Solution2().shortestPathBinaryMatrix(new int[][]{{0,0,0},{1,1,0},{1,1,0}})); + System.out.println(new LeetCode_1091_693().new Solution2().shortestPathBinaryMatrix( + new int[][]{{0,0,1,0,0,0,0} + ,{0,1,0,0,0,0,1} + ,{0,0,1,0,1,0,0} + ,{0,0,0,1,1,1,0} + ,{1,0,0,1,1,0,0} + ,{1,1,1,1,1,0,1} + ,{0,0,1,0,0,0,0}})); + System.out.println(new LeetCode_1091_693().new Solution2().shortestPathBinaryMatrix(new int[][]{{0,1,0,1,1,0,1,0,1,0,1,1,1,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,1,0,1,1,0,0,1,1,1,0,1,0,1,0,1,1,0,0,0,0,0,1,1,1,0,1,1,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1},{1,0,0,0,1,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,0,1,0,0,0,1,1,1,1,0,1,0,1,0,1,1,1,1,1,1,1,0,0,0,1,1,0,0,1,0,0,0,0,1,1,0,1,0,0,0,1,1,1,0,0,0,0,1,0,1,1,1,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1,0,0,0,0,1,1},{0,0,1,0,1,1,1,1,1,1,0,1,0,0,0,0,0,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,0,0,0,0,1,0,0,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,0,0,0,1,0,0,0,0,1,1,0,1,0,0,1,0,1,1,1,0,0,1,1},{0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,1,1,0,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,1,1,1,1,0,0,1,1,0,1,1,0,1,1,1,0,1,0,0,1,1,0,1,0,1,0,0,0,1,0,0,0,0,0,0,1,1,1,0,0,1,0,1,1,0,1,0,0,1,1,1,1,1,1,0,0,1,0,1,0,0,0,0,0,1,0,0},{0,0,1,1,1,0,1,1,1,1,0,1,1,0,1,1,1,0,1,1,1,1,0,1,0,1,1,0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,1,0,0,1,1,0,0,1,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,1,0,0,0,0,1,1,0,1,0,1,0,1,1,0,1,0,1,0,0,1,0,1,1,0,1,0},{1,0,1,1,1,1,0,0,0,0,0,1,0,0,0,1,1,0,1,1,1,0,1,1,0,0,0,0,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,1,1,0,0,1,1,1,1,0,0,0,0,0,1,1,0,1,0,1,0,1,1,1,1,0,0,0,0,0,1,1,1,0,1,1,1,1,1,1,1,1,0,0,0,1,1,1,0,0,1},{0,0,0,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,1,0,1,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,0,0,1,1,1,0,0,1,0,1,1,1,0,0,0,1,0,1,1,1,0,0,1,0,0,0,1,1,1,1,0,1,0,0,1,0,0,0,1,1,0,1,0,1,0,0,0,0,0},{1,0,1,0,0,1,0,0,1,0,0,1,0,0,1,1,0,1,0,0,0,0,1,0,1,0,0,1,1,1,0,1,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,0,0,0,1,0,1,0,1,0,1,0,0,1,1,1,1,0,1,1,1,0,0,1,0,1,0,1,1,0,1,1,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,1},{1,1,1,0,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,0,1,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,1,0,1,0,1,1,1,0,0,1,0,1,0,1,1,0,1,0,0,0,0,1,1,1,1,0,1,0,1,0,0,0,0,1,1,0},{0,1,0,1,1,0,1,1,1,0,0,0,1,0,0,1,1,1,0,0,1,1,1,0,1,1,0,1,0,1,0,0,0,0,0,1,0,1,1,0,0,1,0,1,1,1,0,1,0,0,0,1,0,0,1,0,0,0,1,0,1,1,1,0,0,1,1,1,1,0,0,0,1,1,1,0,1,0,1,0,0,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1},{1,0,0,1,0,0,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,1,1,1,0,0,0,1,1,0,1,0,1,0,0,0,1,0,1,1,0,0,0,0,0,1,0,0,1,1,0,1,0,0,1,0,1,0,1,0,0,0,1,1,1,1,0,0,1,1,0,1,1,0,1,1,0,0,1,0,1,1,0,0,1,0,1,1,0,0,1,1,0,0,0,0,1,0,0,1},{0,1,1,1,1,1,0,0,0,1,0,0,1,0,1,0,1,0,1,1,1,0,1,0,0,1,1,0,1,1,1,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,1,0,1,0,1,1,0,1,0,1,0,0,0,0,0,0,0,1,0},{0,0,0,0,0,1,1,1,0,1,0,0,0,1,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,0,1,0,0,0,1,1,1,0,1,1,0,1,0,1,1,1,0,1,1,1,1,1,0,0,1,0,1,1,1,0,0,1,1,1,1,0,0,1,0,0,1,0,1,1,1,1,0,1,0,1,1,0,0,1,0,0,0,1,1,1,1,0,1,0,0,1,1,1,0},{1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,1,0,1,1,1,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,0,1,0,0,1,1,0,0,0,0,0,1,1,0,1,1,1,1,0,0,0,1,0,1,0,1,0,1,0,1,1,1,1,1},{0,0,1,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,0,0,1,0,0,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0,1,0,1,0,1,0,1,0,0,1,1,0,1,0,1,1,1,1,0,1,1,0,1,1,1,1,0,1,0,0,1,1,1,1,1,0,0,0,1,0,1,1,1,0,1,0,0,0,0},{0,0,1,1,0,1,1,0,0,1,1,1,1,0,0,1,0,1,0,0,0,1,1,1,0,1,0,0,1,0,1,1,1,0,0,1,1,1,1,1,1,1,1,0,0,1,0,1,0,0,0,1,0,0,0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,0,1,1,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,1,0,1,0,1,1,0,0,1,1,0,0,0},{0,1,1,0,1,0,0,0,1,0,1,0,1,0,0,1,0,1,0,0,0,0,1,1,0,1,0,1,1,0,0,1,1,1,0,0,0,0,0,1,1,1,0,0,1,1,1,0,0,1,0,0,1,0,1,1,1,0,1,1,1,0,0,0,0,1,0,0,1,1,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,0,1,1,1,0,1,0,0,1,0,0,0,1,0},{0,0,1,0,0,1,1,1,1,0,0,0,1,1,0,1,1,0,1,1,0,1,1,0,0,0,1,0,0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,1,0,1,0,1,1,1,1,1,1,1,0,1,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,0,1,1,1,1,0,0,0,1,0,1,0,0,1,0,0,1,1,0,1,0,0,1,0,0,0,1,0},{1,0,0,1,1,0,1,0,0,0,1,0,0,0,1,1,1,1,0,0,0,1,1,0,1,1,0,1,0,1,1,1,1,1,1,0,0,0,1,0,1,0,0,0,1,1,1,0,0,1,0,1,1,0,0,0,0,0,0,0,1,1,1,0,1,1,0,0,0,0,0,0,0,1,0,1,1,0,0,1,1,1,1,1,0,1,0,1,0,1,1,1,1,0,0,1,0,1,1,1},{0,1,1,1,1,0,0,0,1,1,0,1,1,0,1,0,1,0,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,1,1,0,0,1,1,0,1,1,0,0,1,0,1,0,1,1,1,0,1,0,0,0,1,0,1,0,1,1,1,0,1,0,0,0,1,0,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1,1,0,0,1,0,1,0,0,0,0},{1,1,1,1,0,1,0,1,0,0,1,1,1,1,1,0,1,1,1,1,0,1,0,1,0,1,1,0,1,0,1,1,1,1,0,0,1,1,1,0,1,1,1,0,1,1,0,0,0,0,0,0,1,1,0,1,0,0,1,0,1,0,1,1,1,0,1,0,1,0,0,0,0,1,0,1,0,0,0,1,0,1,1,0,0,0,1,0,1,0,1,0,1,1,0,1,0,0,0,0},{0,0,0,1,1,1,0,0,0,1,1,1,1,1,0,1,0,0,1,0,1,0,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,0,1,1,0,0,1,1,1,0,1,1,1,1,0,0,0,1,0,0,0,1,1,0,1,0,1,0,1,1,1,1,0,0,1,1,0,1,1,0,1,1,0,1,0,0,1,0,1,0,0,1,1,0,0,0,1,0,0,0,1,0},{1,1,1,1,0,1,1,0,1,0,1,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,1,0,0,0,1,0,0,0,1,1,1,0,0,0,0,0,1,1,1,0,1,0,0,0,0,1,0,1,1,0,0,1,0,0,1,0,1,0,0,0,0,1,0,1,0,1,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,1,0,1,0,1,0,1,0,1,1,1},{1,0,0,1,1,0,0,0,1,1,0,0,0,1,1,0,1,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,1,1,1,0,1,0,1,1,0,0,1,0,0,1,1,1,1,1,0,1,1,0,0,1,0,0,1,0,0,0,1,0,0,0,1,1,0,0,1,1,1,0,0,1,1,0,1,1,1,1,1,0,1,0,0,1,0,0,1},{1,1,0,1,1,0,1,1,1,0,1,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,0,1,0,1,0,0,1,1,1,0,1,1,0,0,1,1,1,1,0,1,0,1,0,0,1,1,1,1,1,0,0,0,0,0,1,0,1,1,0,1,0,1,1,0,1,1,1,0,1,1,0,0,0,0,1,1,1,1,0,1,0,1,1,1,0,1,0,0,1,0,0,1,1},{0,1,0,0,1,0,1,1,0,0,1,1,1,0,0,1,1,0,1,1,0,0,1,1,1,1,1,0,0,1,1,1,1,0,1,1,1,1,0,0,1,1,0,0,0,0,1,0,1,1,0,0,1,0,1,0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,1,1,0,0,1,0,1,0,1,0,1,1,0,1,1,1,1,0,0,0,1,1},{0,1,0,0,0,1,0,0,0,1,1,0,0,0,1,1,1,1,0,1,0,1,1,1,0,1,1,0,0,0,1,1,0,0,0,0,1,0,1,0,1,1,0,1,1,0,0,1,0,1,1,0,0,0,0,0,1,0,0,0,1,0,1,0,1,1,1,0,0,1,0,0,0,0,1,1,0,1,0,1,0,0,1,1,0,1,1,0,0,0,0,1,1,0,1,0,1,1,0,1},{1,1,0,0,0,1,1,1,1,1,1,0,1,1,0,1,1,1,1,0,1,0,1,0,0,0,1,1,1,0,0,0,0,0,0,1,0,1,1,0,1,0,1,0,0,0,1,1,1,1,1,0,0,0,1,1,0,0,0,1,0,0,0,1,1,1,1,1,0,1,1,1,1,1,1,0,1,0,1,1,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,1},{1,0,0,0,0,1,1,0,1,0,1,1,0,1,1,1,0,1,0,0,0,0,1,1,0,0,0,0,1,0,0,0,0,1,0,1,0,1,0,1,1,0,1,0,1,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,1,0,1,1,1,1,0,0,0,1,1,0,1,0,0,0,0,0,0,1,0,0,1,1,0,1,1,0,1,0,0,0,0,1,1,0,1,0},{1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1,1,0,1,0,1,0,0,0,1,0,0,1,1,0,1,0,1,1,0,1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,1,1,0,0,0,1,1,0,1,0,0,1,0,0,0,1,1,1,0,0,1,0,0,0,0},{1,0,1,1,0,1,1,1,1,0,0,0,0,1,1,1,1,1,0,0,1,0,1,1,1,1,1,1,1,0,1,0,1,0,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,1,1,1,1,0,1,1,0,1,0,0,0,0,1,1,1,0,0,1,0,1,0,0,1,1,1,0,0,1,1,1,0,1,0,1,0,1,0,1,1,1,1,0,0,0,0,1,1,0,1},{1,0,0,1,1,1,0,1,0,0,1,1,1,0,0,1,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,1,0,0,1,1,0,0,0,1,1,1,0,0,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,0,1,1,0,0,1,0,0,0,0,1,1,0,0,1,1,1,0,1,0,1,1,1,1,1,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0},{0,0,1,1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,1,0,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,1,1,0,1,1,1,0,0,1,0,1,0,1,0,0,0,1,1,0,1,1,0,0,1,0,0,0,0,0,1,1,1,1,0,1,0,0,0,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,0,1},{0,1,0,1,0,0,0,0,0,0,1,0,1,0,0,0,1,1,1,0,0,1,0,1,0,1,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,0,1,0,1,1,1,1,0,0,0,0,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,0,1,0,1,1,0,1,1,0,1,0,0},{0,1,1,1,0,1,0,1,0,1,0,1,0,0,0,1,1,0,0,1,1,1,1,1,0,0,1,1,0,1,1,0,1,0,1,0,0,1,1,0,0,1,1,1,0,0,1,0,1,1,1,1,1,0,0,0,0,1,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,1,0,1,1,0,1,0,0,1,1,1,1,1,0,0,1,1,1,0,1,0},{1,0,0,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0,1,1,1,1,0,0,0,1,0,0,1,1,1,0,1,1,0,1,0,0,1,0,1,0,1,1,0,1,0,1,1,0,1,1,0,1,0,1,1,1,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,0,0,1,0,1,0,0,1,0,1,1,1,1,1,0,1,1},{1,0,0,1,0,1,1,0,0,1,1,0,1,1,0,1,0,0,0,1,1,0,1,0,0,1,1,0,0,0,1,0,1,0,0,0,0,1,1,1,0,1,0,0,0,0,1,1,0,0,0,0,1,0,0,1,1,1,0,1,0,0,0,1,0,1,1,0,1,1,0,1,1,0,0,1,0,1,1,1,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,1,0,0,0},{0,1,0,0,0,1,0,1,1,1,1,1,1,0,1,1,0,1,0,0,1,0,1,1,1,0,0,0,0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,1,0,0,1,1,0,0,0,0,1,0,1,0,0,1,0,1,0,1,0,0,1,0,1,0,0,0,1,0,0,1,0,0,1,0,1,1,0,1,1,0,1,0,0,0,0,1},{0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,1,0,1,1,1,0,1,0,1,1,1,0,1,0,1,0,0,0,1,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,1,0,0,0,1,1,1,0,1,1,0,1,0,1,0,0,1,0,1,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,0,1},{1,1,1,0,1,0,1,0,1,0,1,1,1,0,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,1,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,1,1,0,0,1,0,1,1,1,1,0,1,0,0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,1,1,1},{0,0,0,1,0,1,0,1,0,1,0,0,1,1,0,0,1,0,1,1,1,0,1,1,0,1,1,1,1,1,1,1,0,0,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,1,0,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,1,1,0,1,1,1,0,1,0,0,0,1,0,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,0,0,0,1},{1,0,1,1,0,0,0,1,1,1,0,0,1,0,1,1,1,0,1,0,1,1,1,0,0,0,0,0,1,1,1,0,1,0,1,0,0,0,0,1,0,0,1,1,0,0,0,0,0,1,1,1,1,0,0,1,0,0,0,1,0,1,0,1,0,1,1,1,0,1,0,0,1,0,1,1,1,1,0,0,0,1,0,0,0,1,1,0,1,0,0,0,0,1,1,1,1,0,1,1},{1,1,0,0,0,0,1,0,0,1,0,1,1,1,1,0,0,1,0,0,1,0,0,1,0,0,1,1,1,0,1,0,1,0,1,1,0,0,1,0,1,0,1,0,1,0,0,1,0,1,0,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0,1,0,1,1,0,1,1,1,0,0,1,0,0,1,0,1,0,0,0,1,1,1,1,1,1,0,1},{0,1,0,0,1,0,0,1,0,1,0,0,1,1,1,0,1,1,1,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,0,0,0,1,0,0,1,0,0,1,1,0,1,1,0,0,1,1,1,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,1,0,0,0,0,1,1,1,0,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{1,0,0,0,1,1,0,1,1,1,1,1,1,1,0,1,1,0,0,1,1,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,1,0,0,1,1,1,0,0,0,1,0,1,0,0,1,0,0,1,0,1,0,0,0,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1,0,1,1,0},{1,1,0,1,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,1,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,1,0,1,1,0,0,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,0},{0,1,1,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,1,1,0,1,0,0,0,1,0,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,0,1,1,1,0,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,1,1,0,0,0,1,1,1,0,0,0,1,0,0,0,1,0,0,0,0,1,1,1,0,0},{1,1,1,0,1,0,1,1,1,0,0,0,0,1,0,1,1,1,1,1,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,0,1,1,1,0,1,0,0,0,1,0,1,1,0,0,0,1,0,0,1,0,1,1,1,1,0,0,0,0,1,1,0,1,1,1,1,1,0,0,1,0,1,0,1,0,1,0,0,0,1,1,1,1,1,1,0,1,1,0},{0,1,0,0,0,0,0,1,1,1,0,1,1,1,1,0,1,1,0,1,1,1,1,0,1,1,1,0,0,1,1,1,0,1,0,0,0,1,0,1,1,1,0,0,0,1,1,1,0,0,1,1,1,0,0,1,0,1,1,1,0,0,1,0,0,1,1,0,1,0,1,0,0,0,0,0,1,0,1,0,1,0,1,0,0,1,0,1,1,1,1,0,0,0,1,1,1,1,1,0},{1,1,1,0,1,0,0,1,0,0,0,0,0,1,1,0,0,0,0,1,0,1,0,0,1,0,0,1,0,1,1,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,1,1,0,0,0,1,0,0,1,0,1,1,0,1,1,1,0,1,0,1,0,0,1,0,1,0,1,0,1,1,0,1,1,0,0,1,0,1,1,0,0,1,0},{1,0,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,1,1,1,1,0,1,0,0,1,0,1,1,1,1,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,1,0,1,0,0,1,0,1,0,1,1,1,1,1},{0,0,0,1,0,1,0,0,0,0,0,0,1,0,1,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,1,1,0,0,1,0,1,0,1,0,1,1,1,1,1,1,0,1,0,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,1,1,1,1,0,0,1,0,0,0,0,0,1,1,1,0,1,1,1,1,0,1,0,0,1,1,0},{1,0,1,0,0,0,0,0,1,1,1,0,1,0,0,0,1,1,0,1,0,1,1,1,1,0,1,1,1,0,0,0,1,0,1,1,1,0,0,1,1,0,1,1,0,0,1,0,0,0,0,0,1,1,1,1,0,1,1,1,1,1,0,0,0,1,0,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,0,1,0,1,0,1,1,0,1,0,0,0,0,0,1,1,1,1},{1,1,0,1,0,1,1,0,1,0,0,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,0,1,0,0,1,0,0,1,1,0,0,1,1,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,0,0,0,1,1,0,0,1,1,1,0,0,1,1,0,1,0,1,1,0,1,0,0,1,1,1,1},{1,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,1,0,0,1,1,1,1,1,0,1,1,1,1,1,0,1,0,0,1,1,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,1,0,0,0,0,0,1,0,0,1,1,1,1,0,1,1,1,1,0,1,1,0,1,0,1,1,0,1,1,1,0,0,0,1,1,1,0,1,0,0,1,0,0,0,1,0,1},{1,1,0,0,0,1,1,1,0,1,1,0,0,0,0,1,0,1,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,0,1,1,0,1,0,1,1,0,1,1,0,1,0,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,0,1,1,0,0,0,1,0,0,0,1,0,1,1,1,0,1,1,0,1,0,1,0,1,1,1,1,0,1,1,0,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,0,1,0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,1,1,1,1,1,1,0,1,0,0,1,1,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1,0,1,0,1,0,1},{0,0,1,1,1,1,0,1,0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,1,0,0,1,1,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,1,1,0,1,1,0,1,0,1,1,0,0,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1,1,0,0,0,1,1,1},{1,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0,1,1,0,1,0,1,1,1,0,1,1,1,1,0,1,1,1,0,0,1,0,0,1,1,1,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,1,0,1,1,1,1,1,0,1,0,0,0,1,1,0,0,0,0,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0},{0,1,1,1,0,0,0,1,0,0,0,0,1,1,1,1,1,0,0,1,0,1,0,0,1,1,0,0,0,0,0,0,1,1,1,0,1,0,1,1,0,0,0,0,0,1,0,1,0,1,1,0,1,0,0,1,1,1,1,0,1,0,0,0,1,1,0,0,0,0,1,1,1,1,0,1,0,0,1,1,1,1,1,1,0,1,1,0,1,0,1,0,0,1,1,1,0,0,0,0},{0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,1,1,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,1,1,1,1,0,1,0,0,0,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,0,0,1,0,0,0,1,0,1,1},{0,0,1,1,0,0,1,0,1,0,1,0,1,0,1,1,1,1,1,0,0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,0,1,0,1,1,0,1,1,0,1,0,1,1,1,1,1,1,0,1,1,1,1,1,0,0,1,1,1,1,1,1,0,1,1,0,1,0,1,0,1,1,1,0,1,0,0,0,0,0,1,0,1,1,0,0,1,0,1,0,1,1,0,0,0,1},{0,1,0,1,0,0,1,0,0,0,0,0,0,1,1,0,1,0,0,0,1,0,1,1,1,0,1,1,0,1,1,0,1,0,1,0,0,1,0,1,0,0,1,0,1,1,0,1,1,1,1,0,0,1,1,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,1,0,1,1,1,0,0,0,1,0},{0,0,0,1,1,1,1,0,1,1,0,0,1,0,0,1,0,0,1,1,1,1,1,1,1,0,1,1,1,0,0,1,1,0,0,0,1,1,0,1,0,1,0,1,0,1,1,0,0,0,1,0,0,0,1,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,1,0,1,1,0,0,1,0,1,0,0,1,0,1,0,1,0,1,1,0,1,1,1,0,0,0,0,1,1,1},{0,1,0,0,0,1,0,1,0,1,1,1,1,0,0,0,1,0,0,0,0,0,0,1,0,0,1,1,1,0,0,0,0,0,1,0,0,0,1,0,1,1,0,1,1,1,0,1,0,1,1,1,1,0,0,1,0,1,1,1,1,1,0,1,0,1,1,1,1,0,1,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,0,1,1,1,1,1,1,0,0,0,1,1,0,0},{0,1,0,1,0,1,1,1,0,1,1,1,1,0,0,0,1,1,1,0,1,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1,1,1,0,0,1,1,0,0,1,0,1,0,1,0,0,1,0,0,0,1,1,0,0,1,0,0,0,1,0,1,1,0,0,0,1,1,1,1,1,1,0,1,1,0,0,0,0,1,1,1,0,1,0,1,1,1,1,1,0,1},{0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,1,0,1,1,1,1,0,1,0,1,0,1,0,0,1,1,1,1,1,1,0,0,1,0,0,1,0,0,0,0,0,1,1,1,0,0,0,1,1,0,0,0,0,1,0,1,1,1,1,1,0,1,0,1,0,1,1,0,0,0,0,1,0,0,1,0,1,0,0,0,0,1,0,1,1,0,0,0,0,0,1,0,1,0,0},{0,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,1,1,0,1,0,1,1,0,0,1,0,1,0,0,1,0,1,0,1,0,0,1,1,0,0,0,0,0,1,0,0,0,1,1,0,1,1,0,0,0,1,1,0,1,1,0,0,1,0,1,1,1,0,1,1,1,1,0,0,0,1,0,1,1,1,0,0,0,1,1,1,1,0,1,1,0,0,1,0,1,1},{1,0,0,0,1,1,0,0,0,1,1,0,0,0,0,1,1,1,0,0,1,1,0,1,0,1,0,1,1,1,0,1,1,0,0,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,1,0,0,1,1,0,1,0,1,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,1,1,1,1,0,1,0,0},{0,0,1,0,0,0,1,0,0,1,0,0,0,0,1,0,1,0,0,0,0,1,0,1,0,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,0,1,1,0,1,0,1,1,1,0,0,0,0,0,1,0,0,0},{1,0,1,0,1,0,1,0,1,1,1,1,0,0,1,0,0,1,1,1,0,1,0,0,1,0,1,0,0,0,1,0,1,0,1,1,1,0,1,1,1,0,0,1,1,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,1,0,1,1,1,1,0,1,0,0,0,1,1,0,0,0,1,0,1,1,0,0,1,0,1,0,1,1,1,0,1,1,0,0,0,0,1,1,0,1},{1,1,0,1,0,1,1,1,1,1,0,0,0,0,1,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,0,0,0,1,0,0,0,0,1,0,1,0,1,1,1,1,0,0,1,1,0,1,0,1,1,1,0,1,0,0,0,0,0,1,1,1,0,0,1,0,0,0,0,1,1,1,1,1,0,0,0,0,1,1,1,0,0,1,1,1,1,0,0,0,1,1,0,1,0},{1,0,1,0,1,1,1,0,0,1,1,0,0,0,1,0,1,0,1,1,1,0,1,0,0,0,1,0,0,1,1,0,0,1,0,1,0,1,0,0,1,0,1,0,0,1,1,1,0,0,1,1,1,1,1,0,1,0,0,0,1,0,0,0,1,0,1,1,1,0,1,1,1,0,0,1,1,1,1,0,1,1,0,0,1,1,0,0,1,0,0,0,0,0,1,0,0,0,1,1},{0,1,1,1,1,0,1,0,1,1,1,0,0,0,1,1,1,1,0,0,0,0,1,0,1,1,1,1,1,1,0,0,1,1,1,0,0,1,0,1,1,1,1,1,0,1,0,0,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,0,0,0,1,1,1,1,1,0,0,1,1,0,1,0,0,0,1,0,0,0,1,0,0,1,0,0,0},{0,0,1,0,1,1,0,1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,1,0,0,0,0,1,0,1,1,0,1,0,0,0,0,1,0,1,1,1,0,0,0,0,1,1,0,1,1,1,1,1,0,0,1,0,1,1,1,1,1,1,1,0,0,1,0,1,0,0,1,1,1,0,0,0,1,1,0,0,1,0,0,1,1,0,1,0,0,1,1,1,0,0,0,1,0},{1,0,1,1,1,0,0,1,1,1,1,1,0,0,1,1,0,1,1,1,0,0,1,0,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1,0,1,1,0,0,1,1,0,1,0,0,0,1,0,1,0,1,1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,0,1,0,1,1,1,0,0,0,1,1,1,1,1,0,1,0,1,0,0,1,0,1,1,0,0,0,0,0},{1,0,1,0,1,0,0,0,0,1,0,1,1,1,0,1,1,1,0,1,0,0,0,1,0,1,1,1,1,1,0,1,1,1,1,0,1,0,0,1,0,1,1,0,1,1,1,0,0,0,1,0,1,0,0,0,0,1,0,1,0,1,0,0,1,0,1,1,0,1,1,0,1,0,1,1,1,0,0,0,1,1,0,0,1,1,1,1,0,0,0,1,1,0,1,0,0,1,1,1},{0,0,1,1,1,1,0,0,0,0,0,0,1,0,1,1,1,1,0,0,1,1,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,1,1,1,1,0,0,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,0,1,1,0,0,1,1,0,1,0,0,0,1,0,1,0,0,0,0,1,1,0,0,1,1,0},{0,0,0,0,1,0,0,0,0,0,1,1,1,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,0,0,0,0,1,0,1,1,0,1,0,0,0,0,0,0,1,1,1,1,1,0,0,0,1,0,1,0,1,0,1,0,1,0,0,0,0,1,0,1,0,0,0,0,1,1,1,0,1,1,1,1,1,0,1,0,1,0,1,0},{0,1,0,0,0,0,0,0,1,1,1,1,1,1,0,1,0,0,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,1,1,1,1,0,0,1,0,1,1,1,1,1,0,1,1,1,1,0,0,1,0,0,1,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,1,0,1,1,1,1,0,0,1,0,1,1,1,0,0,1,1,0},{0,1,0,0,0,1,1,1,1,0,0,1,0,1,1,1,0,1,1,1,0,0,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,0,0,1,0,1,0,0,1,0,1,0,1,0,0,0,0,0,1,0,1,1,1,1,1,1,0,0,1,1,0,1,1,1,1,0,1,0,1,0,1,0,1,0,0,1,1,1,1,1,0,1,0,1,1,0,1,0,0,0,0,0,1},{1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,0,0,1,1,0,1,0,0,1,0,1,0,0,1,0,1,0,0,0,0,1,0,1,0,0,0,1,1,1,1,1,0,0,0,0,1,1,0,1,1,1,1,1,0,0,0,1,0,0,0,0,0,1,1,0,1,0,1,1,1,1,1,0,1,1,0,1,0,1,1,0,0,0,0,0,1,1,1,0,1,0,1,1,1,1},{0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,0,0,1,0,1,0,0,1,0,1,0,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,0,0,0,0,1,1,1,0,1,0,1,1,1,0,0,1,1,1,1,0,1,1,0,0,1,0,1,1,1,0,1,1,0,0,1,1,0,0,0,1,0,1,1,1},{1,1,1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,1,0,0,1,0,0,0,0,0,1,1,0,1,0,0,1,1,0,1,0,0,1,0,1,1,1,1,0,1,1,1,0,0,0,1,1,1,1,0,0,1,0,0,1,1,1,1,1,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0},{1,0,1,0,1,1,0,1,0,1,1,1,0,1,1,1,0,0,1,0,0,0,0,0,0,1,0,1,1,1,1,0,0,1,1,1,0,0,0,0,1,0,1,0,0,0,1,1,1,0,1,1,0,0,1,1,1,1,0,0,1,1,0,1,1,0,0,0,1,0,1,0,1,0,1,1,1,1,0,1,1,1,1,0,1,1,1,0,1,1,1,0,0,0,1,0,0,1,1,0},{0,0,1,0,1,1,1,1,1,1,1,1,0,0,0,1,1,0,0,1,1,1,0,1,1,1,1,0,0,0,0,0,1,1,1,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,1,0,1,1,0,0,1,1,0,0,1,0,1,0,0,0,1,0,0,0,1,1,1,1,0,1,1,0,0,0,1,1,0,1,1,1,1,1,0,1,0,0,1,1,1,1},{0,1,0,1,0,0,1,1,0,1,0,1,0,1,0,1,0,0,0,0,1,0,1,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,1,1,1,1,0,1,0,0,0,0,0,1,1,0,1,1,1,1,1,1,0,1,1,0,1,0,0,0,1,0,1,1,0,1,0,0,1,1,0,1,1,1,0,0,1,0,1,1,1,1,0,1,0,0,0,1,1,1,1,0,1,1},{1,1,1,1,1,1,0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,0,1,1,0,1,0,1,1,0,1,1,1,1,0,0,1,0,0,1,1,1,0,1,1,0,0,0,0,1,0,0,0,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,1,1,1,1,0,0,1,1,0,1,0,0,1,1,1,1,0,0,0,0,0,0,0},{1,1,0,1,1,1,0,0,0,1,1,1,1,1,0,0,1,1,0,0,0,1,0,0,0,1,0,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1,0,1,1,1,0,0,0,1,1,0,1,1,1,1,0,0,1,1,0,0,1,0,1,1,1,1,0,0,0,1,0,0,0,1,0,1,0,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,0,1,0,0},{1,1,0,0,0,0,1,1,1,1,0,1,0,1,0,1,1,0,1,0,0,0,1,1,1,1,1,1,1,1,1,0,0,1,0,1,1,1,1,1,1,1,1,0,1,0,1,0,1,1,1,1,0,1,0,1,0,0,1,1,0,0,1,1,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,1,0,0,1},{0,0,1,1,1,1,0,0,1,0,1,0,1,1,0,0,0,0,0,0,1,0,1,0,0,1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,0,0,0,1,1,1,1,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,0,1,1,1,1,0,0,0,1,1,1,0,1,1,0,0,1,1,1,1,0,1,0,0,0,0,0,0,1,1,1,0,1,1,0,1,1},{1,0,1,1,1,1,1,1,1,1,1,0,0,0,0,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,0,1,0,1,1,1,0,0,1,1,1,0,1,1,0,1,1,1,1,1,1,1},{0,1,0,0,1,1,0,1,1,1,1,1,1,0,1,1,0,0,1,0,0,0,0,1,1,1,1,0,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,1,0,1,1,0,0,1,1,1,0,0,0,1,1,1,0,1,0,0,1,0,1,0,1,1,0,1,0,1,0,0,0,1,1,1,0,1,1,0,1,1,1,1,0,1,1},{1,1,0,1,0,0,1,1,1,1,0,1,0,0,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,1,1,0,1,1,1,0,1,0,0,1,1,1,0,0,1,0,1,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,0,1,0,0,0,0,0,1,1,1,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,1,0,0},{0,0,1,0,0,1,0,1,1,1,1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,1,1,0,1,1,1,1,1,0,1,0,1,0,1,0,1,1,0,1,1,1,0,0,0,0,1,1,1,0,0,0,1,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,1,0,1,0,1,1,0,0,0,0,1,0,0,1,0,1},{0,0,1,0,0,0,0,0,0,0,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0,1,1,0,1,1,1,0,0,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,0,1,0,0,0,1,0,1,0,1,0,1,0,0,0,1,1,0,1,1,1,1,1,0,0,0,1,1,0,1,0,1,0,1,1,0,0,1,0,1,0,1,0,1,0,1,1},{0,0,0,1,0,1,1,0,1,0,0,0,1,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,1,1,0,0,1,1,0,1,1,0,0,0,1,1,0,0,0,1,1,1,0,1,1,0,1,1,0,0,0,1,1,0,0,1,0,1,0,1,0,0,0,1,0,0,0,0,1,0,0,1,0,1,1,0,1,0,0,0},{0,1,0,1,0,1,0,1,1,1,0,0,0,1,0,0,1,0,1,0,1,1,0,0,0,1,1,1,1,1,1,1,1,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,0,0,1,0,0,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,0,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,1,0,0,1,1,0,1,0,1,1},{0,1,1,0,1,0,0,0,1,0,1,0,0,0,0,1,0,1,1,1,1,0,1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,0,1,1,0,1,1,0,1,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,1,1,0,1,1,0,1,0,1,0,1,0,1,1,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,0,0},{0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,1,1,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,1,1,0,0,1,1,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1,1,1,0,0,0,0,1,1,1,1,0,1,0}})); + } + + /* + 2、BFS解法 + */ + + class Solution2 { + public int shortestPathBinaryMatrix(int[][] grid) { + if (grid.length == 0 || grid[0].length == 0) { + return -1; + } + int m = grid.length; + int n = grid[0].length; + if (grid[0][0] == 1 || grid[m - 1][n - 1] == 1) { + return -1; + } + Deque deque = new ArrayDeque<>(); + deque.offer(new int[]{0,0}); + int[][] dir = {{1,0},{0,1},{-1,0},{0,-1},{-1,-1},{1,1},{-1,1},{1,-1}}; + + int path = 1; + while (!deque.isEmpty()) { + int size = deque.size(); + while (--size >= 0) { + int[] temp = deque.pop(); + int x = temp[0]; + int y = temp[1]; + System.out.println(x + " " + y); + if (x == m - 1 && y == n - 1) { + return path; + } + for (int j = 0; j < 8; j++) { + int nx = x + dir[j][0]; + int ny = y + dir[j][1]; + if (nx < 0 || ny < 0 || nx >= m || ny >= n || grid[nx][ny] == 1) { + continue; + } + deque.offer(new int[]{nx,ny}); + grid[nx][ny] = 1; + } + } + path++; + } + return -1; + } + } + + /* + 3、A*(启发式搜索)解法 TODO + */ + + class Solution3 { + public int shortestPathBinaryMatrix(int[][] grid) { + + return -1; + } + } +} diff --git a/Week 06/id_693/LeetCode_130_693.java b/Week 06/id_693/LeetCode_130_693.java new file mode 100644 index 000000000..8edf73657 --- /dev/null +++ b/Week 06/id_693/LeetCode_130_693.java @@ -0,0 +1,114 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 130. 被围绕的区域 https://leetcode-cn.com/problems/surrounded-regions/ + * @Date 2019/11/21 + */ +public class LeetCode_130_693 { + //dfs解法:先从外围搜索O 转变 成#,然后便利修改数据 + class Solution { + public void solve(char[][] board) { + if (board.length == 0 || board[0].length == 0) return; + int m = board.length; + int n = board[0].length; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + boolean edge = i == 0 || j == 0 || i == m - 1 || j == n - 1; + if (edge && board[i][j] == 'O') { + dfs(board,i,j); + } + } + } + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (board[i][j] == '#') { + board[i][j] = 'O'; + } else { + board[i][j] = 'X'; + } + } + } + + } + + private void dfs(char[][] board,int i,int j) { + if (i < 0 || j < 0 || i >= board.length || j >= board[i].length || board[i][j] != 'O') { + return; + } + board[i][j] = '#'; + dfs(board,i + 1,j); + dfs(board,i - 1,j); + dfs(board,i,j + 1); + dfs(board,i,j - 1); + } + } + + //并查集解法:直接把所有未O的边界组合在一个集合,然后进行其他便利,看周围是否有O,如果有 就和周围的合并,最后边界的O都成为一个集合了 + class Solution2 { + int[] parent; + int[] xFront = {1,-1,0,0}; + int[] yFornt = {0,0,-1,1}; + + private void unionBuild(int size) { + parent = new int[size]; + for (int i = 0; i < size; i++) { + parent[i] = i; + } + } + + private int find(int i) { + int root = i; + while (parent[i] != i) { + //路径压缩 + parent[i] = parent[parent[i]]; + i = parent[i]; + } + return i; + } + + private void union(int i,int j) { + int iSet = find(i); + int jSet = find(j); + if (iSet != jSet) { + parent[iSet] = jSet; + } + } + + public void solve(char[][] board) { + if (board.length == 0 || board[0].length == 0) return; + int m = board.length; + int n = board[0].length; + unionBuild(m * n + 1); + int oJihe = parent[m * n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + // 边界一个 + if (board[i][j] == 'O') { + if (i == 0 || j == 0 || i == m - 1 || j == n - 1) { + union(i * n + j,oJihe); + } else { + for (int k = 0; k < 4; k++) { + int x = xFront[k] + i; + int y = yFornt[k] + j; + if(board[x][y] == 'O') { + union(i * n + j,x * n + y); + } + } + } + } + } + } + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (find(i * n + j) == find(oJihe)) { + board[i][j] = 'O'; + } else { + board[i][j] = 'X'; + } + } + } + + } + } +} diff --git a/Week 06/id_693/LeetCode_200_693.java b/Week 06/id_693/LeetCode_200_693.java new file mode 100644 index 000000000..256436331 --- /dev/null +++ b/Week 06/id_693/LeetCode_200_693.java @@ -0,0 +1,89 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 200. 岛屿数量 https://leetcode-cn.com/problems/number-of-islands/ + * @Date 2019/11/21 + */ +public class LeetCode_200_693 { + // dfs + // 并查集 + + class Solution { + int count;//计数 + int[] parent;//数据存储 + int[] rank;//层数 + int[] xlist = {1,-1,0,0}; + int[] ylist = {0,0,1,-1}; + + public int numIslands(char[][] grid) { + if (grid.length == 0 || grid[0].length == 0) return 0; + unionBuild(grid); + int m = grid.length; + int n = grid[0].length; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == '1') { + grid[i][j] = '0'; + for (int k = 0; k < 4; k++) { + int a = xlist[k] + i; + int b = ylist[k] + j; + if (a >= 0 && b >= 0 && a < m && b < n && grid[a][b] == '1') { + union(i * n + j,a * n + b); + } + } + } + } + } + return count; + } + + private void unionBuild(char[][] grid) { + count = 0; + int m = grid.length; + int n = grid[0].length; + parent = new int[m * n]; + rank = new int[m * n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == '1') { + parent[i * n + j] = i * n + j; + count++; + } + rank[i * n + j] = 0; + } + } + } + + private int find(int i) { + int root = i; + while (parent[i] != i) { + i = parent[i]; + } + //路径压缩 (这种压缩方案比下面的那种更优) + while (root != i) { + int t = parent[root]; + parent[root] = i; + root = t; + } + return i; + } + + private void union(int i,int j) { + int iSet = find(i); + int jSet = find(j); + if (iSet != jSet) { + //使用rank进行路径压缩,没有上面的那种好, + if (rank[iSet] > rank[jSet]) { + parent[jSet] = iSet; + } else if (rank[iSet] < rank[jSet]) { + parent[iSet] = jSet; + } else { + parent[iSet] = jSet; + rank[jSet] += 1; + } + --count; + } + } + } +} diff --git a/Week 06/id_693/LeetCode_208_693.java b/Week 06/id_693/LeetCode_208_693.java new file mode 100644 index 000000000..0a28d7801 --- /dev/null +++ b/Week 06/id_693/LeetCode_208_693.java @@ -0,0 +1,91 @@ +package id_693; + +/** + * @Desc 208. 实现 Trie (前缀树) https://leetcode-cn.com/problems/implement-trie-prefix-tree/ + * @Auther 李雷(KyLin) + * @Date 2019/11/18 + */ +public class LeetCode_208_693 { + class Trie { + TreeNode root; + + /** + * Initialize your data structure here. + */ + public Trie() { + this.root = new TreeNode('#'); + } + + /** + * Inserts a word into the trie. + */ + public void insert(String word) { + TreeNode temp = root; + for (int i = 0; i < word.length(); i++) { + char ch = word.charAt(i); + if (temp.elements[ch - 'a'] == null) { + temp.elements[ch - 'a'] = new TreeNode(ch); + } + temp = temp.elements[ch - 'a']; + } + temp.setEnable(); + } + + /** + * Returns if the word is in the trie. + */ + public boolean search(String word) { + TreeNode temp = root; + for (int i = 0; i < word.length(); i++) { + char ch = word.charAt(i); + if (temp.elements[ch - 'a'] == null) { + return false; + } + temp = temp.elements[ch - 'a']; + } + return temp.isEnable(); + } + + /** + * Returns if there is any word in the trie that starts with the given prefix. + */ + public boolean startsWith(String prefix) { + TreeNode temp = root; + for (int i = 0; i < prefix.length(); i++) { + char ch = prefix.charAt(i); + if (temp.elements[ch - 'a'] == null) { + return false; + } + temp = temp.elements[ch - 'a']; + } + return true; + } + + class TreeNode { + TreeNode[] elements; + char val; + boolean isEnable = false; + + public TreeNode(char val) { + this.val = val; + this.elements = new TreeNode[26]; + } + + void setEnable() { + isEnable = true; + } + + boolean isEnable() { + return isEnable; + } + } + } + +/** + * Your Trie object will be instantiated and called as such: + * Trie obj = new Trie(); + * obj.insert(word); + * boolean param_2 = obj.search(word); + * boolean param_3 = obj.startsWith(prefix); + */ +} diff --git a/Week 06/id_693/LeetCode_212_693.java b/Week 06/id_693/LeetCode_212_693.java new file mode 100644 index 000000000..191da4f0a --- /dev/null +++ b/Week 06/id_693/LeetCode_212_693.java @@ -0,0 +1,74 @@ +package id_693; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @Author 李雷(KyLin) + * @Desc 212. 单词搜索 II https://leetcode-cn.com/problems/word-search-ii/ + * @Date 2019/11/21 + */ +public class LeetCode_212_693 { + //单词树解法 + class Solution { + public List findWords(char[][] board,String[] words) { + Set result = new HashSet<>(); + if (board.length == 0 || board[0].length == 0) return new ArrayList<>(result); + TreeNode root = buildTree(words); + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[i].length; j++) { + dfs(result,board,root,i,j); + } + } + return new ArrayList<>(result); + } + + private void dfs(Set result,char[][] board,TreeNode root,int i,int j) { + if (i < 0 || j < 0 || i >= board.length || j >= board[i].length || board[i][j] == '#') { + return; + } + root = root.childrens[board[i][j] - 'a']; + if (root == null) { + return; + } + if (root.isWord) { + result.add(root.val); + } + char temp = board[i][j]; + board[i][j] = '#'; + dfs(result,board,root,i + 1,j); + dfs(result,board,root,i - 1,j); + dfs(result,board,root,i,j - 1); + dfs(result,board,root,i,j + 1); + board[i][j] = temp; + } + + private TreeNode buildTree(String[] words) { + TreeNode root = new TreeNode(); + for (String word : words) { + TreeNode temp = root; + for (char ch : word.toCharArray()) { + if (temp.childrens[ch - 'a'] == null) { + temp.childrens[ch - 'a'] = new TreeNode(); + } + temp = temp.childrens[ch - 'a']; + } + temp.isWord = true; + temp.val = word; + } + return root; + } + + class TreeNode { + String val; + TreeNode[] childrens; + boolean isWord = false; + + public TreeNode() { + this.childrens = new TreeNode[26]; + } + } + } +} diff --git a/Week 06/id_693/LeetCode_36_693.java b/Week 06/id_693/LeetCode_36_693.java new file mode 100644 index 000000000..4dfea8770 --- /dev/null +++ b/Week 06/id_693/LeetCode_36_693.java @@ -0,0 +1,34 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 36.有效的数独 https://leetcode-cn.com/problems/valid-sudoku/ + * @Date 2019/11/24 + */ +public class LeetCode_36_693 { + class Solution { + boolean[][] row = new boolean[9][9]; + boolean[][] col = new boolean[9][9]; + // 分9个区域,(行/3)* 3 + (列 / 3) 表示当前区域 + boolean[][] area = new boolean[9][9]; + + public boolean isValidSudoku(char[][] board) { + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + if (board[i][j] != '.') { + int areaIndex = (i / 3) * 3 + (j / 3); + int val = board[i][j] - '0' - 1; + if (area[areaIndex][val] || row[i][val] || col[j][val]) { + return false; + } else { + area[areaIndex][val] = true; + row[i][val] = true; + col[j][val] = true; + } + } + } + } + return true; + } + } +} diff --git a/Week 06/id_693/LeetCode_37_693.java b/Week 06/id_693/LeetCode_37_693.java new file mode 100644 index 000000000..cd142fd26 --- /dev/null +++ b/Week 06/id_693/LeetCode_37_693.java @@ -0,0 +1,59 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 37. 解数独 https://leetcode-cn.com/problems/sudoku-solver/ + * @Date 2019/11/24 + */ +public class LeetCode_37_693 { + //回溯+dfs 解法 + class Solution { + boolean[][] row = new boolean[9][9]; + boolean[][] col = new boolean[9][9]; + boolean[][] area = new boolean[9][9]; + public void solveSudoku(char[][] board) { + for (int i = 0; i < 9; i++) { + for (int j = 0 ;j < 9;j++) { + if (board[i][j] != '.') { + int areaIndex = (i / 3) * 3 + (j / 3); + int val = board[i][j] - '0' - 1; + row[i][val] = true; + col[j][val] = true; + area[areaIndex][val] = true; + } + } + } + dfs(board,0,0); + } + private boolean dfs(char[][] board,int r,int c) { + if (c == 9) { + r++; + c=0; + if (r== 9) { + return true; + } + } + if (board[r][c] != '.') { + return dfs(board,r,c+1); + } + int areaIndex = (r / 3) * 3 + (c / 3); + for (char k = '1'; k <= '9' ;k++) { + int val = k - '0' - 1; + if (!row[r][val] && !col[c][val] && !area[areaIndex][val]) { + row[r][val] = true; + col[c][val] = true; + area[areaIndex][val] = true; + board[r][c] = k; + if (dfs(board,r,c + 1)){ + return true; + }; + row[r][val] = false; + col[c][val] = false; + area[areaIndex][val] = false; + board[r][c] = '.'; + } + } + return false; + } + } +} diff --git a/Week 06/id_693/LeetCode_547_693.java b/Week 06/id_693/LeetCode_547_693.java new file mode 100644 index 000000000..1186f5898 --- /dev/null +++ b/Week 06/id_693/LeetCode_547_693.java @@ -0,0 +1,80 @@ +package id_693; + +import java.util.Arrays; + +/** + * @Author 李雷(KyLin) + * @Desc 547. 朋友圈 https://leetcode-cn.com/problems/friend-circles/ + * @Date 2019/11/21 + */ +public class LeetCode_547_693 { + //DFS 解法 + class Solution { + public int findCircleNum(int[][] M) { + if (M.length == 0) return 0; + int[] used = new int[M.length]; + int count = 0; + for (int i = 0; i < M.length; i++) { + if (used[i] == 0) { + dfs(M,used,i); + count++; + } + } + return count; + } + + private void dfs(int[][] M,int[] used,int i) { + for (int j = 0; j < M.length; j++) { + if (M[j][i] == 1 && used[j] == 0) { + used[j] = 1; + dfs(M,used,j); + } + } + } + } + + //并查集解法 + class Solution2 { + public int findCircleNum(int[][] M) { + if (M.length == 0) return 0; + int[] used = new int[M.length]; + Arrays.fill(used,-1); + int count = 0; + for (int i = 0; i < M.length; i++) { + for (int j = i + 1; j < M.length; j++) { + if (M[i][j] == 1) { + union(used,i,j); + } + } + } + for (int us : used) { + if (us == -1) count++; + } + return count; + } + + private int find(int[] parent,int i) { + int temp = i; + while (parent[i] != -1) { + i = parent[i]; + } + //路径压缩 + while (temp != i) { + int t = parent[temp]; + parent[temp] = i; + temp = t; + } + return i; + } + + private void union(int[] parent,int i,int j) { + int xSet = find(parent,i); + int ySet = find(parent,j); + if (xSet != ySet) { + parent[xSet] = ySet; + } + } + + + } +} diff --git a/Week 06/id_693/LeetCode_773_693.java b/Week 06/id_693/LeetCode_773_693.java new file mode 100644 index 000000000..f686a462b --- /dev/null +++ b/Week 06/id_693/LeetCode_773_693.java @@ -0,0 +1,75 @@ +package id_693; + +import java.util.*; + +/** + * @Author 李雷(KyLin) + * @Desc 773. 滑动谜题 https://leetcode-cn.com/problems/sliding-puzzle/ + * @Date 2019/11/25 + */ +public class LeetCode_773_693 { + /** + * BFS搜索 + */ + class Solution { + public int slidingPuzzle(int[][] board) { + String startStr = boardToStr(board); + String endStr = "123450"; + Deque deque = new ArrayDeque<>(); + Set visited = new HashSet<>(); + deque.offer(startStr); + visited.add(startStr); + int res = 0; + int[] dir = new int[]{1,-1,-3,3}; + while (!deque.isEmpty()) { + for (int size = deque.size(); size > 0; size--) { + String temp = deque.poll(); + if (temp.equals(endStr)) return res; + int i = temp.indexOf('0'); + for (int d : dir) { + char[] chars = temp.toCharArray(); + int j = i + d; + //用于避免无效交换的条件 + if (j < 0 || j >= chars.length || (i == 2 && j == 3) || (i == 3 && j == 2)) { + continue; + } + char t = chars[i]; + chars[i] = chars[j]; + chars[j] = t; + String s = String.valueOf(chars); + if (visited.add(s)) { + deque.offer(s); + } + } + } + res++; + } + return -1; + } + + private String boardToStr(int[][] board) { + StringBuilder str = new StringBuilder(); + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[i].length; j++) { + str.append(board[i][j]); + } + } + return str.toString(); + } + } + + /** + * A*搜索 (启发式搜索)TODO + */ + class Solution2 { + public int slidingPuzzle(int[][] board) { + return -1; + } + } + + + public static void main(String[] args) { + int[][] a = new int[][]{{1,2,3},{4,5,0}}; + System.out.println(Arrays.deepToString(a).replaceAll("\\[|\\]|,|\\s","")); + } +} diff --git a/Week 06/id_693/practise/LeetCode_79_693.java b/Week 06/id_693/practise/LeetCode_79_693.java new file mode 100644 index 000000000..d92df1ace --- /dev/null +++ b/Week 06/id_693/practise/LeetCode_79_693.java @@ -0,0 +1,108 @@ +package id_693.practise; + +/** + * @Author 李雷(KyLin) + * @Desc 79. 单词搜索 https://leetcode-cn.com/problems/word-search/ + * @Date 2019/11/21 + */ +public class LeetCode_79_693 { + //dfs解法 标准 + class Solution { + public boolean exist(char[][] board,String word) { + if (board.length == 0 || board[0].length == 0 || word.length() == 0) { + return false; + } + int m = board.length; + int n = board[0].length; + char[] chars = word.toCharArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (board[i][j] == word.charAt(0) && backtrack(board,i,j,0,chars)) { + return true; + } + } + } + return false; + } + + private boolean backtrack(char[][] board,int i,int j,int index,char[] word) { + if (index == word.length) { + return true; + } + if (i < 0 || j < 0 || i >= board.length || j >= board[i].length) { + return false; + } + if (board[i][j] == word[index]) { + char temp = board[i][j]; + board[i][j] = '#'; + if (backtrack(board,i + 1,j,index + 1,word) + || backtrack(board,i - 1,j,index + 1,word) + || backtrack(board,i,j + 1,index + 1,word) + || backtrack(board,i,j - 1,index + 1,word)) { + return true; + } + board[i][j] = temp; + } + + return false; + } + } + + //单词树解法 + class Solution2 { + public boolean exist(char[][] board,String word) { + if (board.length == 0 || board[0].length == 0) return false; + TreeNode root = buildTree(word); + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[i].length; j++) { + if (dfs(board,root,i,j)) { + return true; + } + } + } + return false; + } + + private boolean dfs(char[][] board,TreeNode root,int i,int j) { + if (i < 0 || j < 0 || i >= board.length || j >= board[i].length || board[i][j] == '#') { + return false; + } + root = root.childrens[board[i][j] - 'A']; + if (root == null) { + return false; + } + if (root.isWord) { + return true; + } + char temp = board[i][j]; + board[i][j] = '#'; + if (dfs(board,root,i + 1,j) || dfs(board,root,i - 1,j) || dfs(board,root,i,j - 1) || dfs(board,root,i,j + 1)) { + return true; + } + board[i][j] = temp; + return false; + } + + private TreeNode buildTree(String word) { + TreeNode root = new TreeNode(); + TreeNode temp = root; + for (char ch : word.toCharArray()) { + if (temp.childrens[ch - 'A'] == null) { + temp.childrens[ch - 'A'] = new TreeNode(); + } + temp = temp.childrens[ch - 'A']; + } + temp.isWord = true; + return root; + } + + class TreeNode { + TreeNode[] childrens; + boolean isWord = false; + + public TreeNode() { + this.childrens = new TreeNode[58]; + } + } + } +} diff --git a/Week 06/id_698/LeetCode_208_698.java b/Week 06/id_698/LeetCode_208_698.java new file mode 100644 index 000000000..9644c8812 --- /dev/null +++ b/Week 06/id_698/LeetCode_208_698.java @@ -0,0 +1,95 @@ +/** + * 实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。 + * @author gning (id=698) + */ + +public class LeetCode_208_698 { + + class TrieNode { + + private TrieNode[] links; + + private final int R = 26; + + private boolean isEnd; + + public TrieNode() { + links = new TrieNode[R]; + } + + public boolean containsKey(char ch) { + return links[ch - 'a'] != null; + } + + public TrieNode get(char ch) { + return links[ch - 'a']; + } + + public void put(char ch, TrieNode node) { + links[ch - 'a'] = node; + } + + public void setEnd() { + isEnd = true; + } + + public boolean isEnd() { + return isEnd; + } + + } + + private TrieNode root; + + + + /** Initialize your data structure here. */ + public LeetCode_208_698() { + root = new TrieNode(); + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + TrieNode node = root; + + for(int i=0; i < word.length(); i++) { + char currentChar = word.charAt(i); + if(!node.containsKey(currentChar)) { + node.put(currentChar, new TrieNode()); + } + node = node.get(currentChar); + } + node.setEnd(); + } + + private TrieNode searchPrefix(String word) { + TrieNode node = root; + for(int i=0; i < word.length(); i++) { + char currentChar = word.charAt(i); + + if(node.containsKey(currentChar)) { + node = node.get(currentChar); + }else{ + return null; + } + } + + return node; + } + + /** Returns if the word is in the trie. */ + public boolean search(String word) { + TrieNode node = searchPrefix(word); + + return node != null && node.isEnd(); + } + + /** Returns if there is any word in the trie that starts with the given prefix. */ + public boolean startsWith(String prefix) { + TrieNode node = searchPrefix(prefix); + + return node != null; + } + + +} \ No newline at end of file diff --git a/Week 06/id_698/LeetCode_212_698.java b/Week 06/id_698/LeetCode_212_698.java new file mode 100644 index 000000000..acdfe2824 --- /dev/null +++ b/Week 06/id_698/LeetCode_212_698.java @@ -0,0 +1,163 @@ +/** + * 给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。 + * 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。 + * + * @author gning (id=698) + */ + + public class LeetCode_212_698 { + + class TrieNode { + + private TrieNode[] links; + + private final int R = 26; + + private boolean isEnd; + + private String s; + + public TrieNode() { + links = new TrieNode[R]; + } + + public boolean containsKey(char ch) { + return links[ch - 'a'] != null; + } + + public TrieNode get(char ch) { + return links[ch - 'a']; + } + + public void put(char ch, TrieNode node) { + links[ch - 'a'] = node; + } + + public void setEnd() { + isEnd = true; + } + + public boolean isEnd() { + return isEnd; + } + + public void setString(String str) { + s = str; + } + + public String getSTring() { + return s; + } + + } + + class Trie { + private TrieNode root; + + /** Initialize your data structure here. */ + public Trie() { + root = new TrieNode(); + } + + public TrieNode getRoot() { + return root; + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + TrieNode node = root; + + for(int i=0; i < word.length(); i++) { + char currentChar = word.charAt(i); + if(!node.containsKey(currentChar)) { + node.put(currentChar, new TrieNode()); + } + node = node.get(currentChar); + } + node.setEnd(); + node.setString(word); + } + + private TrieNode searchPrefix(String word) { + TrieNode node = root; + for(int i=0; i < word.length(); i++) { + char currentChar = word.charAt(i); + + if(node.containsKey(currentChar)) { + node = node.get(currentChar); + }else{ + return null; + } + } + + return node; + } + + /** Returns if the word is in the trie. */ + public boolean search(String word) { + TrieNode node = searchPrefix(word); + + return node != null && node.isEnd(); + } + + /** Returns if there is any word in the trie that starts with the given prefix. */ + public boolean startsWith(String prefix) { + TrieNode node = searchPrefix(prefix); + + return node != null; + } + + } + + public List findWords(char[][] board, String[] words) { + + Trie trie = new Trie(); + TrieNode root = trie.getRoot(); + + for (String s: words) { + trie.insert(s); + } + + Set result = new HashSet<>(); + + int m = board.length; + int n = board[0].length; + + boolean[][] visited = new boolean[m][n]; + + for(int i=0; i < m; i++ ) { + for (int j=0; j < n; j++ ) { + find(board,visited,i,j,m,n,result,root); + } + } + + return new LinkedList(result); + } + + private void find(char[][] board, boolean[][] visited, int i, int j, int m, int n, Set result, TrieNode node) { + if(i<0 || i>=m || j<0 || j>=n || visited[i][j]) { + return; + } + + node = node.get(board[i][j]); + + visited[i][j] = true; + + if(node == null) { + visited[i][j] = false; + return; + } + + if(node.isEnd()) { + result.add(node.getSTring()); + } + + find(board,visited,i+1,j,m,n,result,node); + find(board,visited,i,j+1,m,n,result,node); + find(board,visited,i,j-1,m,n,result,node); + find(board,visited,i-1,j,m,n,result,node); + + visited[i][j] = false; + + } + } \ No newline at end of file diff --git a/Week 06/id_703/LeetCode_200_703.py b/Week 06/id_703/LeetCode_200_703.py new file mode 100644 index 000000000..dc9a9cbe9 --- /dev/null +++ b/Week 06/id_703/LeetCode_200_703.py @@ -0,0 +1,31 @@ +# +# @lc app=leetcode.cn id=200 lang=python3 +# +# [200] 岛屿数量 +# + +# @lc code=start +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + def flood_fill(i, j): + if grid[i][j] == '1': + grid[i][j] = '0' + flood_fill(i-1, j) + flood_fill(i+1, j) + flood_fill(i, j-1) + flood_fill(i, j+1) + + if not grid: return 0 + grid = [['0'] + row + ['0'] for row in grid] + grid = [['0'] * len(grid[0])] + grid + [['0'] * len(grid[0])] + m, n = len(grid), len(grid[0]) + count = 0 + for i in range(1, m-1): + for j in range(1, n-1): + if grid[i][j] == '1': + count += 1 + flood_fill(i, j) + return count + +# @lc code=end + diff --git a/Week 06/id_703/LeetCode_547_703.py b/Week 06/id_703/LeetCode_547_703.py new file mode 100644 index 000000000..1ce51fa4a --- /dev/null +++ b/Week 06/id_703/LeetCode_547_703.py @@ -0,0 +1,22 @@ +# +# @lc app=leetcode.cn id=547 lang=python3 +# +# [547] 朋友圈 +# + +# @lc code=start +class Solution: + def findCircleNum(self, M: List[List[int]]) -> int: + n = len(M) + p = [[i] for i in range(n)] + ans = n + for i, j in itertools.combinations(range(n), 2): + if M[i][j] == 1 and p[i] is not p[j]: + p[i] += p[j] + for k in p[j]: + p[k] = p[i] + ans -= 1 + return ans + +# @lc code=end + diff --git a/Week 06/id_708/LeetCode_102_708.java b/Week 06/id_708/LeetCode_102_708.java new file mode 100644 index 000000000..df79246c4 --- /dev/null +++ b/Week 06/id_708/LeetCode_102_708.java @@ -0,0 +1,29 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List> levelOrder(TreeNode root) { + List> list = new LinkedList<>(); + Queue queue = new LinkedList<>(); + if (root != null) queue.offer(root); + while (!queue.isEmpty()) { + int size = queue.size(); + List currList = new LinkedList<>(); + TreeNode currNode = null; + while (size-- > 0) { + currNode = queue.poll(); + currList.add(currNode.val); + if (currNode.left != null) queue.offer(currNode.left); + if (currNode.right != null) queue.offer(currNode.right); + } + list.add(currList); + } + return list; + } +} \ No newline at end of file diff --git a/Week 06/id_708/LeetCode_200_708.java b/Week 06/id_708/LeetCode_200_708.java new file mode 100644 index 000000000..ad5562d04 --- /dev/null +++ b/Week 06/id_708/LeetCode_200_708.java @@ -0,0 +1,24 @@ +class Solution { + public int numIslands(char[][] grid) { + int count = 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == '1') { + count++; + dfs(grid, i, j); + } + } + } + return count; + } + + private void dfs(char[][] grid, int i, int j) { + if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == '0') return ; + + grid[i][j] = '0'; + dfs(grid, i+1, j); + dfs(grid, i, j+1); + dfs(grid, i-1, j); + dfs(grid, i, j-1); + } +} \ No newline at end of file diff --git a/Week 06/id_708/LeetCode_208_708.java b/Week 06/id_708/LeetCode_208_708.java new file mode 100644 index 000000000..2f1a317a3 --- /dev/null +++ b/Week 06/id_708/LeetCode_208_708.java @@ -0,0 +1,81 @@ +class TrieNode { + private TrieNode[] links; + private final int R = 26; + private boolean isEnd; + + public TrieNode() { + links = new TrieNode[R]; + } + + private int getIndex(char ch) { + return ch - 'a'; + } + + public boolean containsKey(char ch) { + return links[getIndex(ch)] != null; + } + + public TrieNode get(char ch) { + return links[getIndex(ch)]; + } + + public void put(char ch, TrieNode node) { + links[getIndex(ch)] = node; + } + + public void setEnd() { + isEnd = true; + } + + public boolean isEnd() { + return isEnd; + } +} + + +class Trie { + private TrieNode root; + + /** Initialize your data structure here. */ + public Trie() { + root = new TrieNode(); + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + TrieNode curr = root; + for (char ch : word.toCharArray()) { + if (!curr.containsKey(ch)) curr.put(ch, new TrieNode()); + curr = curr.get(ch); + } + curr.setEnd(); + } + + /** Returns if the word is in the trie. */ + public boolean search(String word) { + TrieNode curr = root; + for (char ch : word.toCharArray()) { + if (!curr.containsKey(ch)) return false; + curr = curr.get(ch); + } + return curr.isEnd(); + } + + /** Returns if there is any word in the trie that starts with the given prefix. */ + public boolean startsWith(String prefix) { + TrieNode curr = root; + for (char ch : prefix.toCharArray()) { + if (!curr.containsKey(ch)) return false; + curr = curr.get(ch); + } + return true; + } +} + +/** + * Your Trie object will be instantiated and called as such: + * Trie obj = new Trie(); + * obj.insert(word); + * boolean param_2 = obj.search(word); + * boolean param_3 = obj.startsWith(prefix); + */ \ No newline at end of file diff --git a/Week 06/id_713/LeetCode_208_ImplementTriePrefixTree.java b/Week 06/id_713/LeetCode_208_ImplementTriePrefixTree.java new file mode 100644 index 000000000..486fb4f57 --- /dev/null +++ b/Week 06/id_713/LeetCode_208_ImplementTriePrefixTree.java @@ -0,0 +1,130 @@ +package id_713; + +/** + * 208. 实现 Trie (前缀树) + */ +public class LeetCode_208_ImplementTriePrefixTree { + + /* + 实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。 + + 示例: + + Trie trie = new Trie(); + + trie.insert("apple"); + trie.search("apple"); // 返回 true + trie.search("app"); // 返回 false + trie.startsWith("app"); // 返回 true + trie.insert("app"); + trie.search("app"); // 返回 true + + 说明: + + 你可以假设所有的输入都是由小写字母 a-z 构成的。 + 保证所有输入均为非空字符串。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/implement-trie-prefix-tree + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + + /* + 思路: + 1. 字典顾名思义, 用数据结构存储字母, 随字母长度开辟层数, 并存储第N个字母 + + 举例(a-e), 存储ace, add + + 坐标(如果有值赋值, ch - 'a', 如第一个字母a, 则值为0) + a b c d e + + [0, null, null, null, null] + [null, null, 2, 3, null] + [null, null, null, 3, 4] + + */ + + private class Trie { + + private TrieNode root; + + public Trie() { + root = new TrieNode(); + } + + public void insert(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char curr = word.charAt(i); + if (!node.containsKey(curr)) { + node.put(curr, new TrieNode()); + } + node = node.get(curr); // 进入到下一层 + } + node.setEnd(); // 遍历完字符串后, 标记为结束 + + } + + public boolean search(String word) { + TrieNode node = this.searchPrefix(word); + return node != null && node.getEnd(); + } + + public boolean startsWith(String prefix) { + return this.searchPrefix(prefix) != null; + + } + + public TrieNode searchPrefix(String prefix) { + TrieNode node = root; + for (int i = 0; i < prefix.length(); i++) { + char curr = prefix.charAt(i); + if (!node.containsKey(curr)) return null; + + node = node.get(curr); // 进入到下一层 + } + return node; + } + + private class TrieNode { + private TrieNode[] links; + private final int R = 26; // 提示中写到a-z + private boolean isEnd; + + public TrieNode() { + links = new TrieNode[R]; + } + + public boolean containsKey(char ch) { + return links[ch - 'a'] != null; + } + + public TrieNode get(char ch) { + return links[ch - 'a']; + } + + public void put(char ch, TrieNode node) { + links[ch - 'a'] = node; + } + + public void setEnd() { + this.isEnd = true; + } + + public boolean getEnd() { + return this.isEnd; + } + } + } + + +/** + * Your Trie object will be instantiated and called as such: + * Trie obj = new Trie(); + * obj.insert(word); + * boolean param_2 = obj.search(word); + * boolean param_3 = obj.startsWith(prefix); + */ + +} diff --git a/Week 06/id_713/LeetCode_212_WordSearchII.java b/Week 06/id_713/LeetCode_212_WordSearchII.java new file mode 100644 index 000000000..5a89283f9 --- /dev/null +++ b/Week 06/id_713/LeetCode_212_WordSearchII.java @@ -0,0 +1,155 @@ +package id_713; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 212. 单词搜索 II + */ +public class LeetCode_212_WordSearchII { + /* + 给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。 + + 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。 + + 示例: + + 输入: + words = ["oath","pea","eat","rain"] and board = + [ + ['o','a','a','n'], + ['e','t','a','e'], + ['i','h','k','r'], + ['i','f','l','v'] + ] + + 输出: ["eat","oath"] + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/word-search-ii + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + + public List findWords(char[][] board, String[] words) { + if (words == null || words.length == 0) return new ArrayList<>(); + if (words[0] == null || words[0].length() == 0) return new ArrayList<>(); + + Trie trie = new Trie(); + TrieNode root = trie.root; + for (String str : words) { + trie.insert(str); + } + + Set result = new HashSet<>(); + int m = board.length; + int n = board[0].length; + boolean[][] visited = new boolean[m][n]; + + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board [0].length; j++) { + find(board, visited, i, j, result, root); + } + } + + return new ArrayList<>(result); + + + } + + private void find(char[][] board, boolean[][] visited, int i, int j, Set result, TrieNode node) { + if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || visited[i][j]) return; + + node = node.get(board[i][j]); + visited[i][j] = true; + + if (node == null) { + visited[i][j] = false; // 回溯 + return; + } + + if (node.isEnd()) { + result.add(node.val); + } + + find(board, visited, i - 1, j, result, node); + find(board, visited, i + 1, j, result, node); + find(board, visited, i, j - 1, result, node); + find(board, visited, i, j + 1, result, node); + + visited[i][j] = false; // 回溯 + + + } + + private class Trie { + TrieNode root; + + public Trie() { + root = new TrieNode(); + } + + public void insert(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char curr = word.charAt(i); + if (!node.containsKey(curr)) { + node.put(curr, new TrieNode()); + } + node = node.get(curr); + } + node.setEnd(); + node.val = word; + } + + public TrieNode searchPrefix(String prefix) { + TrieNode node = root; + for (int i = 0; i < prefix.length(); i++) { + char curr = prefix.charAt(i); + if (!node.containsKey(curr)) return null; + + node = node.get(curr); + } + return node; + } + + public boolean search(String word) { + TrieNode node = this.searchPrefix(word); + return node != null && node.isEnd(); + } + + } + + private class TrieNode { + private TrieNode[] links; + private final int R = 26; + private boolean isEnd; + private String val; + + public TrieNode() { + links = new TrieNode[R]; + } + + public boolean containsKey(char ch) { + return this.get(ch) != null; + } + + public TrieNode get(char ch) { + return links[ch - 'a']; + } + + public void put(char ch, TrieNode node) { + links[ch - 'a'] = node; + } + + public boolean isEnd() { + return isEnd; + } + + public void setEnd() { + this.isEnd = true; + } + } +} \ No newline at end of file diff --git a/Week 06/id_713/LeetCode_36_ValidSudoku.java b/Week 06/id_713/LeetCode_36_ValidSudoku.java new file mode 100644 index 000000000..4813fe833 --- /dev/null +++ b/Week 06/id_713/LeetCode_36_ValidSudoku.java @@ -0,0 +1,112 @@ +package id_713; + +import java.util.HashMap; + +/** + * 36. 有效的数独 + */ +public class LeetCode_36_ValidSudoku { + + + /* + 判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。 + + 数字 1-9 在每一行只能出现一次。 + 数字 1-9 在每一列只能出现一次。 + 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 + + 上图是一个部分填充的有效的数独。 + + 数独部分空格内已填入了数字,空白格用 '.' 表示。 + + 示例 1: + + 输入: + [ + ["5","3",".",".","7",".",".",".","."], + ["6",".",".","1","9","5",".",".","."], + [".","9","8",".",".",".",".","6","."], + ["8",".",".",".","6",".",".",".","3"], + ["4",".",".","8",".","3",".",".","1"], + ["7",".",".",".","2",".",".",".","6"], + [".","6",".",".",".",".","2","8","."], + [".",".",".","4","1","9",".",".","5"], + [".",".",".",".","8",".",".","7","9"] + ] + 输出: true + + 示例 2: + + 输入: + [ + ["8","3",".",".","7",".",".",".","."], + ["6",".",".","1","9","5",".",".","."], + [".","9","8",".",".",".",".","6","."], + ["8",".",".",".","6",".",".",".","3"], + ["4",".",".","8",".","3",".",".","1"], + ["7",".",".",".","2",".",".",".","6"], + [".","6",".",".",".",".","2","8","."], + [".",".",".","4","1","9",".",".","5"], + [".",".",".",".","8",".",".","7","9"] + ] + 输出: false + 解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。 + 但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。 + + 说明: + + 一个有效的数独(部分已被填充)不一定是可解的。 + 只需要根据以上规则,验证已经填入的数字是否有效即可。 + 给定数独序列只包含数字 1-9 和字符 '.' 。 + 给定数独永远是 9x9 形式的。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/valid-sudoku + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + + public boolean isValidSudoku(char[][] board) { + // 初始化数据 + HashMap[] rows = new HashMap[9]; + HashMap[] columns = new HashMap[9]; + HashMap[] boxes = new HashMap[9]; + + for (int i = 0; i < 9; i++) { + rows[i] = new HashMap<>(); + columns[i] = new HashMap<>(); + boxes[i] = new HashMap<>(); + } + + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + char num = board[i][j]; + if(num != '.') { + int n = (int) num; + /* + 这个公式来头: + 每行第1列分别为 0, 3, 6 + 每行第2列分别为 1, 5, 7 + + 对于i,j而言, 先用对应基数 [0,3,6] 再用j对应偏移量 +0, +1, +2 + + (i / 3) * 3 则对应 [0,3,6] + (j / 3)则对应 [0,1,2] + 所以公式为 (i / 3) * 3 + j / 3 + */ + int boxIndex = (i / 3) * 3 + j / 3; + + rows[i].put(n, rows[i].getOrDefault(n , 0) + 1); + columns[j].put(n, columns[j].getOrDefault(n , 0) + 1); + boxes[boxIndex].put(n, boxes[i].getOrDefault(n , 0) + 1); + + if (rows[i].get(n) > 1 || columns[j].get(n) > 1 || boxes[boxIndex].get(n) > 1) { + return false; + } + } + } + } + + return true; + } +} diff --git a/Week 06/id_713/LeetCode_51_NQueens.java b/Week 06/id_713/LeetCode_51_NQueens.java new file mode 100644 index 000000000..d8fcea4a4 --- /dev/null +++ b/Week 06/id_713/LeetCode_51_NQueens.java @@ -0,0 +1,117 @@ +package id_713; + +import java.util.ArrayList; +import java.util.List; + +/** + * 51. N皇后 + */ +public class LeetCode_51_NQueens { + + /* + n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。 + + 上图为 8 皇后问题的一种解法。 + + 给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。 + + 每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。 + + 示例: + + 输入: 4 + 输出: [ + [".Q..", // 解法 1 + "...Q", + "Q...", + "..Q."], + + ["..Q.", // 解法 2 + "Q...", + "...Q", + ".Q.."] + ] + 解释: 4 皇后问题存在两个不同的解法。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/n-queens + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + + int rows[]; + int hills[]; // 撇 + int dales[]; // 捺 + int n; + List> output = new ArrayList<>(); + int queens[]; + + public List> solveNQueens(int n) { + this.n = n; + this.rows = new int[n]; + this.hills = new int[4 * n - 1]; + this.dales = new int[2 * n - 1]; + this.queens = new int[n]; + + this.backtrace(0); + return output; + } + + private void backtrace(int row) { + for (int col = 0; col < n; col++) { + if (this.isNotUnderAttack(row, col)) { + this.placeQueen(row, col); + // 如果已经放置了 + if (row + 1 == n) this.addSulotion(); + else backtrace(row + 1); + + this.removeQueen(row, col); + } + } + } + + + private boolean isNotUnderAttack(int row, int col) { + // 下一层正下方, 下一层撇, 下一层捺, 相加为0 则认为是不会被攻击 + int res = rows[col] + hills[row - col + 2 * n] + dales[row + col]; + return res == 0; + } + + + private void placeQueen(int row, int col) { + queens[row] = col; + rows[col] = 1; + hills[row - col + 2 * n - 1] = 1; + dales[row + col] = 1; + } + + + private void addSulotion() { + List solution = new ArrayList<>(); + + for (int i = 0; i < n; i++) { + int col = queens[i]; + StringBuilder sb = new StringBuilder(); + + for (int j = 0; j < col; j++) { + sb.append("."); + } + sb.append("Q"); + + for (int j = 0; j < n - col - 1; j++) { + sb.append("."); + } + sb.append("Q"); + } + output.add(solution); + + } + + private void removeQueen(int row, int col) { + queens[row] = 0; + rows[col] = 0; + hills[row - col + 2 * n - 1] = 0; + dales[row + col] = 0; + } + +} diff --git a/Week 06/id_713/NOTE.md b/Week 06/id_713/NOTE.md index a6321d6e2..434e9b39b 100644 --- a/Week 06/id_713/NOTE.md +++ b/Week 06/id_713/NOTE.md @@ -2,3 +2,278 @@ +# 字典树(Trie) + +## 性质 + +* 节点本身不存完整单词 +* 从根节点到某一节点, 路径上经过的字符连接起来, 为该节点对应的字符串 +* 每个节点的所有子节点路径代表的字符都不相同 + + + +### 核心思想 + +* 空间换时间 +* 利用字符串公共前缀来降低查询时间的开销, 以达到提高效率的目的 + + + +## 代码实现 + +### Java版本 + +```java +class TrieNode { + private TrieNode[] links; + + private final int R = 26; + + private boolean isEnd; + + public TrieNode() { + links = new TrieNode[R]; + } + + public boolean containsKey(char ch) { + // int asc2 = link[ch]; + // asc2 = asc2 - 'a'; + // return link[asc2] != null; + // 简化上述过程 + return link[ch - 'a'] != null; + } + + public TrieNode get(char ch) { + return links[ch - 'a']; + } + + public void put(char ch, TrieNode node) { + links[ch - 'a'] = node; + } + + public void setEnd() { + this.isEnd = true; + } + + public boolean isEnd() { + return isEnd; + } +} + +class Trie { + private TrieNode root; + + public Trie() { + root = new TrieNode(); + } + + // 插入字符串 + public void insert(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char curr = word.charAt(i); + if (!node.containsKey(curr)) { + node.put(curr, new TrieNode()); + } + node = node.get(curr); + } + node.setEnd(); + } + + // 搜索前缀,返回Node + public TrieNode searchPrefix(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char curr = word.charAt(i); + if (node.containsKey(curr)) { + node = node.get(curr); + } else { + return null; + } + } + return node; + } + + // 搜索 + public boolean search(String word) { + TrieNode node = this.searchPrefix(word); + return node != null && node.isEnd(); + } + +} +``` + + + +# 并查集(Disjoint Set) + +## 适用场景 + +* 组团 & 配对 -> 这2个个体是否在一个集合中 +* group or not + +## 基本操作 + +* makeSet(s): 建立一个新的并查集, 其中包含s个单元素集合 +* unionSet(x, y): 把元素x和元素y所在的集合合并, 要求x和y所在的集合不相交, 如果相交和不合并 +* find(x): 找到元素x所在的集合的代表, 该操作也可以用于判断两个元素是否位于同一个集合, 只要将他们各自的代表比较一下即可 + + + +## 代码实现 + +### Java版本 + +```java +class UnionFind { + private int count = 0; + private int[] parent; + + public UnionFind(int n) { + count = n; + parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; // 并查集常见初始化方式 + } + } + + public int find(int p) { + while (p != parent[p]) { // parent[i] == i时,说明找到领头元素 + parent[p] = parent[parent[p]]; // 继续往上找, 以及路径压缩(见下示意图) + p = parent[p]; + } + return p; + } + + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) return; + + parent[rootP] = rootQ; + count--; + } + + /* + 路径压缩 + a a + / /|\ + b b c d + / + c + / + d + */ +} +``` + + + +## 实战例题 + + + +```java +/** + * 使用并查集 + */ +public int findCircleNum(int[][] M) { + int n = M.length; + UnionFind uf = new UnionFind(n); + for (int i = 0; i < n - 1; i++) { + for (int j = i + 1; j < n; j++) { + if (M[i][j] == 1) { + uf.unoin(i, j); + } + } + } + return uf.count; +} + +class UnionFind { + private int count = 0; + private int[] parent; + + public UnionFind(int n) { + count = n; + parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + public int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) return; + parent[rootP] = rootQ; + count--; + } +} + + +/** + * 使用 DFS + */ +public int findCircleNumByBfs(int[][] M) { + int[] visited = new int[M.length]; + int count = 0; + for (int i = 0; i < M.length; i++) { + if (visited[i] == 0) { + dfs(M, visited, i); + count++ + } + } + return count; +} + +public void dfs(int[][] M, int[] visited, int i) { + for (int j = 0; i < M.length; j++) { + if(M[i][j] == 1 && visited[j] == 0) { + visited[j] = 1; + dfs(M, visited, j); + } + } +} +``` + + + + + +# 启发式搜索 + +## 估价函数 + +* 启发式函数: h(n), 用于评价那个节点最优可能是我们要找的节点, h(n)会返回一个实数, 表示节点n到目标节点路径的估计成本 + + + +### 代码模板 + +#### Python版本 + +```python +def AstarSearch(graph, start, end): + pq = collections.priority_queue() # 优先级 -> 估价函数 + pq.append([start]) + visited.add(start) + + while pq: + node = pq.pop() + visited.add(node) + + process(node) + nodes = generate_related_nodes(node) + unvisited = [node for node in nodes if node not in visited] + pq.push(unvisited) +``` + diff --git a/Week 06/id_718/127.word-ladder.py b/Week 06/id_718/127.word-ladder.py new file mode 100644 index 000000000..25bebb706 --- /dev/null +++ b/Week 06/id_718/127.word-ladder.py @@ -0,0 +1,65 @@ +# +# @lc app=leetcode id=127 lang=python3 +# +# [127] Word Ladder +# + +# @lc code=start + +class Solution: + def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: + wordList = set(wordList) + if endWord not in wordList: + return 0 + front, back = {beginWord}, {endWord} + ret = 1 # notice : 1 here + n = len(beginWord) + while front: + ret += 1 + next_front = set() + for word in front: + for i in range(n): + for c in 'abcdefghijklmnopqrstuvwxyz': + if c != word[i]: + nw = word[:i] + c + word[i+1:] + if nw in back: + return ret + if nw in wordList: + next_front.add(nw) + wordList.remove(nw) # could also use Visited + front = next_front + if len(front) > len(back): + front, back = back, front + return 0 + +''' +from collections import deque +class Solution: + def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: + wordList = set(wordList) + visited = set([beginWord]) + q = deque([beginWord]) + ret = 0 + while q: + ret += 1 + for _ in range(len(q)): + word = q.popleft() + if word == endWord: + return ret + for w in self.generate_next_words(word, wordList): + if w not in visited: + q.append(w) + visited.add(w) + return 0 + + def generate_next_words(self, word, wordList): + ret = [] + for i in range(len(word)): + for c in 'abcdefghijklmnopqrstuvwxyz': + next_word = word[:i] + c + word[i+1:] + if next_word != word and next_word in wordList: + ret.append(next_word) + return ret +''' +# @lc code=end + diff --git a/Week 06/id_718/200.number-of-islands.py b/Week 06/id_718/200.number-of-islands.py new file mode 100644 index 000000000..ff63817c3 --- /dev/null +++ b/Week 06/id_718/200.number-of-islands.py @@ -0,0 +1,110 @@ +# +# @lc app=leetcode id=200 lang=python3 +# +# [200] Number of Islands +# + +# @lc code=start +from typing import List +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + class UnionFind: + def __init__(self, n): + self.count = n + self.parent = [i for i in range(n)] + self.rank = [1 for _ in range(n)] + def get_count(self): + return self.count + def find(self, p): + while p != self.parent[p]: + self.parent[p] = self.parent[self.parent[p]] + p = self.parent[p] + return p + def is_connected(self, p, q): + return self.find(p) == self.find(q) + def union(self, p, q): + p_root = self.find(p) + q_root = self.find(q) + if p_root == q_root: + return + if self.rank[p_root] > self.rank[q_root]: + self.parent[q_root] = p_root + elif self.rank[p_root] < self.rank[q_root]: + self.parent[p_root] = q_root + else: + self.parent[q_root] = p_root + self.rank[p_root] += 1 + self.count -= 1 + + row = len(grid) + if row == 0: + return 0 + def get_index(x, y): + return x * col + y + + col = len(grid[0]) + directions = [(1, 0), (0, 1)] + dummy_node = row * col + uf = UnionFind(dummy_node + 1) + for i in range(row): + for j in range(col): + if grid[i][j] == '0': + uf.union(get_index(i, j), dummy_node) + if grid[i][j] == '1': + for direction in directions: + new_x = i + direction[0] + new_y = j + direction[1] + if new_x < row and new_y < col and grid[new_x][new_y] == '1': + uf.union(get_index(i, j), get_index(new_x, new_y)) + return uf.get_count() - 1 + + +''' +def numIslands(self, grid: List[List[str]]) -> int: + ret = 0 + if not grid: + return ret + directions = ((0,1), (0,-1), (1, 0), (-1, 0)) + for i in range(len(grid)): + for j in range(len(grid[0])): + if grid[i][j] == '1': + ret += 1 + q = collections.deque([(i, j)]) + while q: + x, y = q.popleft() + for d in directions: + nx, ny = x + d[0], y + d[1] + if 0 <= nx < len(grid) and 0 <= ny < len(grid[0]) and grid[nx][ny] == '1': + grid[nx][ny] = '0' + q.append((nx, ny)) + return ret +''' + +''' +from collections import deque +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + if not grid or not grid[0]: return 0 + M, N = len(grid), len(grid[0]) + res = 0 + for i in range(M): + for j in range(N): + if grid[i][j] == '1': + res += 1 + self.sink(grid, i, j, M, N) + return res + + def sink(self, grid, i, j, M, N): + grid[i][j] = '0' + q = deque([(i, j)]) + directions = ((0, 1), (0, -1), (1, 0), (-1, 0)) + while q: + x, y = q.pop() + for d in directions: + nx, ny = x + d[0], y + d[1] + if 0 <= nx < M and 0 <= ny < N and grid[nx][ny] == '1': + grid[nx][ny] = '0' + q.append((nx, ny)) +''' +# @lc code=end + diff --git a/Week 06/id_718/208.implement-trie-prefix-tree.py b/Week 06/id_718/208.implement-trie-prefix-tree.py new file mode 100644 index 000000000..7c82eeb71 --- /dev/null +++ b/Week 06/id_718/208.implement-trie-prefix-tree.py @@ -0,0 +1,55 @@ +# +# @lc app=leetcode id=208 lang=python3 +# +# [208] Implement Trie (Prefix Tree) +# + +# @lc code=start +class Trie: + def __init__(self): + """ + Initialize your data structure here. + """ + self.root = {} + self.end_of_word = "#" + + def insert(self, word: str) -> None: + """ + Inserts a word into the trie. + """ + node = self.root + for c in word: + node = node.setdefault(c, {}) + node[self.end_of_word] = self.end_of_word + + + def search(self, word: str) -> bool: + """ + Returns if the word is in the trie. + """ + node = self.root + for c in word: + if c not in node: + return False + node = node[c] + return self.end_of_word in node + + + def startsWith(self, prefix: str) -> bool: + """ + Returns if there is any word in the trie that starts with the given prefix. + """ + node = self.root + for c in prefix: + if c not in node: + return False + node = node[c] + return True + +# Your Trie object will be instantiated and called as such: +# obj = Trie() +# obj.insert(word) +# param_2 = obj.search(word) +# param_3 = obj.startsWith(prefix) +# @lc code=end + diff --git a/Week 06/id_718/212.word-search-ii.py b/Week 06/id_718/212.word-search-ii.py new file mode 100644 index 000000000..63f3dcb3d --- /dev/null +++ b/Week 06/id_718/212.word-search-ii.py @@ -0,0 +1,47 @@ +# +# @lc app=leetcode id=212 lang=python3 +# +# [212] Word Search II +# + +# @lc code=start +from collections import defaultdict +dx, dy = [-1, 1, 0, 0], [0, 0, -1, 1] +END_OF_WORD = '#' +class Solution: + def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: + if not board or not board[0]: + return [] + if not words: + return [] + # trie + root = defaultdict() + for word in words: + node = root + for c in word: + node = node.setdefault(c, defaultdict()) + node[END_OF_WORD] = END_OF_WORD + + # search with a trie + m, n = len(board), len(board[0]) + ret = set() + for i in range(m): + for j in range(n): + if board[i][j] in root: + self._dfs(board, ret, i, j,'', root) + return list(ret) + + def _dfs(self, board, ret, i, j, cur_word, cur_dict): + m, n = len(board), len(board[0]) + cur_word += board[i][j] + cur_dict = cur_dict[board[i][j]] + if END_OF_WORD in cur_dict: + ret.add(cur_word) + tmp, board[i][j] = board[i][j], '@' + for k in range(4): + x, y = i + dx[k], j + dy[k] + if 0 <= x < m and 0 <= y < n and board[x][y] != '@' and board[x][y] in cur_dict: + self._dfs(board, ret, x, y, cur_word, cur_dict) + board[i][j] = tmp +# @lc code=end + diff --git a/Week 06/id_718/36.valid-sudoku.py b/Week 06/id_718/36.valid-sudoku.py new file mode 100644 index 000000000..1a5f618e7 --- /dev/null +++ b/Week 06/id_718/36.valid-sudoku.py @@ -0,0 +1,32 @@ +# +# @lc app=leetcode id=36 lang=python3 +# +# [36] Valid Sudoku +# + +# @lc code=start +class Solution: + def isValidSudoku(self, board: List[List[str]]) -> bool: + row = [set([]) for i in range(9)] + col = [set([]) for i in range(9)] + grid = [set([]) for i in range(9)] + + for r in range(9): + for c in range(9): + if board[r][c] == '.': + continue + if board[r][c] in row[r]: + return False + if board[r][c] in col[c]: + return False + g = r // 3 * 3 + c // 3 + if board[r][c] in grid[g]: + return False + grid[g].add(board[r][c]) + row[r].add(board[r][c]) + col[c].add(board[r][c]) + + return True + +# @lc code=end + diff --git a/Week 06/id_718/37.sudoku-solver.py b/Week 06/id_718/37.sudoku-solver.py new file mode 100644 index 000000000..272dd2c4c --- /dev/null +++ b/Week 06/id_718/37.sudoku-solver.py @@ -0,0 +1,78 @@ +# +# @lc app=leetcode id=37 lang=python3 +# +# [37] Sudoku Solver +# + +# @lc code=start +class Solution: + def solveSudoku(self, board: List[List[str]]) -> None: + """ + Do not return anything, modify board in-place instead. + """ + row = [set(range(1, 10)) for _ in range(9)] + col = [set(range(1, 10)) for _ in range(9)] + grid = [set(range(1, 10)) for _ in range(9)] + empty = [] + for i in range(9): + for j in range(9): + if board[i][j] != '.': + val = int(board[i][j]) + row[i].remove(val) + col[j].remove(val) + grid[ (i // 3) * 3 + j // 3].remove(val) + else: + empty.append((i,j)) + + def backtrack(iter=0): + if iter == len(empty): + return True + i, j = empty[iter] + g = (i // 3) * 3 + j // 3 + for val in row[i] & col[j] & grid[g]: + row[i].remove(val); col[j].remove(val); grid[g].remove(val) + board[i][j] = str(val) + if backtrack(iter+1): + return True + row[i].add(val); col[j].add(val); grid[g].add(val) + return(False) + + backtrack() + + +''' +class Solution: + def solveSudoku(self, board): + def isValid(board, x, y): + for i in range(9): + if i != x and board[i][y] == board[x][y]: + return False + for j in range(9): + if j != y and board[x][j] == board[x][y]: + return False + i = 3 * (x // 3) + while i < 3 * (x // 3 + 1): + j = 3 * (y // 3) + while j < 3 * (y // 3 + 1): + if (i != x or j != y) and board[i][j] == board[x][y]: + return False + j += 1 + i += 1 + return True + + def solver(board): + for i in range(9): + for j in range(9): + if(board[i][j] == '.'): + for k in range(1,10): + board[i][j] = str(k) + if isValid(board, i, j) and solver(board): + return True + board[i][j] = '.' + return False + return True + solver(board) +''' + +# @lc code=end + diff --git a/Week 06/id_718/547.friend-circles.py b/Week 06/id_718/547.friend-circles.py new file mode 100644 index 000000000..65b4f7f7b --- /dev/null +++ b/Week 06/id_718/547.friend-circles.py @@ -0,0 +1,35 @@ +# +# @lc app=leetcode id=547 lang=python3 +# +# [547] Friend Circles +# + +# @lc code=start +class Solution: + def findCircleNum(self, M: List[List[int]]) -> int: + if not M: + return 0 + # union find + def parent(p,i): + root = i + while p[root] != root: + root = p[root] + while p[i] != i: + x = i; i = p[i]; p[x] = root + return root + + def union(p, i, j): + p1, p2 = parent(p, i), parent(p, j) + p[p2] = p1 + + n = len(M) + p = [i for i in range(n)] + for i in range(n): + for j in range(n): + if M[i][j] == 1: + union(p, i, j) + + return(len(set([parent(p, i) for i in range(n)]))) + +# @lc code=end + diff --git a/Week 06/id_733/LeetCode_208_733.go b/Week 06/id_733/LeetCode_208_733.go new file mode 100644 index 000000000..85f956cfe --- /dev/null +++ b/Week 06/id_733/LeetCode_208_733.go @@ -0,0 +1,71 @@ +type Trie struct { + nodes map[rune]*Trie + isEnd bool +} + + +/** Initialize your data structure here. */ +func Constructor() Trie { + return Trie{} +} + + +/** Inserts a word into the trie. */ +func (this *Trie) Insert(word string) { + node := this + for _, c := range word { + if node.nodes != nil { + if subNode, ok := node.nodes[c]; ok { + node = subNode + continue + } + } else { + node.nodes = make(map[rune]*Trie) + } + + newNode := Constructor() + subNode := &newNode + node.nodes[c] = subNode + node = subNode + } + + node.isEnd = true +} + + +/** Returns if the word is in the trie. */ +func (this *Trie) Search(word string) bool { + node, ok := this.getEndNode(word) + return ok && node.isEnd +} + + +/** Returns if there is any word in the trie that starts with the given prefix. */ +func (this *Trie) StartsWith(prefix string) bool { + _, ok := this.getEndNode(prefix) + return ok +} + + +func (this *Trie) getEndNode(text string) (*Trie, bool) { + node := this + for _, c := range text { + if subNode, ok := node.nodes[c]; ok { + node = subNode + } else { + return nil, false + } + } + + return node, true +} + + + +/** + * Your Trie object will be instantiated and called as such: + * obj := Constructor(); + * obj.Insert(word); + * param_2 := obj.Search(word); + * param_3 := obj.StartsWith(prefix); + */ diff --git a/Week 06/id_733/LeetCode_212_733.go b/Week 06/id_733/LeetCode_212_733.go new file mode 100644 index 000000000..a118215c7 --- /dev/null +++ b/Week 06/id_733/LeetCode_212_733.go @@ -0,0 +1,99 @@ +var A byte = 'a' +var Used byte = '*' +var dx = []int{-1, 0, 1, 0} +var dy = []int{0, -1, 0, 1} + +func findWords(board [][]byte, words []string) []string { + var res []string + h := len(board) + if h == 0 { + return res + } + + w := len(board[0]) + if w == 0 { + return res + } + + root := initTrie(words) + resMap := make(map[string]struct{}) + for i := 0; i < w; i++ { + for j := 0; j < h; j++ { + doFindWords(board, root, resMap, i, j, w, h) + } + } + + for k, _ := range resMap { + res = append(res, k) + } + + return res +} + +func doFindWords(board [][]byte, node *TrieNode, resMap map[string]struct{}, x, y, w, h int) { + if x < 0 || x >= w || y < 0 || y >= h { + return + } + + cur := board[y][x] + if cur == Used { + return + } + + node = node.SearchNode(cur) + if node == nil { + return + } + + if node.IsWord { + resMap[node.Word] = struct{}{} + } + + board[y][x] = Used + for i := 0; i < len(dx); i++ { + nx, ny := x+dx[i], y+dy[i] + doFindWords(board, node, resMap, nx, ny, w, h) + } + + board[y][x] = cur +} + +func initTrie(words[] string) *TrieNode { + t := NewTrieNode() + for _, w := range words { + t.Insert(w) + } + + return t +} + +type TrieNode struct { + nodes []*TrieNode + IsWord bool + Word string +} + +func NewTrieNode() *TrieNode { + t := TrieNode{} + t.nodes = make([]*TrieNode, 26) + return &t +} + +func (t *TrieNode) Insert(word string) { + node := t + for i := 0; i < len(word); i++ { + idx := word[i] - A + if node.nodes[idx] == nil { + node.nodes[idx] = NewTrieNode() + } + + node = node.nodes[idx] + } + + node.IsWord = true + node.Word = word +} + +func (t *TrieNode) SearchNode(b byte) *TrieNode { + return t.nodes[b - A] +} diff --git a/Week 06/id_733/LeetCode_36_733.go b/Week 06/id_733/LeetCode_36_733.go new file mode 100644 index 000000000..b8c16bc21 --- /dev/null +++ b/Week 06/id_733/LeetCode_36_733.go @@ -0,0 +1,41 @@ +const ( + n = 9 + empty = '.' +) + +func isValidSudoku(board [][]byte) bool { + if len(board) != n || len(board[0]) != n { + return false + } + + cols := newSudokuRecorder() + boxes := newSudokuRecorder() + for i := 0; i < n; i++ { + rows := make([]bool, n) + for j := 0; j < n; j++ { + if board[i][j] == empty { + continue + } + + idx, cIdx, bIdx := board[i][j]-'1', j, (i/3)*3+j/3 + if rows[idx] || cols[cIdx][idx] || boxes[bIdx][idx] { + return false + } + + rows[idx] = true + cols[cIdx][idx] = true + boxes[bIdx][idx] = true + } + } + + return true +} + +func newSudokuRecorder() [][]bool { + r := make([][]bool, n) + for i := 0; i < n; i++ { + r[i] = make([]bool, n) + } + + return r +} diff --git a/Week 06/id_733/LeetCode_37_733.go b/Week 06/id_733/LeetCode_37_733.go new file mode 100644 index 000000000..ba6ae4bdb --- /dev/null +++ b/Week 06/id_733/LeetCode_37_733.go @@ -0,0 +1,73 @@ +const ( + n = 9 + numCnt = n * n + empty = '.' +) + +func solveSudoku(board [][]byte) { + if len(board) != n || len(board[0]) != n { + return + } + + rows := newSudokuRecorder() + cols := newSudokuRecorder() + boxes := newSudokuRecorder() + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + if board[i][j] != empty { + k := board[i][j] - '1' + rows[i][k] = true + cols[j][k] = true + boxes[(i/3)*3 + j/3][k] = true + } + } + } + + doSolveSudoku(board, 0, rows, cols, boxes) +} + +func doSolveSudoku(board [][]byte, idx int, rows, cols, boxes [][]bool) bool { + if idx >= numCnt { + return true + } + + i, j := idx/n, idx%n + if board[i][j] != empty { + return doSolveSudoku(board, idx+1, rows, cols, boxes) + } + + for k := 0; k < n; k++ { + if rows[i][k] || cols[j][k] { + continue + } + + bIdx := (i/3)*3 + j/3 + if boxes[bIdx][k] { + continue + } + + board[i][j] = byte('1' + k) + rows[i][k] = true + cols[j][k] = true + boxes[bIdx][k] = true + if doSolveSudoku(board, idx+1, rows, cols, boxes) { + return true + } + + rows[i][k] = false + cols[j][k] = false + boxes[bIdx][k] = false + } + + board[i][j] = empty + return false +} + +func newSudokuRecorder() [][]bool { + r := make([][]bool, n) + for i := 0; i < n; i++ { + r[i] = make([]bool, n) + } + + return r +} diff --git a/Week 06/id_733/LeetCode_433_733.go b/Week 06/id_733/LeetCode_433_733.go new file mode 100644 index 000000000..4fe0fbbe8 --- /dev/null +++ b/Week 06/id_733/LeetCode_433_733.go @@ -0,0 +1,66 @@ +var genes = []rune{'A', 'C', 'G', 'T'} + +func minMutation(start string, end string, bank []string) int { + if len(bank) == 0 { + return -1 + } + + bankSet := make(map[string]struct{}, len(bank)) + for _, g := range bank { + bankSet[g] = struct{}{} + } + + if _, ok := bankSet[end]; !ok { + return -1 + } + + delete(bankSet, end) + + beginSet := make(map[string]struct{}) + endSet := make(map[string]struct{}) + visited := make(map[string]struct{}) + beginSet[start] = struct{}{} + endSet[end] = struct{}{} + + times := 1 + for len(beginSet) > 0 { + newBeginSet := make(map[string]struct{}) + for geneStr, _ := range beginSet { + curGenes := []rune(geneStr) + for i, gene := range curGenes { + for _, g := range genes { + if g == gene { + continue + } + + curGenes[i] = g + mutatedGenes := string(curGenes) + if _, ok := endSet[mutatedGenes]; ok { + return times + } + + if _, ok := bankSet[mutatedGenes]; !ok { + continue + } + + if _, ok := visited[mutatedGenes]; ok { + continue + } + + newBeginSet[mutatedGenes] = struct{}{} + visited[mutatedGenes] = struct{}{} + } + + curGenes[i] = gene + } + } + + beginSet = newBeginSet + times++ + if len(beginSet) > len(endSet) { + beginSet, endSet = endSet, beginSet + } + } + + return -1 +} diff --git a/Week 06/id_733/LeetCode_51_733.go b/Week 06/id_733/LeetCode_51_733.go new file mode 100644 index 000000000..ef826772b --- /dev/null +++ b/Week 06/id_733/LeetCode_51_733.go @@ -0,0 +1,57 @@ +const ( + queen = 'Q' + empty = '.' +) + +func solveNQueens(n int) [][]string { + res := make([][]string, 0) + if n <= 0 { + return res + } + + board := make([][]rune, n) + for i := range board { + board[i] = make([]rune, n) + for j := range board[i] { + board[i][j] = empty + } + } + + cols := make([]bool, n) + leftSlash := make([]bool, n*2) + rightSlash := make([]bool, n*2) + doSolveNQueens(board, &res, 0, n, cols, leftSlash, rightSlash) + return res +} + +func doSolveNQueens(board [][]rune, res *[][]string, row, n int, + cols, leftSlash, rightSlash []bool) { + if row == n { + r := make([]string, n) + for i, v := range board { + r[i] = string(v) + } + + *res = append(*res, r) + return + } + + for i := 0; i < n; i++ { + cIdx, lsIdx, rsIdx := i, row+i, row-i+n + if cols[cIdx] || leftSlash[lsIdx] || rightSlash[rsIdx] { + continue + } + + board[row][i] = queen + cols[cIdx] = true + leftSlash[lsIdx] = true + rightSlash[rsIdx] = true + + doSolveNQueens(board, res, row+1, n, cols, leftSlash, rightSlash) + + board[row][i] = empty + cols[cIdx] = false + leftSlash[lsIdx] = false + rightSlash[rsIdx] = false + } +} diff --git a/Week 06/id_733/LeetCode_547_733.go b/Week 06/id_733/LeetCode_547_733.go new file mode 100644 index 000000000..f899a2605 --- /dev/null +++ b/Week 06/id_733/LeetCode_547_733.go @@ -0,0 +1,59 @@ +func findCircleNum(M [][]int) int { + n := len(M) + if n == 0 || len(M[0]) != n { + return 0 + } + + d := NewDisjointSet(n) + for i := 0; i < n; i++ { + for j := i + 1; j < n; j++ { + if M[i][j] == 1 { + d.Union(i, j) + } + } + } + + return d.SetCnt() +} + +type DisjointSet struct { + parents []int + setCnt int +} + +func NewDisjointSet(n int) *DisjointSet { + set := DisjointSet{} + set.parents = make([]int, n) + for i := range set.parents { + set.parents[i] = i + } + + set.setCnt = n + return &set +} + +func (d *DisjointSet) Union(p, q int) { + rootP, rootQ := d.findRoot(p), d.findRoot(q) + if rootP == rootQ { + return + } + + d.parents[rootQ] = rootP + d.setCnt-- +} + +func (d *DisjointSet) SetCnt() int { + return d.setCnt +} + +func (d *DisjointSet) findRoot(p int) int { + parent := d.parents[p] + for parent != p { + newParent := d.parents[parent] + d.parents[p] = newParent + p = parent + parent = newParent + } + + return parent +} diff --git a/Week 06/id_738/LeetCode_130_738.py b/Week 06/id_738/LeetCode_130_738.py new file mode 100644 index 000000000..21a34356c --- /dev/null +++ b/Week 06/id_738/LeetCode_130_738.py @@ -0,0 +1,62 @@ +class Solution(object): + def solve(self, board): + """ + 被围绕的区域: https://leetcode-cn.com/problems/surrounded-regions/ + + :type board: List[List[str]] + :rtype: None Do not return anything, modify board in-place instead. + """ + # 并查集解法二: + + + +################################################################### + + # 并查集解法一: + # 建立并查集 + if not board: + return + m = len(board) + n = len(board[0]) + p = [i * n + j for i in range(m) for j in range(n)] + def _find(p, i): + root = p[i] + while p[root] != root: + root = p[root] + # 压缩空间 + while p[i] != i: + tmp = p[i] + p[i] = root + i = tmp + return root + def _union(p, i, j): + a = _find(p, i) + b = _find(p, j) + # 有一个在边缘,则边缘的为root + _a = p[a] // n + _b = p[a] % n + if _a == 0 or _a == m - 1 or _b == 0 or _b == n - 1: + p[b] = a + else: + p[a] = b + # 利用并查集解答 + dx = [0, 0, 1, -1] + dy = [1, -1, 0, 0] + for i in range(m): + for j in range(n): + if board[i][j] == 'O': + # 合并上下左右 + for di in range(4): + new_i = i + dy[di] + new_j = j + dx[di] + if 0 <= new_i < m and 0 <= new_j < n and board[new_i][new_j] == 'O': + _union(p, new_i * n + new_j, i * n + j) + + # 遍历并查集,root不在边上的集合全部置X + for i in range(m): + for j in range(n): + root = _find(p, i * n + j) + _a = p[root] // n + _b = p[root] % n + if not (_a == 0 or _a == m - 1 or _b == 0 or _b == n - 1): + board[i][j] = 'X' diff --git a/Week 06/id_738/LeetCode_433_738.py b/Week 06/id_738/LeetCode_433_738.py new file mode 100644 index 000000000..a6c978139 --- /dev/null +++ b/Week 06/id_738/LeetCode_433_738.py @@ -0,0 +1,59 @@ +class Solution(object): + def minMutation(self, start, end, bank): + """ + 最小基因变化:https://leetcode-cn.com/problems/minimum-genetic-mutation/#/description + + :type start: str + :type end: str + :type bank: List[str] + :rtype: int + """ + # 双向BFS + bank = set(bank) + if end not in bank: + return -1 + change = {'A': 'TCG', 'T': 'ACG', 'C': 'ATG', 'G': 'ATC'} + head = {start} + tail = {end} + step = 0 + while head: + step += 1 + next_head = set() + for n in head: + for i in range(len(n)): + for c in change[n[i]]: + new_n = n[:i] + c + n[i + 1:] + if new_n in tail: + return step + if new_n in bank: + next_head.add(new_n) + bank.remove(new_n) + head = next_head + if len(head) > len(tail): + head, tail = tail, head + return -1 + + +##################################################################### + + # BFS + change = {'A': 'TCG', 'T': 'ACG', 'C': 'ATG', 'G': 'ATC'} + queue = [start] + step = -1 + bank = set(bank) + while queue: + l = [] + step += 1 + while queue: + n = queue.pop() + if n == end: + return step + l.append(n) + for n in l: + for i in range(len(n)): + for c in change[n[i]]: + new_n = n[:i] + c + n[i + 1:] + if new_n in bank: + queue.append(new_n) + bank.remove(new_n) + return -1 diff --git a/Week 06/id_738/NOTE.md b/Week 06/id_738/NOTE.md index a6321d6e2..f151634d4 100644 --- a/Week 06/id_738/NOTE.md +++ b/Week 06/id_738/NOTE.md @@ -1,4 +1,302 @@ -# NOTE +# 第六周学习总结 + 双向BFS代码模板 + 分析单词搜索2用Tire树方式实现的时间复杂度 +## 知识点总结 +### 字典树 +- 字典树:Trie树、单词查找树、键树 + - 应用 + - 统计和排序大量的字符串(但并不仅限于字符串),经常被用在搜索引擎系统用于文本词频统计 + - 基本性质结构 + - 树形结构 + - 节点本身不存完整单词 + - 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串(图中节点里面的字符并不是代表节点会存这么一个或者多个字符) + - 每个节点的所有子节点路径代表的字符都不同 + - 如果涉及到统计频次,那么节点存储额外的数字,代表频次 + - 优点: + - 最大限度地减少无谓的字符串比较 + - 查询效率比哈希表高 + - 核心思想 + - 空间换时间 + - 利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的 + - 代码模板: +```python + +class Trie(object): + def __init__(self): + self.root = {} + self.end_of_word = "#" + + def insert(self, word): + node = self.root + for char in word: + node = node.setdefault(char, {}) + node[self.end_of_word] = self.end_of_word + + def search(self, word): + node = self.root + for char in word: + if char not in node: + return False + node = node[char] + return self.end_of_word in node + + def startsWith(self, prefix): + node = self.root + for char in prefix: + if char not in node: + return False + node = node[char] + return True + +``` + +### 并查集 + +- 不会,压根就不会,一会,直接用就行了。。。 +- 适用场景 + - 组团、配对问题:判断两个东西是不是一个集体中 +- 并查集基本操作 + - makeSet(s): 建立一个新的并查集,其中包含s个单元素集合。 + - unionSet(x, y): 把元素x和元素y所在的集合合并,要求x和y所在的集合不相交,如果相交则不合并。 + - find(x): 找到元素x所在的集合的代表,该操作也可以用于判断两个元素是否位于同一个集合,只要它们各自的代表比较一下就可以了。 +- 并查集和BFS、DFS的运用场景差异一些做题总结 + +``` +这周课程中的题目,并查集解题 的 时间复杂度 永远比 BFS,DFS要高。 + +对静态的集体数据,一次性解决问题,用并查集方法并不是最优的。 + +但是对于变化的集体数据,并查集可以基于已经构建好的数据空间,进行调整和改动,并能在O(1)下随时查出两者的归组问题,这是BFS,DFS做不到的。 + +在动态变化的情况下,并查集还是空间换时间的思想。 +``` + +- 实现代码 + +```python +def init(p): + # for i = 0 .. n: p[i] = i; + p = [i for i in range(n)] + +def union(self, p, i, j): + p1 = self.parent(p, i) + p2 = self.parent(p, j) + p[p1] = p2 + +def parent(self, p, i): + root = i + while p[root] != root: + root = p[root] + while p[i] != i: # 路径压缩 + x = i + i = p[i] + p[x] = root + return root +``` + +### 高级搜索 + +#### 优化搜索 +- 初级搜索 + - 朴素搜索 + - 优化方式:不重复,剪枝 + - 搜索方向: + - DFS + - BFS + - 双向搜索 + - 启发式搜索 + +#### 双向BFS +- 代码模板 + +```python +# 如果搜索变换结果有限制在一个列表limit里面,那么先转化为set方便后面查找 +limit = set(limit) +# 特殊值过滤,如果题解要求搜索目标一定要在limit里面,则要过滤掉哪些没有在里面的情况返回 +if target not in limit: + return +# 头尾BFS,定义头尾集合,存储头尾每一层到达的值 +head = {start} +tail = {target} +# 遍历步数,按照题解可能为0或者-1 +step = 0 +# 对头尾进行广度优先搜索,这里有个细节处理,始终将head和tail长度小的集合赋值给head +# 这样搜索效率更高 +while head: + # 每一层步数+1 + step += 1 + # 准备下一层到达的值的集合 + next_head = set() + # 处理本层逻辑 + for n in head: + next_n = get_next_level_node(n) # 通过本层的节点获取下一层处理节点 + # 如果替换的值在tail里面找到表示搜索到了,返回步数 或者 结果 + if new_n in tail: + return step (or) value + if new_n in limit: # 如果新节点满足题意限制条件 + next_head.add(new_n) # 加入到一下层处理节点集合中 + # 遍历过的节点从限定列表里面移除,防止重复遍历 + limit.remove(new_n) + # 更新head集合为下一层的值的集合 + head = next_head + # 头尾互换,前面提到的 始终将head和tail长度小的集合赋值给head + if len(head) > len(tail): + head, tail = tail, head +# 没有找到返回异常结果值-1 +return -1 + +``` + +#### 启发式搜索 + +- 启发式搜索:智能搜索、A*搜索 + - 通过优先级去进行搜索 + - 代码模板: + +```python +def AstarSearch(graph, start, end): + pq = collections.priority_queue() #优先级 -> 估价函数 + pq.append([start]) + visited.add(start) + while pq: + node = pq.pop() # can we add more inteligence here + visited.add(node) + + process(node) + nodes = generate_related_nodes(node) + unvisited = [node for node in nodes if node not in visited] + pq.push(unvisited) +``` + +- 估价函数 + - h(n),它用来评价哪些节点是最有希望的是一个我们要找的及诶点,h(n)会返回一个非负实数,也可以认为是从节点n的目标节点路径的估计成本。 + - 启发式函数式一种告知搜索方向的方法。它提供了一种明智的方法来猜测哪个邻居节点会导向一个目标。 + - [相似度测量方法](https://dataaspirant.com/2015/04/11/five-most-popular-similarity-measures-implementation-in-python/) + - [8 puzzles 解法比较](https://zxi.mytechroad.com/blog/searching/8-puzzles-bidirectional-astar-vs-bidirectional-bfs/) + +### 红黑树和AVL树 + +#### 平衡树的作用和种类 + +- 二叉搜索树在最坏的情况下,可能是一条“棍子”,搜索时间复杂度退化到O(n)。 + +- 保证性能的关键 + - 保证二维维度 -> 左右子树节点平衡 + - Balanced -> 平衡二叉树 +- [平衡树](https://en.wikipedia.org/wiki/Self-balancing_binary_search_tree):面试着重掌握 + - 2-3树 + - AVL树 + - B树 + - 红黑树 + +#### AVL树 +- AVL树 + - 发明者G.M.Adelson-Velsky和Evgenii Landis + - Balance Factor(平衡因子): + - 左子树的高度减去它的右子树的高度(有时相反)。 + - balance factor = {-1, 0, 1} + - 平衡二叉搜索树 + - 每个节点存平衡因子balance factor = {-1, 0, 1} + - 不足 + - 节点需要存储额外信息 + - 调整次数频繁 + - 判断是否是AVL树 + - 扫描每个节点,看节点存储的旋转因子是否为{-1, 0, 1} + - 通过旋转操作来进行平衡(四种)(注意旋转节点带有子树的情况) + - 左左型 -> 右旋平衡 + - 右右型 -> 左旋平衡 + - 左右型 -> 先左旋,再右旋平衡 + - 右左型 -> 先右旋,再左旋平衡 + +#### 红黑树 + +- 红黑树 + - 近似平衡二叉树 + - 能够确保任何一个节点的左右子树的高度差小于两倍 + - 比如左子树高度为2,右子树最大高度为4 + - 比如右子树高度为5,左子树最大高度为10 +- 红黑树是满足如下条件的二叉搜索树: + - 每个节点要么是红色,要么是黑色 + - 根节点是黑色 + - 每个叶节点是黑色的,且是空节点 + - 不能有相邻接的两个红色节点 + - 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点 +- 性质 + - 从根到叶子节点最长的可能路径不多于最短的可能路径的两倍长 + +## 分析单词搜索 2 用 Tire 树方式实现的时间复杂度 + +[单词搜索2](https://leetcode-cn.com/problems/word-search-ii/) + +```python +class Solution(object): + def findWords(self, board, words): + """ + 单词搜索2:https://leetcode-cn.com/problems/word-search-ii/ + + :type board: List[List[str]] + :type words: List[str] + :rtype: List[str] + """ + def dfs(x, y, cur_word, cur_node, board): + if '#' in cur_node: + result.append(cur_word) + tmp, board[y][x] = board[y][x], None + for (dx, dy) in ((-1, 0), (1, 0), (0, -1), (0, 1)): + _x, _y = x + dx, y + dy + if 0 <= _x < col_size and 0 <= _y < row_size and board[_y][_x] and board[_y][_x] in cur_node: + dfs(_x, _y, cur_word + board[_y][_x], cur_node[board[_y][_x]], board) + board[y][x] = tmp + + root = {} + for word in words: + node = root + for w in word: + node = node.setdefault(w, {}) + node['#'] = '#' + + result, col_size, row_size = [], len(board[0]), len(board) + for n in range(col_size): + for m in range(row_size): + if board[m][n] in root: + dfs(n, m, board[m][n], root[board[m][n]], board) + return set(result) +``` + +- 时间复杂度分析 + +``` +设words长度为 k ,每个单词的平均长度为 t , board长宽为 n * m +1. 构建words的字典树时间复杂度为 O(k * t) +2. 对board的每个字母进行扫描,时间复杂度为 O(n * m) +3. 对board的每个字母进行dfs操作 + 3.1 从字典树的根节点,判断到该字母为止的字符串是否为一个单词,时间复杂度为O(1) + 3.2 对该字母的扩散4个方向的字母,进行下探递归dfs + 3.2.1 这里可以理解为一个四叉树,四叉树的平均深度为字典树的平均深度,也就是单词的平均长度 t + 3.2.2 深度为 t的四叉树,节点一共有 4^0 + 4^1 + ... + 4^i {i<=t},等比数列求和得节点 (4^t - 1) / 3 + 3.3.3 综上,dfs这个方法的时间复杂度为 O((4^t- 1) / 3) +综上,该题解的时间复杂度为: +O(k * t) + O(n * m * (4^t- 1) / 3) +规整为: +O(n * m * 4^t) + +``` + + + + + + + + + + + + + + + + + + diff --git a/Week 06/id_738/pratice/LeetCode_1091_738.py b/Week 06/id_738/pratice/LeetCode_1091_738.py new file mode 100644 index 000000000..3e510812b --- /dev/null +++ b/Week 06/id_738/pratice/LeetCode_1091_738.py @@ -0,0 +1,42 @@ +class Solution(object): + def shortestPathBinaryMatrix(self, grid): + """ + 二进制矩阵中的最短路径:https://leetcode-cn.com/problems/shortest-path-in-binary-matrix/ + :type grid: List[List[int]] + :rtype: int + """ + # BFS + n = len(grid) + if grid[0][0] or grid[-1][-1]: + return -1 + elif n <= 2: + return n + q = [(0, 0)] + step = 0 + dx = [0, 0, 1, -1, 1, -1, 1, -1] + dy = [1, -1, 0, 0, 1, -1, -1, 1] + while q: + step += 1 + l = [] + while q: + l.append(q.pop()) + for i, j in l: + for new_i, new_j in [(i - 1, j - 1), (i - 1, j), (i - 1, j + 1), (i, j - 1), (i, j + 1), (i + 1, j - 1),(i + 1, j), (i + 1, j + 1)]: + if new_j == new_i == n - 1: + return step + 1 + if 0 <= new_i < n and 0 <= new_j < n and not grid[new_i][new_j]: + q.append((new_i, new_j)) + grid[new_i][new_j] = 1 + return -1 + + + + + + + + + + + + diff --git a/Week 06/id_738/pratice/LeetCode_127_738.py b/Week 06/id_738/pratice/LeetCode_127_738.py new file mode 100644 index 000000000..cdfef37b0 --- /dev/null +++ b/Week 06/id_738/pratice/LeetCode_127_738.py @@ -0,0 +1,75 @@ +class Solution(object): + def ladderLength(self, beginWord, endWord, wordList): + """ + 单词接龙: https://leetcode-cn.com/problems/word-ladder/submissions/ + + :type beginWord: str + :type endWord: str + :type wordList: List[str] + :rtype: int + """ + # 双向BFS + wordList = set(wordList) + if endWord not in wordList: + return 0 + head = {beginWord} + tail = {endWord} + step = 0 + while head: + step += 1 + next_head = set() + for word in head: + for i in range(len(word)): + for c in "abcdefghijklmnopqrstuvwxyz": + new_w = word[:i] + c + word[i + 1:] + if new_w in tail: + return step + 1 + if new_w in wordList: + next_head.add(new_w) + wordList.remove(new_w) + head = next_head + # 选择少的节点进行扩散,这样效率会更高 + if len(head) > len(tail): + head, tail = tail, head + return 0 + + +############################################################################## + # 单向BFS + step = 0 + queue = [beginWord] + m = collections.defaultdict(list) + # 过滤出wordList每个单词,能到达该单词的原来的词的特点 + for word in wordList: + for i in range(len(word)): + m[word[:i] + '*' + word[i + 1:]].append(word) + # wordList一个单词可能加入到m的多个映射中,对于已经扩散到的单词,最短路径就是第一次扩散到的时候,所以在搜索的时候,要排除已经第一次扩散到的单词,剪枝加快速度 + visited = set() + while queue: + step += 1 + l = [] + while queue: + n = queue.pop() + if n == endWord: + return step + l.append(n) + for w in l: + for i in range(len(w)): + _w = w[:i] + '*' + w[i + 1:] + for new_w in m[_w]: + if new_w not in visited: + visited.add(new_w) + queue.append(new_w) + m[_w] = [] + return 0 + + + + + + + + + + + diff --git a/Week 06/id_738/pratice/LeetCode_200_738.java b/Week 06/id_738/pratice/LeetCode_200_738.java new file mode 100644 index 000000000..4cc80bf74 --- /dev/null +++ b/Week 06/id_738/pratice/LeetCode_200_738.java @@ -0,0 +1,67 @@ +public class Solution { + // 岛屿数量:https://leetcode-cn.com/problems/number-of-islands/submissions/ + + class UnionFind{ + private int[] parent; + // 并查集有多少个组 + private int group = 0; + UnionFind(int count) { + //最后一个元素为特殊元素,所有水都是和他属于同个组 + parent = new int[count + 1]; + group = count + 1; + for (int i = 0; i < parent.length; i++) { + parent[i] = i; + } + } + int find(int i) { + int root = parent[i]; + while (root != parent[root]) { + root = parent[root]; + } + // 路径压缩 + while (i != parent[i]) { + int tmp = parent[i]; + parent[i] = root; + i = tmp; + } + return root; + } + void union(int x, int y) { + int rootX = find(x); + int rootY = find(y); + if (rootX != rootY) { + parent[rootY] = rootX; + group --; + } + } + int countGroup() { + return group; + } + } + + int numIslands(char[][] grid) { + if (grid == null || grid.length == 0) return 0; + int m = grid.length; + int n = grid[0].length; + UnionFind uf = new UnionFind(m * n); + int[] dx = new int[]{0, 0, 1, -1}; + int[] dy = new int[]{1, -1, 0, 0}; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == '1') { + for (int di = 0; di < 4; di ++) { + int new_i = i + dy[di]; + int new_j = j + dx[di]; + if (0 <= new_i && new_i < m && 0 <= new_j && new_j < n && grid[new_i][new_j] == '1') { + uf.union(i * n + j, new_i * n + new_j); + } + } + } else { + uf.union(i * n + j, m * n); + } + } + } + // 并查集有多少个组就是多少个岛屿 + return uf.countGroup() - 1; + } +} \ No newline at end of file diff --git a/Week 06/id_738/pratice/LeetCode_208_738.py b/Week 06/id_738/pratice/LeetCode_208_738.py new file mode 100644 index 000000000..711e6dffa --- /dev/null +++ b/Week 06/id_738/pratice/LeetCode_208_738.py @@ -0,0 +1,59 @@ +class Trie(object): + """ + 实现Trie树:https://leetcode-cn.com/problems/implement-trie-prefix-tree/ + """ + + def __init__(self): + """ + Initialize your data structure here. + """ + self.trie = {} + self.end_of_word = '#' + + + def insert(self, word): + """ + Inserts a word into the trie. + :type word: str + :rtype: None + """ + node = self.trie + for c in word: + node = node.setdefault(c, {}) + node[self.end_of_word] = self.end_of_word + + + def search(self, word): + """ + Returns if the word is in the trie. + :type word: str + :rtype: bool + """ + node = self.trie + for w in word: + if w not in node: + return False + node = node[w] + return self.end_of_word in node + + + def startsWith(self, prefix): + """ + Returns if there is any word in the trie that starts with the given prefix. + :type prefix: str + :rtype: bool + """ + node = self.trie + for w in prefix: + if w not in node: + return False + node = node[w] + return True + + + +# Your Trie object will be instantiated and called as such: +# obj = Trie() +# obj.insert(word) +# param_2 = obj.search(word) +# param_3 = obj.startsWith(prefix) \ No newline at end of file diff --git a/Week 06/id_738/pratice/LeetCode_212_738.py b/Week 06/id_738/pratice/LeetCode_212_738.py new file mode 100644 index 000000000..062854f95 --- /dev/null +++ b/Week 06/id_738/pratice/LeetCode_212_738.py @@ -0,0 +1,42 @@ +class Solution(object): + def findWords(self, board, words): + """ + 单词搜索2:https://leetcode-cn.com/problems/word-search-ii/ + + :type board: List[List[str]] + :type words: List[str] + :rtype: List[str] + """ + def dfs(x, y, cur_word, cur_node, board): + if '#' in cur_node: + result.append(cur_word) + tmp, board[y][x] = board[y][x], None + for (dx, dy) in ((-1, 0), (1, 0), (0, -1), (0, 1)): + _x, _y = x + dx, y + dy + if 0 <= _x < col_size and 0 <= _y < row_size and board[_y][_x] and board[_y][_x] in cur_node: + dfs(_x, _y, cur_word + board[_y][_x], cur_node[board[_y][_x]], board) + board[y][x] = tmp + + root = {} + for word in words: + node = root + for w in word: + node = node.setdefault(w, {}) + node['#'] = '#' + + result, col_size, row_size = [], len(board[0]), len(board) + for n in range(col_size): + for m in range(row_size): + if board[m][n] in root: + dfs(n, m, board[m][n], root[board[m][n]], board) + return set(result) + + + + + + + + + + diff --git a/Week 06/id_738/pratice/LeetCode_22_738.py b/Week 06/id_738/pratice/LeetCode_22_738.py new file mode 100644 index 000000000..1ca9fc95b --- /dev/null +++ b/Week 06/id_738/pratice/LeetCode_22_738.py @@ -0,0 +1,56 @@ +class Solution(object): + def generateParenthesis(self, n): + """ + 括号生成:https://leetcode-cn.com/problems/generate-parentheses/#/description + + :type n: int + :rtype: List[str] + """ + # 动态规划 / 高级搜索 + # 设i为括号的对数 + # 那么 思考:i对括号的有效组合 和i - 1 对括号的所有有效组合有什么关系呢 + # 观察到: i对括号和i - 1对括号有如下关系: + # '(' + 'p对括号的组合' + ')' + 'q对括号的组合' {p + q = i - 1} + # 所有可以从i = 0和i = 1对括号组合算起,根据上面"公式"计算出i = 2...n对括号的组合 + result = [] + result.append(['']) # i == 0 + result.append(['()']) # i == 1 + #计算i == 2到n + for i in range(2, n + 1): + l = [] + for p in range(i): + a = result[p] # p对括号所有组合 + b = result[i - p - 1] # q对括号所有组合 + for p in a: + for q in b: + l.append('(' + p + ')' + q) + result.append(l) + return result[n] + +###########################################################3 + # DFS + # def dfs(l_num, r_num, s, n = n): + # if len(s) == n * 2: + # self.result.append(s) + # return + # if l_num < n: + # dfs(l_num + 1, r_num, s + '(') + # if r_num < l_num and r_num < n: + # dfs(l_num, r_num + 1, s + ')') + # self.result = [] + # dfs(1, 0, '(') + # return self.result +###########################################################3 + # BFS + # queue = [(1, 0, '(')] + # result = [] + # while queue: + # l_num, r_num, cur = queue.pop(0) + # if len(cur) == n * 2: + # result.append(cur) + # continue + # if l_num < n: + # queue.append((l_num + 1, r_num, cur + '(')) + # if r_num < n and r_num < l_num: + # queue.append((l_num, r_num + 1, cur + ')')) + # return result \ No newline at end of file diff --git a/Week 06/id_738/pratice/LeetCode_36_738.py b/Week 06/id_738/pratice/LeetCode_36_738.py new file mode 100644 index 000000000..581186bbd --- /dev/null +++ b/Week 06/id_738/pratice/LeetCode_36_738.py @@ -0,0 +1,26 @@ +class Solution(object): + def isValidSudoku(self, board): + """ + 有效的数独: https://leetcode-cn.com/problems/valid-sudoku/submissions/ + + :type board: List[List[str]] + :rtype: bool + """ + # 对所有元素进行遍历一遍 + # 对9行每一行的数字进行计数 + # 对9列每一列的数字进行计数 + # 对9个格子每个格子的数字进行计数 + # 如果上面3个计数任何一个计数数字>1则False + # 计数方法用字典,三种计数每种计数需要9个字典存储 + row = [{} for _ in range(9)] + col = [{} for _ in range(9)] + box = [{} for _ in range(9)] + for i in range(9): #行 + for j in range(9): #列 + num = board[i][j] + if num == '.': + continue + if num in row[i] or num in col[j] or num in box[i // 3 * 3 + j // 3]: + return False + row[i][num] = col[j][num] = box[i // 3 * 3 + j // 3][num] = 1 + return True diff --git a/Week 06/id_738/pratice/LeetCode_37_738.py b/Week 06/id_738/pratice/LeetCode_37_738.py new file mode 100644 index 000000000..1794a3b43 --- /dev/null +++ b/Week 06/id_738/pratice/LeetCode_37_738.py @@ -0,0 +1,53 @@ +class Solution(object): + def solveSudoku(self, board): + """ + 解数独:https://leetcode-cn.com/problems/sudoku-solver/#/description + + :type board: List[List[str]] + :rtype: None Do not return anything, modify board in-place instead. + """ + # 先将所有需要填充的格子找出来放到to_fill数组中 + to_fill = [] + # 计算出需要填充的格子可以填充的数字 + row = [set(range(1, 10)) for _ in range(9)] + col = [set(range(1, 10)) for _ in range(9)] + box = [set(range(1, 10)) for _ in range(9)] + for i in range(9): + for j in range(9): + if board[i][j] == '.': # 找出需要填充的格子 + to_fill.append((i, j)) + else: # 已经有数字的格子,写入row, col, box + num = int(board[i][j]) + row[i].remove(num) + col[j].remove(num) + box[i // 3 * 3 + j // 3].remove(num) + # 上面初始化完毕,下面进行真正的递归填充判断 + def fill(n): + if n == len(to_fill): + return True + i, j = to_fill[n] + k = i //3 * 3 + j // 3 + # 循环num可以填充的数字 + for num in row[i] & col[j] & box[k]: + board[i][j] = str(num) + row[i].remove(num) + col[j].remove(num) + box[k].remove(num) + if fill(n + 1): + return True + row[i].add(num) + col[j].add(num) + box[k].add(num) + return False + fill(0) + + + + + + + + + + + diff --git a/Week 06/id_738/pratice/LeetCode_51_738.py b/Week 06/id_738/pratice/LeetCode_51_738.py new file mode 100644 index 000000000..85d713bbb --- /dev/null +++ b/Week 06/id_738/pratice/LeetCode_51_738.py @@ -0,0 +1,55 @@ +class Solution(object): + def solveNQueens(self, n): + """ + N皇后:https://leetcode-cn.com/problems/n-queens + + :type n: int + :rtype: List[List[str]] + """ + # 分治,剪枝,精简写法(自己第N次不断练习,每次写法都不一样,后写出来的) + result = [] + def dfs(solution, pie, na): + row = len(solution) + if row == n: + result.append(solution) + return + for col in range(n): + if col not in solution and col + row not in na and col - row not in pie: + dfs(solution + [col], pie + [col - row], na + [col + row]) + dfs([], [], []) + return [ ['.' * i + 'Q' + '.' * (n - i - 1) for i in l ] for l in result] + +######################################################################################## + + # 之前的写法 + def dfs(l, level, pie, na, su, size): + if len(l) == size: + self.result.append(l[:]) + return + if level >= size: + return + + for x in range(size): + p = x + level + n = x - level + if p not in pie and n not in na and x not in su: + pie.append(p) + na.append(n) + su.append(x) + l.append(x) + dfs(l, level + 1, pie, na, su, size) + l.pop() + pie.pop() + na.pop() + su.pop() + self.result = [] + dfs([], 0, [], [], [], n) + print(self.result) + # self.result转化成棋盘 + ana = [] + for a in self.result: + r = ['.' * n] * n + for i in range(len(a)): + r[i] = r[i][:a[i]] + 'Q' + r[i][a[i] + 1:] + ana.append(r) + return ana diff --git a/Week 06/id_738/pratice/LeetCode_547_738.py b/Week 06/id_738/pratice/LeetCode_547_738.py new file mode 100644 index 000000000..a7e552c53 --- /dev/null +++ b/Week 06/id_738/pratice/LeetCode_547_738.py @@ -0,0 +1,80 @@ +class Solution(object): + def findCircleNum(self, M): + """ + 朋友圈: https://leetcode-cn.com/problems/friend-circles/ + :type M: List[List[int]] + :rtype: int + """ + # DFS写法2: + # 基于这样一个事实: + # 对于矩阵的n个人,如果[i][j]为1表示第i个人和第j个人互为朋友 + # 如果同时[j][k]为1,则表示i j k 互为朋友,可见具有传导性 + # 扫描每一个人i,在M中对i进行和其它人进行配对扫描[i][j] {j = 0...len(M)} + # 如果[i][j] {j = 0...len(M)} 为1表示i和j互为朋友,这时候就要扫描j的朋友了 + # 所以继续递归深度扫描[j][j'] {j' = 0...len(M)},同时标记i和j已经被扫描过了(被扫描过表示同一个朋友圈) + # 最后扫描几次就是结果值 + n = len(M) + visited = [0] * n + # find all friends + def dfs(i): + for j in range(n): + if M[i][j] and not visited[j]: + visited[j] = 1 + dfs(j) + cnt = 0 + for i in range(n): + if not visited[i]: + cnt += 1 + dfs(i) + + return cnt + +############################################################################## + + # DFS解法1:效率慢:对矩形上半区域进行扫描,遇到1的,递归dfs该节点的行和列。 + size = len(M) + + def _dfs(i, j): + M[i][j] = 0 + for k in range(i, size): + new_j = k + if 0 <= new_j < size and M[i][new_j] == 1: + _dfs(i, new_j) + for s in range(size): + new_i = s + if 0 <= new_i < size and M[new_i][new_j] == 1: + _dfs(new_i, new_j) + + + count = 0 + for i in range(size): + if M[i][i] == 1: + count += 1 + _dfs(i, i) + return count + + +############################################################################## + # 并查集 + # 并查集实现 + p = [i for i in range(len(M))] + def _find(p, i): + root = p[i] + while root != p[root]: + root = p[root] + # 路径压缩 + while i != p[i]: + tmp = p[i] + p[i] = root + i = tmp + return root + def _union(p, i, j): + a = _find(p, i) + b = _find(p, j) + p[a] = b + # 利用并查集统计有多少个朋友圈 + for i in range(len(M)): + for j in range(len(M[0])): + if M[i][j] == 1: + _union(p, i, j) + return len(set([_find(p, i) for i in range(len(M))])) \ No newline at end of file diff --git a/Week 06/id_738/pratice/LeetCode_773_738.py b/Week 06/id_738/pratice/LeetCode_773_738.py new file mode 100644 index 000000000..1997a2f58 --- /dev/null +++ b/Week 06/id_738/pratice/LeetCode_773_738.py @@ -0,0 +1,57 @@ +class Solution(object): + def slidingPuzzle(self, board): + """ + 滑动谜题:https://leetcode-cn.com/problems/sliding-puzzle/ + + :type board: List[List[int]] + :rtype: int + """ + # BFS + # 技术处理,将二维结构映射成一维数组,并预先构建0在一维数组中的位置可以移动到的位置 + moves = { + 0: [1, 3], + 1: [0, 2, 4], + 2: [1, 5], + 3: [0, 4], + 4: [1, 3, 5], + 5: [2, 4] + } + # 将二维矩阵映射成一个字符串 + s = "".join(str(n) for row in board for n in row) + step = -1 + q = [s] + visited = set(q) + while q: + step += 1 + new_q = [] + for s in q: + if s == "123450": + return step + l = list(s) + zero_index = l.index('0') + for move in moves[zero_index]: + new_l = l[:] + new_l[zero_index], new_l[move] = new_l[move], new_l[zero_index] + new_s = "".join(new_l) + if new_s not in visited: + new_q.append(new_s) + visited.add(new_s) + q = new_q + return -1 + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git "a/Week 07/id_003/190.\351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.js" "b/Week 07/id_003/190.\351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.js" new file mode 100644 index 000000000..601e434db --- /dev/null +++ "b/Week 07/id_003/190.\351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.js" @@ -0,0 +1,21 @@ +/* + * @lc app=leetcode.cn id=190 lang=javascript + * + * [190] 颠倒二进制位 + */ + +// @lc code=start +/** + * @param {number} n - a positive integer + * @return {number} - a positive integer + */ +var reverseBits = function(n) { + let result = 0; + for(let i=0; i < 32; i++){ + result = (result << 1) + (n & 1); + n >>= 1; + } + return result >>> 0; +}; +// @lc code=end + diff --git "a/Week 07/id_003/191.\344\275\215-1-\347\232\204\344\270\252\346\225\260.js" "b/Week 07/id_003/191.\344\275\215-1-\347\232\204\344\270\252\346\225\260.js" new file mode 100644 index 000000000..327e8f8d2 --- /dev/null +++ "b/Week 07/id_003/191.\344\275\215-1-\347\232\204\344\270\252\346\225\260.js" @@ -0,0 +1,49 @@ +/* + * @lc app=leetcode.cn id=191 lang=javascript + * + * [191] 位1的个数 + */ + +// @lc code=start +/** + * @param {number} n - a positive integer + * @return {number} + */ + +// 方法四 +/* var hammingWeight = function(n) { + let count = 0; + while (n) { + if(n & 1 == 1){ + count++; + } + n >>>= 1; + } + return count +}; */ + /* 方法三: 正则 */ +/* var hammingWeight = function(n) { + return ((n.toString(2).match(/1/g)) || []).length; +}; */ +/* 方法二:位操作技巧 => n & n-1可消去最后一位的一 */ +/* var hammingWeight = function(n) { + let sum =0; + while(n!=0){ + sum++; + n &= (n-1); + } + + return sum; +}; */ + /* 方法一 : 循环和位移动 */ +/* var hammingWeight = function(n) { + let count = 0; + let mask = 1; + for(let i = 0; i < 32; i++){ + if((n & mask)!=0){ + count++; + } + mask <<= 1; + } + return count +}; */ diff --git "a/Week 07/id_003/231.2-\347\232\204\345\271\202.js" "b/Week 07/id_003/231.2-\347\232\204\345\271\202.js" new file mode 100644 index 000000000..1f6c5b536 --- /dev/null +++ "b/Week 07/id_003/231.2-\347\232\204\345\271\202.js" @@ -0,0 +1,16 @@ +/* + * @lc app=leetcode.cn id=231 lang=javascript + * + * [231] 2的幂 + */ + +// @lc code=start +/** + * @param {number} n + * @return {boolean} + */ +var isPowerOfTwo = function(n) { + return n > 0 && ((n & (n-1)) === 0) +}; +// @lc code=end + diff --git a/Week 07/id_003/NOTE.md b/Week 07/id_003/NOTE.md index a6321d6e2..ef99b4ff1 100644 --- a/Week 07/id_003/NOTE.md +++ b/Week 07/id_003/NOTE.md @@ -1,4 +1,93 @@ # NOTE +## 位运算符 +* 左移 << +* 右移 >> +* 按位或 | +* 按位与 & +* 按位取反 ~ +* 按位异或 6 + +### 异或操作特性: + +x ^ 0 = x + +x ^ 1s = ~x // 注意 1s = ~0 +x ^ (~x) = 1s + +x ^ x = 0 +c = a ^ b => a ^ c = b, b ^ c = a // 交换两个数 + +a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c // associative +### 指定位置的异或 + +1. 将 x 最右边的 n 位清零:x & (~0 << n) + +2. 获取 x 的第 n 位值(0 或者 1): (x >> n) & 1 + +3. 获取 x 的第 n 位的幂值:x & (1 << (n -1)) + +4. 仅将第 n 位置为 1:x | (1 << n) + +5. 仅将第 n 位置为 0:x & (~ (1 << n)) + +6. 将 x 最高位至第 n 位(含)清零:x & ((1 << n) - 1) + +7. 将第 n 位至第 0 位(含)清零:x & (~ ((1 << (n + 1)) - 1)) + +### 常用位运算 +* 判断奇偶: + + x % 2 == 1 —> (x & 1) == 1 + + x % 2 == 0 —> (x & 1) == 0 + +* x >> 1 —> x / 2 + + 即: x = x / 2; —> x = x >> 1; + + mid = (left + right) / 2; —> mid = (left + right) >> 1; + +* X = X & (X-1) 清零最低位的 1 + +* X & -X => 得到最低位的 1 + +* X & ~X => 0 + +## 布隆过滤器 +一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索 +一个元素是否在一个集合中。 + +优点是空间效率和查询时间都远远超过一般的算法, + +缺点是有一定的误识别率和删除困难。 + +## 排序算法 +### 归并 +``` +public static void mergeSort(int[] array, int left, int right) { + if (right <= left) return; + int mid = (left + right) >> 1; // (left + right) / 2 + mergeSort(array, left, mid); + mergeSort(array, mid + 1, right); + merge(array, left, mid, right); +} +``` +``` +public static void merge(int[] arr, int left, int mid, int right) { + int[] temp = new int[right - left + 1]; // 中间数组 + int i = left, j = mid + 1, k = 0; + while (i <= mid && j <= right) { + temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++]; + } + while (i <= mid) temp[k++] = arr[i++]; + while (j <= right) temp[k++] = arr[j++]; + for (int p = 0; p < temp.length; p++) { + arr[left + p] = temp[p]; + } + // 也可以⽤ System.arraycopy(a, start1, b, start2, length) + } +``` +* [十大排序算法](https://www.cnblogs.com/onepixel/p/7674659.html) \ No newline at end of file diff --git a/Week 07/id_008/LeetCode_190_008.js b/Week 07/id_008/LeetCode_190_008.js new file mode 100644 index 000000000..7e416f48d --- /dev/null +++ b/Week 07/id_008/LeetCode_190_008.js @@ -0,0 +1,14 @@ +/** + * @param {number} n - a positive integer + * @return {number} - a positive integer + */ +var reverseBits = function(n) { + var result = 0; + + for (var i = 0; i < 32; ++i){ + result = (result << 1) + (n & 1); + n >>= 1; + } + + return result >>> 0; +}; diff --git a/Week 07/id_008/LeetCode_191_008.js b/Week 07/id_008/LeetCode_191_008.js new file mode 100644 index 000000000..0c271308a --- /dev/null +++ b/Week 07/id_008/LeetCode_191_008.js @@ -0,0 +1,7 @@ +/** + * @param {number} n - a positive integer + * @return {number} + */ +var hammingWeight = function(n) { + return n.toString(2).replace(/0/g, "").length; +}; diff --git a/Week 07/id_008/LeetCode_231_008.js b/Week 07/id_008/LeetCode_231_008.js new file mode 100644 index 000000000..59072a881 --- /dev/null +++ b/Week 07/id_008/LeetCode_231_008.js @@ -0,0 +1,24 @@ +/** + * @param {number} n + * @return {boolean} + */ +var isPowerOfTwo = function(n) { + if (n <= 0) { + return false; + } + + var len = n.toString(2).length - 1; + return ((n >> len) << len) === n; +}; + +/** + * @param {number} n + * @return {boolean} + */ +var isPowerOfTwo = function(n) { + if (n <= 0) { + return false; + } + + return (n & (n - 1)) === n; +}; diff --git a/Week 07/id_008/LeetCode_338_008.js b/Week 07/id_008/LeetCode_338_008.js new file mode 100644 index 000000000..4ad83ffc4 --- /dev/null +++ b/Week 07/id_008/LeetCode_338_008.js @@ -0,0 +1,15 @@ +/** + * @param {number} num + * @return {number[]} + */ +var countBits = function(num) { + var result = []; + var n = 0; + + while (n <= num){ + result.push(n.toString(2).replace(/0/g, "").length) + ++n; + } + + return result; +}; diff --git a/Week 07/id_008/LeetCode_51_008.js b/Week 07/id_008/LeetCode_51_008.js new file mode 100644 index 000000000..d23c61c91 --- /dev/null +++ b/Week 07/id_008/LeetCode_51_008.js @@ -0,0 +1,108 @@ +/** + * @param {number} n + * @return {string[][]} + */ +var solveNQueens = function(n) { + // 生成地图,外围扩大一圈,用于边界判断 + var map = []; + + for (var i = 0; i < n + 2; ++i) { + map.push(0); + } + + for (var i = 0; i < n; ++i) { + map.push(0); + + for (j = 0; j < n; ++j) { + map.push("."); + } + + map.push(0); + } + + for (var i = 0; i < n + 2; ++i) { + map.push(0); + } + + // 生成步进,只搜索上面,左右及下面都不需要搜索 + var step = [-n - 3, -n - 2, -n - 1]; + + var result = []; + + // 从第一行开始递归 + putPieceToLine(0); + + return result; + + // 指定行放棋子 + function putPieceToLine(line){ + if (line === n) { + pushResult(); + return true; + } + + var blankPoint = findBlankFromLine(line); + + if (blankPoint.length === 0) { + return false; + } + + for (var i = 0; i < blankPoint.length; ++i) { + map[blankPoint[i]] = "Q"; + putPieceToLine(line + 1); + map[blankPoint[i]] = "."; + } + } + + // 搜索指定行可放棋子点位 + function findBlankFromLine(line){ + var start = (line + 1) * (n + 2) + 1; + var end = start + n; + var result = []; + + // 该行空白点都检查一遍 + find: for (var i = start; i < end; ++i) { + // 三个方向都检查一遍 + for (var j = 0; j < step.length; ++j) { + var k = i; + + while (1) { + // 向指定方向步进 + k += step[j]; + + // 已到达边界,跳出该方向 + if (!map[k]) { + break; + } + + // 发现棋子,跳过该空白点 + if (map[k] === "Q") { + continue find; + } + } + } + + // 合格点位,记录 + result.push(i); + } + + return result; + } + + // 将当前 map 输出为题目要求的格式 + function pushResult(){ + var lines = []; + + for (var i = 1; i <= n; ++i) { + var str = ""; + + for (var j = 1; j <= n; ++j) { + str += map[i * (n + 2) + j]; + } + + lines.push(str); + } + + result.push(lines); + } +}; diff --git a/Week 07/id_008/LeetCode_52_008.js b/Week 07/id_008/LeetCode_52_008.js new file mode 100644 index 000000000..b47b81b64 --- /dev/null +++ b/Week 07/id_008/LeetCode_52_008.js @@ -0,0 +1,118 @@ +/** + * @param {number} n + * @return {number} + */ +var totalNQueens = function(n) { + // 生成地图,外围扩大一圈,用于边界判断 + var map = []; + + for (var i = 0; i < n + 2; ++i) { + map.push(0); + } + + for (var i = 0; i < n; ++i) { + map.push(0); + + for (j = 0; j < n; ++j) { + map.push("."); + } + + map.push(0); + } + + for (var i = 0; i < n + 2; ++i) { + map.push(0); + } + + // 生成步进,只搜索上面,左右及下面都不需要搜索 + var step = [-n - 3, -n - 2, -n - 1]; + + //var result = []; + var result = 0; + + // 从第一行开始递归 + putPieceToLine(0); + + return result; + + // 指定行放棋子 + function putPieceToLine(line){ + if (line === n) { + //pushResult(); + ++result; + return true; + } + + var blankPoint = findBlankFromLine(line); + + if (blankPoint.length === 0) { + return false; + } + + for (var i = 0; i < blankPoint.length; ++i) { + map[blankPoint[i]] = "Q"; + putPieceToLine(line + 1); + map[blankPoint[i]] = "."; + } + } + + // 搜索指定行可放棋子点位 + function findBlankFromLine(line){ + var start = (line + 1) * (n + 2) + 1; + var end = start + n; + var result = []; + + // 该行空白点都检查一遍 + find: for (var i = start; i < end; ++i) { + // 三个方向都检查一遍 + for (var j = 0; j < step.length; ++j) { + var k = i; + + while (1) { + // 向指定方向步进 + k += step[j]; + + // 已到达边界,跳出该方向 + if (!map[k]) { + break; + } + + // 发现棋子,跳过该空白点 + if (map[k] === "Q") { + continue find; + } + } + } + + // 合格点位,记录 + result.push(i); + } + + return result; + } + + // 将当前 map 输出为题目要求的格式 + function pushResult(){ + var lines = []; + + for (var i = 1; i <= n; ++i) { + var str = ""; + + for (var j = 1; j <= n; ++j) { + str += map[i * (n + 2) + j]; + } + + lines.push(str); + } + + result.push(lines); + } +}; + +/** + * @param {number} n + * @return {number} + */ +var totalNQueens = function(n) { + return [1, 1, 0, 0, 2, 10, 4, 40, 92, 352, 724, 2680, 14200][n]; +} diff --git a/Week 07/id_013/LeetCode_01_013.py b/Week 07/id_013/LeetCode_01_013.py new file mode 100644 index 000000000..060443e51 --- /dev/null +++ b/Week 07/id_013/LeetCode_01_013.py @@ -0,0 +1,15 @@ +""" +第1题:191. 位1的个数 +""" + +class Solution(object): + def hammingWeight(self, n): + """ + :type n: int + :rtype: int + """ + count = 0 + while n: + n = n&(n-1) + count += 1 + return count \ No newline at end of file diff --git a/Week 07/id_013/LeetCode_02_013.py b/Week 07/id_013/LeetCode_02_013.py new file mode 100644 index 000000000..412dc9c7b --- /dev/null +++ b/Week 07/id_013/LeetCode_02_013.py @@ -0,0 +1,3 @@ +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + return n > 0 and (n&(n-1)) == 0 \ No newline at end of file diff --git a/Week 07/id_013/LeetCode_03_013.py b/Week 07/id_013/LeetCode_03_013.py new file mode 100644 index 000000000..1cebfbd9e --- /dev/null +++ b/Week 07/id_013/LeetCode_03_013.py @@ -0,0 +1,11 @@ +class Solution: + def reverseBits(self, n: int) -> int: + ans = 0 + MASK = 1 + + for i in range(32): + if n & MASK: + ans |= 1 << (31-i) + MASK <<= 1 + + return ans \ No newline at end of file diff --git a/Week 07/id_013/NOTE.md b/Week 07/id_013/NOTE.md index a6321d6e2..2e536bb08 100644 --- a/Week 07/id_013/NOTE.md +++ b/Week 07/id_013/NOTE.md @@ -1,4 +1,45 @@ -# NOTE - - - +# NOTE +选择排序 +运行时间:O(n^2) +示例代码: +找出数组中最小元素: +def findSmallest(arr): + smallest = arr[0] + smallest_index = 0 + for i in range(1, len(arr)): + if arr[i] < smallest: + smallest = arr[i] + smallest_index = i + return smallest_index + +2.编写排序算法 + +def selectionSort(arr): + newArry = [] + for i in range(len(arr)): + smallest = findSmallest(arr) + newArry.append(arr.pop(smallest)) #arr剔除一个值,newArry添加一个 + return newArry +完整运行: +def findSmallest(arr): + smallest = arr[0] + smallest_index = 0 + for i in range(1, len(arr)): + if arr[i] < smallest: + smallest = arr[i] + smallest_index = i + return smallest_index + + +def selectionSort(arr): + newArry = [] + for i in range(len(arr)): + smallest = findSmallest(arr) + newArry.append(arr.pop(smallest)) + return newArry + + +print(selectionSort([5, 3, 6, 9, 9, 6, 9])) + + + diff --git a/Week 07/id_023/leetCode_23_056.js b/Week 07/id_023/leetCode_23_056.js new file mode 100644 index 000000000..dad46fb7b --- /dev/null +++ b/Week 07/id_023/leetCode_23_056.js @@ -0,0 +1,19 @@ +var merge = function (intervals) { + if (intervals.length == 0) { + return intervals; + } + intervals.sort((a, b) => a[0] - b[0]); + let res = []; + res.push(intervals.reduce((acc, cur) => { + if (acc[1] >= cur[0]) { + if (acc[1] < cur[1]) { + acc[1] = cur[1]; + } + return acc; + } else { + res.push(acc); + return cur; + } + })); + return res; +}; \ No newline at end of file diff --git a/Week 07/id_023/leetCode_32_1122.js b/Week 07/id_023/leetCode_32_1122.js new file mode 100644 index 000000000..134215783 --- /dev/null +++ b/Week 07/id_023/leetCode_32_1122.js @@ -0,0 +1,22 @@ +/** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ +var relativeSortArray = function(arr1, arr2) { + let result = [], i = 0, target + while (true) { + if (i === 0) { + i = arr1.length + target = arr2.pop() + } + if (arr1[--i] === target) { + result.unshift(target) + arr1.splice(i, 1) + } + if (!arr2.length && !i) { + break + } + } + return result.concat(arr1.sort((a, b) => a - b)) +}; \ No newline at end of file diff --git a/Week 07/id_038/week-07-038/.gitignore b/Week 07/id_038/week-07-038/.gitignore new file mode 100644 index 000000000..168781b21 --- /dev/null +++ b/Week 07/id_038/week-07-038/.gitignore @@ -0,0 +1,165 @@ +.vertx +*.ipr +# log files +*.log +*.log.2* +.log +out +output +dist +dist-3rd +*.txt +!agent-shell-banner.txt +!shell-banner.txt +!VERSION.txt + +# For Eclipse + +.metadata +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders +*.iml +.DS_Store + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + + +# For Java + +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# For Maven + +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# For JetBrains + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml +../../.idea/ + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +#data module ignore +node_modules + +static +public + +BETHUNE_PRO_HOST_PID +BETHUNE_PRO_AGENT_PID +*PID +APP_PID +PID_* +alert-rules-*.yml +altmng-configs.yml +alertmanager.yml +app-info-*.properties +app-info.properties +inspectResult/ + +/file-uploads/ diff --git a/Week 07/id_038/week-07-038/pom.xml b/Week 07/id_038/week-07-038/pom.xml new file mode 100644 index 000000000..81cfadace --- /dev/null +++ b/Week 07/id_038/week-07-038/pom.xml @@ -0,0 +1,95 @@ + + 4.0.0 + com.github.kylefeng + week-07-038 + jar + 1.0-SNAPSHOT + week-07-038 + http://maven.apache.org + + UTF-8 + 1.8 + 4.12 + 5.0.0 + ${junit.version}.0 + 1.0.0 + + + + + + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + + + + maven-surefire-plugin + 2.19.1 + + + **/Test*.java + **/*Test.java + **/*Tests.java + **/*TestCase.java + + + + slow + + + + + + org.junit.platform + junit-platform-surefire-provider + ${junit.platform.version} + + + + + + + + + com.google.guava + guava + 28.1-jre + + + + org.assertj + assertj-core + 3.13.2 + test + + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test + + + + org.junit.platform + junit-platform-launcher + ${junit.platform.version} + test + + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + \ No newline at end of file diff --git a/Week 07/id_038/week-07-038/src/main/java/com/github/kylefeng/LeetCode_1122_038.java b/Week 07/id_038/week-07-038/src/main/java/com/github/kylefeng/LeetCode_1122_038.java new file mode 100644 index 000000000..ccddcd118 --- /dev/null +++ b/Week 07/id_038/week-07-038/src/main/java/com/github/kylefeng/LeetCode_1122_038.java @@ -0,0 +1,32 @@ +package com.github.kylefeng; + +/** + * @author kylefeng + * @time 2019/12/1 15:35 + */ +public class LeetCode_1122_038 { + public static int[] relativeSortArray(int[] arr1, int[] arr2) { + int[] bucket = new int[1001]; + int[] result = new int[arr1.length]; + int index = 0; + + for (int i : arr1) { + bucket[i]++; + } + + for (int i : arr2) { + while (bucket[i]-- > 0) { + result[index++] = i; + } + } + + for (int i = 0; i < 1001; i++) { + if (bucket[i] > 0) { + while (bucket[i]-- > 0) { + result[index++] = i; + } + } + } + return result; + } +} diff --git a/Week 07/id_038/week-07-038/src/main/java/com/github/kylefeng/LeetCode_146_038.java b/Week 07/id_038/week-07-038/src/main/java/com/github/kylefeng/LeetCode_146_038.java new file mode 100644 index 000000000..30a36dfe4 --- /dev/null +++ b/Week 07/id_038/week-07-038/src/main/java/com/github/kylefeng/LeetCode_146_038.java @@ -0,0 +1,108 @@ +package com.github.kylefeng; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author kylefeng + * @time 2019/12/1 15:23 + */ +public class LeetCode_146_038 { + + static class LRUCache { + private Map cache = new HashMap<>(); + private int count; + private int capacity; + private Node head; + private Node tail; + + public LRUCache(int capacity) { + this.count = 0; + this.capacity = capacity; + + head = new Node(); + head.pre = null; + + tail = new Node(); + tail.next = null; + + head.next = tail; + tail.pre = head; + } + + + public int get(int key) { + Node node = cache.get(key); + if (node == null) { + return -1; + } + + this.moveToHead(node); + return node.value; + } + + + public void put(int key, int value) { + Node node = cache.get(key); + + if (node == null) { + + Node newNode = new Node(); + newNode.key = key; + newNode.value = value; + + this.cache.put(key, newNode); + this.addNode(newNode); + + ++count; + + if (count > capacity) { + // pop the tail + Node tail = this.popTail(); + this.cache.remove(tail.key); + --count; + } + } else { + // update the value. + node.value = value; + this.moveToHead(node); + } + } + + + private void addNode(Node node) { + node.pre = head; + node.next = head.next; + + head.next.pre = node; + head.next = node; + } + + private void removeNode(Node node) { + Node pre = node.pre; + Node post = node.next; + + pre.next = post; + post.pre = pre; + } + + private void moveToHead(Node node) { + this.removeNode(node); + this.addNode(node); + } + + private Node popTail() { + Node res = tail.pre; + this.removeNode(res); + return res; + } + + } + + static class Node { + int key; + int value; + Node pre; + Node next; + } +} diff --git a/Week 07/id_038/week-07-038/src/main/java/com/github/kylefeng/LeetCode_191_038.java b/Week 07/id_038/week-07-038/src/main/java/com/github/kylefeng/LeetCode_191_038.java new file mode 100644 index 000000000..c668057da --- /dev/null +++ b/Week 07/id_038/week-07-038/src/main/java/com/github/kylefeng/LeetCode_191_038.java @@ -0,0 +1,18 @@ +package com.github.kylefeng; + +/** + * @author kylefeng + * @time 2019/12/1 15:15 + */ +public class LeetCode_191_038 { + + public static int hammingWeight(int n) { + int result = 0; + while (n != 0) { + result++; + n -= (n & -n); + } + return result; + } + +} diff --git a/Week 07/id_038/week-07-038/src/main/java/com/github/kylefeng/LeetCode_242_038.java b/Week 07/id_038/week-07-038/src/main/java/com/github/kylefeng/LeetCode_242_038.java new file mode 100644 index 000000000..3c9695634 --- /dev/null +++ b/Week 07/id_038/week-07-038/src/main/java/com/github/kylefeng/LeetCode_242_038.java @@ -0,0 +1,45 @@ +package com.github.kylefeng; + +import java.util.Arrays; + +/** + * 242. 有效的字母异位词 + * + * @author kylefeng + * @time 2019/10/26 10:32 + */ +public class LeetCode_242_038 { + + + /** + * @param str1 + * @param str2 + * @return + */ + public static boolean isAnagram(String str1, String str2) { + if (str1 == null && str2 == null) { + return true; + } + + if (str1 == null || str2 == null) { + return false; + } + + if (str1.equals("") || str2.equals("")) { + return true; + } + + char[] chars1 = str1.toCharArray(); + char[] chars2 = str2.toCharArray(); + + if (chars1.length != chars2.length) { + return false; + } + + Arrays.sort(chars1); + Arrays.sort(chars2); + + return Arrays.equals(chars1, chars2); + } + +} diff --git a/Week 07/id_048/048_Week07.md b/Week 07/id_048/048_Week07.md new file mode 100644 index 000000000..9a3915086 --- /dev/null +++ b/Week 07/id_048/048_Week07.md @@ -0,0 +1,20 @@ +归并排序: +归并就是二分,一直分直到可以最轻松地判断大小来排序。 + +public void mergeSort(int[] q,int l,int r){ + if(l>=r) return; + int mid = l + r >> 1; + mergeSort(q,l,mid); + mergeSort(q,mid+1,r); + int i = l,j=mid+1,k=0; + int temp[] = new int[r-l+1]; + while(i<=mid && j<=r){ + if(q[i]<=q[j]) temp[k++] = q[i++]; + else temp[k++]= q[j++]; + } + while(i<=mid) temp[k++] = q[i++]; + while(j<=r) temp[k++] = q[j++]; + for(i =l,j=0;i<=r;i++,j++){ + q[i] = temp[j]; + } +} \ No newline at end of file diff --git a/Week 07/id_048/LeetCode_1122_048.java b/Week 07/id_048/LeetCode_1122_048.java new file mode 100644 index 000000000..412c6ba70 --- /dev/null +++ b/Week 07/id_048/LeetCode_1122_048.java @@ -0,0 +1,53 @@ +package com.leetcode.week07; + +/** + * Created by tim on 2019/12/1. + * 给你两个数组,arr1 和 arr2, + + arr2 中的元素各不相同 + arr2 中的每个元素都出现在 arr1 中 + 对 arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。未在 arr2 中出现过的元素需要按照升序放在 arr1 的末尾。 + + 示例: + + 输入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6] + 输出:[2,2,2,1,4,3,3,9,6,7,19] +   + 提示: + arr1.length, arr2.length <= 1000 + 0 <= arr1[i], arr2[i] <= 1000 + arr2 中的元素 arr2[i] 各不相同 + arr2 中的每个元素 arr2[i] 都出现在 arr1 中 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/relative-sort-array + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_1122_048 { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + int[] m = new int[1001]; + + int[] ref = new int[arr1.length]; + + for(int i = 0; i < arr1.length; i++) { + m[arr1[i]]++; + } + + int cnt = 0; + for(int i = 0; i < arr2.length; i++) { + while(m[arr2[i]] > 0) { + ref[cnt++] = arr2[i]; + m[arr2[i]]--; + } + } + + for(int i = 0; i < 1001; i++) { + while(m[i] > 0) { + ref[cnt++] = i; + m[i]--; + } + } + return ref; + } + +} diff --git a/Week 07/id_048/LeetCode_493_048.java b/Week 07/id_048/LeetCode_493_048.java new file mode 100644 index 000000000..d2987e04e --- /dev/null +++ b/Week 07/id_048/LeetCode_493_048.java @@ -0,0 +1,67 @@ +package com.leetcode.week07; + +import java.util.Arrays; + +/** + * Created by tim on 2019/12/1. + * 493 翻转对 + * 给定一个数组 nums ,如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对。 + + 你需要返回给定数组中的重要翻转对的数量 + 示例 1: + + 输入: [1,3,2,3,1] + 输出: 2 + 示例 2: + + 输入: [2,4,3,5,1] + 输出: 3 + 注意: + + 给定数组的长度不会超过50000。 + 输入数组中的所有数字都在32位整数的表示范围内 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/reverse-pairs + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_493_048 { + + public int reversePairs(int[] nums) { + return mergeSort(nums,0,nums.length-1); + } + + private int mergeSort(int[] array, int left, int right) { + if (right <= left) { + return 0; + } + int mid = (left + right) >> 1; // (left + right) / 2 + int cnt = mergeSort(array,left,mid) + mergeSort(array,mid + 1, right); +// merge(array,left,mid,right); + for (int i=left,j=mid+1;i<=mid;i++) { + while (j<=right && array[i]/2.0 > array[j]) { + j++; + } + cnt += j-(mid+1); + } + Arrays.sort(array,left,right+1 ); + return cnt; + } + + private void merge(int[] arr, int left, int mid ,int right) { + int[] temp = new int[right-left+1]; //中间数组 + int i = left, j = mid+1 ,k=0; + while (i<=mid && j<=right) { + temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++]; + } + while (i <= mid) { + temp[k++] = arr[i++]; + } + while (j<=right) { + temp[k++] = arr[j++]; + } + for (int p=0;p params[j+1]) { + int temp = params[j]; + params[j] = params[j+1]; + params[j+1] = temp; + } + } + } + return params; + } + + public static int[] selectionSort(int[] params){ + int minIndex,temp; + for (int i = 0;i < params.length; i++){ + minIndex = i; +// temp = params[minIndex]; + for (int j = i+1;j < params.length - 1;j++){ + if (params[minIndex] > params[j]){ + minIndex = j; + } + temp = params[minIndex]; + params[minIndex] = params[j]; + params[j] = temp; + } + } + return params; + } + + public static void main(String[] args) { + int[] params = {-1,-5,-3,4,6,9,8,0}; +// params = bubbleSort(params); + params = selectionSort(params); + System.out.println(Arrays.toString(params)); + + } +} diff --git "a/Week 07/id_053/[191]\344\275\2151\347\232\204\344\270\252\346\225\260.java" "b/Week 07/id_053/[191]\344\275\2151\347\232\204\344\270\252\346\225\260.java" new file mode 100644 index 000000000..5e5e3fe2f --- /dev/null +++ "b/Week 07/id_053/[191]\344\275\2151\347\232\204\344\270\252\346\225\260.java" @@ -0,0 +1,55 @@ +//编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。 +// +// +// +// 示例 1: +// +// 输入:00000000000000000000000000001011 +//输出:3 +//解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。 +// +// +// 示例 2: +// +// 输入:00000000000000000000000010000000 +//输出:1 +//解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。 +// +// +// 示例 3: +// +// 输入:11111111111111111111111111111101 +//输出:31 +//解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。 +// +// +// +// 提示: +// +// +// 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 +// 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。 +// +// +// +// +// 进阶: +//如果多次调用这个函数,你将如何优化你的算法? +// Related Topics 位运算 + + + +//leetcode submit region begin(Prohibit modification and deletion) +public class Solution { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + if(n == 0) return 0; + int count = 0; + while (n != 0){ + n = n & (n - 1); + count++; + } + return count; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week 07/id_053/[231]2\347\232\204\345\271\202.java" "b/Week 07/id_053/[231]2\347\232\204\345\271\202.java" new file mode 100644 index 000000000..7d45411c3 --- /dev/null +++ "b/Week 07/id_053/[231]2\347\232\204\345\271\202.java" @@ -0,0 +1,35 @@ +//给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 +// +// 示例 1: +// +// 输入: 1 +//输出: true +//解释: 20 = 1 +// +// 示例 2: +// +// 输入: 16 +//输出: true +//解释: 24 = 16 +// +// 示例 3: +// +// 输入: 218 +//输出: false +// Related Topics 位运算 数学 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public boolean isPowerOfTwo(int n) { + //2^0 = 0001; + //2^1 = 0010; + //2^2 = 0100; + if (n == 0) return false; +// return n == 1 || (n &(n - 1)) == 0; + //n不能为负数,所以判断n必须大于0; + return n > 0 & (n & (n - 1)) == 0; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week 07/id_053/[58]\346\234\200\345\220\216\344\270\200\344\270\252\345\215\225\350\257\215\347\232\204\351\225\277\345\272\246.java" "b/Week 07/id_053/[58]\346\234\200\345\220\216\344\270\200\344\270\252\345\215\225\350\257\215\347\232\204\351\225\277\345\272\246.java" new file mode 100644 index 000000000..b00e10233 --- /dev/null +++ "b/Week 07/id_053/[58]\346\234\200\345\220\216\344\270\200\344\270\252\345\215\225\350\257\215\347\232\204\351\225\277\345\272\246.java" @@ -0,0 +1,32 @@ +//给定一个仅包含大小写字母和空格 ' ' 的字符串,返回其最后一个单词的长度。 +// +// 如果不存在最后一个单词,请返回 0 。 +// +// 说明:一个单词是指由字母组成,但不包含任何空格的字符串。 +// +// 示例: +// +// 输入: "Hello World" +//输出: 5 +// +// Related Topics 字符串 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int lengthOfLastWord(String s) { + if (s == null || " ".equals(s)) return 0; + String[] split = s.split(" "); + return split.length == 0? 0:split[split.length - 1].length(); + //调用系统函数很慢,以下是别人写的代码,后面再看 + /*int end = s.length() - 1; + while(end >= 0 && s.charAt(end) == ' ') end--; + if(end < 0) return 0; + int start = end; + while(start >= 0 && s.charAt(start) != ' ') start--; + return end - start;*/ + + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week 07/id_053/[709]\350\275\254\346\215\242\346\210\220\345\260\217\345\206\231\345\255\227\346\257\215.java" "b/Week 07/id_053/[709]\350\275\254\346\215\242\346\210\220\345\260\217\345\206\231\345\255\227\346\257\215.java" new file mode 100644 index 000000000..69cba2ce1 --- /dev/null +++ "b/Week 07/id_053/[709]\350\275\254\346\215\242\346\210\220\345\260\217\345\206\231\345\255\227\346\257\215.java" @@ -0,0 +1,40 @@ +//实现函数 ToLowerCase(),该函数接收一个字符串参数 str,并将该字符串中的大写字母转换成小写字母,之后返回新的字符串。 +// +// +// +// 示例 1: +// +// +//输入: "Hello" +//输出: "hello" +// +// 示例 2: +// +// +//输入: "here" +//输出: "here" +// +// 示例 3: +// +// +//输入: "LOVELY" +//输出: "lovely" +// +// Related Topics 字符串 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public String toLowerCase(String str) { + if (str == null) return null; + char[] chs = str.toCharArray(); + for (int i = 0;i < chs.length; i++){ + if (chs[i] >= 'A' && chs[i] <= 'Z') { + chs[i] = (char)(chs[i] + 32); + } + } + return new String(chs); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week 07/id_053/[746]\344\275\277\347\224\250\346\234\200\345\260\217\350\212\261\350\264\271\347\210\254\346\245\274\346\242\257.java" "b/Week 07/id_053/[746]\344\275\277\347\224\250\346\234\200\345\260\217\350\212\261\350\264\271\347\210\254\346\245\274\346\242\257.java" new file mode 100644 index 000000000..b878c1afc --- /dev/null +++ "b/Week 07/id_053/[746]\344\275\277\347\224\250\346\234\200\345\260\217\350\212\261\350\264\271\347\210\254\346\245\274\346\242\257.java" @@ -0,0 +1,48 @@ +//数组的每个索引做为一个阶梯,第 i个阶梯对应着一个非负数的体力花费值 cost[i](索引从0开始)。 +// +// 每当你爬上一个阶梯你都要花费对应的体力花费值,然后你可以选择继续爬一个阶梯或者爬两个阶梯。 +// +// 您需要找到达到楼层顶部的最低花费。在开始时,你可以选择从索引为 0 或 1 的元素作为初始阶梯。 +// +// 示例 1: +// +// +//输入: cost = [10, 15, 20] +//输出: 15 +//解释: 最低花费是从cost[1]开始,然后走两步即可到阶梯顶,一共花费15。 +// +// +// 示例 2: +// +// +//输入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] +//输出: 6 +//解释: 最低花费方式是从cost[0]开始,逐个经过那些1,跳过cost[3],一共花费6。 +// +// +// 注意: +// +// +// cost 的长度将会在 [2, 1000]。 +// 每一个 cost[i] 将会是一个Integer类型,范围为 [0, 999]。 +// +// Related Topics 数组 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int minCostClimbingStairs(int[] cost) { + //dp[i] 表示走到第i个阶梯的最小花费 + //dp[i] = min(dp[i-1],dp[i-2]) + cost[i] + if(cost.length == 2) return Math.min(cost[0],cost[1]); + int[] dp = new int[cost.length]; + dp[0] = cost[0]; + dp[1] = cost[1]; + for(int i = 2;i < cost.length;i++) { + dp[i] = Math.min(dp[i-2],dp[i-1]) +cost[i]; + } + return Math.min(dp[cost.length-1],dp[cost.length-2]); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 07/id_063/Leetcode_1122_063.java b/Week 07/id_063/Leetcode_1122_063.java new file mode 100644 index 000000000..6b550ff3c --- /dev/null +++ b/Week 07/id_063/Leetcode_1122_063.java @@ -0,0 +1,43 @@ +/* + +计数排序实现 + + */ + + +import java.util.Arrays; + +class Solution { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + int cnt[] = new int[1001]; + int[] ans = new int[arr1.length]; + + for (int val : arr2) cnt[val]--; + + int j = arr1.length - 1; + int sortCnt = 0; + for (int i = arr1.length - 1; i >= 0; i--) { + cnt[arr1[i]] = (cnt[arr1[i]] == 0) ? 0 : (cnt[arr1[i]] == -1) ? 1 : cnt[arr1[i]] + 1; + if (cnt[arr1[i]] == 0) { + ans[j--] = arr1[i]; + sortCnt++; + } + } + + j = 0; + for (int value : arr2) { + if (cnt[value] > 0) { + while (cnt[value] > 0) { + ans[j++] = value; + cnt[value]--; + } + } + } + + if (sortCnt > 0) { + Arrays.sort(ans, ans.length - sortCnt, ans.length); + } + + return ans; + } +} \ No newline at end of file diff --git a/Week 07/id_063/Leetcode_1244_063.java b/Week 07/id_063/Leetcode_1244_063.java new file mode 100644 index 000000000..243024da2 --- /dev/null +++ b/Week 07/id_063/Leetcode_1244_063.java @@ -0,0 +1,55 @@ +/* + +简单数据结构时间 +两个Map实现 + */ + + +import java.util.*; + +class Leaderboard { + private Map id2score; + private Map score2cnt; + + public Leaderboard() { + id2score = new HashMap<>(); + score2cnt = new TreeMap<>((a, b) -> (b - a)); + } + + public void addScore(int playerId, int score) { + if (id2score.containsKey(playerId)) { + int oldScore = id2score.get(playerId); + + score2cnt.put(oldScore, score2cnt.get(oldScore) - 1); + score2cnt.putIfAbsent(oldScore + score, 0); + score2cnt.put(oldScore + score, score2cnt.get(oldScore + score) + 1); + + id2score.put(playerId, oldScore + score); + } else { + score2cnt.putIfAbsent(score, 0); + score2cnt.put(score, score2cnt.get(score) + 1); + + id2score.put(playerId, score); + } + } + + public int top(int K) { + int sum = 0; + int cnt = K; + for (Map.Entry entry : score2cnt.entrySet()) { + if (cnt >= entry.getValue()) { + sum += entry.getKey() * entry.getValue(); + cnt -= entry.getValue(); + } else { + sum += entry.getKey() * cnt; + break; + } + } + + return sum; + } + + public void reset(int playerId) { + addScore(playerId, -id2score.get(playerId)); + } +} \ No newline at end of file diff --git a/Week 07/id_063/Leetcode_146_063.java b/Week 07/id_063/Leetcode_146_063.java new file mode 100644 index 000000000..5d910b28b --- /dev/null +++ b/Week 07/id_063/Leetcode_146_063.java @@ -0,0 +1,109 @@ +import java.util.HashMap; +import java.util.Map; + +class LRUCache { + + private class Node { + int key; + int val; + Node prev; + Node next; + + public Node(int key, int val) { + this.key = key; + this.val = val; + } + } + + Node head; + Node tail; + int cap; + Map key2node; + + public LRUCache(int capacity) { + head = new Node(0 ,0); + tail = new Node(0, 0); + + head.prev = tail; + head.next = null; + tail.next = head; + tail.prev = null; + + key2node = new HashMap<>(); + + cap = capacity; + } + + private void moveToTail(Node node) { + node.prev.next = node.next; + node.next.prev = node.prev; + + tail.next.prev = node; + node.next = tail.next; + node.prev = tail; + tail.next = node; + } + + private Node removeFromHead() { + Node ret = head.prev; + + head.prev.prev.next = head; + head.prev = head.prev.prev; + + return ret; + } + + private void addToTail(int key, int val) { + Node node = new Node(key, val); + + node.next = tail.next; + node.prev = tail; + tail.next.prev = node; + tail.next = node; + } + + private Node getTail() { + return tail.next; + } + + public int get(int key) { + if (key2node.containsKey(key)) { + Node node = key2node.get(key); + moveToTail(node); + return node.val; + } + + return -1; + } + + public void put(int key, int value) { + if (key2node.containsKey(key)) { + Node node = key2node.get(key); + node.val = value; + moveToTail(node); + } else { + if (key2node.size() == cap) { + key2node.remove(removeFromHead().key); + } + + addToTail(key, value); + key2node.put(key, getTail()); + } + } +} + +public class Solution { + public static void main(String[] args) { + LRUCache cache = new LRUCache( 2 /* 缓存容量 */ ); + + cache.put(1, 1); + cache.put(2, 2); + cache.get(1); // 返回 1 + cache.put(3, 3); // 该操作会使得密钥 2 作废 + cache.get(2); // 返回 -1 (未找到) + cache.put(4, 4); // 该操作会使得密钥 1 作废 + cache.get(1); // 返回 -1 (未找到) + cache.get(3); // 返回 3 + cache.get(4); // 返回 4 + } +} diff --git a/Week 07/id_063/Leetcode_190_063.java b/Week 07/id_063/Leetcode_190_063.java new file mode 100644 index 000000000..b5a0ba227 --- /dev/null +++ b/Week 07/id_063/Leetcode_190_063.java @@ -0,0 +1,23 @@ +/* +简单二进制位操作 + */ + +public class Solution { + // you need treat n as an unsigned value + public int reverseBits(int n) { + int num = n; + int result = 0; + for (int i = 31; i >= 0; i--) { + if ((num & 1) == 1) { + result |= (1 << i); + } + + num >>= 1; + if (num == 0) { + break; + } + } + + return result; + } +} \ No newline at end of file diff --git a/Week 07/id_063/Leetcode_191_063.java b/Week 07/id_063/Leetcode_191_063.java new file mode 100644 index 000000000..50ab7324a --- /dev/null +++ b/Week 07/id_063/Leetcode_191_063.java @@ -0,0 +1,19 @@ +/* +思路 +利用 x & (x-1) 可以把数字最右边的1去掉进行计算 +一直迭代到x 变为0 即可 + +*/ + +public class Solution { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int cnt = 0; + while (n != 0) { + cnt++; + n &= (n-1); + } + + return cnt; + } +} \ No newline at end of file diff --git a/Week 07/id_063/Leetcode_231_063.java b/Week 07/id_063/Leetcode_231_063.java new file mode 100644 index 000000000..c5fc71014 --- /dev/null +++ b/Week 07/id_063/Leetcode_231_063.java @@ -0,0 +1,9 @@ +/* +去掉最右边的0后如果结果是0表示该数字二进制表示中只有一个1,必然就是2次幂 + */ + +class Solution { + public boolean isPowerOfTwo(int n) { + return (n > 0) && ((n & (n-1)) == 0); + } +} diff --git a/Week 07/id_063/Leetcode_242_063.java b/Week 07/id_063/Leetcode_242_063.java new file mode 100644 index 000000000..12e496c64 --- /dev/null +++ b/Week 07/id_063/Leetcode_242_063.java @@ -0,0 +1,35 @@ +/* +思路: +用Hash保存每一个字符出现的频次,遍历s中所有字符统计字符频次,然后遍历t中所有字符 +将hash中对应字符的频次进行递减,t中出现任何不在hash中的字符或者有任何一个字符频次 +减少到0以下,都说明t和s不匹配,可以立即判断失败,不必在最后去统计是不是所有字符的频次 +都减到0 + +自己用数组实现Hash比起用库里面的hash表要快一些 + */ + +class Solution { + public boolean isAnagram(String s, String t) { + int[] cnt = new int[256]; + + if (s.length() != t.length()) { + return false; + } + + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + cnt[(int)ch]++; + } + + for (int i = 0; i < t.length(); i++) { + char ch = t.charAt(i); + cnt[(int)ch]--; + + if (cnt[(int)ch] < 0) { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/Week 07/id_063/Leetcode_338_063.java b/Week 07/id_063/Leetcode_338_063.java new file mode 100644 index 000000000..80fd21f8b --- /dev/null +++ b/Week 07/id_063/Leetcode_338_063.java @@ -0,0 +1,28 @@ +/* + + +dp 递推实现, 可以吧时间复杂度缩减到O(N) + +假设递推从左往右进行,每一个数如果把它最左边的1去掉,那么剩下的数对应的1的个数已经计算出来了 +且当前这个数的1个数就是剩下那个数1个数加1 + + */ + + +class Solution { + public int[] countBits(int num) { + int[] dp = new int[num + 1]; + dp[0] = 0; + + int cnt = 1, i = 1; + while (i <= num) { + for (int j = 0; j < cnt && i <= num; j++) { + dp[i] = dp[i - cnt] + 1; i++; + } + + cnt = cnt << 1; + } + + return dp; + } +} diff --git a/Week 07/id_063/Leetcode_493_063.java b/Week 07/id_063/Leetcode_493_063.java new file mode 100644 index 000000000..094075cab --- /dev/null +++ b/Week 07/id_063/Leetcode_493_063.java @@ -0,0 +1,44 @@ +/* +跟逆序对的思路一样,用归并排序的思想解决 + */ + +import java.util.Arrays; + +class Solution { + private int mergeSort(int[] nums, int start, int end) { + if (start == end) { + return 0; + } + + int mid = start + (end - start) / 2; + int lcnt = mergeSort(nums, start, mid); + int rcnt = mergeSort(nums, mid+1, end); + + int sum = lcnt + rcnt; + + // 累加本次归并过程中的反转对数量, dp解决, i, j都只可能往右边移动 + int i, j = mid + 1, cnt = 0; + for (i = start; i <= mid; i++) { + while (j <= end && nums[j] < ((nums[i]>>1) + (nums[i]&1)) ) { + cnt++; j++; + } + + sum += cnt; + } + + // 偷懒直接用库函数排序,代码简洁,浪费一点计算量 + Arrays.sort(nums, start, end+1); + return sum; + } + + public int reversePairs(int[] nums) { + if (nums.length == 0) { + return 0; + } + return mergeSort(nums, 0, nums.length - 1); + } + + public static void main(String[] args) { + System.out.println( new Solution().reversePairs(new int[] {2,4,3,5,1}) ); + } +} \ No newline at end of file diff --git a/Week 07/id_063/Leetcode_51_063.java b/Week 07/id_063/Leetcode_51_063.java new file mode 100644 index 000000000..16c73e1d0 --- /dev/null +++ b/Week 07/id_063/Leetcode_51_063.java @@ -0,0 +1,72 @@ +/* + +采用位运算的方式维护当前棋盘上面的占位信息 +colMask表示当前行之前所有行上填的皇后已经占的列数,每一位代表一列 +ldMask表示当前行上被前面所有皇后左对角线占用的列 +rdMask表示当前行上被前面所有皇后右对角线占用的列 + +可以巧妙用或运算快速算出当前行剩余可以填的列有哪些 + +到最后如果所有列能填满, 说明找到一种方案 + + */ + + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +class Solution { + + private String getStr(int row, int n) { + int mask = (1 << (n-1)); + StringBuilder builder = new StringBuilder(); + + while (mask != 0) { + builder.append(((row & mask) != 0) ? "Q" : "."); + mask >>= 1; + } + + return builder.toString(); + } + + void dfs(int col, int ldMask, int rdMask, int n, List> ans, List path) { + //System.out.println(String.format("%x", col)); + if (col == (1< l = new ArrayList<>(n); + for (Integer row : path) { + l.add(getStr(row, n)); + } + ans.add(l); + return; + } + + int validPos = (~((col | ldMask | rdMask))) & ((1<> 1, n, ans, path); + path.remove(path.size() - 1); + } + } + + public List> solveNQueens(int n) { + List> ans = new LinkedList<>(); + + dfs(0, 0, 0, n, ans, new ArrayList<>(n)); + return ans; + } + + public static void main(String[] args) { + System.out.println( new Solution().solveNQueens(8) ); + } +} diff --git a/Week 07/id_063/Leetcode_52_063.java b/Week 07/id_063/Leetcode_52_063.java new file mode 100644 index 000000000..92bce1733 --- /dev/null +++ b/Week 07/id_063/Leetcode_52_063.java @@ -0,0 +1,47 @@ +/* + +采用位运算的方式维护当前棋盘上面的占位信息 +colMask表示当前行之前所有行上填的皇后已经占的列数,每一位代表一列 +ldMask表示当前行上被前面所有皇后左对角线占用的列 +rdMask表示当前行上被前面所有皇后右对角线占用的列 + +可以巧妙用或运算快速算出当前行剩余可以填的列有哪些 + +到最后如果所有列能填满, 说明找到一种方案 + + */ + +class Solution { + + void dfs(int col, int ldMask, int rdMask, int n, int[] totalCnt) { + //System.out.println(String.format("%x", col)); + if (col == (1<> 1, n, totalCnt); + } + } + + public int totalNQueens(int n) { + int[] ans = new int[] {0}; + dfs(0, 0, 0, n, ans); + return ans[0]; + } + + public static void main(String[] args) { + System.out.println( new Solution().totalNQueens(8) ); + } +} \ No newline at end of file diff --git a/Week 07/id_063/Leetcode_56_063.java b/Week 07/id_063/Leetcode_56_063.java new file mode 100644 index 000000000..419a8272c --- /dev/null +++ b/Week 07/id_063/Leetcode_56_063.java @@ -0,0 +1,38 @@ +/* + +思路 +排序所有区间,然后从前到后合并 + + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +class Solution { + public int[][] merge(int[][] intervals) { + Arrays.sort(intervals, (a, b) -> (a[0] - b[0])); + List ans = new ArrayList<>(); + + for (int[] val : intervals) { + if (ans.isEmpty()) { + ans.add(val); + continue; + } + + int[] lastVal = ans.get(ans.size()-1); + if (val[0] <= lastVal[1]) { + lastVal[1] = Math.max(lastVal[1], val[1]); + } else { + ans.add(val); + } + } + + int[][] ret = new int[ans.size()][2]; + for (int i = 0; i < ans.size(); i++) { + ret[i] = ans.get(i); + } + + return ret; + } +} \ No newline at end of file diff --git a/Week 07/id_078/LeetCode_1122_078.java b/Week 07/id_078/LeetCode_1122_078.java new file mode 100644 index 000000000..326612fe2 --- /dev/null +++ b/Week 07/id_078/LeetCode_1122_078.java @@ -0,0 +1,56 @@ +//给你两个数组,arr1 和 arr2, +// +// +// arr2 中的元素各不相同 +// arr2 中的每个元素都出现在 arr1 中 +// +// +// 对 arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。未在 arr2 中出现过的元素需要按照升序放在 arr1 的末尾。 +// +// +// +// 示例: +// +// 输入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6] +//输出:[2,2,2,1,4,3,3,9,6,7,19] +// +// +// +// +// 提示: +// +// +// arr1.length, arr2.length <= 1000 +// 0 <= arr1[i], arr2[i] <= 1000 +// arr2 中的元素 arr2[i] 各不相同 +// arr2 中的每个元素 arr2[i] 都出现在 arr1 中 +// +// Related Topics 排序 数组 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + int[] m = new int[1001]; + int[] ref = new int[arr1.length]; + for(int i = 0; i < arr1.length; i++) { + m[arr1[i]]++; + } + int cnt = 0; + for(int i = 0; i < arr2.length; i++) { + while(m[arr2[i]] > 0) { + ref[cnt++] = arr2[i]; + m[arr2[i]]--; + } + } + for(int i = 0; i < 1001; i++) { + while(m[i] > 0) { + ref[cnt++] = i; + m[i]--; + } + } + return ref; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 07/id_078/LeetCode_338_078.java b/Week 07/id_078/LeetCode_338_078.java new file mode 100644 index 000000000..ef652d663 --- /dev/null +++ b/Week 07/id_078/LeetCode_338_078.java @@ -0,0 +1,40 @@ +//给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。 +// +// 示例 1: +// +// 输入: 2 +//输出: [0,1,1] +// +// 示例 2: +// +// 输入: 5 +//输出: [0,1,1,2,1,2] +// +// 进阶: +// +// +// 给出时间复杂度为O(n*sizeof(integer))的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗? +// 要求算法的空间复杂度为O(n)。 +// 你能进一步完善解法吗?要求在C++或任何其他语言中不使用任何内置函数(如 C++ 中的 __builtin_popcount)来执行此操作。 +// +// Related Topics 位运算 动态规划 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int[] countBits(int num) { + int[] ans = new int[num + 1]; + for (int i = 0; i <= num; ++i) + ans[i] = popcount(i); + return ans; + } + + private int popcount(int x) { + int count; + for (count = 0; x != 0; ++count) + x &= x - 1; //zeroing out the least significant nonzero bit + return count; + } + +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 07/id_083/LeetCode_1122_083.java b/Week 07/id_083/LeetCode_1122_083.java new file mode 100644 index 000000000..5c4596acf --- /dev/null +++ b/Week 07/id_083/LeetCode_1122_083.java @@ -0,0 +1,31 @@ +/* + * @lc app=leetcode id=1122 lang=java + * + * [1122] Relative Sort Array + */ + +// @lc code=start +class Solution { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + //O(1) + int []cnt = new int[1001]; + //计算每个桶有多少个数 + for(int n:arr1) cnt[n]++; + int i=0; + //按arr2顺序依次取出数值 + for(int n:arr2){ + while(cnt[n]-- > 0){ + arr1[i++] = n; + } + } + //没在arr2中的,按升序输出 + for(int n=0;n 0){ + arr1[i++] = n; + } + } + return arr1; + } +} +// @lc code=end + diff --git a/Week 07/id_083/LeetCode_146_083.java b/Week 07/id_083/LeetCode_146_083.java new file mode 100644 index 000000000..63e662bac --- /dev/null +++ b/Week 07/id_083/LeetCode_146_083.java @@ -0,0 +1,39 @@ +import java.util.LinkedHashMap; +import java.util.Map; + +/* + * @lc app=leetcode id=146 lang=java + * + * [146] LRU Cache + */ + +// @lc code=start +class LRUCache extends LinkedHashMap{ + private int capacity; + + public LRUCache(int capacity) { + super(capacity, 0.75F, true); + this.capacity = capacity; + } + + public int get(int key) { + return super.getOrDefault(key, -1); + } + + public void put(int key, int value) { + super.put(key, value); + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > capacity; + } +} +/** + * Your LRUCache object will be instantiated and called as such: + * LRUCache obj = new LRUCache(capacity); + * int param_1 = obj.get(key); + * obj.put(key,value); + */ +// @lc code=end + diff --git a/Week 07/id_083/LeetCode_191_083.java b/Week 07/id_083/LeetCode_191_083.java new file mode 100644 index 000000000..b38fe2bdc --- /dev/null +++ b/Week 07/id_083/LeetCode_191_083.java @@ -0,0 +1,34 @@ +/* + * @lc app=leetcode id=191 lang=java + * + * [191] Number of 1 Bits + */ + +// @lc code=start +public class Solution { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + + // //solution 1,O(1) + // int bits = 0 , mask = 1; + // for(int i=0;i<32;i++){ + // if((n & mask)!= 0){ + // bits ++; + // } + // mask <<= 1; + // } + // return bits; + + //solution 2,O(1) + int sum = 0; + while(n != 0 ){ + sum ++; + n &= (n-1); + //将n和n−1做与运算会将最低位的1变成0,并保持其他位不变 + } + return sum; + + } +} +// @lc code=end + diff --git a/Week 07/id_083/LeetCode_242_083.java b/Week 07/id_083/LeetCode_242_083.java new file mode 100644 index 000000000..c2e8b6c1d --- /dev/null +++ b/Week 07/id_083/LeetCode_242_083.java @@ -0,0 +1,25 @@ +/* + * @lc app=leetcode id=242 lang=java + * + * [242] Valid Anagram + */ + +// @lc code=start +class Solution { + public boolean isAnagram(String s, String t) { + if(s.length()!= t.length()) + return false; + char []s1 = s.toCharArray(); + char []t1 = t.toCharArray(); + Arrays.sort(s1); + Arrays.sort(t1); + // for(int i=0;i>1] + (i&1); + //x/2 == x>>1 + //x%2 == x&1; + } + return ans; + } +} +// @lc code=end + diff --git a/Week 07/id_083/LeetCode_493_083.java b/Week 07/id_083/LeetCode_493_083.java new file mode 100644 index 000000000..d7c0c1c48 --- /dev/null +++ b/Week 07/id_083/LeetCode_493_083.java @@ -0,0 +1,38 @@ +import java.util.Arrays; + +/* + * @lc app=leetcode id=493 lang=java + * + * [493] Reverse Pairs + */ + +// @lc code=start +class Solution { + + //O(nlogn*logn) + public int reversePairs(int[] nums) { + return mergeSort(nums,0,nums.length-1); + } + + private int mergeSort(int[] nums, int start, int end) { + if(start>=end) return 0; + int mid = start + (end-start)/2; + + //左右进行归并排序 + int cnt = mergeSort(nums, start, mid)+mergeSort(nums, mid+1, end); + + for(int i = start,j=mid+1;i<=mid;i++){ + //统计重要反转对的个数 + while(j<=end && nums[i]/2.0 > nums[j]) + j++; + cnt += j - (mid+1); + } + + //最后归并 + Arrays.sort(nums,start,end+1); + return cnt; + } + +} +// @lc code=end + diff --git a/Week 07/id_083/LeetCode_56_083.java b/Week 07/id_083/LeetCode_56_083.java new file mode 100644 index 000000000..007963b4d --- /dev/null +++ b/Week 07/id_083/LeetCode_56_083.java @@ -0,0 +1,41 @@ +import java.util.ArrayList; +import java.util.List; + +/* + * @lc app=leetcode id=56 lang=java + * + * [56] Merge Intervals + */ + +// @lc code=start + +// 思路: +// 先按首位置进行排序; +// 接下来,如何判断两个区间是否重叠呢?比如 a = [1,4],b = [2,3] +// 当 a[1] >= b[0] 说明两个区间有重叠. +// 但是如何把这个区间找出来呢? +// 左边位置一定是确定,就是 a[0],而右边位置是 max(a[1], b[1]) +// 所以,我们就能找出整个区间为:[1,4] + +class Solution { + public int[][] merge(int[][] intervals) { + List res = new ArrayList<>(); + if(intervals.length==0 || intervals==null) + return res.toArray(new int[0][]); + Arrays.sort(intervals,(a,b)-> a[0]-b[0]); + int i=0; + while(i 0 && (n&(n-1)) == 0 ; + } +} \ No newline at end of file diff --git a/Week 07/id_098/LeetCode_493_098.java b/Week 07/id_098/LeetCode_493_098.java new file mode 100644 index 000000000..def8ab26d --- /dev/null +++ b/Week 07/id_098/LeetCode_493_098.java @@ -0,0 +1,52 @@ +class Solution { + private int cnt; + public int reversePairs(int[] nums) { + int len = nums.length; + sort(nums, Arrays.copyOf(nums, len), 0, len - 1); + return cnt; + } + + private void sort(int[] src, int[] dest, int s, int e) { + if (s < e) { + int mid = (s + e) >> 1; + sort(dest, src, s, mid); + sort(dest, src, mid + 1, e); + + merge(src, dest, s, mid, e); + } + } + + private void merge(int[] src, int[] dest, int s, int mid, int e) { + int i = s, j = mid + 1, k = s; + + //独立出来做了一遍count重要逆序对 + while (i <= mid && j <= e) { + if ((long) src[i] > 2 * ((long) src[j])) { + cnt += mid - i + 1; + j++; + } else { + i++; + } + } + + i = s; + j = mid + 1; + + while (i <= mid && j <= e) { + if (src[i] <= src[j]) { + dest[k++] = src[i++]; + } else { + + dest[k++] = src[j++]; + } + } + + while (i <= mid) { + dest[k++] = src[i++]; + } + + while (j <= e) { + dest[k++] = src[j++]; + } + } +} \ No newline at end of file diff --git a/Week 07/id_098/LeetCode_52_098.java b/Week 07/id_098/LeetCode_52_098.java new file mode 100644 index 000000000..c076b6f2b --- /dev/null +++ b/Week 07/id_098/LeetCode_52_098.java @@ -0,0 +1,25 @@ +class Solution { + + private int size; + private int count; + + private void solve(int row, int ld, int rd) { + if (row == size) { + count++; + return; + } + int pos = size & (~(row | ld | rd)); + while (pos != 0) { + int p = pos & (-pos); + pos -= p; // pos &= pos - 1; + solve(row | p, (ld | p) << 1, (rd | p) >> 1); + } + } + + public int totalNQueens(int n) { + count = 0; + size = (1 << n) - 1; + solve(0, 0, 0); + return count; + } +} \ No newline at end of file diff --git a/Week 07/id_103/LeeCode_242_103.java b/Week 07/id_103/LeeCode_242_103.java new file mode 100644 index 000000000..7ff2cffbf --- /dev/null +++ b/Week 07/id_103/LeeCode_242_103.java @@ -0,0 +1,28 @@ +/* + * @lc app=leetcode.cn id=242 lang=java + * + * [242] 有效的字母异位词 + */ + +// @lc code=start +class Solution { + public boolean isAnagram(String s, String t) { + if(s.length()!=t.length()) + { + return false; + } + int[] counter=new int[26]; + for(int i=0;i() { + @Override + public int compare(int[] arg0, int[] arg1) { + return arg0[0]-arg1[0]; + } + }); + List list=new ArrayList<>(); + int i=0; + while(i=intervals[i+1][0]){ + // 如果能吞并,则i++,直到波段i不能吞并波段i+1 + if(max list = new LinkedList(); + private Map map = new HashMap(); + + public LeeCode_146_108(int capacity) { + this.capacity = capacity; + } + + public int get(int key) { + if (map.containsKey(key)) { + list.remove(new Integer(key)); + list.offerFirst(key); + return map.get(key); + } else { + return -1; + } + } + + public void put(int key, int value) { + if (map.containsKey(key)) { + list.remove(new Integer(key)); + } else { + if (capacity <= map.size()) { + Integer lastKey = list.pollLast(); + map.remove(lastKey); + } + } + list.offerFirst(key); + map.put(key, value); + } +} diff --git a/Week 07/id_108/LeeCode_231_108.java b/Week 07/id_108/LeeCode_231_108.java new file mode 100644 index 000000000..b79675df7 --- /dev/null +++ b/Week 07/id_108/LeeCode_231_108.java @@ -0,0 +1,20 @@ +package study; + +/** + * + * 思路:公式法 + * 如果一个数是2的次方数的话,那么它的二进数必然是最高位为1,其它都为0, + * 那么如果此时我们减1的话,则最高位会降一位,其余为0的位现在都为变为1,那么我们把两数相与,就会得到0 + * 用这个性质也能来解题(官方题解明确说明) + */ +public class LeeCode_231_108 { + + public static boolean isPowerOfTwo(int n) { + return n > 0 && (n & (n - 1)) == 0; + } + public static void main(String[] args) { + System.out.println(isPowerOfTwo(218)); + + } + +} diff --git a/Week 07/id_113/LeetCode190.py b/Week 07/id_113/LeetCode190.py new file mode 100644 index 000000000..17ad5f011 --- /dev/null +++ b/Week 07/id_113/LeetCode190.py @@ -0,0 +1,10 @@ +class Solution: + # @param n, an integer + # @return an integer + def reverseBits(self, n): + ans, MASK = 0, 1 + for i in range(32): + if n&MASK: + ans |= 1 << (31 - i) + MASK <<= 1 + return ans \ No newline at end of file diff --git a/Week 07/id_113/LeetCode191.py b/Week 07/id_113/LeetCode191.py new file mode 100644 index 000000000..304f7b388 --- /dev/null +++ b/Week 07/id_113/LeetCode191.py @@ -0,0 +1,20 @@ +class Solution(object): + def hammingWeight1(self, n): + """ + :type n: int + :rtype: int + """ + count = 0 + while n: + count += n&1 + n >>= 1 + return count + + def hammingWeight2(self, n): + count = 0 + while n: + res = n % 2 + if res == 1: + count += 1 + n //= 2 + return count \ No newline at end of file diff --git a/Week 07/id_113/LeetCode231.py b/Week 07/id_113/LeetCode231.py new file mode 100644 index 000000000..e9c93bd1f --- /dev/null +++ b/Week 07/id_113/LeetCode231.py @@ -0,0 +1,3 @@ +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + return n > 0 and n & (n - 1) == 0 \ No newline at end of file diff --git a/Week 07/id_113/LeetCode338.py b/Week 07/id_113/LeetCode338.py new file mode 100644 index 000000000..dc0d2d178 --- /dev/null +++ b/Week 07/id_113/LeetCode338.py @@ -0,0 +1,15 @@ +class Solution(object): + def countBits(self, num): + """ + :type num: int + :rtype: List[int] + """ + ans = [] + for n in range(num + 1): + count = 0 + while n: + count += n & 1 + n >>= 1 + ans.append(count) + + return ans \ No newline at end of file diff --git a/Week 07/id_113/LeetCoe146.py b/Week 07/id_113/LeetCoe146.py new file mode 100644 index 000000000..d53b2d560 --- /dev/null +++ b/Week 07/id_113/LeetCoe146.py @@ -0,0 +1,26 @@ +class LRUCache(object): + def __init__(self, capacity): + self.dic = collections.OrderedDict() + self.remain = capacity + + def get(self, key): + if key not in self.dic: + return -1 + v = self.dic.pop(key) + self.dic[key] = v # key as the newest one + return v + + def put(self, key, value): + if key in self.dic: + self.dic.pop(key) + else: + if self.remain > 0: + self.remain -= 1 + else: # self.dic is full + self.dic.popitem(last=False) + self.dic[key] = value + +# Your LRUCache object will be instantiated and called as such: +# obj = LRUCache(capacity) +# param_1 = obj.get(key) +# obj.put(key,value) \ No newline at end of file diff --git a/Week 07/id_118/LeetCode_146_118.py b/Week 07/id_118/LeetCode_146_118.py new file mode 100644 index 000000000..d37cc2c5d --- /dev/null +++ b/Week 07/id_118/LeetCode_146_118.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Created by Isaac Zhou at 11/30/19 +from collections import OrderedDict + + +class LRUCache: + def __init__(self, capacity: int): + self.dic = OrderedDict() + self.remain = capacity + + def get(self, key): + if key not in self.dic: + return -1 + v = self.dic.pop(key) + self.dic[key] = v + return v + + def put(self, key, value): + if key in self.dic: + self.dic.pop(key) + else: + if self.remain > 0: + self.remain -= 1 + else: + self.dic.popitem(last=False) + self.dic.update({key: value}) diff --git a/Week 07/id_118/LeetCode_190_118.py b/Week 07/id_118/LeetCode_190_118.py new file mode 100644 index 000000000..b534fb626 --- /dev/null +++ b/Week 07/id_118/LeetCode_190_118.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Created by Isaac Zhou at 11/30/19 + +class Solution: + def reverseBits(self, n: int) -> int: + return int('{0:032b}'.format(n)[::-1], 2) diff --git a/Week 07/id_118/LeetCode_191_118.py b/Week 07/id_118/LeetCode_191_118.py new file mode 100644 index 000000000..e1ce8b6e1 --- /dev/null +++ b/Week 07/id_118/LeetCode_191_118.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Created by Isaac Zhou at 11/30/19 +class Solution: + def hammingWeight(self, n:int) -> int: + return str(bin(n)).count("1") \ No newline at end of file diff --git a/Week 07/id_118/LeetCode_231_118.py b/Week 07/id_118/LeetCode_231_118.py new file mode 100644 index 000000000..9446e080c --- /dev/null +++ b/Week 07/id_118/LeetCode_231_118.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Created by Isaac Zhou at 11/30/19 + +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + return n > 0 and bin(n).count("1") == 1 diff --git a/Week 07/id_118/LeetCode_338_118.py b/Week 07/id_118/LeetCode_338_118.py new file mode 100644 index 000000000..710a7d56f --- /dev/null +++ b/Week 07/id_118/LeetCode_338_118.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# Created by Isaac Zhou at 11/30/19 +from typing import List + + +class Solution: + def countBits(self, num:int) -> List[int]: + initArr = [0] + if num > 0: + amountToAdd = 1 + while len(initArr) < num + 1: + initArr.extend([x+1 for x in initArr]) + + return initArr[0:num+1] \ No newline at end of file diff --git a/Week 07/id_123/LeetCode_191_123.c b/Week 07/id_123/LeetCode_191_123.c new file mode 100644 index 000000000..68d2a4346 --- /dev/null +++ b/Week 07/id_123/LeetCode_191_123.c @@ -0,0 +1,20 @@ +// int hammingWeight(uint32_t n) { +// int count = 0; +// for(int i = 0; i< 32; i++ ){ +// if((n&1)==1) { +// count++; +// } +// n=n>>1; +// } +// return count; + +// } +int hammingWeight(uint32_t n) { + int count = 0; + while(n>0){ + n=n&n-1; + count++; + } + return count; + +} \ No newline at end of file diff --git a/Week 07/id_123/LeetCode_231_123.c b/Week 07/id_123/LeetCode_231_123.c new file mode 100644 index 000000000..f596d60db --- /dev/null +++ b/Week 07/id_123/LeetCode_231_123.c @@ -0,0 +1,30 @@ +// //超时 +// bool isPowerOfTwo(int n){ +// if(n==1){ +// return true; +// } +// if(n&1==1&&(n!=1)) +// return false; +// for(int i=0; i<=n/2; i++){ +// if(pow(2,i)==n){ +// return true; +// } +// } +// return false; + +// } +//2的幂次方,二进制有且仅有一个1 +// bool isPowerOfTwo(int n){ +// int count = 0; +// while(n>0){ +// n=n&n-1; +// count++; +// } +// if(count==1) +// return true; +// return false; +// } +//简单写法 +bool isPowerOfTwo(int n){ + return ((n>0)&&((n&n-1)==0)); +} \ No newline at end of file diff --git a/Week 07/id_128/LeetCode_190_128.cs b/Week 07/id_128/LeetCode_190_128.cs new file mode 100644 index 000000000..a5d1da021 --- /dev/null +++ b/Week 07/id_128/LeetCode_190_128.cs @@ -0,0 +1,28 @@ +public class Solution +{ + public uint reverseBits(uint n) + { + uint result = 0; + for (int i = 0; i < 31; i++) + { + var bit = (n >> i) & 1; + result |= bit << (31 - i); + } + return result; + } +} + + +public class Solution +{ + public uint reverseBits(uint n) + { + uint result = 0; + for (int i = 0; i < 32; i++) + { + result = (result << 1) + (n & 1); + n >>= 1; + } + return result; + } +} \ No newline at end of file diff --git a/Week 07/id_128/LeetCode_191_128.cs b/Week 07/id_128/LeetCode_191_128.cs new file mode 100644 index 000000000..6d08da199 --- /dev/null +++ b/Week 07/id_128/LeetCode_191_128.cs @@ -0,0 +1,51 @@ +//loop and right shift the number +public class Solution +{ + public int HammingWeight(uint n) + { + var count = 0; + for (int i = 0; i < 32; i++) + { + if ((n & 1) == 1) + count++; + n = n >> 1; + } + return count; + } +} + +//loop and left shift the mask +public class Solution +{ + public int HammingWeight(uint n) + { + uint mask = 1; + int count = 0; + for (int i = 0; i < 32; i++) + { + if ((n & mask) > 0) + { + count++; + } + + mask = mask << 1; + } + return count; + } +} + + +//n & (n-1) +public class Solution +{ + public int HammingWeight(uint n) + { + var count = 0; + while (n > 0) + { + count++; + n = n & (n - 1); + } + return count; + } +} \ No newline at end of file diff --git a/Week 07/id_128/LeetCode_231_128.cs b/Week 07/id_128/LeetCode_231_128.cs new file mode 100644 index 000000000..e28292bb0 --- /dev/null +++ b/Week 07/id_128/LeetCode_231_128.cs @@ -0,0 +1,49 @@ +//iteration, time out +public class Solution +{ + public bool IsPowerOfTwo(int n) + { + if (n < 1) + return false; + int a = 1; + while (a < n) + { + a *= 2; + } + if (a == n) + return true; + return false; + } +} + +//n & n-1 +public class Solution +{ + public bool IsPowerOfTwo(int n) + { + if (n < 1) + return false; + if ((n & (n - 1)) == 0) + return true; + return false; + } +} + +//one liner +public class Solution +{ + public bool IsPowerOfTwo(int n) + { + return (n > 0) && (n & (n - 1)) == 0; + } +} + + +//one liner, n & -n +public class Solution +{ + public bool IsPowerOfTwo(int n) + { + return (n > 0) && (n & (-n)) == n; + } +} \ No newline at end of file diff --git a/Week 07/id_128/NOTE.md b/Week 07/id_128/NOTE.md index a6321d6e2..6ac3d7176 100644 --- a/Week 07/id_128/NOTE.md +++ b/Week 07/id_128/NOTE.md @@ -1,4 +1,32 @@ # NOTE +~ 反 +^ 异或 相当于 不进位加法 + +x ^ 0 = x +x ^ 1s = ~x +x ^ x = 0 +x ^ (~x) = 1s + +c = a ^ b => a = c ^ b, b = a ^ c 交换性 +a ^ b ^ c = (a ^ b) ^ c = a ^ (b ^ c) 结合性 + + +实战要点 + +判断奇偶 +x % 2 == 1 => x & 1 == 1 +x % 2 == 0 => x & 1 == 0 + +除2 +x/2 => x>>1 +mid = (left+right)/2 => mid = (left+right) >> 1 + +清零最低位的1 +x = x & (x-1) + +得到最低位的1 +x & -x + diff --git a/Week 07/id_133/leetcode_231_133.java b/Week 07/id_133/leetcode_231_133.java new file mode 100644 index 000000000..2bdf9694c --- /dev/null +++ b/Week 07/id_133/leetcode_231_133.java @@ -0,0 +1,12 @@ + +/** + * https://leetcode-cn.com/problems/power-of-two/ + * 题号:231 + * 题目:2的幂 + */ + +public class Solution { + public boolean isPowerOfTwo(int n) { + return n > 0 && (n & (n - 1)) == 0; + } +} \ No newline at end of file diff --git a/Week 07/id_133/leetcode_51_133.java b/Week 07/id_133/leetcode_51_133.java new file mode 100644 index 000000000..d3414146a --- /dev/null +++ b/Week 07/id_133/leetcode_51_133.java @@ -0,0 +1,76 @@ + +/** + * https://leetcode-cn.com/problems/n-queens/description/ + * 题号:51 + * 题目:N皇后 + */ + +class Solution { + int rows[]; + // "hill" diagonals + int hills[]; + // "dale" diagonals + int dales[]; + int n; + // output + List> output = new ArrayList(); + // queens positions + int queens[]; + + public boolean isNotUnderAttack(int row, int col) { + int res = rows[col] + hills[row - col + 2 * n] + dales[row + col]; + return (res == 0) ? true : false; + } + + public void placeQueen(int row, int col) { + queens[row] = col; + rows[col] = 1; + hills[row - col + 2 * n] = 1; // "hill" diagonals + dales[row + col] = 1; //"dale" diagonals + } + + public void removeQueen(int row, int col) { + queens[row] = 0; + rows[col] = 0; + hills[row - col + 2 * n] = 0; + dales[row + col] = 0; + } + + public void addSolution() { + List solution = new ArrayList(); + for (int i = 0; i < n; ++i) { + int col = queens[i]; + StringBuilder sb = new StringBuilder(); + for(int j = 0; j < col; ++j) sb.append("."); + sb.append("Q"); + for(int j = 0; j < n - col - 1; ++j) sb.append("."); + solution.add(sb.toString()); + } + output.add(solution); + } + + public void backtrack(int row) { + for (int col = 0; col < n; col++) { + if (isNotUnderAttack(row, col)) { + placeQueen(row, col); + // if n queens are already placed + if (row + 1 == n) addSolution(); + // if not proceed to place the rest + else backtrack(row + 1); + // backtrack + removeQueen(row, col); + } + } + } + + public List> solveNQueens(int n) { + this.n = n; + rows = new int[n]; + hills = new int[4 * n - 1]; + dales = new int[2 * n - 1]; + queens = new int[n]; + + backtrack(0); + return output; + } +} \ No newline at end of file diff --git a/Week 07/id_138/LeetCode_146_138.java b/Week 07/id_138/LeetCode_146_138.java new file mode 100644 index 000000000..f470268fc --- /dev/null +++ b/Week 07/id_138/LeetCode_146_138.java @@ -0,0 +1,149 @@ +import java.util.Hashtable; +import java.util.LinkedHashMap; + +/** + * LRU缓存机制 + * @author L + * 获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 + 写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。 + */ +public class LeetCode_146_138 { + /** + * 借助linkedhashmap实现 有序字典 + * @author L + * + */ + class LRUCache extends LinkedHashMap{ + private int capacity; + + public LRUCache(int capacity) { + super(capacity,0.75F,true); + this.capacity = capacity; + } + + public int get(int key) { + return super.getOrDefault(key, -1); + } + + public void put(int key,int value) { + super.put(key, value); + } + + /** + * 重写父类的删除方法 + */ + @Override + protected boolean removeEldestEntry(java.util.Map.Entry eldest) { + return size()>capacity; + } + + } + + +// /哈希表 + 双向链表 + public class LRUCache2 { + + class DLinkedNode { + int key; + int value; + DLinkedNode prev; + DLinkedNode next; + } + + private void addNode(DLinkedNode node) { + /** + * Always add the new node right after head. + */ + node.prev = head; + node.next = head.next; + + head.next.prev = node; + head.next = node; + } + + private void removeNode(DLinkedNode node){ + /** + * Remove an existing node from the linked list. + */ + DLinkedNode prev = node.prev; + DLinkedNode next = node.next; + + prev.next = next; + next.prev = prev; + } + + private void moveToHead(DLinkedNode node){ + /** + * Move certain node in between to the head. + */ + removeNode(node); + addNode(node); + } + + private DLinkedNode popTail() { + /** + * Pop the current tail. + */ + DLinkedNode res = tail.prev; + removeNode(res); + return res; + } + + private Hashtable cache = + new Hashtable(); + private int size; + private int capacity; + private DLinkedNode head, tail; + + public LRUCache2(int capacity) { + this.size = 0; + this.capacity = capacity; + + head = new DLinkedNode(); + // head.prev = null; + + tail = new DLinkedNode(); + // tail.next = null; + + head.next = tail; + tail.prev = head; + } + + public int get(int key) { + DLinkedNode node = cache.get(key); + if (node == null) return -1; + + // move the accessed node to the head; + moveToHead(node); + + return node.value; + } + + public void put(int key, int value) { + DLinkedNode node = cache.get(key); + + if(node == null) { + DLinkedNode newNode = new DLinkedNode(); + newNode.key = key; + newNode.value = value; + + cache.put(key, newNode); + addNode(newNode); + + ++size; + + if(size > capacity) { + // pop the tail + DLinkedNode tail = popTail(); + cache.remove(tail.key); + --size; + } + } else { + // update the value. + node.value = value; + moveToHead(node); + } + } + } + +} diff --git a/Week 07/id_138/LeetCode_191_138.java b/Week 07/id_138/LeetCode_191_138.java new file mode 100644 index 000000000..487824301 --- /dev/null +++ b/Week 07/id_138/LeetCode_191_138.java @@ -0,0 +1,24 @@ +/** + * 位1的个数 + * 输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数 + * @author L + * + */ +public class LeetCode_191_138 { + + int hammingWeight(int n) { + int count = 0; + while (n != 0) { // 4 100 + n = n & n - 1; // 3 011 + // & 0 + count++; + } + return count; + } + + public static void main(String[] args) { + LeetCode_191_138 l138 = new LeetCode_191_138(); + int x = l138.hammingWeight(4); + System.out.println(x); + } +} diff --git a/Week 07/id_138/LeetCode_493_138.java b/Week 07/id_138/LeetCode_493_138.java new file mode 100644 index 000000000..5973812fe --- /dev/null +++ b/Week 07/id_138/LeetCode_493_138.java @@ -0,0 +1,48 @@ +/** + * 翻转对 给定一个数组 nums ,如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对。 + * @author L + * + */ +public class LeetCode_493_138 { + void merge(int[] A, int start, int mid, int end) + { + int n1 = (mid - start + 1); + int n2 = (end - mid); + int L[] = new int[n1], R[] = new int[n2]; + for (int i = 0; i < n1; i++) + L[i] = A[start + i]; + for (int j = 0; j < n2; j++) + R[j] = A[mid + 1 + j]; + int i = 0, j = 0; + for (int k = start; k <= end; k++) { + if (j >= n2 || (i < n1 && L[i] <= R[j])) + A[k] = L[i++]; + else + A[k] = R[j++]; + } + } + + int mergesort_and_count(int[] A, int start, int end) + { + if (start < end) { + int mid = (start + end) / 2; + int count = mergesort_and_count(A, start, mid) + mergesort_and_count(A, mid + 1, end); + int j = mid + 1; + for (int i = start; i <= mid; i++) { + while (j <= end && A[i] > A[j] * 2L)//整数越界考虑 + j++; + count += j - (mid + 1); + } + merge(A, start, mid, end); + return count; + } + else + return 0; + } + + public int reversePairs(int[] nums) + { + return mergesort_and_count(nums, 0, nums.length - 1); + } + +} diff --git a/Week 07/id_138/LeetCode_51_138_bit.java b/Week 07/id_138/LeetCode_51_138_bit.java new file mode 100644 index 000000000..75eb9aaf5 --- /dev/null +++ b/Week 07/id_138/LeetCode_51_138_bit.java @@ -0,0 +1,36 @@ +/** + * N皇后 + * @author L + * + */ +public class LeetCode_51_138_bit { + + private int size ,count; + + public int queenCount(int n) { + count = 0; + size = (1 << n)-1;// 1*2^n -1 + solve(0,0,0);//可以放皇后的位置 + return count; + } + + private void solve(int row, int ld, int rd) { + // TODO Auto-generated method stub + if(row == size) + { + count++; + return; + } + int pos = size &(~(row|ld|rd)); + while(pos!=0) { + int p = pos &(-pos); + pos -= p; + solve(row|p,(ld|p)<<1,(rd|p)>>1); + } + } + + public static void main(String[] args) { + LeetCode_51_138_bit lb = new LeetCode_51_138_bit(); + System.out.println(lb.queenCount(4)); + } +} diff --git a/Week 07/id_138/LeetCode_56_138.java b/Week 07/id_138/LeetCode_56_138.java new file mode 100644 index 000000000..4a2982268 --- /dev/null +++ b/Week 07/id_138/LeetCode_56_138.java @@ -0,0 +1,51 @@ +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; + +/** + * 合并区间 + * 给出一个区间的集合,请合并所有重叠的区间。 + * @author L + * + */ +public class LeetCode_56_138 { + class Interval{ + int start; + int end; + + public Interval() { + + } + public Interval(int start, int end) { + this.start = start; + this.end = end; + } + } + + //自定义排序器 + private class IntervalComparator implements Comparator { + @Override + public int compare(Interval a, Interval b) { + return a.start < b.start ? -1 : a.start == b.start ? 0 : 1; + } + } + + public List merge(List intervals) { + //排序 + Collections.sort(intervals, new IntervalComparator()); + + LinkedList merged = new LinkedList(); + for (Interval interval : intervals) { + //如果为空或者存在交叉 + if (merged.isEmpty() || merged.getLast().end < interval.start) { + merged.add(interval); + } + else { + merged.getLast().end = Math.max(merged.getLast().end, interval.end); + } + } + + return merged; + } +} diff --git a/Week 07/id_138/NOTE.md b/Week 07/id_138/NOTE.md index a6321d6e2..3e559c4e6 100644 --- a/Week 07/id_138/NOTE.md +++ b/Week 07/id_138/NOTE.md @@ -1,4 +1,140 @@ # NOTE +Week07学习总结 + 本周主要学习位运算操作(算是大学计算机知识复习),布隆过滤器(Boolm Filter),LRUCache的实现,应用以及常见排序算法 +1.位运算的常规操作: + 1)二进制与十进制数相互转换: + 10->2:求余取模商继续 + 2->10:从右向右累加2次方 + 2)& 每一位去运算,只有两位都为 1 的时候才是 1 + | 每一位去运算,只要有一位为 1 就是 1 + ~ 每一位取反 + ^ 异或运算:每一位去运算,相同就是 1 不同就是 0 + >> 位移运算:相当于求开方 4>>1=2 + << 位移运算:求平方 2<<1=4 + 判断奇偶数: n&1==0?'奇数':'偶数' + 交换两数: int a = 5; + int b = 9; + a ^= b; + b ^= a; + a ^= b; + 求整数的绝对值: n>>31 + 变换符号: ~n+1 + 取余运算: 对于 a%b,当b为2的n次方的时候 a%b等价于a&(b-1) + 取int型变量的第k位: a>>(k-1)&1;运算顺序为先右移k-1位,在与1 + 取末k位: a&((1<10110) x >> 1 + 在最后加一个0 (101101->1011010) x < < 1 + 在最后加一个1 (101101->1011011) x < < 1+1 + 把最后一位变成1 (101100->101101) x | 1 + 把最后一位变成0 (101101->101100) x | 1-1 + 最后一位取反 (101101->101100) x ^ 1 + 把右数第k位变成1 (101001->101101,k=3) x | (1 < < (k-1)) + 把右数第k位变成0 (101101->101001,k=3) x & ~ (1 < < (k-1)) + 右数第k位取反 (101001->101101,k=3) x ^ (1 < < (k-1)) + 取末三位 (1101101->101) x & 7 + 取末k位 (1101101->1101,k=5) x & ((1 < < k)-1) + 取右数第k位 (1101101->1,k=4) x >> (k-1) & 1 + 把末k位变成1 (101001->101111,k=4) x | (1 < < k-1) + 末k位取反 (101001->100110,k=4) x ^ (1 < < k-1) + 把右边连续的1变成0 (100101111->100100000) x & (x+1) + 把右起第一个0变成1 (100101111->100111111) x | (x+1) + 把右边连续的0变成1 (11011000->11011111) x | (x-1) + 取右边连续的1 (100101111->1111) (x ^ (x+1)) >> 1 + 去掉右起第一个1的左边 (100101000->1000) x & (x ^ (x-1)) + 判断奇数 (x&1)==1 + 判断偶数 (x&1)==0 - +2.布隆过滤器(Bloom Filter)的原理和实现(https://www.cnblogs.com/cpselvis/p/6265825.html) + 类似hash运算,本身是一个很长的二进制向量 +实践场景: + 缓存击穿、垃圾邮件识别、集合判重 + 大量数据,判断给定的是否在其中 + 现在有大量的数据,而这些数据的大小已经远远超出了服务器的内存,现在再给你一个数据,如何判断给你的数据在不在其中。如果服务器的内存足够大,那么用HashMap是一个不错的解决方案,理论上的时间复杂度可以达到O(1),但是现在数据的大小已经远远超出了服务器的内存,所以无法使用HashMap,这个时候就可以使用“布隆过滤器”来解决这个问题。但是还是同样的,会有一定的“误判率”。 +java实现:https://github.com/lovasoa/bloomfilter/blob/master/src/main/java/BloomFilter.java + +3.LRUCache:最近最少使用算法,java可以借助LinkedHashMap有序字典树实现。 + + #python代码示例 + class LRUCache(object): + + def __init__(self, capacity): + self.dic = collections.OrderedDict() + self.remain = capacity + + def get(self, key): + if key not in self.dic: + return -1 + v = self.dic.pop(key) + self.dic[key] = v # key as the newest one + return v + + def put(self, key, value): + if key in self.dic: + self.dic.pop(key) + else: + if self.remain > 0: + self.remain -= 1 + else: # self.dic is full + self.dic.popitem(last=False) + self.dic[key] = value +4 排序算法: + 插入排序:工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。 + 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。 + 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。) + 希尔排序: + 选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1; + 按增量序列个数 k,对序列进行 k 趟排序; + 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整 个序列的长度。 + 选择排序: + 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。 + 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。 + 重复第二步,直到所有元素均排序完毕。 + 堆排序: + 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列; + 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列; + 堆排序的平均时间复杂度为 Ο(nlogn)。 + 1. 算法步骤 + 创建一个堆 H[0……n-1]; + 把堆首(最大值)和堆尾互换; + 把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置; + 重复步骤 2,直到堆的尺寸为 1。 + 冒泡排序: + 比较相邻的元素。如果第一个比第二个大,就交换他们两个。 + 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。 + 针对所有的元素重复以上的步骤,除了最后一个。 + 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较 + 快速排序: + 从数列中挑出一个元素,称为 "基准"(pivot); + 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位 置。这个称为分区(partition)操作; + 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序; + 归并排序: + 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列; + 设定两个指针,最初位置分别为两个已经排序序列的起始位置; + 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置; + 重复步骤 3 直到某一指针达到序列尾; + 将另一序列剩下的所有元素直接复制到合并序列尾。 + 计数排序: + (1)找出待排序的数组中最大和最小的元素 + (2)统计数组中每个值为i的元素出现的次数,存入数组C的第i项 + (3)对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加) + (4)反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1 + 桶排序 + 桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,我们需要做到这两点: + 在额外空间充足的情况下,尽量增大桶的数量 + 使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中 + 同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。 + 基数排序: + 基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮 点 数,所以基数排序也不是只能使用于整数。 + 1. 基数排序 vs 计数排序 vs 桶排序 + 基数排序有两种方法: + 这三种排序算法都利用了桶的概念,但对桶的使用方法上有明显差异: + 基数排序:根据键值的每位数字来分配桶; + 计数排序:每个桶只存储单一键值; + 桶排序:每个桶存储一定范围的数值; + + + diff --git a/Week 07/id_143/LeetCode_191_143.java b/Week 07/id_143/LeetCode_191_143.java new file mode 100644 index 000000000..6fc852914 --- /dev/null +++ b/Week 07/id_143/LeetCode_191_143.java @@ -0,0 +1,22 @@ +/* + * @lc app=leetcode.cn id=191 lang=java + * + * [191] 位1的个数 + * 思路 + * 利用 x & (x-1) 可以把数字最右边的1去掉进行计算 + * 一直迭代到x 变为0 即可 + */ + +// @lc code=start +public class Solution { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int num = 0; + while(n != 0){ + num ++; + n = n & (n-1); + } + return num; + } +} +// @lc code=end \ No newline at end of file diff --git a/Week 07/id_143/LeetCode_52_143.java b/Week 07/id_143/LeetCode_52_143.java new file mode 100644 index 000000000..420e64085 --- /dev/null +++ b/Week 07/id_143/LeetCode_52_143.java @@ -0,0 +1,38 @@ +/* + * @lc app=leetcode.cn id=52 lang=java + * 采用位运算的方式维护当前棋盘上面的占位信息 + * colMask表示当前行之前所有行上填的皇后已经占的列数,每一位代表一列 + * lMask表示当前行上被前面所有皇后左对角线占用的列,rMask表示当前行上被前面所有皇后右对角线占用的列 + * 可以巧妙用位运算快速算出当前行剩余可以填的列有哪些,到最后如果所有列能填满, 说明找到一种方案 + * [52] N皇后 II + */ + +// @lc code=start +class Solution { + void dfs(int col, int lMask, int rMask, int n, int[] totalCnt) { + //终止条件 + if (col == (1<> 1, n, totalCnt); + } + } + + public int totalNQueens(int n) { + int[] ans = new int[] {0}; + dfs(0, 0, 0, n, ans); + return ans[0]; + } +} +// @lc code=end + diff --git a/Week 07/id_143/LeetCode_56_143.java b/Week 07/id_143/LeetCode_56_143.java new file mode 100644 index 000000000..7c73a6e55 --- /dev/null +++ b/Week 07/id_143/LeetCode_56_143.java @@ -0,0 +1,40 @@ +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/* + * @lc app=leetcode.cn id=56 lang=java + * + * [56] 合并区间 + * 排序所有区间,然后从前到后合并 + */ + +// @lc code=start +class Solution { + public int[][] merge(int[][] intervals) { + Arrays.sort(intervals,(a,b) -> (a[0] - b[0])); + List temp = new ArrayList(); + int[] lasteValue = new int[2]; + for(int[] value : intervals ){ + if(temp.isEmpty()){ + temp.add(value); + lasteValue = value; + } + + if(lasteValue[1] >= value[0]){ + lasteValue[1] = Math.max(value[1],lasteValue[1]); + }else{ + temp.add(value); + lasteValue = value; + } + } + int[][] result = new int[temp.size()][2]; + for (int i = 0; i < temp.size(); i++) { + result[i] = temp.get(i); + } + return result; + + } +} +// @lc code=end + diff --git a/Week 07/id_143/NOTE.md b/Week 07/id_143/NOTE.md index a6321d6e2..212c8668b 100644 --- a/Week 07/id_143/NOTE.md +++ b/Week 07/id_143/NOTE.md @@ -1,4 +1,439 @@ -# NOTE +# 算法训练营学习 +# 第七周 +## 第十六课 +### 位运算 +1. 位运算符 + +|含义|运算符|示例|备注| +|:---|:---:|---:|---:| +| 左移|<<|0011 -> 0110| 乘以2| +|右移|>>|0110 -> 0011| 除以2| +|按位或|\||0011 \| 1011| 结果 1011| +|按位与|&|0011 & 1011| 结果 0011| +|取反|~|0011| 结果 1100| +|按位异或|^|0011 ^ 1011| 结果 1000| + +2. 异或运算特点: + - x ^ 0 = x + - x ^ 1s = ~x // 1s为全1, 全1相当于0取反(~0) + - x ^ (~x) = 1s + - x ^ x = 0 + - c = a ^ b => a ^ c = b, b ^ c = a // 交换两个数 + - a ^ b ^ c = a ^ ( b ^ c ) = ( a ^ b ) ^ c // 结合律 +3. 指定位置的位运算 + +|项目|公式|说明| +|:---|:---:|---:| +|将x最右边的n位清零|x & (~0 << n)|0与任何数(0,1)为0;任何数(0,1)与1相与保持不变。| +|获取x的第n个位的值(0或者1)| (x >> n) & 1|与上同理,仅比较1个位| +|获取x的第n次幂| x & (1 << (n - 1))|该位为1,其它位全为0| +|仅将第n位, 置为1| x \| (1 << n)|| +|仅将第n为, 置为0| x & ( ~(1 << n))|| +|将x最高位至第n位(含)清零| x & ((1 << n) - 1)|| +|将第n位至第0位(含)清零| x & (~((1<< (n + 1)) - 1))|| + +4. 实战常用技巧 + - 奇偶判断 + ``` + x % 2 == 1 -> (x & 1) == 1 + x % 2 == 0 -> (x & 1) == 0 + ```` + - 除以2 + ``` + x / 2 -> x >> 1 + mid = (left + right) / 2 -> mid = (left + right) >> 1 + ``` + - *清零最低位的1* + ``` + x = x & (x - 1) + 太常用,比较难想到。 + ``` + - *得到最低位的1* + ``` + x & -x + ``` + - 与自己取反再与,实现清0 + ``` + x & ~x = 0 + ``` +## 第十七课 +### 布隆过滤器和LRU缓存 +#### 布隆过滤器特性 +1. 一个很长的二进制向量和一系列的随机映射函数 +2. 可以用于检索一个元素是否在一个集合中 +3. 优点:空间效率和查询时间都远远超过一般的算法 +4. 缺点:有一定的误识别率和删除困扰 +5. 总结: + > 可百分之百判断元素不存在于集合中,但不可准确判断元素存在集合中,故一般用于前置缓存(模糊判断),这里解释一下,即在真正进入缓存之前的判断。 + +6. 案例: + > 比特币、分布式系统、Redis缓存、垃圾邮件和评论的过滤等 +#### LRU(Least Recently Used) Cahce特点 +1. 两个要素:缓存大小、替换策略 +2. 总结: + >淘汰最远使用的元素 + +## 第十八课 +### 排序算法 +1. 比较类排序 +2. 非比较类排序 +3. 常用算法复杂度总表 + +|排序算法|平均时间复杂度|最坏时间复杂度|最好时间复杂度| 空间复杂度| 是否稳定| +|:---|:---|:---|:---|:---|:---| +|冒泡排序|O(n^2) |O(n^2)|O(n)|O(1)| 是| +|选择排序 |O(n^2) |O(n^2)|O(n^2)|O(1)| 不是| +|插入排序 |O(n^2) |O(n^2)|O(n)| O(1)| 是| +|希尔排序 |O(n^1.3) |O(n^2)|O(n)| O(1)| 不是| +|归并排序 |O(nlogn) |O(nlogn)|O(nlogn)| O(n)| 是| +|快速排序|O(nlogn)| O(n^2)|O(nlogn)| O(nlogn))| 不是| +|堆排序 |O(nlogn)|O(nlogn)|O(nlogn)|O(1)| 不是| +|计数排序 |O(n+k) |O(n+k)|O(n+k)|O(n+k) |是| +|桶排序| O(n+k) |O(n^2)|O(n)|O(n+k) |是| +|基数排序 |O(n∗k)|O(n∗k)| O(n∗k)| O(n+k)| 是| + +#### 选择排序(O(n^2)) +1. 算法描述 + > 每次找最小值,然后放到排序数组的起始位置 + +2. 选择排序代码示例 + ``` + public static void selectionSort(int[] nums){ + for(int i = 0;i< nums.length;i++){ + //最小值 + int m = i; + int min = Integer.MAX_VALUE; + for(int j = i;j < nums.length;j++){ + if(nums[j] < min){ + m = j; + min = nums[j]; + } + } + nums[m] = nums[i]; + nums[i] = min; + } + } + ``` +#### 冒泡排序(O(n^2)) +1. 算法描述 + > 嵌套循环,每次查看相邻的元素,若逆序,则交换。 + +2. 冒泡排序代码示例 + ``` + public static void bubbleSort(int[] nums){ + for(int i = 0;i < nums.length;i++){ + for(int j = 0;j < nums.length-i-1 ;j++){ + //比较相邻元素,若逆序 + if(nums[j] > nums[j+1]){ + //交换 + int tmp = nums[j]; + nums[j] = nums[j+1]; + nums[j+1] = tmp; + } + } + } + } + ``` + +#### 插入排序(O(n^2)) +1. 算法描述 + > 从前往后逐步构建有序序列;对于未排序数据,在已排序序列中从后往前扫描,找到相应位置并插入。 + +2. 插入排序代码示例 + ``` + public static void insertionSort(int[] nums){ + for(int i = 0;i < nums.length;i++){ + int curr = nums[i]; + for(int j = i;j > 0;j--){ + if(curr >= nums[j-1]){ + break; + }else{ + //后移一位 + nums[j] = nums[j-1]; + i--; + } + } + nums[i] = curr; + } + } + ``` + +#### 希尔排序(Shell Sort)平均(O(n^1.3))最坏(O(n^2))最好(O(n)) +> 1959年Shell发明,第一个突破O(n2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。 +1. 算法描述 + 先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述: + - 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1; + - 按增量序列个数k,对序列进行k 趟排序; + - 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。 + +2. 希尔排序代码示例 + ``` + function shellSort(arr) { + var len = arr.length; + for (var gap = Math.floor(len / 2); gap > 0; gap = Math.floor(gap / 2)) { + // 注意:这里和动图演示的不一样,动图是分组执行,实际操作是多个分组交替执行 + for (var i = gap; i < len; i++) { + var j = i; + var current = arr[i]; + while (j - gap >= 0 && current < arr[j - gap]) { + arr[j] = arr[j - gap]; + j = j - gap; + } + arr[j] = current; + } + } + return arr; + } + ``` +3. 算法分析 + > 希尔排序的核心在于间隔序列的设定。既可以提前设定好间隔序列,也可以动态的定义间隔序列。动态定义间隔序列的算法是《算法(第4版)》的合著者Robert Sedgewick提出的。 + + +#### 归并排序(Merge Sort)(O(nlogn)) +> 归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。 +1. 算法描述 + - 把长度为n的输入序列分成两个长度为n/2的子序列; + - 对这两个子序列分别采用归并排序; + - 将两个排序好的子序列合并成一个最终的排序序列。 + +2. 归并排序代码示例 + ``` + public static void mergeSort(int[] nums,int begin,int end){ + //终止条件 + if(begin <= end)return; + //下钻 + int mid = (begin + end) >> 1; + mergeSort(nums,begin,mid); + mergeSort(nums,mid+1,end); + //执行当前层逻辑,即合并两个有序数组 + merge(nums,begin,mid,end); + } + + private static void merge(int[] nums, int begin, int mid, int end) { + int[] tmp = new int[end-begin+1]; + int i = begin,j = mid+1,k = 0; + while (i<=mid && j<=end){ + tmp[k++] = nums[i] <= nums[j]?nums[i++] : nums[j++]; + } + while (i<=mid){ + tmp[k++] = nums[i++]; + } + while (j<=end){ + tmp[k++] = nums[j++]; + } + for(int p = 0;p < tmp.length;p++){ + nums[begin+p] = tmp[p]; + } + // 也可以用 System.arraycopy(a, start1, b, start2, length); + // System.arraycopy(tmp, 0, arr, left, tmp.length); + } + ``` +3. 算法复杂度分析 + > 归并排序是一种稳定的排序方法。和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是O(nlogn)的时间复杂度。代价是需要额外的内存空间。 +#### 快速排序(O(nlogn)) 简称快排 +> 数组取标杆pivot, 将小元素放在pivot左边, 大元素放右侧, 然后依次对左边和右边的子数组进行快排, 从而达到整个数组有序。 +1. 算法描述 + - 从数列中挑出一个元素,称为 “基准”(pivot); + - 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作; + - 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。 +2. 快速排序代码示例 + ``` + public static void quickSort(int[] array, int begin, int end) { + if (end <= begin) return; + int pivot = partition(array, begin, end); + quickSort(array, begin, pivot - 1); + quickSort(array, pivot + 1, end); + } + + static int partition(int[] a, int begin, int end) { + // pivot: 标杆位置,counter: 小于pivot的元素的个数 + int pivot = end, counter = begin; + for (int i = begin; i < end; i++) { + if (a[i] < a[pivot]) { + int temp = a[counter]; a[counter] = a[i]; a[i] = temp; + counter++; + } + } + int temp = a[pivot]; a[pivot] = a[counter]; a[counter] = temp; + return counter; + } + ``` +3. 快排与归并排序的比较与总结: +> 1. 归并: 先排序左右子数组, 然后合并2个有序数组 +> 2. 快排: 先调配出左右子数组, 然后对于左右子数组进行排序 + +#### 堆排序 Heap Sort:插入 O(longN), 取最大/最小值 O(1) +> 1. 数组元素依次建立大|小顶堆依次取堆顶元素, 并删除。 +> 2. 堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。 +1. 算法描述 + - 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区; + - 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n]; + - 由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。 +2. 堆排序代码示例 + ``` + static void heapify(int[] array, int length, int i) { + int left = 2 * i + 1, right = 2 * i + 2; + int largest = i; + + if (left < length && array[left] > array[largest]) { + largest = left; + } + if (right < length && array[right] > array[largest]) { + largest = right; + } + + if (largest != i) { + int temp = array[i]; array[i] = array[largest]; array[largest] = temp; + heapify(array, length, largest); + } + } + + public static void heapSort(int[] array) { + if (array.length == 0) return; + + int length = array.length; + for (int i = length / 2-1; i >= 0; i-) + heapify(array, length, i); + + for (int i = length - 1; i >= 0; i--) { + int temp = array[0]; array[0] = array[i]; array[i] = temp; + heapify(array, i, 0); + } + } + ``` + +#### 计数排序(Counting Sort) +> 计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。 +1. 算法描述 + - 找出待排序的数组中最大和最小的元素; + - 统计数组中每个值为i的元素出现的次数,存入数组C的第i项; + - 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加); + - 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。 +2. 计数排序代码示例(js) + ``` + 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; + } + ``` +3. 算法复杂度分析 + >计数排序是一个稳定的排序算法。当输入的元素是 n 个 0到 k 之间的整数时,时间复杂度是O(n+k),空间复杂度也是O(n+k),其排序速度快于任何比较排序算法。当k不是很大并且序列比较集中时,计数排序是一个很有效的排序算法。 + +#### 桶排序(Bucket Sort) +> 桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。 +1. 算法描述 + - 设置一个定量的数组当作空桶; + - 遍历输入数据,并且把数据一个一个放到对应的桶里去; + - 对每个不是空的桶进行排序; + - 从不是空的桶里把排好序的数据拼接起来。 +2. 桶排序代码示例(js) + ``` + 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; + } + ``` +3. 算法复杂度分析 + > 桶排序最好情况下使用线性时间O(n),桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,因为其它部分的时间复杂度都为O(n)。很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。 + +#### 基数排序(Radix Sort) +> 基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。 +1. 算法描述 + - 取得数组中的最大数,并取得位数; + - arr为原始数组,从最低位开始取每个位组成radix数组; + - 对radix进行计数排序(利用计数排序适用于小范围数的特点); +2. 基数排序代码示例(js) + ``` + 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; + } + ``` +3. 算法复杂度分析 + > 基数排序基于分别排序,分别收集,所以是稳定的。但基数排序的性能比桶排序要略差,每一次关键字的桶分配都需要O(n)的时间复杂度,而且分配之后得到新的关键字序列又需要O(n)的时间复杂度。假如待排数据可以分为d个关键字,则基数排序的时间复杂度将是O(d*2n) ,当然d要远远小于n,因此基本上还是线性级别的。 + + 基数排序的空间复杂度为O(n+k),其中k为桶的数量。一般来说n>>k,因此额外空间需要大概n个左右。 + + + +## 个人感悟 + - **课程学习计划问题**: 年底,各项任务交付压力过大,一周能抽出的时间太少,排好计划,准备重扫几次。 + - **练习问题**: 五毒神掌,真金不怕火炼。 + - 团队问题: 定期"**叫醒**"服务。团队方面,除了在坚持的,就是没有在坚持的。大家都忙,希望有时间还是回来系统性学习较好! diff --git a/Week 07/id_153/LeetCode_1122_153.js b/Week 07/id_153/LeetCode_1122_153.js new file mode 100644 index 000000000..2c94ed493 --- /dev/null +++ b/Week 07/id_153/LeetCode_1122_153.js @@ -0,0 +1,32 @@ +/** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ +var relativeSortArray = function(arr1, arr2) { + let bucket = {}; + let result = []; + + for (let i = 0; i < arr1.length; i++) { + if (bucket[arr1[i]]) { + bucket[arr1[i]]++; + } else { + bucket[arr1[i]] = 1; + } + } + + for (let j = 0; j < arr2.length; j++) { + while(bucket[arr2[j]]) { + result.push(arr2[j]); + bucket[arr2[j]]--; + } + } + + for (const key in bucket) { + while(bucket[key]) { + result.push(key); + bucket[key]--; + } + } + return result +}; diff --git a/Week 07/id_153/LeetCode_190_153.js b/Week 07/id_153/LeetCode_190_153.js new file mode 100644 index 000000000..d96190e0a --- /dev/null +++ b/Week 07/id_153/LeetCode_190_153.js @@ -0,0 +1,12 @@ +/** + * @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; +}; diff --git a/Week 07/id_153/LeetCode_191_153.js b/Week 07/id_153/LeetCode_191_153.js new file mode 100644 index 000000000..fe429d8bd --- /dev/null +++ b/Week 07/id_153/LeetCode_191_153.js @@ -0,0 +1,24 @@ +/** + * @param {number} n - a positive integer + * @return {number} + */ +var hammingWeight = function(n) { + let bits = 0; + let mask = 1; + for (let i = 0; i < 32; i++) { + if ((n & mask) !== 0) { + bits++; + } + mask <<=1; + } + return bits; +}; + +var hammingWeight1 = function(n) { + let sum = 0; + while (n !== 0) { + sum++; + n &= (n - 1); + } + return sum; +}; \ No newline at end of file diff --git a/Week 07/id_153/NOTE.md b/Week 07/id_153/NOTE.md index a6321d6e2..a91659d0f 100644 --- a/Week 07/id_153/NOTE.md +++ b/Week 07/id_153/NOTE.md @@ -1,4 +1,131 @@ -# NOTE +# 总结 +## 位运算 +* 机器里的数字表示方式和存储格式就是二进制 - +### 指定位置的位运算 +1. 将 x 最右边的 n 位清零: x & (~0 << n) +2. 获取 x 的第 n 位值(0 或者 1): (x >> n) & 1 +3. 获取 x 的第 n 位的幂值:x & (1 << (n -1)) +4. 仅将第 n 位置为 1:x | (1 << n) +5. 仅将第 n 位置为 0:x & (~ (1 << n)) +6. 将 x 最高位至第 n 位(含)清零:x & ((1 << n) - 1) +7. 将第 n 位至第 0 位(含)清零:x & (~ ((1 << (n + 1)) - 1)) +### 实战位运算要点 +1. 判断奇偶: + x % 2 == 1 —> (x & 1) == 1 + x % 2 == 0 —> (x & 1) == 0 +2. x >> 1 —> x / 2 + 即: x = x / 2; —> x = x >> 1; + mid = (left + right) / 2; ---> mid = (left + right) >> 1; +3. X = X & (X-1) 清零最低位的 1 +4. X & -X => 得到最低位的 1 +5. X & ~X => 0 + +## 排序算法 +### 初级排序 - O(n^2) +1. 选择排序(Selection Sort) + 每次找最小值,然后放到待排序数组的起始位置。 +2. 插入排序(Insertion Sort) + 从前到后逐步构建有序序列;对于未排序数据,在已排序序列中从后 向前扫描,找到相应位置并插入。 +3. 冒泡排序(Bubble Sort) + 嵌套循环,每次查看相邻的元素如果逆序,则交换。 + +### 高级排序 - O(nlogn) +1. 快速排序(Quick Sort) + 数组取标杆 pivot,将小元素放 pivot左边,大元素放右侧,然后依次 对右边和右边的子数组继续快排;以达到整个序列有序。 + ```java + public static void quickSort(int[] array, int begin, int end) { + if (end <= begin) return; + int pivot = partition(array, begin, end); + quickSort(array, begin, pivot - 1); + quickSort(array, pivot + 1, end); + } + static int partition(int[] a, int begin, int end) { + // pivot: 标杆位置,counter: 小于 pivot的元素的个数 + int pivot = end, counter = begin; + for (int i = begin; i < end; i++) { + if (a[i] < a[pivot]) { + int temp = a[counter]; a[counter] = a[i]; a[i] = temp; counter++; + } + } + int temp = a[pivot]; a[pivot] = a[counter]; a[counter] = temp; + return counter; + } + ``` + +2. 归并排序(Merge Sort)— 分治 + 把长度为n的输入序列分成两个长度为n/2的子序列; + 对这两个子序列分别采用归并排序; + 将两个排序好的子序列合并成一个最终的排序序列。 + ```java + public static void mergeSort(int[] array, int left, int right) { + if (right <= left) return; + int mid = (left + right) >> 1; // (left + right) / 2 + + mergeSort(array, left, mid); + mergeSort(array, mid + 1, right); + merge(array, left, mid, right); + } + public static void merge(int[] arr, int left, int mid, int right) { + int[] temp = new int[right - left + 1]; // 中间数组 + int i = left, j = mid + 1, k = 0; + + while (i <= mid && j <= right) { + temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++]; + } + + while (i <= mid) temp[k++] = arr[i++]; + while (j <= right) temp[k++] = arr[j++]; + + for (int p = 0; p < temp.length; p++) { + arr[left + p] = temp[p]; + } + // 也可以用 System.arraycopy(a, start1, b, start2, length) + } + ``` + +3. 堆排序(Heap Sort) — 堆插入 O(logN),取最大/小值 O(1) + 数组元素依次建立小顶堆 + 依次取堆顶元素,并删除 + ```java + static void heapify(int[] array, int length, int i) { + int left = 2 * i + 1, right = 2 * i + 2; + int largest = i; + + if (left < length && array[left] > array[largest]) { + largest = left; + } + if (right < length && array[right] > array[largest]) { + largest = right; + } + + if (largest != i) { + int temp = array[i]; array[i] = array[largest]; array[largest] = temp; + heapify(array, length, largest); + } + } + + public static void heapSort(int[] array) { + if (array.length == 0) return; + + int length = array.length; + for (int i = length / 2-1; i >= 0; i-) + heapify(array, length, i); + + for (int i = length - 1; i >= 0; i--) { + int temp = array[0]; array[0] = array[i]; array[i] = temp; + heapify(array, i, 0); + } + } + ``` + + +### 特殊排序 +1. 计数排序(Counting Sort) + 计数排序要求输入的数据必须是有确定范围的整数。将输入的数据值转化为键存 储在额外开辟的数组空间中;然后依次把计数大于 1 的填充回原数组 +2. 桶排序(Bucket Sort) + 桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有 限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式 继续使用桶排序进行排)。 +3. 基数排序(Radix Sort) + 基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类 推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按 高优先级排序。 + diff --git "a/Week 07/id_158/190.\351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.cs" "b/Week 07/id_158/190.\351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.cs" new file mode 100644 index 000000000..14ef04b69 --- /dev/null +++ "b/Week 07/id_158/190.\351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.cs" @@ -0,0 +1,23 @@ +/* + * @lc app=leetcode.cn id=190 lang=csharp + * + * [190] 颠倒二进制位 + */ + +// @lc code=start +public class Solution +{ + public uint reverseBits(uint n) + { + uint res = 0; + for (int i = 0; i < 32; i++) + { + res <<= 1; + res += n & 1; + n >>= 1; + } + return res; + } +} +// @lc code=end + diff --git "a/Week 07/id_158/493.\347\277\273\350\275\254\345\257\271.cs" "b/Week 07/id_158/493.\347\277\273\350\275\254\345\257\271.cs" new file mode 100644 index 000000000..e560d4dec --- /dev/null +++ "b/Week 07/id_158/493.\347\277\273\350\275\254\345\257\271.cs" @@ -0,0 +1,33 @@ +/* + * @lc app=leetcode.cn id=493 lang=csharp + * + * [493] 翻转对 + */ + +// @lc code=start +using System; + +public class Solution { + public int ReversePairs(int[] nums) { + return mergeSort(nums,0,nums.Length-1); + } + + private int mergeSort(int[] nums,int left,int right){ + if(left>=right){ + return 0; + } + int mid = left + (right-left)>>2; + int cnt = mergeSort(nums,left,mid) + mergeSort(nums,mid+1,right); + for (int i = left,j=mid+1; i <= mid; i++) + { + while(j<=right && nums[i]/2.0 > nums[j]){ + j++; + } + cnt += j-(mid + 1) ; + } + Array.Sort(nums,left,right+1); + return cnt; + } +} +// @lc code=end + diff --git "a/Week 07/id_158/52.n\347\232\207\345\220\216-ii.cs" "b/Week 07/id_158/52.n\347\232\207\345\220\216-ii.cs" new file mode 100644 index 000000000..867e34c54 --- /dev/null +++ "b/Week 07/id_158/52.n\347\232\207\345\220\216-ii.cs" @@ -0,0 +1,37 @@ +/* + * @lc app=leetcode.cn id=52 lang=csharp + * + * [52] N皇后 II + */ + +// @lc code=start +public class Solution +{ + private int size; + private int count; + + private void Solve(int row, int ld, int rd) + { + if (row == size) + { + count++; + return; + } + int pos = size & (~(row | ld | rd)); + while (pos != 0) + { + int p = pos & (-pos); + pos -= p; // pos &= pos - 1; + Solve(row | p, (ld | p) << 1, (rd | p) >> 1); + } + } + public int TotalNQueens(int n) + { + count = 0; + size = (1 << n) - 1; + Solve(0, 0, 0); + return count; + } +} +// @lc code=end + diff --git "a/Week 07/id_158/56.\345\220\210\345\271\266\345\214\272\351\227\264.cs" "b/Week 07/id_158/56.\345\220\210\345\271\266\345\214\272\351\227\264.cs" new file mode 100644 index 000000000..a691c1dbe --- /dev/null +++ "b/Week 07/id_158/56.\345\220\210\345\271\266\345\214\272\351\227\264.cs" @@ -0,0 +1,45 @@ +/* + * @lc app=leetcode.cn id=56 lang=csharp + * + * [56] 合并区间 + */ + +// @lc code=start +using System; +using System.Collections.Generic; + +public class Solution +{ + public int[][] Merge(int[][] intervals) + { + Array.Sort(intervals, (a, b) => a[0] - b[0]); + Stack stack = new Stack(); + + foreach (int[] a in intervals) + { + + if (stack.Count == 0) stack.Push(a); + else + { + + int[] arr = stack.Pop(); + if (arr[1] >= a[0]) + { + int[] combine = { arr[0], Math.Max(arr[1], a[1]) }; + stack.Push(combine); + } + else + { + stack.Push(arr); + stack.Push(a); + } + + } + + } + + return stack.ToArray(); + } +} +// @lc code=end + diff --git a/Week 07/id_173/LeetCode_146_173.cpp b/Week 07/id_173/LeetCode_146_173.cpp new file mode 100644 index 000000000..fa568996d --- /dev/null +++ b/Week 07/id_173/LeetCode_146_173.cpp @@ -0,0 +1,85 @@ +/* + * 146. LRU缓存机制 + */ + +class LRUCache { +private: + struct DLNode { + int key; + int value; + DLNode *prev; + DLNode *next; + DLNode() : key(0), value(0), prev(nullptr), next(nullptr) {} + }; + + DLNode *head; + DLNode *tail; + + unordered_map cache; + int size; + int capacity; + + void addHead(DLNode* node) { + node->prev = head; + node->next = head->next; + head->next->prev = node; + head->next = node; + } + + void removeNode(DLNode* node) { + DLNode* prev = node->prev; + DLNode* next = node->next; + prev->next = next; + next->prev = prev; + } + + DLNode* removeTail() { + DLNode* node = tail->prev; + removeNode(node); + return node; + } + + void moveToHead(DLNode* node) { + removeNode(node); + addHead(node); + } + +public: + LRUCache(int capacity) { + this->size = 0; + this->capacity = capacity; + head = new DLNode(); + tail = new DLNode(); + head->next = tail; + tail->prev = head; + } + + int get(int key) { + if(cache.count(key) == 0) + return -1; + DLNode* node = cache[key]; + moveToHead(node); + return node->value; + } + + void put(int key, int value) { + if(cache.count(key) == 0) { + DLNode* newNode = new DLNode(); + newNode->key = key; + newNode->value = value; + addHead(newNode); + cache.insert({key, newNode}); + ++size; + if(size > capacity) { + DLNode* node = removeTail(); + cache.erase(node->key); + --size; + } + } + else { + DLNode* node = cache[key]; + node->value = value; + moveToHead(node); + } + } +}; \ No newline at end of file diff --git a/Week 07/id_173/LeetCode_190_173.cpp b/Week 07/id_173/LeetCode_190_173.cpp new file mode 100644 index 000000000..1921f4c98 --- /dev/null +++ b/Week 07/id_173/LeetCode_190_173.cpp @@ -0,0 +1,17 @@ +/* + * 190. 颠倒二进制位 + */ + +class Solution { +public: + uint32_t reverseBits(uint32_t n) { + uint32_t res = 0; + + for(int i=0; i<32; ++i) { + res <<= 1; + res |= (n>>i)&1; + } + + return res; + } +}; \ No newline at end of file diff --git a/Week 07/id_173/LeetCode_191_173.cpp b/Week 07/id_173/LeetCode_191_173.cpp new file mode 100644 index 000000000..4bb622f83 --- /dev/null +++ b/Week 07/id_173/LeetCode_191_173.cpp @@ -0,0 +1,17 @@ +/* + * 191. 位1的个数 + */ + +class Solution { +public: + int hammingWeight(uint32_t n) { + int count = 0; + + while(n) { + count++; + n &= n-1; + } + + return count; + } +}; \ No newline at end of file diff --git a/Week 07/id_173/LeetCode_231_173.cpp b/Week 07/id_173/LeetCode_231_173.cpp new file mode 100644 index 000000000..1f752b834 --- /dev/null +++ b/Week 07/id_173/LeetCode_231_173.cpp @@ -0,0 +1,10 @@ +/* + * 231. 2的幂 + */ + +class Solution { +public: + bool isPowerOfTwo(int n) { + return (n>0)&&(n&(n-1))==0; + } +}; \ No newline at end of file diff --git a/Week 07/id_173/LeetCode_338_173.cpp b/Week 07/id_173/LeetCode_338_173.cpp new file mode 100644 index 000000000..c6291edab --- /dev/null +++ b/Week 07/id_173/LeetCode_338_173.cpp @@ -0,0 +1,16 @@ +/* + * 338. 比特位计数 + */ + +class Solution { +public: + vector countBits(int num) { + vector dp(num+1, 0); + + for(int n=1; n<=num; ++n) { + dp[n] = dp[n>>1] + (n&1); + } + + return dp; + } +}; \ No newline at end of file diff --git a/Week 07/id_173/NOTE.md b/Week 07/id_173/NOTE.md index a6321d6e2..3bce5323d 100644 --- a/Week 07/id_173/NOTE.md +++ b/Week 07/id_173/NOTE.md @@ -1,4 +1,25 @@ -# NOTE +## 一、常用位运算技巧 +  1. 将 x 最右边的 n 位清零:`x & (~0 << n)`
+  2. 获取 x 第 n 位的值(0或1):`(x >> n) & 1`
+  3. 获取 x 的第 n 位的幂值:`x & (1 << (n-1))`
+  4. 仅将 x 第 n 位置为1:`x | (1 << n)`
+  5. 仅将 x 第 n 位置为0:`x & (~(1 << n))`
+  6. 将 x 最高位至第 n 位(含第 n 位)清零:`x & ((1 << n) - 1)`
+  7. 将 x 第 n 位至第 0 位(含第 0 位)清零:`x & (~((1 << (n + 1)) - 1))`
+  8. 清零 x 最低位的1:`x & (x-1)`
+  9. 得到 x 最低位的1:`x & -x`
- +## 二、布隆过滤器(Bloom Filter) +#### 1. 概述 +  一个很长的二进制向量和一系列随机映射函数,它可以用于检索一个元素是否在一个集合中。
+#### 2. 优缺点 +- 优点:空间效率高、查询时间快
+- 缺点:有一定误识别率,且删除困难
+## 三、排序算法 +#### 1. 比较类排序 +  通过比较来决定元素间的相对次序,由于其时间复杂度不能突破`O(nlogn)`,因此也称为非线性时间比较类排序。
+#### 2. 非比较类排序 +  不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。
+#### 3. 总结 +  [八大排序算法(图解+代码)](https://blog.csdn.net/X_Perseverance/article/details/80489804)
diff --git a/Week 07/id_183/Leetcode_1122_183.cpp b/Week 07/id_183/Leetcode_1122_183.cpp new file mode 100644 index 000000000..311cfbcc6 --- /dev/null +++ b/Week 07/id_183/Leetcode_1122_183.cpp @@ -0,0 +1,44 @@ +/* + * @lc app=leetcode id=1122 lang=cpp + * + * [1122] Relative Sort Array + */ + +// @lc code=start +class Solution { +public: + vector relativeSortArray(vector& arr1, vector& arr2) { + + int pool[1001] = {0}; + vector output; + + for(int i=0; i0){ + + output.push_back(i); + } + } + + + return output; + } +}; + +// @lc code=end + diff --git a/Week 07/id_183/Leetcode_56_183.cpp b/Week 07/id_183/Leetcode_56_183.cpp new file mode 100644 index 000000000..125b7cb83 --- /dev/null +++ b/Week 07/id_183/Leetcode_56_183.cpp @@ -0,0 +1,47 @@ +/* + * @lc app=leetcode id=56 lang=cpp + * + * [56] Merge Intervals + */ + +// @lc code=start +class Solution { +public: + static bool cmp(const vector&a,const vector&b ) + { + if(a[0]==b[0]) + return a[1]>b[1]; + return a[0]> merge(vector>& intervals) { + if(intervals.empty()) return intervals; + vector> res; + int count=0; + sort(intervals.begin(),intervals.end(),cmp); + vector temp; + temp.push_back(intervals[0][0]); + temp.push_back(intervals[0][1]); + res.push_back(temp); + for(int i=1;i=intervals[i][0]) + { + if(res[count][1]<=intervals[i][1]) + { + res[count][1]=intervals[i][1]; + } + } + else + { + count++; + temp[0]=intervals[i][0]; + temp[1]=intervals[i][1]; + res.push_back(temp); + } + } + return res; + } +}; + +// @lc code=end + diff --git a/Week 07/id_188/leetCode-146-188.go b/Week 07/id_188/leetCode-146-188.go new file mode 100644 index 000000000..d5dac20f7 --- /dev/null +++ b/Week 07/id_188/leetCode-146-188.go @@ -0,0 +1,97 @@ +//运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。 +// +// 获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 +//写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。 +// +// 进阶: +// +// 你是否可以在 O(1) 时间复杂度内完成这两种操作? +// +// 示例: +// +// LRUCache cache = new LRUCache( 2 /* 缓存容量 */ ); +// +//cache.put(1, 1); +//cache.put(2, 2); +//cache.get(1); // 返回 1 +//cache.put(3, 3); // 该操作会使得密钥 2 作废 +//cache.get(2); // 返回 -1 (未找到) +//cache.put(4, 4); // 该操作会使得密钥 1 作废 +//cache.get(1); // 返回 -1 (未找到) +//cache.get(3); // 返回 3 +//cache.get(4); // 返回 4 +// +// Related Topics 设计 +//[146]LRU缓存机制 +//leetcode submit region begin(Prohibit modification and deletion) +type Node struct { + key, value int + prev, next *Node +} + +type LRUCache struct { + capacity int + cache map[int]*Node + head, tail *Node +} + +func Constructor(capacity int) LRUCache { + head, tail := &Node{}, &Node{} + head.next = tail + tail.prev = head + return LRUCache{ + capacity: capacity, + cache: make(map[int]*Node), + head: head, + tail: tail, + } +} + +func (this *LRUCache) Get(key int) int { + node, exist := this.cache[key] + if !exist { + return -1 + } + this.remove(node) + this.insert(node) + return node.value +} + +func (this *LRUCache) Put(key int, value int) { + node, exist := this.cache[key] + if exist { + this.remove(node) + this.insert(&Node{key: key, value: value}) + return + } + if this.capacity == len(this.cache) { + this.remove(this.head.next) + } + node = &Node{ + key: key, + value: value, + } + this.insert(node) +} + +func (this *LRUCache) insert(node *Node) { + node.next = this.tail + node.prev = this.tail.prev + this.tail.prev.next = node + this.tail.prev = node + this.cache[node.key] = node +} + +func (this *LRUCache) remove(node *Node) { + node.prev.next = node.next + node.next.prev = node.prev + delete(this.cache, node.key) +} + +/** + * Your LRUCache object will be instantiated and called as such: + * obj := Constructor(capacity); + * param_1 := obj.Get(key); + * obj.Put(key,value); + */ +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 07/id_188/leetCode-190-188.go b/Week 07/id_188/leetCode-190-188.go new file mode 100644 index 000000000..15691d184 --- /dev/null +++ b/Week 07/id_188/leetCode-190-188.go @@ -0,0 +1,47 @@ +//颠倒给定的 32 位无符号整数的二进制位。 +// +// +// +// 示例 1: +// +// 输入: 00000010100101000001111010011100 +//输出: 00111001011110000010100101000000 +//解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596, +// 因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。 +// +// 示例 2: +// +// 输入:11111111111111111111111111111101 +//输出:10111111111111111111111111111111 +//解释:输入的二进制串 11111111111111111111111111111101 表示无符号整数 4294967293, +//  因此返回 3221225471 其二进制表示形式为 10101111110010110010011101101001。 +// +// +// +// 提示: +// +// +// 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 +// 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 2 中,输入表示有符号整数 -3,输出表示有符号整数 -1073741825。 +// +// +// +// +// 进阶: +//如果多次调用这个函数,你将如何优化你的算法? +// Related Topics 位运算 + +//leetcode submit region begin(Prohibit modification and deletion) +func reverseBits(num uint32) uint32 { + if num < 1 { + return 0 + } + var ret uint32 = 0 + for index := 0; index < 32; index++ { + ret = (ret << 1) + (num & 1) + num >>= 1 + } + return ret +} + +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 07/id_188/leetCode-231-188.go b/Week 07/id_188/leetCode-231-188.go new file mode 100644 index 000000000..5b48226d5 --- /dev/null +++ b/Week 07/id_188/leetCode-231-188.go @@ -0,0 +1,26 @@ +//给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 +// +// 示例 1: +// +// 输入: 1 +//输出: true +//解释: 20 = 1 +// +// 示例 2: +// +// 输入: 16 +//输出: true +//解释: 24 = 16 +// +// 示例 3: +// +// 输入: 218 +//输出: false +// Related Topics 位运算 数学 + +//leetcode submit region begin(Prohibit modification and deletion) +func isPowerOfTwo(n int) bool { + return n > 0 && n&(n-1) == 0 +} + +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 07/id_188/leetCoe-191-188.go b/Week 07/id_188/leetCoe-191-188.go new file mode 100644 index 000000000..1e8c8724a --- /dev/null +++ b/Week 07/id_188/leetCoe-191-188.go @@ -0,0 +1,50 @@ +//编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。 +// +// +// +// 示例 1: +// +// 输入:00000000000000000000000000001011 +//输出:3 +//解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。 +// +// +// 示例 2: +// +// 输入:00000000000000000000000010000000 +//输出:1 +//解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。 +// +// +// 示例 3: +// +// 输入:11111111111111111111111111111101 +//输出:31 +//解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。 +// +// +// +// 提示: +// +// +// 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 +// 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。 +// +// +// +// +// 进阶: +//如果多次调用这个函数,你将如何优化你的算法? +// Related Topics 位运算 + +//leetcode submit region begin(Prohibit modification and deletion) +func hammingWeight(num uint32) int { + count := 0 + for num > 0 { + num &= num - 1 + count++ + } + return count +} + +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 07/id_198/LeetCode_119_198.go b/Week 07/id_198/LeetCode_119_198.go new file mode 100644 index 000000000..e7f514d01 --- /dev/null +++ b/Week 07/id_198/LeetCode_119_198.go @@ -0,0 +1,11 @@ +package leetcode + +func hammingWeight(num uint32) int { + + count := 0 + for ; num != 0; num = num & (num - 1) { + + count++ + } + return count +} diff --git a/Week 07/id_198/LeetCode_146_198.go b/Week 07/id_198/LeetCode_146_198.go new file mode 100644 index 000000000..051ce662f --- /dev/null +++ b/Week 07/id_198/LeetCode_146_198.go @@ -0,0 +1,23 @@ +package leetcode + +type LRUCache struct { +} + +func Constructor(capacity int) LRUCache { + +} + +func (this *LRUCache) Get(key int) int { + +} + +func (this *LRUCache) Put(key int, value int) { + +} + +/** + * Your LRUCache object will be instantiated and called as such: + * obj := Constructor(capacity); + * param_1 := obj.Get(key); + * obj.Put(key,value); + */ diff --git a/Week 07/id_198/LeetCode_190_198.go b/Week 07/id_198/LeetCode_190_198.go new file mode 100644 index 000000000..8899e9d33 --- /dev/null +++ b/Week 07/id_198/LeetCode_190_198.go @@ -0,0 +1,13 @@ +package leetcode + +func reverseBits(num uint32) uint32 { + + var rt uint32 + rt = 0 + for i := 0; i < 32; i++ { + //每次取最后一位追加到结果值,然后对结果进行移位 + rt = (rt << 1) + (num & 1) + num = num >> 1 + } + return rt +} diff --git a/Week 07/id_198/LeetCode_231_198.go b/Week 07/id_198/LeetCode_231_198.go new file mode 100644 index 000000000..bdbceb67f --- /dev/null +++ b/Week 07/id_198/LeetCode_231_198.go @@ -0,0 +1,7 @@ +package leetcode + + +func isPowerOfTwo(n int) bool { + + return n > 0 && n & (n - 1) == 0 +} diff --git a/Week 07/id_198/LeetCode_338_198.go b/Week 07/id_198/LeetCode_338_198.go new file mode 100644 index 000000000..6b3428faf --- /dev/null +++ b/Week 07/id_198/LeetCode_338_198.go @@ -0,0 +1,13 @@ +package leetcode + +//利用奇偶特性 +func countBits(num int) []int { + + rt := make([]int, num+1) + rt[0] = 0 + for i := 1; i <= num; i++ { + + rt[i] = rt[i&(i-1)] + 1 + } + return rt +} diff --git a/Week 07/id_198/LeetCode_493_198.go b/Week 07/id_198/LeetCode_493_198.go new file mode 100644 index 000000000..f2f489e94 --- /dev/null +++ b/Week 07/id_198/LeetCode_493_198.go @@ -0,0 +1,48 @@ +package leetcode + +import "fmt" + +func reversePairs(nums []int) int { + + if nums == nil || len(nums) == 0 { + + return 0 + } + + return mergeSort(nums, 0, len(nums)-1) +} + +func mergeSort(nums []int, l, r int) int { + + fmt.Printf("l=%d;r=%d\n", l, r) + if l >= r { + return 0 + } + mid := l + (r-l)>>1 + count := mergeSort(nums, l, mid) + mergeSort(nums, mid+1, r) + cache := make([]int, r-l+1) + i := l + t := l + c := 0 + for j := mid + 1; j <= r; j++ { + + for i <= mid && int64(nums[i]) <= 2*int64(nums[j]) { + i++ + } + for t <= mid && nums[t] < nums[j] { + cache[c] = nums[t] + c++ + t++ + } + cache[c] = nums[j] + count += (mid - i + 1) + c++ + } + for t <= mid { + cache[c] = nums[t] + c++ + t++ + } + copy(nums, cache) + return count +} diff --git a/Week 07/id_198/LeetCode_493_198_test.go b/Week 07/id_198/LeetCode_493_198_test.go new file mode 100644 index 000000000..28513985f --- /dev/null +++ b/Week 07/id_198/LeetCode_493_198_test.go @@ -0,0 +1,24 @@ +package leetcode + +import "testing" + +func Test_reversePairs(t *testing.T) { + type args struct { + nums []int + } + tests := []struct { + name string + args args + want int + }{ + {name: "test", args: args{nums: []int{1, 3, 2, 3, 1}}, want: 2}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := reversePairs(tt.args.nums); got != tt.want { + t.Errorf("reversePairs() = %v, want %v", got, tt.want) + } + t.Logf("%v\n", tt.args.nums) + }) + } +} diff --git a/Week 07/id_198/NOTE.md b/Week 07/id_198/NOTE.md index a6321d6e2..bff3b464d 100644 --- a/Week 07/id_198/NOTE.md +++ b/Week 07/id_198/NOTE.md @@ -1,4 +1,12 @@ # NOTE - +## 扩展学习 + +- [Learn Binary Numbers & Math](http://www.binarymath.info/) +- [Understanding the Meltdown exploit – in my own simple words](https://www.sqlpassion.at/archive/2018/01/06/understanding-the-meltdown-exploit-in-my-own-simple-words/) +- [Cache replacement policies](https://en.wikipedia.org/wiki/Cache_replacement_policies) +- [Cuckoo filter](https://en.wikipedia.org/wiki/Cuckoo_filter#:~:targetText=A%20cuckoo%20filter%20is%20similar,answers%20to%20set%2Dmembership%20queries%3A&targetText=In%20contrast%2C%20Bloom%20filters%20can,false%20positive%20rate%20before%20expansion.) +- [十大经典排序算法(动图演示)](https://www.cnblogs.com/onepixel/p/7674659.html) +- [【简单明了】9种经典排序算法可视化动画](https://www.bilibili.com/video/av25136272) +- [6分钟看完15种排序算法动画展示](https://www.bilibili.com/video/av63851336) diff --git a/Week 07/id_198/quick_sort.go b/Week 07/id_198/quick_sort.go new file mode 100644 index 000000000..2742814da --- /dev/null +++ b/Week 07/id_198/quick_sort.go @@ -0,0 +1,28 @@ +package leetcode + +func QuickSort(array []int, begin, end int) { + + if end <= begin { + + return + } + + pivot := partition(array, begin, end) + QuickSort(array, begin, pivot-1) + QuickSort(array, pivot+1, end) +} + +func partition(array []int, begin, end int) int { + + counter := begin + pivot := end + for i := begin; i < end; i++ { + + if array[i] < array[pivot] { + array[counter], array[i] = array[i], array[counter] + counter++ + } + } + array[counter], array[pivot] = array[pivot], array[counter] + return counter +} diff --git a/Week 07/id_198/quick_sort_test.go b/Week 07/id_198/quick_sort_test.go new file mode 100644 index 000000000..a709dc411 --- /dev/null +++ b/Week 07/id_198/quick_sort_test.go @@ -0,0 +1,23 @@ +package leetcode + +import "testing" + +func TestQuickSort(t *testing.T) { + type args struct { + array []int + begin int + end int + } + tests := []struct { + name string + args args + }{ + {name: "test", args: args{array: []int{10, 28, 29, 12, 122, 3, 1, 2}, begin: 0, end: 7}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + QuickSort(tt.args.array, tt.args.begin, tt.args.end) + t.Logf("%v\n", tt.args.array) + }) + } +} diff --git a/Week 07/id_203/LeetCode_146_203.go b/Week 07/id_203/LeetCode_146_203.go new file mode 100644 index 000000000..073bacd8d --- /dev/null +++ b/Week 07/id_203/LeetCode_146_203.go @@ -0,0 +1,111 @@ +package week07 + +/** +运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。 + +获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 +写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。 + +进阶: + +你是否可以在 O(1) 时间复杂度内完成这两种操作? + +示例: + +LRUCache cache = new LRUCache( 2 ); + +cache.put(1, 1); +cache.put(2, 2); +cache.get(1); // 返回 1 +cache.put(3, 3); // 该操作会使得密钥 2 作废 +cache.get(2); // 返回 -1 (未找到) +cache.put(4, 4); // 该操作会使得密钥 1 作废 +cache.get(1); // 返回 -1 (未找到) +cache.get(3); // 返回 3 +cache.get(4); // 返回 4 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/lru-cache +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +type LRUCache struct { + head *node + tail *node + store map[int]*node + cap int +} + +type node struct { + prev *node + next *node + key int + val int +} + + +func Constructor(capacity int) LRUCache { + return LRUCache{cap:capacity, store:make(map[int]*node)} +} + +func (c *LRUCache) removeFromChain(n *node) { + if n == c.head { + c.head = n.next + } + + if n == c.tail { + c.tail = n.prev + } + + if n.next != nil { + n.next.prev = n.prev + } + + if n.prev != nil { + n.prev.next = n.next + } +} + +func (c *LRUCache) addToChain(n *node) { + n.prev = nil + n.next = c.head + if n.next != nil { + n.next.prev = n + } + c.head = n + if c.tail == nil { + c.tail = n + } +} + +func (c *LRUCache) Get(key int) int { + n, ok := c.store[key] + if !ok { + return -1 + } + + c.removeFromChain(n) + c.addToChain(n) + return n.val +} + + +func (c *LRUCache) Put(key int, value int) { + n, ok := c.store[key] + if !ok { + n = &node{val:value, key:key} + c.store[key] = n + } else { + n.val = value + c.removeFromChain(n) + } + + c.addToChain(n) + if len(c.store) > c.cap { + n = c.tail + if n != nil { + c.removeFromChain(n) + delete(c.store, n.key) + } + } +} \ No newline at end of file diff --git a/Week 07/id_203/LeetCode_231_203.go b/Week 07/id_203/LeetCode_231_203.go new file mode 100644 index 000000000..abfc37a3d --- /dev/null +++ b/Week 07/id_203/LeetCode_231_203.go @@ -0,0 +1,36 @@ +package week07 + +/** +给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 + +示例 1: + +输入: 1 +输出: true +解释: 20 = 1 +示例 2: + +输入: 16 +输出: true +解释: 24 = 16 +示例 3: + +输入: 218 +输出: false + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/power-of-two +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +func isPowerOfTwo(n int) bool { + if n<=0 { + return false + } + + if n&(n-1) == 0{ + return true + } + + return false +} diff --git a/Week 07/id_213/CountBits.java b/Week 07/id_213/CountBits.java new file mode 100644 index 000000000..fadb46adc --- /dev/null +++ b/Week 07/id_213/CountBits.java @@ -0,0 +1,45 @@ +class Solution { +//暴力求解法。通过遍历获取每个数的位1个数存入数组中。 + public int[] countBits(int num) { + int[] ans = new int[num + 1]; + for (int i = 0; i <= num; i++) + ans[i] = popcount(i); + return ans; + } + private int popcount(int x) { + int count; + for (count = 0; x != 0; count++) + x = x & (x - 1); + return count; + } +//动态规划 + 最高有效位 +//需要找到状态转移函数 f(x + b) = f(x) + 1; b = 2^m > x; + public int[] countBits2(int num) { + int[] ans = new int[num + 1]; + int i = 0; int b = 1; + while (b <= num) { + while (i + b <= num && i < b) { + ans[i + b] = ans[i] + 1; + ++i; + } + i = 0; + b <<= 1; + } + return ans; + } +//DP + 最低有效位 +//状态转移函数 有f(x) = f(x/2) + (x mod 2); + public int[] countBits3(int num) { + int[] ans = new int[n + 1]; + for (int i = 1; i <= num; ++i) + ans[i] = ans[i >> 1] + (i & 1); + retrun ans; + } +//DP + 最后设置位 + public int[] countBits4(int num) { + int[] ans = new int[num + 1]; + for (int i = 1; i <= num; ++i) + ans[i] = ans[i & (i - 1)] + 1; + return ans; + } +} \ No newline at end of file diff --git a/Week 07/id_213/HammingWeight.java b/Week 07/id_213/HammingWeight.java new file mode 100644 index 000000000..c5f757b2a --- /dev/null +++ b/Week 07/id_213/HammingWeight.java @@ -0,0 +1,12 @@ +class Solution { +//主要思路 n & (n - 1)可以将最后一位1化为0; + public int hammingWeight(int n) { + int sum = 0; + while (n != 0) { + sum++; + n& = (n - 1); + } + return sum; + } + +} \ No newline at end of file diff --git a/Week 07/id_213/IsPowerOfTwo.java b/Week 07/id_213/IsPowerOfTwo.java new file mode 100644 index 000000000..2c2305959 --- /dev/null +++ b/Week 07/id_213/IsPowerOfTwo.java @@ -0,0 +1,6 @@ +class Solution { + public boolean isPowerOfTwo(int n) { + return n > 0 && (n & (n - 1)) == 0; + } +} + diff --git a/Week 07/id_218/LeetCode_190_218.java b/Week 07/id_218/LeetCode_190_218.java new file mode 100644 index 000000000..c35065210 --- /dev/null +++ b/Week 07/id_218/LeetCode_190_218.java @@ -0,0 +1,18 @@ +package leetcode.week7; + +/** + * https://leetcode-cn.com/problems/reverse-bits/ + * + * @author eason.feng at 2019/12/1/0001 11:42 + **/ +public class LeetCode_190_218 { + public int reverseBits(int n) { + int res = 0b0; + for (int i = 0; i < 32; i++) { + res = res << 1; + res += (n & 1); + n >>>= 1; + } + return res; + } +} diff --git a/Week 07/id_218/LeetCode_191_218.java b/Week 07/id_218/LeetCode_191_218.java new file mode 100644 index 000000000..f002fff80 --- /dev/null +++ b/Week 07/id_218/LeetCode_191_218.java @@ -0,0 +1,22 @@ +package leetcode.week7; + +/** + * https://leetcode-cn.com/problems/number-of-1-bits/ + * + * @author eason.feng at 2019/12/1/0001 11:23 + **/ +public class LeetCode_191_218 { + + public int hammingWeight(int n) { + int count = 0; + int mask = 1; + for (int i = 0; i < 32; i++) { + if ((n & mask) == 1) { + count++; + } + mask <<= 1; + } + return count; + } + +} diff --git a/Week 07/id_218/LeetCode_191_218_V2.java b/Week 07/id_218/LeetCode_191_218_V2.java new file mode 100644 index 000000000..1004eb1ff --- /dev/null +++ b/Week 07/id_218/LeetCode_191_218_V2.java @@ -0,0 +1,19 @@ +package leetcode.week7; + +/** + * https://leetcode-cn.com/problems/number-of-1-bits/ + * + * @author eason.feng at 2019/12/1/0001 11:36 + **/ +public class LeetCode_191_218_V2 { + + public int hammingWeight(int n) { + int count = 0; + while (n != 0) { + n &= (n - 1); + count++; + } + return count; + } + +} diff --git a/Week 07/id_218/LeetCode_231_218.java b/Week 07/id_218/LeetCode_231_218.java new file mode 100644 index 000000000..bc2d18dfc --- /dev/null +++ b/Week 07/id_218/LeetCode_231_218.java @@ -0,0 +1,19 @@ +package leetcode.week7; + +/** + * https://leetcode-cn.com/problems/power-of-two/submissions/ + * 2进制表示法中只有1个1 + * @author eason.feng at 2019/12/1/0001 11:16 + **/ +public class LeetCode_231_218 { + + public boolean isPowerOfTwo(int n) { + return (n > 0) && ((n & (n - 1)) == 0); + } + + public static void main(String[] args) { + LeetCode_231_218 leetCode_231_218 = new LeetCode_231_218(); + System.out.println(leetCode_231_218.isPowerOfTwo(15)); + } + +} diff --git a/Week 07/id_218/LeetCode_338_218.java b/Week 07/id_218/LeetCode_338_218.java new file mode 100644 index 000000000..99085913e --- /dev/null +++ b/Week 07/id_218/LeetCode_338_218.java @@ -0,0 +1,18 @@ +package leetcode.week7; + +/** + * https://leetcode-cn.com/problems/counting-bits/ + * https://leetcode-cn.com/problems/counting-bits/solution/bi-te-wei-ji-shu-by-leetcode/ + * + * @author eason.feng at 2019/12/1/0001 15:19 + **/ +public class LeetCode_338_218 { + + public int[] countBits(int num) { + int[] res = new int[num + 1]; + for (int i = 0; i <= num; i++) { + res[i] = res[i >> 1] + (i & 1); + } + return res; + } +} diff --git a/Week 07/id_218/LeetCode_493_218.java b/Week 07/id_218/LeetCode_493_218.java new file mode 100644 index 000000000..01341a8b7 --- /dev/null +++ b/Week 07/id_218/LeetCode_493_218.java @@ -0,0 +1,31 @@ +package leetcode.week7; + +import java.util.Arrays; + +/** + * https://leetcode-cn.com/problems/reverse-pairs/ + * + * @author eason.feng at 2019/12/1/0001 18:13 + **/ +public class LeetCode_493_218 { + + public int reversePairs(int[] nums) { + return mergeSort(nums, 0, nums.length - 1); + } + + private int mergeSort(int[] nums, int s, int e) { + if (s >= e) { + return 0; + } + int mid = (s + e) >>> 1; + int cnt = mergeSort(nums, s, mid) + mergeSort(nums, mid + 1, e); + for (int i = s, j = mid + 1; i <= mid; i++) { + while (j <= e && nums[i] / 2.0 > nums[j]) { + j++; + } + cnt += j - (mid + 1); + } + Arrays.sort(nums, s, e + 1); + return cnt; + } +} diff --git a/Week 07/id_218/LeetCode_51_218.java b/Week 07/id_218/LeetCode_51_218.java new file mode 100644 index 000000000..ece6c32d6 --- /dev/null +++ b/Week 07/id_218/LeetCode_51_218.java @@ -0,0 +1,36 @@ +package leetcode.week7; + +import java.util.List; + +/** + * https://leetcode-cn.com/problems/n-queens-ii/description/ + * + * @author eason.feng at 2019/12/1/0001 15:04 + **/ +public class LeetCode_51_218 { + + private int size; + private int count; + + public int totalNQueens(int n) { + count = 0; + size = (1 << n) - 1; + solve(0, 0, 0); + return count; + } + + private void solve(int row, int ld, int rd) { + if (row == size) { + count++; + return; + } + int pos = size & (~ (row | ld | rd)); + while (pos != 0) { + int p = pos & (-pos); + pos -= p; + solve(row | p, (ld | p) << 1, (rd | p) >> 1); + } + } + + +} diff --git a/Week 07/id_218/LeetCode_MergeSort.java b/Week 07/id_218/LeetCode_MergeSort.java new file mode 100644 index 000000000..395b5044b --- /dev/null +++ b/Week 07/id_218/LeetCode_MergeSort.java @@ -0,0 +1,36 @@ +package leetcode.week7; + +/** + * @author eason.feng at 2019/12/1/0001 17:47 + **/ +public class LeetCode_MergeSort { + + public static void mergeSort(int[] array, int left, int right) { + if (right <= left) { + return; + } + int mid = (left + right) >>> 1; + + mergeSort(array, left, mid); + mergeSort(array, mid + 1, right); + merge(array, left, mid, right); + } + + private static void merge(int[] array, int left, int mid, int right) { + int[] temp = new int[right - left + 1]; + int i = left, j = mid + 1, k = 0; + while (i <= mid && j <= right) { + temp[k++] = array[i] <= array[j] ? array[i++] : array[j++]; + } + while (i <= mid) { + temp[k++] = array[i++]; + } + while (j <= right) { + temp[k++] = array[j++]; + } + for (int p = 0; p < temp.length; p++) { + array[left + p] = temp[p]; + } + } + +} diff --git a/Week 07/id_218/LeetCode_QuickSort.java b/Week 07/id_218/LeetCode_QuickSort.java new file mode 100644 index 000000000..c26c88250 --- /dev/null +++ b/Week 07/id_218/LeetCode_QuickSort.java @@ -0,0 +1,33 @@ +package leetcode.week7; + +/** + * @author eason.feng at 2019/12/1/0001 17:40 + **/ +public class LeetCode_QuickSort { + + public static void quickSort(int[] array, int begin, int end) { + if (end <= begin) { + return; + } + int pivot = partition(array, begin, end); + quickSort(array, begin, pivot - 1); + quickSort(array, pivot + 1, end); + } + + private static int partition(int[] a, int begin, int end) { + int pivot = end, counter = begin; + for (int i = begin; i < end; i++) { + if (a[i] < a[pivot]) { + int temp = a[counter]; + a[counter] = a[i]; + a[i] = temp; + counter++; + } + } + int temp = a[pivot]; + a[pivot] = a[counter]; + a[counter] = temp; + return counter; + } + +} diff --git a/Week 07/id_218/NOTE.md b/Week 07/id_218/NOTE.md index a6321d6e2..5fab5b4b9 100644 --- a/Week 07/id_218/NOTE.md +++ b/Week 07/id_218/NOTE.md @@ -1,4 +1,81 @@ # NOTE +#### 位运算 - +* 由来:计算机以二进制位存储方式 +* 或 与 非 异或 +* x ^ 0 = x +* x ^ 1 = ~x +* x ^ (~x) = 1 +* x ^ x = 0 +* c = a ^ b => a ^ c = b, b ^ c = a //交换两个数 +* a ^ b ^ c = a ^ ( b ^ c) = ( a ^ b ) ^ c // associative +* 指定位置的位运算 + * 将x最右边的n位清零: x & ( ~0 << n) + * 获取x的第n位值(0 或者 1):(x >> n) & 1 + * 获取x的第n位的幂值:x&(1<<(n-1)) + * 仅将第n位置为1:x | (1 << n) + * 仅将第n位置为0:x | (~1< (x & 1) == 1 + * x % 2 == 0 --> (x & 1) == 0 +* 除二 + * x >> 1 --> x/2 +* x = x & (x - 1) 清零最低位的1 +* x & -x =>得到最低位的1 +* x & ~x =》0 + +#### 布隆过滤器(Bloom Filter vs HashTable) + +* 一个很长的二进制向量和一系列随机映射函数。可以用于检索一个元素是否在一个集合中。 +* 优点:空间效率远远高于一般的算法。 +* 缺点:有一定的的误识别率和删除困难。 +* 案例: + * 1. 比特币网络 + * 2. 分布式系统(Map-Reduce) --Hadoop,search engine + * 3. Redis缓存 + * 4. 垃圾邮件、评论等的过滤 +#### LRU缓存,最近最少使用(Least Recently used) + +* 两个要素:大小、**替换策略和推荐系统有异曲同工之处 ** +* Hash Table + DoubleLinkedList +* O(1)查询 +* O(1)修改,更新 + + +#### Java LRU实现 + +#### 排序算法 + +* 初级排序和高级排序的实现和特性 + * 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogN),因此也称为非线性时间比较类排序。可以通过comparable 或者 compartor等接口来实现类型比较。 + * 高级排序,重点看O(nlogN): + * 堆排序(Heap Sort,插入删除维护O(logN),取最大/最小值O(1) + * 1:数组元素一次建立小顶堆 + * 2:一次取堆顶元素,并删除 + * 快速排序(Quick Sort使用了分治的思想) + * 数组去标杆pivot,将小元素放pivot左边,大元素则放右侧,然后依次对右边和右边的子数组继续快排;以达到整个序列有序。 + * 归并排序(Merge Sort,使用了分治的思想,思路和快排相反) + * 把长度为n的输入序列分成两个长度为n/2的子序列; + * 对这两个子序列分别采用归并排序; + * 将两个排序好的子序列合并成一个最终的排序序列。 + * 总结(归并和快排具有相似性,但步骤顺序相反): + * 归并:先排序左右子数组,然后合并两个有序子数组 + * 快排:先调配处左右子数组,然后对于左右子数组进行排序。 + * 初级排序(O(N^2)) + * 选择排序:每次找最小值,然后放到待排序数组的起始位置。 + * 插入排序:从前到后逐步构建有序序列;对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。 + * 冒泡排序(Bubble Sort):嵌套循环,每次查看相邻的元素,如果逆序,则交换。 + +* 非比较类排序: + * 不通过比较来决定元素间的相对次序,它可以突破基于比较排序时间下界,以线性时间运行,因此也称为线性时间非比较类排序,不过一般只能比较整数类型的。 + * 计数排序(Counting Sort,O(n)):计数排序要求输入的数据必须是在有确定范围的整数。将输入的数据值转化为键存储在额外开辟的数组空间中;然后依次把技术大于1的填充回原数组。 + * 桶排序(Bucket Sort)计数排序的升级版; + * 基数排序(Radix Sort) + + \ No newline at end of file diff --git a/Week 07/id_223/LeetCode_1122_223.java b/Week 07/id_223/LeetCode_1122_223.java new file mode 100644 index 000000000..ee71fae33 --- /dev/null +++ b/Week 07/id_223/LeetCode_1122_223.java @@ -0,0 +1,21 @@ +class Solution { + //计数排序 + public int[] relativeSortArray(int[] arr1, int[] arr2) { + int[] counter = new int[1001]; + for (int n : arr1) { + counter[n]++; + } + int i = 0; + for (int n : arr2) { + while (counter[n]-- > 0) { + arr1[i++] = n; + } + } + for (int n = 0; n < counter.length; n++) { + while (counter[n]-- > 0) { + arr1[i++] = n; + } + } + return arr1; + } +} \ No newline at end of file diff --git a/Week 07/id_223/LeetCode_191_223.java b/Week 07/id_223/LeetCode_191_223.java new file mode 100644 index 000000000..d0fb97a63 --- /dev/null +++ b/Week 07/id_223/LeetCode_191_223.java @@ -0,0 +1,8 @@ +public class Solution { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int count = 0; + while (n > 0) {count++; n &= (n - 1);} + return count; + } +} \ No newline at end of file diff --git a/Week 07/id_223/NOTE.md b/Week 07/id_223/NOTE.md index a6321d6e2..c7b120950 100644 --- a/Week 07/id_223/NOTE.md +++ b/Week 07/id_223/NOTE.md @@ -1,4 +1,67 @@ # NOTE - +本周主要学习位运算和排序算法。 +O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) + +x ^ 0 =x +x ^ 1s = ~x // 注意 1s = ~0 +x ^ (~x) = 1s +x ^ x= 0 +c = a ^ b => a ^ c = b, b ^ c = a a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c +// 交换两个数 // associative + + +归并排序(merge sort) + +``` +public static void mergeSort(int[] array, int left, int right) { + if (right <= left) return; + int mid = (left + right) >> 1; // (left + right) / 2 + mergeSort(array, left, mid); + mergeSort(array, mid + 1, right); + merge(array, left, mid, right); +} + +public static void merge(int[] arr, int left, int mid, int right) { + int[] temp = new int[right - left + 1]; // 中间数组 + int i = left, j = mid + 1, k = 0; + while (i <= mid && j <= right) { + temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++]; + } + while (i <= mid) temp[k++] = arr[i++]; + while (j <= right) temp[k++] = arr[j++]; + for (int p = 0; p < temp.length; p++) { + arr[left + p] = temp[p]; + } +// 也可以用 System.arraycopy(a, start1, b, start2, length) + +} +``` + + + + +快速排序(quick sort) + +``` +public static void quickSort(int[] array, int begin, int end) { if (end <= begin) return; + int pivot = partition(array, begin, end); + quickSort(array, begin, pivot - 1); + quickSort(array, pivot + 1, end); +} +static int partition(int[] a, int begin, int end) { + // pivot: 标杆位置,counter: 小于pivot的元素的个数 + int pivot = end, counter = begin; + for (int i = begin; i < end; i++) { + if (a[i] < a[pivot]) { + int temp = a[counter]; a[counter] = a[i]; a[i] = temp; counter++; + } + } + int temp = a[pivot]; + a[pivot] = a[counter]; + a[counter] = temp; + return counter; +} +``` + diff --git a/Week 07/id_243/LeetCode_190_243.java b/Week 07/id_243/LeetCode_190_243.java new file mode 100644 index 000000000..84f878632 --- /dev/null +++ b/Week 07/id_243/LeetCode_190_243.java @@ -0,0 +1,16 @@ +/** + * @author eazonshaw + * @date 2019/12/1 0:17 + */ +public class LeetCode_190_243 { + + public int reverseBits(int n) { + int rs = 0; + for(int i = 0;i < 32;i++){ + rs = (rs << 1) + (n & 1); + n = n >> 1; + } + return rs; + } + +} diff --git a/Week 07/id_243/LeetCode_191_243.java b/Week 07/id_243/LeetCode_191_243.java new file mode 100644 index 000000000..f4ca7e6be --- /dev/null +++ b/Week 07/id_243/LeetCode_191_243.java @@ -0,0 +1,16 @@ +/** + * @author eazonshaw + * @date 2019/12/1 0:16 + */ +public class LeetCode_191_243 { + + public int hammingWeight(int n) { + int sum = 0; + while(n!=0){ + sum++; + n = n & (n-1); + } + return sum; + } + +} diff --git a/Week 07/id_243/LeetCode_231_243.java b/Week 07/id_243/LeetCode_231_243.java new file mode 100644 index 000000000..fc6e5c3ec --- /dev/null +++ b/Week 07/id_243/LeetCode_231_243.java @@ -0,0 +1,11 @@ +/** + * @author eazonshaw + * @date 2019/12/1 0:16 + */ +public class LeetCode_231_243 { + + public boolean isPowerOfTwo(int n) { + return n > 0 && (n & (n - 1)) == 0; + } + +} diff --git a/Week 07/id_243/LeetCode_493_243.java b/Week 07/id_243/LeetCode_493_243.java new file mode 100644 index 000000000..78f5be210 --- /dev/null +++ b/Week 07/id_243/LeetCode_493_243.java @@ -0,0 +1,57 @@ +import java.util.Arrays; + +/** + * @author eazonshaw + * @date 2019/12/1 9:11 + */ +public class LeetCode_493_243 { + + //归并 + public int reversePairs(int[] nums) { + return mergeSort(nums,0,nums.length-1); + } + + //递归函数 + private int mergeSort(int[] nums,int begin,int end){ + //终止条件 + if(begin >= end)return 0; + //下坠 + int mid = (begin + end) >> 1; + int count = mergeSort(nums,begin,mid) + mergeSort(nums,mid+1,end); + + int i = begin,j = mid+1; + while(i <= mid){ + while(j <= end && nums[i] > nums[j]*2L){ + j++; + } + //TODO 之前想着为什么不直接在循环里执行count++ + count += j - (mid + 1); + i++; + } + //执行当前层逻辑 + //merge(nums,begin,mid,end); + Arrays.sort(nums,begin,end+1); + return count; + } + + private int merge(int[] nums, int begin, int mid, int end) { + int count = 0; + int[] tmp = new int[end-begin+1]; + int i = begin,j = mid+1,k = 0; + while (i<=mid && j<=end){ + tmp[k++] = nums[i] <= nums[j]?nums[i++] : nums[j++]; + } + while (i<=mid){ + tmp[k++] = nums[i++]; + } + while (j<=end){ + tmp[k++] = nums[j++]; + } + for(int p = 0;p < tmp.length;p++){ + nums[begin+p] = tmp[p]; + } + return count; + } + + +} diff --git a/Week 07/id_243/NOTE.md b/Week 07/id_243/NOTE.md index a6321d6e2..19c6ef5de 100644 --- a/Week 07/id_243/NOTE.md +++ b/Week 07/id_243/NOTE.md @@ -1,4 +1,181 @@ -# NOTE +# 学习总结 +## 位运算 +### 位运算符 +含义 | 运算符 | 示例 +---|---|--- +左移 | << | 0011 --> 0100 +右移 | >> | 0110 --> 0011 +按位或 |\|| 0011\|1011 --> 1011 +按位与 | & | 0011&1011 --> 0011 +按位取反 | ~ | 0011 --> 1100 +按位异或(同性相斥,异性相吸) | ^ | 0011^1011 --> 1000 +### 指定位置的位运算 +含义 | 操作 +---|--- +将x最右边的n位清零 | x & (~0 << n) +获取x的第n位的值(0或1) | (x >> n) & 1 +获取x的第n位的幂值 | x & (1 << (n-1)) +仅将第n位置为1 | x \| (1 << n) +仅将第n位置为0 | x & (~(1 << n)) +将x最高位至第n位(含)清零 | x & ((1 << n) -1) +将第n位至第0位(含)清零 | x & (~((1 << (n+1)) - 1)) +### 实战位运算要点(重点) +1. 判断奇偶 - +含义 | 常规 | 位运算 +---|---|--- +判断偶数 | x % 2 == 0 | (x & 1) == 0 +判断基数 | x % 2 == 1 | (x & 1) == 1 +2. 除以2 + +常规 | 位运算 +---|--- +x / 2| x >> 1 +int mid = (left + right)/2| int mid = (left + right) >> 1 + +3. 清零最低位的1(即:从第0位开始往前,第1个1置为0) +``` +x = x & (x - 1) +``` +4. 获取最低位的1(即:从第0位开始往前,获取第1个1) +``` +x & (-x) +``` +5. 取0 +``` +x & (~x) +``` +### leetCode习题 +[N皇后位运算实现模板](https://shimo.im/docs/rHTyt8hcpT6D9Tj8/read) + +## 布隆过滤器和LRU缓存 +### 布隆过滤器 +* 一个很长的二进制向量和一系列的随机映射函数 +* 可以用于检索一个元素是否在一个集合中 +* **优点**:空间效率和查询时间都远远超过一般的算法 +* **缺点**:有一定的误识别率和删除困扰 +> 总结:可百分之百判断元素不存在于集合中,但不可准确判断元素存在集合中,故一般用于前置缓存(模糊判断)。 +* 案例:比特币、分布式系统、Redis缓存、垃圾邮件和评论的过滤等 +### LRU(Least Recently Used) Cahce +* 两个要素:大小、替换策略 +> 总结:淘汰最远使用的元素 +## 排序算法 +### 概述(脑图) +1. 比较类排序 +2. 非比较排序 +### 重点掌握的排序算法 +#### 选择排序(O(n^2)) +> 每次找最小值,然后放到排序数组的起始位置 +```java +//选择排序 +public static void selectionSort(int[] nums){ + for(int i = 0;i< nums.length;i++){ + //最小值 + int m = i; + int min = Integer.MAX_VALUE; + for(int j = i;j < nums.length;j++){ + if(nums[j] < min){ + m = j; + min = nums[j]; + } + } + nums[m] = nums[i]; + nums[i] = min; + } +} +``` +#### 插入排序(O(n^2)) +> 从前往后逐步构建有序序列;对于未排序数据,在已排序序列中从后往前扫描,找到相应位置并插入。 +```java +//插入排序 +public static void insertionSort(int[] nums){ + for(int i = 0;i < nums.length;i++){ + int curr = nums[i]; + for(int j = i;j > 0;j--){ + if(curr >= nums[j-1]){ + break; + }else{ + //后移一位 + nums[j] = nums[j-1]; + i--; + } + } + nums[i] = curr; + } +} +``` +#### 冒泡排序(O(n^2)) +> 嵌套循环,每次查看相邻的元素,若逆序,则交换。 + +```java +//冒泡排序 +public static void bubbleSort(int[] nums){ + for(int i = 0;i < nums.length;i++){ + for(int j = 0;j < nums.length-i-1 ;j++){ + //比较相邻元素,若逆序 + if(nums[j] > nums[j+1]){ + //交换 + int tmp = nums[j]; + nums[j] = nums[j+1]; + nums[j+1] = tmp; + } + } + } +} +``` +#### 归并排序(O(nlogn)) +[归并排序模板](https://shimo.im/docs/YqgG6vtdKwkXJkWx/read) +```java +//归并 +public static void mergeSort(int[] nums,int begin,int end){ + //终止条件 + if(begin <= end)return; + //下坠 + int mid = (begin + end) >> 1; + mergeSort(nums,begin,mid); + mergeSort(nums,mid+1,end); + //执行当前层逻辑,即合并两个有序数组 + merge(nums,begin,mid,end); +} + +private static void merge(int[] nums, int begin, int mid, int end) { + int[] tmp = new int[end-begin+1]; + int i = begin,j = mid+1,k = 0; + while (i<=mid && j<=end){ + tmp[k++] = nums[i] <= nums[j]?nums[i++] : nums[j++]; + } + while (i<=mid){ + tmp[k++] = nums[i++]; + } + while (j<=end){ + tmp[k++] = nums[j++]; + } + for(int p = 0;p < tmp.length;p++){ + nums[begin+p] = tmp[p]; + } +} +``` +#### 快速排序(O(nlogn)) +[快速排序模板](https://shimo.im/docs/98KjvGwwGpTpYGKy/read) +```java +public static void quickSort(int[] array, int begin, int end) { + if (end <= begin) return; + int pivot = partition(array, begin, end); + quickSort(array, begin, pivot - 1); + quickSort(array, pivot + 1, end); +} + +static int partition(int[] a, int begin, int end) { + // pivot: 标杆位置,counter: 小于pivot的元素的个数 + int pivot = end, counter = begin; + for (int i = begin; i < end; i++) { + if (a[i] < a[pivot]) { + int temp = a[counter]; a[counter] = a[i]; a[i] = temp; + counter++; + } + } + int temp = a[pivot]; a[pivot] = a[counter]; a[counter] = temp; + return counter; +} +``` diff --git a/Week 07/id_253/LeetCode_146_253.java b/Week 07/id_253/LeetCode_146_253.java new file mode 100644 index 000000000..557abcece --- /dev/null +++ b/Week 07/id_253/LeetCode_146_253.java @@ -0,0 +1,69 @@ +import java.util.Hashtable; +class LRUCache { + class DLinkedNode{ + int key; + int value ; + DLinkedNode prev; + DLinkedNode next; + } + private void addNode(DLinkedNode node ){ + node.prev = head; + node.next = head.next; + head.next.prev = node; + head.next = node; + } + private void removeNode(DLinkedNode node ){ + DLinkedNode next = node.next; + DLinkedNode prev = node.prev; + node.prev.next = next; + node.next.prev = prev; + } + private void moveToHead(DLinkedNode node ){ + removeNode(node); + addNode(node); + } + private DLinkedNode popTail(){ + DLinkedNode node = tail.prev; + removeNode(tail.prev); + return node; + } + private Hashtable cache= new Hashtable<>(); + private int size; + private int capacity; + private DLinkedNode head , tail; + public LRUCache(int capacity) { + this.size = 0; + this.capacity = capacity; + head = new DLinkedNode(); + tail = new DLinkedNode(); + head.next = tail; + tail.prev = head; + } + + public int get(int key) { + DLinkedNode node = cache.get(key); + if(node == null ) return -1; + moveToHead(node); + return node.value; + } + + public void put(int key, int value) { + DLinkedNode node = cache.get(key); + if(node == null){ + DLinkedNode newnode = new DLinkedNode(); + newnode.value = value ; + newnode.key = key ; + cache.put(key,newnode); + addNode(newnode); + size++; + if(size > capacity){ + DLinkedNode tail = popTail(); + cache.remove(tail.key); + size -- ; + } + }else{ + node.value = value ; + moveToHead(node); + } + } +} diff --git a/Week 07/id_253/LeetCode_190_253.java b/Week 07/id_253/LeetCode_190_253.java new file mode 100644 index 000000000..598c48df0 --- /dev/null +++ b/Week 07/id_253/LeetCode_190_253.java @@ -0,0 +1,12 @@ +public class Solution { + // you need treat n as an unsigned value + public int reverseBits(int n) { + int res =0 ; + for(int i =0 ; i< 32 ; i++){ + res <<= 1; + res += (n&1); + n >>= 1; + } + return res; + } +} diff --git a/Week 07/id_253/LeetCode_191_253.java b/Week 07/id_253/LeetCode_191_253.java new file mode 100644 index 000000000..af2bec980 --- /dev/null +++ b/Week 07/id_253/LeetCode_191_253.java @@ -0,0 +1,10 @@ +public class Solution { + public int hammingWeight(int n) { + int sum = 0 ; + while(n != 0){ + sum++; + n=n&(n-1); + } + return sum; + } +} diff --git a/Week 07/id_253/LeetCode_231_253.java b/Week 07/id_253/LeetCode_231_253.java new file mode 100644 index 000000000..b2e44d3f0 --- /dev/null +++ b/Week 07/id_253/LeetCode_231_253.java @@ -0,0 +1,5 @@ +class Solution { + public boolean isPowerOfTwo(int n) { + return n > 0 && (n&(n-1))==0; + } +} diff --git a/Week 07/id_253/LeetCode_338_253.java b/Week 07/id_253/LeetCode_338_253.java new file mode 100644 index 000000000..392acee75 --- /dev/null +++ b/Week 07/id_253/LeetCode_338_253.java @@ -0,0 +1,9 @@ +class Solution { + public int[] countBits(int num) { + int[] ans = new int[num+1]; + for(int i =1 ; i < num+1 ; i++){ + ans[i] = ans[i&(i-1)] + 1; + } + return ans; + } +} diff --git a/Week 07/id_253/LeetCode_493_253.java b/Week 07/id_253/LeetCode_493_253.java new file mode 100644 index 000000000..2377f2374 --- /dev/null +++ b/Week 07/id_253/LeetCode_493_253.java @@ -0,0 +1,17 @@ +class Solution { + public int reversePairs(int[] nums) { + int cnt = mergeArray(nums,0,nums.length-1); + return cnt; + } + public int mergeArray(int[] nums , int begin , int end){ + if(end <= begin) return 0; + int mid = begin + (end-begin)/2; + int cnt = mergeArray(nums , begin , mid) + mergeArray(nums , mid+1 , end); + for(int i = begin , j = mid+1 ; i <= mid ; i++){ + while(j<=end && nums[i]/2.0 > nums[j]) j++; + cnt += j - mid - 1; + } + Arrays.sort(nums , begin , end+1); + return cnt; + } +} diff --git a/Week 07/id_253/LeetCode_56_253.java b/Week 07/id_253/LeetCode_56_253.java new file mode 100644 index 000000000..283697649 --- /dev/null +++ b/Week 07/id_253/LeetCode_56_253.java @@ -0,0 +1,19 @@ +class Solution { + public int[][] merge(int[][] intervals) { + List res = new ArrayList<>(); + if(intervals.length == 0 || intervals == null) return res.toArray(new int[0][]); + Arrays.sort(intervals , (a , b) -> a[0] - b[0]); + int i =0 ; + while( i < intervals.length) { + int left = intervals[i][0]; + int right = intervals[i][1]; + while(i < intervals.length - 1 && right >= intervals[i+1][0]) { + right = intervals[i+1][1] > right ? intervals[i+1][1] : right; + i++; + } + res.add(new int[]{left , right}); + i++; + } + return res.toArray(new int[0][]); + } +} diff --git a/Week 07/id_258/LeetCode_1122_258.js b/Week 07/id_258/LeetCode_1122_258.js new file mode 100644 index 000000000..2470ee696 --- /dev/null +++ b/Week 07/id_258/LeetCode_1122_258.js @@ -0,0 +1,34 @@ +/* + * @lc app=leetcode.cn id=1122 lang=javascript + * + * [1122] 数组的相对排序 + */ + +// @lc code=start +/** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ +var relativeSortArray = function (arr1, arr2) { + let resultPart1 = []; + resultPart2 = []; + base = arr1; + distinct = arr2; + + for (let i = 0; i < base.length; i++) { + if (!distinct.includes(base[i])) { + resultPart2.push(base[i]); + } else { + resultPart1 = [...resultPart1, ...base.filter((item) => { + return item === distinct[i]; + })] + } + } + + return resultPart1.concat(resultPart2.sort((a, b) => { + return a - b; + })) +}; +// @lc code=end + diff --git a/Week 07/id_258/LeetCode_146_258.js b/Week 07/id_258/LeetCode_146_258.js new file mode 100644 index 000000000..a96e7f178 --- /dev/null +++ b/Week 07/id_258/LeetCode_146_258.js @@ -0,0 +1,56 @@ +/* + * @lc app=leetcode.cn id=146 lang=javascript + * + * [146] LRU缓存机制 + */ + +// @lc code=start +/** + * @param {number} capacity + */ +var LRUCache = function (capacity) { + this.m = new Map(); + this.capacity = capacity; +}; + +/** + * @param {number} key + * @return {number} + */ +LRUCache.prototype.get = function (key) { + if (this.m.has(key)) { + let value = this.m.get(key); + this.m.delete(key); + this.m.set(key, value); + return value; + } else { + return -1; + } +}; + +/** + * @param {number} key + * @param {number} value + * @return {void} + */ +LRUCache.prototype.put = function (key, value) { + if (this.m.has(key)) { + this.m.delete(key); + this.m.set(key, value); + } else if (this.m.size == this.capacity) { + let first_key = this.m.keys().next().value; + this.m.delete(first_key); + this.m.set(key, value); + } else { + this.m.set(key, value); + } +}; + +/** + * Your LRUCache object will be instantiated and called as such: + * var obj = new LRUCache(capacity) + * var param_1 = obj.get(key) + * obj.put(key,value) + */ +// @lc code=end + diff --git a/Week 07/id_273/LeetCode_1122_273.java b/Week 07/id_273/LeetCode_1122_273.java new file mode 100644 index 000000000..f37d79f87 --- /dev/null +++ b/Week 07/id_273/LeetCode_1122_273.java @@ -0,0 +1,72 @@ +//1122. 数组的相对排序 + +//解法1:暴力解法 +//思路:遍历arr2, 找到arr1中与arr2[i]相等的元素, 并存入res数组, +// arr1中判断过的元素置为-1, 然后将剩下arr1中不为-1的值依次添加到res末尾然后对这部分进行排序 +//时间复杂度:O(m*n) +public int[] relativeSortArray(int[] arr1, int[] arr2) { + int[] res = new int[arr1.length]; + int k = 0; + int count = 0; + for (int i = 0; i < arr2.length; i++) { + for (int j = 0; j < arr1.length; j++) { + if (arr1[j] == arr2[i]) { + res[k++] = arr1[j]; + arr1[j] = -1; + count++; + } + } + } + for (int j = 0; j < arr1.length; j++) { + if (arr1[j] != -1) res[k++] = arr1[j]; + } + Arrays.sort(res, count, res.length); + return res; +} + +//解法2:计数法 +//思路:由题可知arr1和arr2的范围在0 ~ 1000, 因此创建一个1001大小的数组用于存储arr1出现的次数 +// 然后遍历arr2, 观察arr2[i]这个值在计数数组中出现了多少次, 并按该次数赋值给arr1, 到目前为止, 按arr2的相对排序就完成了 +// 剩下的一些未出现在arr2中的值, 只需要在计数数组中寻找不为0的元素, 并依次添加到arr1尾部即可。 +//时间复杂度:O(1) +//空间复杂度:O(1) 常数的数组大小 +public int[] relativeSortArray(int[] arr1, int[] arr2) { + int[] temp = new int[1001]; + int k = 0; + for (int i : arr1) { + temp[i]++; + } + for (int j : arr2) { + while (temp[j]-- > 0) arr1[k++] = j; + } + for (int i = 0; i < temp.length; i++) { + while (temp[i]-- > 0) arr1[k++] = i; + } + return arr1; +} + +//解法2.1:计数法TreeMap实现 +//思路:如果不限制arr1和arr2的范围, 那么我们可以用基于红黑树的TreeMap对arr1出现的次数进行统计 +//时间复杂度:O(n * log2N) +//空间复杂度:O(n) +//总结:TreeMap存储的是一个有序的集合, 若未通过比较器指定, 集合内元素默认自然排序 +public int[] relativeSortArray3(int[] arr1, int[] arr2) { + TreeMap map = new TreeMap<>(); + int k = 0; + for (int i : arr1) { + map.put(i, map.getOrDefault(i, 0) + 1); + } + for (int i : arr2) { + while (map.get(i) != 0) { + arr1[k++] = i; + map.put(i, map.get(i) - 1); + } + } + for (int i : map.keySet()) { + while (map.get(i) != 0) { + arr1[k++] = i; + map.put(i, map.get(i) - 1); + } + } + return arr1; +} \ No newline at end of file diff --git a/Week 07/id_273/LeetCode_146_273.java b/Week 07/id_273/LeetCode_146_273.java new file mode 100644 index 000000000..9f474c92e --- /dev/null +++ b/Week 07/id_273/LeetCode_146_273.java @@ -0,0 +1,112 @@ +//146. LRU缓存机制 + +//解法1:双端链表 + HashMap 执行用时:32ms +//思路:采用双端链表+哈希表的数据结构, 保证了put、get方法的时间复杂度都为O(1) +// put:涉及了链表的增、删操作, 双端链表数据结构保证了在链表头、尾部的增、删操作的时间复杂度为O(1) +// get:涉及了链表的查找操作, 那么通过哈希表存储链表节点的key值, 这样就可以保证在获取指定节点的时间复杂度为O(1) +public class LRUCache { + private HashMap map; + private DLinkedList cache; + private int capacity; + + public LRUCache(int capacity) { + this.capacity = capacity; + this.map = new HashMap<>(); + this.cache = new DLinkedList(); + } + + public int get(int key) { + //if exit, get and update + if (map.containsKey(key)) { + DLinkedList.Node node = map.get(key); + put(key, node.value); + return node.value; + } + return -1; + } + + public void put(int key, int value) { + //if already exit -> update + DLinkedList.Node x = new DLinkedList.Node(key, value); + if (map.containsKey(key)) { + cache.remove(map.get(key)); + map.remove(key); + cache.addFirst(x); + map.put(key, x); + } else {//add new + //if capacity is not enough + if (capacity <= cache.size()) { + //delete oldest, add latest + DLinkedList.Node node = cache.removeLast(); + map.remove(node.key); + } + cache.addFirst(x); + map.put(key, x); + } + } + + public static void main(String[] args) { + LRUCache cache = new LRUCache( 2 /* 缓存容量 */ ); + + cache.put(2, 1); + cache.put(2, 2); + cache.get(2); // 返回 1 + cache.put(1, 1); // 该操作会使得密钥 2 作废 + cache.put(4, 1); // 该操作会使得密钥 1 作废 + cache.get(2); // 返回 -1 (未找到) + } +} + +class DLinkedList { + private Node head; + private Node tail; + private int size; + + public DLinkedList() { + head = new Node(0, 0); + tail = new Node(0, 0); + head.next = tail; + tail.prev = head; + this.size = 0; + } + + public void addFirst(Node node) { + Node headNext = head.next; + head.next = node; + node.prev = head; + headNext.prev = node; + node.next = headNext; + size++; + } + + public Node remove(Node node) { + Node nodePrev = node.prev; + Node nodeNext = node.next; + nodePrev.next = nodeNext; + nodeNext.prev = nodePrev; + size--; + return node; + } + + public Node removeLast() { + if (tail.prev == head) return null; + Node node = remove(tail.prev); + return node; + } + + public int size() { + return size; + } + + static class Node { + public int value; + public int key; + private Node prev; + private Node next; + + public Node(int key, int value) { + this.key = key; + this.value = value; + } + } +} \ No newline at end of file diff --git a/Week 07/id_273/LeetCode_190_273.java b/Week 07/id_273/LeetCode_190_273.java new file mode 100644 index 000000000..50145dd23 --- /dev/null +++ b/Week 07/id_273/LeetCode_190_273.java @@ -0,0 +1,15 @@ +//190. 颠倒二进制位 + +//解法1:位移解法 执行用时1ms +//思路:首先想一下如果要我们手算这道题的思路, 就是从n的最低位开始逐个取出, 然后从res最高位逐个放入, 将思路转换位代码即可 +// 这里需要注意的是, 我们是先将res左移了一位再进行添加操作 +// 这是因为res只需要左移30次, 最后一次会导致结果多出一位 +public int reverseBits(int n) { + int res = 0; + for (int i = 0; i < 32; i++) { + res <<= 1; + res += (n & 1); + n >>= 1; + } + return res; +} \ No newline at end of file diff --git a/Week 07/id_273/LeetCode_191_273.java b/Week 07/id_273/LeetCode_191_273.java new file mode 100644 index 000000000..44cabb71c --- /dev/null +++ b/Week 07/id_273/LeetCode_191_273.java @@ -0,0 +1,63 @@ +//191. 位1的个数 + +//解法1:lowBit公式 执行用时:1ms +//思路:通过x & (-x)能够获取到x最低位的1, 不断减去x的最低位1, 并记录减去的次数, 直到x = 0 +//时间复杂度:O(1) 因为最坏情况下也就只有32次计算 +//空间复杂度:O(1) +//总结:获取最低位1公式:x & (-x) +public int hammingWeight(int n) { + int count = 0; + while (n != 0) { + count++; + int lowBit = n & (-n); + n -= lowBit; + } + return count; +} + +//解法2:按位& 执行用时:1ms +//思路:从x的最低位开始, 进行按位&操作, +// 例如: i == 1 n = 1011 n&1 = 0001 +// i == 2 n = 1011 n&10 = 0010 +// i == 3 n = 1011 n&100 = 0 该位不存在1 +// i == 4 n = 1011 n&1000 = 1000 +public int hammingWeight(int n) { + int count = 0; + int mask = 1; + for (int i = 0; i < 32; i++) { + if ((n & mask) != 0) count++; + mask <<= 1; + } + return count; +} + +//解法3:按位&优化 执行用时:1ms +//思路:二进制中, x中的最低位1所在的位置在(x - 1)中总是0, +// 那么只需要进行x & (x - 1)就能够获得x抹去最低位1的二进制数 +// 循环操作并记录抹去最低位1的次数, 直到x == 0 +//总结:清零最低位1公式:x & (x - 1) +public int hammingWeight(int n) { + int count = 0; + while (n != 0) { + count++; + n &= (n - 1); + } + return count; +} + + +//解法4:余数判断 执行超时 +//思路:判断x % 2 == 1 ? 若true, 说明当前x存在1, x减去1后右移一位继续计算, 直到x == 0 +//总结:x % 2 等同于 x & 1 +// x / 2 等同于 x >> 1 +public int hammingWeight(int n) { + int count = 0; + while (n != 0) { + if ((n & 1) != 0) { //x % 2 != 0 + n -= 1; + count++; + } + n >>= 1; + } + return count; +} \ No newline at end of file diff --git a/Week 07/id_273/LeetCode_231_273.java b/Week 07/id_273/LeetCode_231_273.java new file mode 100644 index 000000000..9634ad2e0 --- /dev/null +++ b/Week 07/id_273/LeetCode_231_273.java @@ -0,0 +1,11 @@ +//231. 2的幂 + +//解法1:&运算 执行用时:2ms +//思路:首先明确n如果是二的幂次, 那么它的二进制数一定只存在一个1 +// 移除二进制数n的最低位1, 如果移除后n == 0, 说明二进制数n只存在一个1 +//时间复杂度:O(1) +//空间复杂度:O(1) +//总结:移除二进制数的最低位1:n & (n - 1) +public boolean isPowerOfTwo(int n) { + return ((n > 0) && (n & (n - 1)) == 0); +} \ No newline at end of file diff --git a/Week 07/id_273/LeetCode_242_273.java b/Week 07/id_273/LeetCode_242_273.java new file mode 100644 index 000000000..7516e5d9f --- /dev/null +++ b/Week 07/id_273/LeetCode_242_273.java @@ -0,0 +1,33 @@ +//242. 有效的字母异位词 + + +//解法1:排序比较法 执行用时:5ms +//思路:将s和t转换为数组,然后比较排序后两个数组是否相同 +//时间复杂度O(nlogN) +public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) return false; + char temp1[] = s.toCharArray(); + char temp2[] = t.toCharArray(); + Arrays.sort(temp1); + Arrays.sort(temp2); + return Arrays.equals(temp1,temp2); +} + +//解法2:数组映射法 执行用时:2ms +//思路:创建一个包含26位字符的数组,用于存储字符串中26个字符出现的次数 +//时间复杂度O(n) +//空间复杂度O(n) + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) return false; + char[] sc = s.toCharArray(); + char[] tc = t.toCharArray(); + int[] temp = new int[26]; + for (int i = 0; i < sc.length; i++) { + temp[sc[i] - 'a']++; + temp[tc[i] - 'a']--; + } + for (int i = 0; i < temp.length; i++) { + if (temp[i] != 0) return false; + } + return true; +} \ No newline at end of file diff --git a/Week 07/id_273/LeetCode_338_273.java b/Week 07/id_273/LeetCode_338_273.java new file mode 100644 index 000000000..69f1b535d --- /dev/null +++ b/Week 07/id_273/LeetCode_338_273.java @@ -0,0 +1,35 @@ +//338. 比特位计数 + +//解法1:循环计数法 执行用时:3ms +//思路:遍历0 ~ n, 分别对各个数锁包含的1进行计数, 计数的方法可以参考191题 +//时间复杂度:O(n * sizeOf(i)) +//空间复杂度:O(n) +public int[] countBits(int num) { + int[] res = new int[num + 1]; + for (int i = 1; i <= num; i++) { + res[i] = countOne(i); + } + return res; +} + +private int countOne(int i) { + int count = 0; + while (i != 0) { + i = i & (i - 1); + count++; + } + return count; +} + +//解法2:动态规划 执行用时:1ms +//思路:递归公式:res[i] = res[i >> 1] + res[i & 1] +// 例如1001, 它的1的个数 = 前3位"100"所包含的1的个数 + 最后一位0或1所包含的1的个数 +public int[] countBits(int num) { + if (num == 0) return new int[]{0}; + int[] res = new int[num + 1]; + res[1] = 1; + for (int i = 2; i <= num; i++) { + res[i] = res[i >> 1] + res[i & 1]; + } + return res; +} \ No newline at end of file diff --git a/Week 07/id_273/LeetCode_493_273.java b/Week 07/id_273/LeetCode_493_273.java new file mode 100644 index 000000000..71bb11f8a --- /dev/null +++ b/Week 07/id_273/LeetCode_493_273.java @@ -0,0 +1,47 @@ +//493. 翻转对 + +//解法1:暴力求解 执行超时 +//思路:枚举所有i < j 的组合, 当nums[i]/2.0 > 2*nums[j]时, count++, 注意使用"/2.0"是为了在出现Integer.MAX_VALUE时避免越界 +public int reversePairs(int[] nums) { + int count = 0; + for (int i = nums.length - 1; i >= 0; i--) { + for (int j = 0; j < i; j++) { + if (nums[j]/2.0 > nums[i]) count++; + } + } + return count; +} + +//解法2:归并排序 执行用时:59ms +//思路:基于归并排序的思路, 能够发现左半区域的index一定是小于右半区域的, 这样就可以满足翻转对的第一个条件i < j +// 接下来只需要观察左半区的元素中是否有大于右半区*2的元素, 找到了count++即可 +// 因为归并排序的特性, 检索过的区间都被置为有序, 也就意味着不会出现重复判断的情况 +//时间复杂度:O(log2N) +//空间复杂度:O(n) +public int reversePairs(int[] nums) { + if (nums.length == 0 || nums == null) return 0; + return mergeSort(nums, 0, nums.length - 1); +} + +private int mergeSort(int[] nums, int left, int right) { + if (left >= right) return 0; + int[] temp = new int[right - left + 1]; + int mid = left + ((right - left) >> 1); + int count = mergeSort(nums, left, mid) + mergeSort(nums, mid + 1, right); + int reverseIdx = left, i = left, k = 0; + for (int j = mid + 1; j <= right; j++) { + while (reverseIdx <= mid && nums[reverseIdx]/2.0 <= nums[j]) { + reverseIdx++; + } + while (i <= mid && nums[i] <= nums[j] ) { + temp[k++] = nums[i++]; + } + count += mid + 1 - reverseIdx; + temp[k++] = nums[j]; + } + while (i <= mid) { + temp[k++] = nums[i++]; + } + System.arraycopy(temp, 0, nums, left, temp.length); + return count; +} \ No newline at end of file diff --git a/Week 07/id_273/LeetCode_51_273.java b/Week 07/id_273/LeetCode_51_273.java new file mode 100644 index 000000000..8844ef3c0 --- /dev/null +++ b/Week 07/id_273/LeetCode_51_273.java @@ -0,0 +1,101 @@ +//51. N皇后 + + +//解法1:回溯+Set 执行用时:16ms +//思路:由于皇后的攻击范围是所在的行/列/对角线 ,因此在某一行放置了一个皇后, 该行不可能再放置第二个皇后 +// 因此我们只需要对行数作记录, 判断每一列的位置是否available即可 +// 通过3个Set分别存放当前皇后所占据的列,左/右对角线, 下一个皇后在放置之前通过Set判断当前位置是否可以放置 +class { + private Set col = new HashSet<>(); + private Set diag1 = new HashSet<>(); + private Set diag2 = new HashSet<>(); + + public List> solveNQueens(int n) { + List> result = new ArrayList<>(); + dfs(result, new ArrayList(), n, 0); + return result; + } + + private void dfs(List> result, List list, int n, int row) { + if (row == n) { + result.add(new ArrayList<>(list)); + return; + } + for (int i = 0; i < n; i++) { + if (col.contains(i) || diag1.contains(row + i) || diag2.contains(row - i)) continue; + char[] temp = new char[n]; + Arrays.fill(temp, '.'); + temp[i] = 'Q'; + list.add(new String(temp)); + col.add(i); + diag1.add(row + i); + diag2.add(row - i); + //drill down + dfs(result, list, n, row + 1); + //reverse + list.remove(list.size() - 1); + col.remove(i); + diag1.remove(row + i); + diag2.remove(row - i); + } + } +} + +//解法2:回溯 + boolean[] +public List> solveNQueens(int n) { + List> res = new ArrayList<>(); + recur(n, 0, new boolean[n], new boolean[2 * n], new boolean[2 * n], new ArrayList<>(), res); + return res; +} + +private void recur(int n, int row, boolean[] cols, boolean[] diag1, boolean[] diag2, List temp, List> res) { + if (row == n) { + res.add(new ArrayList<>(temp)); + return; + } + for (int i = 0; i < n; i++) { + int d1 = row + i; + int d2 = row - i + n - 1; + if (cols[i] || diag1[d1] || diag2[d2]) continue; + cols[i] = true; diag1[d1] = true; diag2[d2] = true; + char[] charArr = new char[n]; + Arrays.fill(charArr, '.'); + charArr[i] = 'Q'; + temp.add(String.valueOf(charArr)); + recur(n, row + 1, cols, diag1, diag2, temp, res); + cols[i] = false; + diag1[d1] = false; + diag2[d2] = false; + temp.remove(temp.size() - 1); + } +} + +//解法3:Bitmask +public List> solveNQueens(int n) { + List> res = new ArrayList<>(); + int size = (1 << n) - 1; + recur(n, size, 0, 0, 0, new ArrayList<>(), res); + return res; +} + +private void recur(int n, int size, int row, int diag1, int diag2, List temp, List> res) { + if (row == size) { + res.add(new ArrayList<>(temp)); + return; + } + int pos = size & (~(row | diag1 | diag2)); + while (pos != 0) { + int p = pos & (-pos); + pos -= p; + char[] charArr = new char[n]; + Arrays.fill(charArr, '.'); + charArr[n - logTwo(p) - 1] = 'Q'; + temp.add(String.valueOf(charArr)); + recur(n, size, p | row, (p | diag1) << 1, (p | diag2) >> 1, temp, res); + temp.remove(temp.size() - 1); + } +} + +private int logTwo(int n) { + return (int)(Math.log(n)/Math.log(2)); +} \ No newline at end of file diff --git a/Week 07/id_273/LeetCode_52_273.java b/Week 07/id_273/LeetCode_52_273.java new file mode 100644 index 000000000..5ab254970 --- /dev/null +++ b/Week 07/id_273/LeetCode_52_273.java @@ -0,0 +1,57 @@ +//52. N皇后II + +//解法1:DFS + 位运算 执行用时:0ms +//思路:递归寻找每一行中能够放置皇后的位置, 成功放置后将之前放置的皇后攻击范围添加上当前皇后的攻击范围作为状态进行下一次深度优先递归 +// 重点在于该方法采用二进制数来记录皇后攻击的范围: +// 1. row:"p | row"这个操作记录了当前皇后 + 之前皇后能够攻击到的竖向范围 +// 2. diag1:"(P | diag1) << 1"这个操作记录了当前皇后与之前皇后的左对角线攻击范围 +// 3. diag2:"(P | diag2) >> 1"这个操作记录了当前皇后与之前皇后的右对角线攻击范围 +int size; +int count; +public int totalNQueens(int n) { + count = 0; + size = (1 << n) - 1; + solve(0, 0, 0); + return count; +} + +private void solve(int row, int diag1, int diag2) { + //如果NQueens有解, 说明一定分散在同的列上, 因此若row == size, 说明棋盘上已经放了N个皇后 + if (row == size) { + count++; + return; + } + int pos = size & (~ (row | diag1 | diag2)); + while (pos != 0) {//说明有位置放上皇后 + int p = pos & (-pos);//取最低位1 + pos -= p; + solve(p | row, (p | diag1) << 1, (p | diag2) >> 1); + } +} + +//解法2:DFS 执行用时:18ms +//思路:基于N皇后的解法稍加修改 +Set diag1 = new HashSet<>(), diag2 = new HashSet<>(), col = new HashSet<>(); +int count; +public int totalNQueens(int n) { + recur(n, 0); + return count; +} + +private void recur(int n, int row) { + if (row == n) { + count++; + return; + } + for (int i = 0; i < n; i++) { + if (!diag1.contains(row + i) && !diag2.contains(row - i) & !col.contains(i)) { + diag1.add(row + i); + diag2.add(row - i); + col.add(i); + recur(n, row + 1); + col.remove(i); + diag1.remove(row + i); + diag2.remove(row - i); + } + } +} \ No newline at end of file diff --git a/Week 07/id_273/LeetCode_56_273.java b/Week 07/id_273/LeetCode_56_273.java new file mode 100644 index 000000000..5880637f1 --- /dev/null +++ b/Week 07/id_273/LeetCode_56_273.java @@ -0,0 +1,22 @@ +//56. 合并区间 + +//解法1:遍历比较二维数组 执行用时:11ms +//思路:若相邻的区间, 前一位的末尾大于等于后一位的起始, 我们就可以将其合并为一个区间, 新的区间末尾为前一位末尾和后一位末尾的较大值 +//时间复杂度:O(nlogn * n) +//空间复杂度:O(n) +public int[][] merge(int[][] intervals) { + if (intervals.length == 0 || intervals == null) return intervals; + Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0])); + int[] temp = intervals[0]; + List list = new ArrayList<>(); + list.add(temp); + for (int[] interval : intervals) { + if (interval[0] <= temp[1]) { + temp[1] = Math.max(temp[1], interval[1]); + } else { + temp = interval; + list.add(temp); + } + } + return list.toArray(new int[list.size()][]); +} \ No newline at end of file diff --git a/Week 07/id_273/NOTE.md b/Week 07/id_273/NOTE.md index a6321d6e2..fb0d8b8b5 100644 --- a/Week 07/id_273/NOTE.md +++ b/Week 07/id_273/NOTE.md @@ -1,4 +1,80 @@ # NOTE - +### 第十六课 位运算的基础以及实战要点 + +#### 异或的一些相应的操作(有点类似于乘法分配律的感觉): +1. x^0 = x 1011^0000 = 1011 +2. x^1s = ~x 1011^1111 = 0100 +3. x^(~x) = 1s 1011^0100 = 1111 +4. x^x = 0 1011^1011 = 0 +5. a^b = c --> a^c = b, b^c = a +6. a^b^c = a^(b^c) = (a^c)^b + +#### 位运算常用技巧: +1. 判断奇偶: +x%2 == 1/0 ---> (x&1) == 1/0 +2. 除二, 同理乘二 +x/2 == x >> 1 +3. 清零最低位1 +x&(x-1) +4. 获取最低位1 +x&(-x) +5. x&(~x) == 0 + +### 第十七课 布隆过滤器和LRU缓存 + +#### 布隆过滤器的原理和实现 +1. 布隆过滤器的作用: +判断一个元素是否存在于集合中, 但是只能确定元素一定不在集合, 不能确定元素一定在集合 + +2. 与HashMap的区别: +HashMap在使用时都是先将键值对存储后, 才能够判断集合中是否存在某元素 +而布隆过滤器在使用时只需要存储"某个元素是否存在与集合中"这个boolean值, 而不必存入实体类 +因此, 如果是仅仅需要判断元素是否存在与集合中这么一个操作, 毫无疑问采用布隆过滤器更加高效 + +3. 底层实现 +由一个很长的二进制向量和一系列随机映射函数组成, 若需要判断一个元素是否存在于集合, 只需要通过映射函数计算之后观察二进制向量中对应位置是否都为1, 即可判断元素是否存在某集合 + +4. 布隆过滤器缺点: +删除困难, 模糊查询 + +5. 使用场景: +由于其模糊查询的特性, 布隆过滤器可以充当一个外部缓存使用。假设我们需要在数据表查询某些数据, 如果在进入数据表中检索之前先通过布隆过滤器过滤掉那些一定不可能存在于数据表中的数据, 那么就可以省去很多无用的检索操作, 只需要查询可能存在于数据表的数据即可。 +常用于Redis缓存, Hadoop, 垃圾邮件以及评论的过滤等 + +#### LRU Cahce 最近最少使用缓存 +1. 怎么理解缓存: +我们可以把缓存理解为人脑的记忆, 例如两人同时做一道数学题, A已经背下了解题过程所以解答的速度非常快, B却要慢慢计算才能对问题进行解答。由此可见, 代码模块中的缓存对于提升程序性能有非常大的帮助。 + +2. LRU核心思想: +淘汰最久未使用的, 更新最近使用过的 + +3. 应用场景: +最直观的场景就是智能手机的后台进程, 假设后台进程的最大容量是3个进程, 当开启了3个后台进程后再开启一个新的进程, 此时就会淘汰调那个最久未使用的进程, 然后添加最新使用过的进程 + +4. 底层实现: +HashMap + 双端链表 +采用双端链表+哈希表的数据结构, 保证了put、get方法的时间复杂度都为O(1) +put:涉及了链表的增、删操作, 双端链表数据结构保证了在链表头、尾部的增、删操作的时间复杂度为O(1) +get:涉及了链表的查找操作, 那么通过哈希表存储链表节点的key值, 这样就可以保证在获取指定节点的时间复杂度为O(1) + +### 第十八课 排序算法 + +#### 初级排序和高级排序的实现和特性 +##### 初级排序 +1. 选择排序:每次找最小值, 放在待排序序列的起始位置 + +2. 插入排序:将序列分为待排序序列和已排序序列, 每次从待排序序列中选择第一个元素插入到已排序序列中的正确位置 + +3. 冒泡排序:每次遍历比较相邻元素, 若前者大于后者, 前者与后者交换, 也就是每一次遍历最大的元素会浮到序列最后一个位置 + +##### 高级排序 +1. 快速排序:在序列中选取一个ref, 将小于ref的元素都放在ref左侧, 大于ref的元素都放在ref右侧, 然后对左半区和右半区分治 + +2. 归并排序:将序列分为两个子序列, 然后对这两个子序列递归调用归并排序, 将两个排序好的子序列进行合并 + +3. 堆排序:将序列分为待排序序列和已排序序列, 每次对待排序序列构建大顶堆, 然后将序列首元素与待排序元素末尾进行交换 + +4. 基数排序:每次排序整型序列的个位, 十位, 百位等 + diff --git a/Week 07/id_273/sort/BubbleSort_273.java b/Week 07/id_273/sort/BubbleSort_273.java new file mode 100644 index 000000000..05f49569b --- /dev/null +++ b/Week 07/id_273/sort/BubbleSort_273.java @@ -0,0 +1,15 @@ +//交换排序:冒泡排序 + +//思路:遍历N次, 每一次将当前序列中最大的元素冒泡到序列末尾 +//时间复杂度:O(N^2) +//空间复杂度:O(1) +public int[] bubble(int[] arr) { + for (int i = 0; i < arr.length; i++) { + for (int j = 0; j < arr.length - i - 1; j++) { + if (arr[j] > arr[j + 1]) { + int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; + } + } + } + return arr; +} \ No newline at end of file diff --git a/Week 07/id_273/sort/HeapSort.java b/Week 07/id_273/sort/HeapSort.java new file mode 100644 index 000000000..edee62a79 --- /dev/null +++ b/Week 07/id_273/sort/HeapSort.java @@ -0,0 +1,23 @@ +//堆排序 + +//思路:根据序列构建大顶堆, 将序列首部的最大元素移动到无序区末尾 +public void heapSort(int arr[]) { + for (int i = arr.length - 1; i > 0; i--) { + maxHeap(arr, i);//最大元素移动到首部 + int temp = arr[0]; + arr[0] = arr[i]; + arr[i] = temp; + } +} + +private void maxHeap(int arr[], int n) { + for (int i = (n - 1)/2; i >= 0; i--) { + int child = i * 2 + 1; + if (child != n && arr[child] < arr[child + 1]) { + child++; + } + if (arr[child] > arr[i]) { + int temp = arr[child]; arr[child] = arr[i]; arr[i] = temp; + } + } +} \ No newline at end of file diff --git a/Week 07/id_273/sort/InsertSort_273.java b/Week 07/id_273/sort/InsertSort_273.java new file mode 100644 index 000000000..aaeac4c0f --- /dev/null +++ b/Week 07/id_273/sort/InsertSort_273.java @@ -0,0 +1,15 @@ +//插入排序 + +//思路:将序列分为有序区和无序区, 遍历无序区的所有元素, 将其插入到有序区的正确位置 +//时间复杂度:O(n^2) +//空间复杂度:O(1) +public int[] insert(int[] arr) { + for (int i = 1; i < arr.length; i++) { + for (int j = i; j > 0; j--) { + if (arr[j] < arr[j - 1]) { + int temp = arr[j]; arr[j] = arr[j - 1]; arr[j - 1] = temp; + } + } + } + return arr; +} \ No newline at end of file diff --git a/Week 07/id_273/sort/MergeSort_273.java b/Week 07/id_273/sort/MergeSort_273.java new file mode 100644 index 000000000..566de8db0 --- /dev/null +++ b/Week 07/id_273/sort/MergeSort_273.java @@ -0,0 +1,21 @@ +//归并排序 + +//思路:将序列折半, 对左边和右边的序列分别进行排序后合并 +public void mergeSort(int[] array, int left, int right) { + if (left >= right) return; + int mid = (left + right) >> 1; + mergeSort(array, left, mid); + mergeSort(array, mid + 1, right); + merge(array, left, mid, right); +} + +private void merge(int[] array, int left, int mid, int right) { + int[] temp = new int[right - left + 1]; + int i = left, j = mid + 1, k = 0; + while (i <= mid && j <= right) { + temp[k++] = array[i] < array[j] ? array[i++] : array[j++]; + } + while (i <= mid) temp[k++] = array[i++]; + while (j <= right) temp[k++] = array[j++]; + System.arraycopy(temp, 0, array, left, temp.length); +} \ No newline at end of file diff --git a/Week 07/id_273/sort/QuickSort_273.java b/Week 07/id_273/sort/QuickSort_273.java new file mode 100644 index 000000000..e1588d559 --- /dev/null +++ b/Week 07/id_273/sort/QuickSort_273.java @@ -0,0 +1,24 @@ +//快速排序 + +//思路:将序列根据一个"基准"划分为左右两块区域, 左边都小于等于该基准, 右边都大于该基准, 分别递归对左右两个区域进行重复操作, 直到整个序列有序 +//时间复杂度:O(nlog2N) +//空间复杂度:O(1) +public void quickSort(int[] array, int begin, int end) { + if (begin >= end) return; + int ref = partition(array, begin, end);//基准 + quickSort(array, begin, ref - 1); + quickSort(array, ref + 1, end); +} + +//根据基准划分为左右两块区域 : 这里基准选取序列的末尾元素 +private int partition(int[] array, int begin, int end) { + int count = begin; + for (int i = begin; i < end; i++) { + if (array[i] <= array[end]) { + int temp = array[i]; array[i] = array[count]; array[count] = temp; + count++; + } + int temp = array[count]; array[count] = array[end]; array[end] = temp; + } + return count; +} \ No newline at end of file diff --git a/Week 07/id_273/sort/SelectSort_273.java b/Week 07/id_273/sort/SelectSort_273.java new file mode 100644 index 000000000..8e78173be --- /dev/null +++ b/Week 07/id_273/sort/SelectSort_273.java @@ -0,0 +1,17 @@ +//选择排序 + +//思路:将序列分为有序区与无序区, 遍历无序区, 选择一个最小的元素放入有序区末尾 +//时间复杂度:O(n^2) +//空间复杂度:O(1) +public int[] select(int[] arr) { + for (int i = 0; i < arr.length; i++) { + int min = 0; + for (int j = i; j < arr.length; j++) { + min = arr[j] < arr[min] ? j : min; + } + int temp = arr[i]; + arr[i] = arr[min]; + arr[min] = temp; + } + return arr; +} \ No newline at end of file diff --git a/Week 07/id_278/Leetcode_146_278.py b/Week 07/id_278/Leetcode_146_278.py new file mode 100644 index 000000000..c92e52efb --- /dev/null +++ b/Week 07/id_278/Leetcode_146_278.py @@ -0,0 +1,51 @@ +class LinkedNode: + + def __init__(self, key=None, value=None, next=None): + self.key = key + self.value = value + self.next = next + +class LRUCache: + + def __init__(self, capacity): + self.key_to_prev = {} + self.dummy = LinkedNode() + self.tail = self.dummy + self.capacity = capacity + + def push_back(self, node): + self.key_to_prev[node.key] = self.tail + self.tail.next = node + self.tail = node + + def pop_front(self): + head = self.dummy.next + del self.key_to_prev[head.key] + self.dummy.next = head.next + self.key_to_prev[head.next.key] = self.dummy + + def kick(self, prev): + node = prev.next + if node == self.tail: + return + + prev.next = node.next + self.key_to_prev[node.next.key] = prev + node.next = None + + self.push_back(node) + + def get(self, key): + if key not in self.key_to_prev: + return -1 + self.kick(self.key_to_prev[key]) + return self.key_to_prev[key].next.value + + def put(self, key, value): + if key in self.key_to_prev: + self.kick(self.key_to_prev[key]) + self.key_to_prev[key].next.value = value + else: + self.push_back(LinkedNode(key, value)) + if len(self.key_to_prev) > self.capacity: + self.pop_front() diff --git a/Week 07/id_278/Leetcode_242_278.py b/Week 07/id_278/Leetcode_242_278.py new file mode 100644 index 000000000..16ca6d015 --- /dev/null +++ b/Week 07/id_278/Leetcode_242_278.py @@ -0,0 +1,17 @@ +class Solution(object): + def isAnagram(self, s, t): + """ + :type s: str + :type t: str + :rtype: bool + """ + set_s = [0] * 256 + set_t = [0] * 256 + for i in range(0, len(s)): + set_s[ord(s[i])] += 1 + for i in range(0, len(t)): + set_t[ord(t[i])] += 1 + for i in range(0, 256): + if set_s[i] != set_t[i]: + return False + return True diff --git a/Week 07/id_283/Leetcode_1122_283.java b/Week 07/id_283/Leetcode_1122_283.java new file mode 100644 index 000000000..1e9014645 --- /dev/null +++ b/Week 07/id_283/Leetcode_1122_283.java @@ -0,0 +1,40 @@ +/* + * @lc app=leetcode id=1122 lang=java + * + * [1122] Relative Sort Array + */ + +// @lc code=start +class Solution { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + int max = arr1[0], min = arr1[0]; + for (int i = 0; i < arr1.length; i++){ + if (arr1[i] > max){ + max = arr1[i]; + } + if (arr1[i] < min){ + min = arr1[i]; + } + } + int [] arr1new = new int [max-min+1]; + for (int i = 0; i < arr1.length; i++){ + arr1new[arr1[i] - min]++; + } + int [] ans = new int [arr1.length];int p = 0; + for (int i = 0; i < arr2.length; i++){ + while(arr1new[arr2[i] - min] > 0){ + ans[p++] = arr2[i] ; + arr1new[arr2[i] - min]--; + } + } + for (int i = 0; i < arr1new.length; i++){ + while (arr1new[i] > 0){ + ans[p++] = i + min; + arr1new[i] --; + } + } + return ans; + } +} +// @lc code=end + diff --git a/Week 07/id_283/Leetcode_231_283.java b/Week 07/id_283/Leetcode_231_283.java new file mode 100644 index 000000000..fbf143e26 --- /dev/null +++ b/Week 07/id_283/Leetcode_231_283.java @@ -0,0 +1,14 @@ +/* + * @lc app=leetcode id=231 lang=java + * + * [231] Power of Two + */ + +// @lc code=start +class Solution { + public boolean isPowerOfTwo(int n) { + return n > 0 && (n & (n - 1)) == 0; + } +} +// @lc code=end + diff --git a/Week 07/id_298/lru-cache.py b/Week 07/id_298/lru-cache.py new file mode 100644 index 000000000..0b1e69929 --- /dev/null +++ b/Week 07/id_298/lru-cache.py @@ -0,0 +1,34 @@ +from collections import OrderedDict + +class LURCache(OrderedDict): + def __init__(self, capacity): + """ + :type capacity: int + """ + self.capacity = capacity + + def get(self, key): + """ + :type key: int + :rtype : int + """ + if key not in self: + return -1 + self.move_to_end(key) + return self[key] + + def put(self, key, value): + """ + :type key: int + :type value: int + :rtype void + """ + if key in self: + self.move_to_end(key) + self[key] = value + if len(self) > self.capacity: + self.popitem(lask=False) + +# 使用有序字典数据结构,存取的时间都是O(1) +# 取值的时候根据key查字典 +# 存值的时候如果如果容量满了需要移除最旧的一个元素 diff --git a/Week 07/id_298/n-queens.py b/Week 07/id_298/n-queens.py new file mode 100644 index 000000000..9629b6d66 --- /dev/null +++ b/Week 07/id_298/n-queens.py @@ -0,0 +1,18 @@ +class Solution: + def solveNQueens(self, n: int) -> List[List[str]]: + result = [] + def _dfs(queens, pie, na): + # 皇后的个数 + q = len(queens) + # 递归终止条件 + if q == n: + result.append(queens) + return + # 遍历每个皇后 + for queen in range(n): + # pie是皇后前一个位置的组成,na是皇后后面一个位置的组成 + # 判断当前皇后是否在已经在 已放皇后的列,以放pie的pie中,已放na的na中 + if (queen not in queens) and (q - queen not in pie) and (q + queen not in na): + _dfs(queens + [queen], pie + [q - queen], na + [q + queen]) + _dfs([], [], []) + return [['.' * i + 'Q' + '.' * (n - i - 1) for i in q] for q in result] diff --git a/Week 07/id_298/number-of-1-bits.py b/Week 07/id_298/number-of-1-bits.py new file mode 100644 index 000000000..bed824318 --- /dev/null +++ b/Week 07/id_298/number-of-1-bits.py @@ -0,0 +1,13 @@ +class Solution(object): + def hammingWeight(self, n): + """ + :type n: int + :rtype: int + """ + count = 0 + while n: + count += n & 1 + n >>= 1 + return count +# n & 1 如果n的二进制最后一位是1,则结果是1,如果是0则结果是0 +# 上一步判断一位,右移一位,不断循环知道n为0 diff --git a/Week 07/id_298/power-of-two.py b/Week 07/id_298/power-of-two.py new file mode 100644 index 000000000..893a4c185 --- /dev/null +++ b/Week 07/id_298/power-of-two.py @@ -0,0 +1,12 @@ +class Solution(object): + def isPowerOfTwo(self, n): + """ + :type n: int + :rtype: bool + """ + return n > 0 and n & (n - 1) == 0 + +# 首先如果是2的幂次方,则只有一位为1,其它全为0 +# n & (n -1) 是类似0b1000 和0b0111,按位与为0,则一定是2的幂次方 + + diff --git a/Week 07/id_298/reverse-bits.py b/Week 07/id_298/reverse-bits.py new file mode 100644 index 000000000..7a866a228 --- /dev/null +++ b/Week 07/id_298/reverse-bits.py @@ -0,0 +1,13 @@ +class Solution: + # @param n, an integer + # @return an integer + def reverseBits(self, n): + ans = 0 + for _ in range(32): + ans = (ans << 1) + (n & 1) + n >>= 1 + return ans + +# 定义ans保存颠倒后的值 +# n & 1 是取n的最后一位,追加在向左移位后的ans的末位 +# n 右移1位直到32位处理完 diff --git a/Week 07/id_303/CountingBits.swift b/Week 07/id_303/CountingBits.swift new file mode 100644 index 000000000..0bcefcfb1 --- /dev/null +++ b/Week 07/id_303/CountingBits.swift @@ -0,0 +1,25 @@ +// +// CountingBits.swift + +import Foundation + + +func countBits(_ num: Int) -> [Int] { + guard num > 0 else { + return [0] + } + + var result = [0] + var i = 0 + var total = 1 + + for j in 1...num { + result.append(result[i] + 1) + i += 1 + if i == total { + i = 0 + total = j + 1 + } + } + return result +} diff --git a/Week 07/id_303/LRUCache.swift b/Week 07/id_303/LRUCache.swift new file mode 100644 index 000000000..ea1233918 --- /dev/null +++ b/Week 07/id_303/LRUCache.swift @@ -0,0 +1,92 @@ +// +// LRUCache.swift + +import Foundation +class LRUCache { + class RecencyNode { + let key: Int + var value: Int + var next: RecencyNode? + var prev: RecencyNode? + + init(key: Int, value: Int) { + self.key = key + self.value = value + } + } + + var head: RecencyNode? + var tail: RecencyNode? + + var buckets: [Int: RecencyNode] + + let capacity: Int + init(_ capacity: Int) { + buckets = [Int: RecencyNode]() + buckets.reserveCapacity(capacity) + self.capacity = capacity + } + + func get(_ key: Int) -> Int { + if let existing = buckets[key] { + upgradeRecency(existing) + return existing.value + } + return -1 + } + + func put(_ key: Int, _ value: Int) { + let node: RecencyNode + if let existingNode = buckets[key] { + // value exists for this key, just modify it + existingNode.value = value + node = existingNode + } else { + // new insert, should check for possible eviction + if buckets.count == self.capacity { + evict() + } + node = RecencyNode(key: key, value: value) + } + + upgradeRecency(node) + buckets[key] = node + } + + private func evict() { + guard let oldHead = head else { return } + guard buckets.count >= capacity else { return } + + self.head = oldHead.next + if self.head == nil { + tail = self.head + } + + buckets[oldHead.key] = nil + oldHead.next = nil + oldHead.prev = nil + + } + + private func upgradeRecency(_ node: RecencyNode) { + + if let tail = tail { + if tail === node { return } + if let head = head, head === node { + self.head = head.next + } + + node.prev?.next = node.next + node.next?.prev = node.prev + node.prev = tail + node.next = nil + tail.next = node + self.tail = tail.next + + } else { + // empty list + tail = node + head = tail + } + } +} diff --git a/Week 07/id_303/RelativeSortArray.swift b/Week 07/id_303/RelativeSortArray.swift new file mode 100644 index 000000000..b12b0fe3f --- /dev/null +++ b/Week 07/id_303/RelativeSortArray.swift @@ -0,0 +1,33 @@ +// +// RelativeSortArray.swift + +import Foundation + + +func relativeSortArray(_ arr1: [Int], _ arr2: [Int]) -> [Int] { + var dict = [Int: Int]() + var result = [Int]() + for a in arr1 { + dict[a] = (dict[a] ?? 0)+1 + } + + for a in arr2 { + if let val = dict[a] { + for i in 0.. -1) { + res.push(arr2[i]); + arr1.splice(arr1.indexOf(arr2[i]),1); + }else i++; + } + + return res.concat(arr1.sort((a,b)=>a-b)); + +}; \ No newline at end of file diff --git a/Week 07/id_308/LeedCode_146.js b/Week 07/id_308/LeedCode_146.js new file mode 100644 index 000000000..7067aaa02 --- /dev/null +++ b/Week 07/id_308/LeedCode_146.js @@ -0,0 +1,52 @@ +/** + * 题目: LRU缓存机制 + * 语言: JavaScript + * 执行结果: 打败了77.02%的用户 + * */ + + + +/** + * @param {number} capacity + */ + +class LRUCache{ + constructor(capacity){ + this.capacity = capacity; + this.cache = new Map(); + } + + /** + * @param {number} key + * @return {number} + */ + get (key){ + const cache = this.cache; + if(cache.has(key)){ + const temp = cache.get(key); + cache.delete(key); + cache.set(key,temp); + return temp; + } + + return -1; + } + + /** + * @param {number} key + * @param {number} value + * @return {void} + */ + put (key,value){ + let cache = this.cache; + if(cache.has(key)){ + cache.delete(key); + }else if(cache.size >= this.capacity){ + cache.delete(cache.keys().next().value) + } + + cache.set(key,value); + } +} + + diff --git a/Week 07/id_308/LeedCode_190.js b/Week 07/id_308/LeedCode_190.js new file mode 100644 index 000000000..ce33e7cc9 --- /dev/null +++ b/Week 07/id_308/LeedCode_190.js @@ -0,0 +1,39 @@ +/** + * 题目: 颠倒二进制位 + * 语言: JavaScript + * 执行结果: 打败了25.38%的用户 + * */ + + +/** + * @param {number} n - a positive integer + * @return {number} - a positive integer + */ +const solveNQueens = function(n){ + let answers = [], + uppeeerlim = (1<> 1,uppeeerlim & (rd | current) << 1); + queens.pop(); + } + } + } + + backtrack(0, 0, 0); + return answers; +}; diff --git a/Week 07/id_308/LeedCode_191.js b/Week 07/id_308/LeedCode_191.js new file mode 100644 index 000000000..9395bcaf7 --- /dev/null +++ b/Week 07/id_308/LeedCode_191.js @@ -0,0 +1,19 @@ +/** + * 题目: 位1的个数 + * 语言: JavaScript + * 执行结果: 打败了73.09%的用户 + * */ + +/** + * @param {number} n - a positive integer + * @return {number} + */ +var hammingWeight = function(n) { + let count = 0; + while(n) { + n&=(n-1); + count++; + } + + return count; +}; diff --git a/Week 07/id_308/LeedCode_231.js b/Week 07/id_308/LeedCode_231.js new file mode 100644 index 000000000..0ba3b9b33 --- /dev/null +++ b/Week 07/id_308/LeedCode_231.js @@ -0,0 +1,16 @@ + +/** + * 题目: 2的幂 + * 语言: JavaScript + * 执行结果: 打败了94.00%的用户 + * */ + + + +/** + * @param {number} n + * @return {boolean} + */ +var isPowerOfTwo = function(n) { + return n>0 && (n & (n-1)) === 0; +}; diff --git a/Week 07/id_308/LeedCode_242.js b/Week 07/id_308/LeedCode_242.js new file mode 100644 index 000000000..f4e86501d --- /dev/null +++ b/Week 07/id_308/LeedCode_242.js @@ -0,0 +1,25 @@ +/** + * 题目: 有效的字母异位词 + * 语言: JavaScript + * 执行结果: 打败了61.19%的用户 + * */ + + + +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function(s, t) { + if(s.length !== t.length) return false; + + s = s.split('').sort(); + t = t.split('').sort(); + + for(let i = 0; i < s.length;i++) { + if(s[i] !== t[i]) return false; + } + + return true; +}; \ No newline at end of file diff --git a/Week 07/id_308/LeedCode_338.js b/Week 07/id_308/LeedCode_338.js new file mode 100644 index 000000000..6abc3ad0d --- /dev/null +++ b/Week 07/id_308/LeedCode_338.js @@ -0,0 +1,24 @@ +/** + * 题目: 比特位计数 + * 语言: JavaScript + * 执行结果: 打败了83.64%的用户 + * */ + +/** + * @param {number} num + * @return {number[]} + */ +var countBits = function(num) { + const res = []; + for(let i=0;i<=num;i++) res[i] = totalCount(i); + return res; +}; + +function totalCount(n) { + let count = 0; + while(n) { + n &= (n-1); + count++; + } + return count; +} diff --git a/Week 07/id_308/NOTE.md b/Week 07/id_308/NOTE.md index a6321d6e2..5a1da0bd7 100644 --- a/Week 07/id_308/NOTE.md +++ b/Week 07/id_308/NOTE.md @@ -1,4 +1,66 @@ -# NOTE +#### 位运算 - +- 按位异或(相同为0不同为1) +- 位运算符 + * 按位或 | + * 按位与 & + * 按位取反 ~ + * 按位异或(相同为0不同为1) ^ +- 异或 + * x^0 = x + * x^1s = ~x + * x^(~x) = 1s + * x^x = 0 + * c = a^b -> a^c=b,b^c=a + * a^b^c = a^(b^c) = (a^b)^c +- 实战位运算要点 + * 判断奇偶 + - x%2 == 1 -> (x&1)==1 + - x%2 == 0 -> (x&1)==0 + * x>>1 -> x/2 + 即 x=x/2; -> x=x>>1 + * x=x&(x-1) 清零最低位的1 + * x&-x=> 得到最低位的1 + * x&-x=> 0 + + + +#### 布隆过滤器和LRU缓存 + +- 一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。 +- 优点是空间效率和查询时间都远远超过一般的算法 +- 缺点是有一定的误识别率和删除困难 + +#### LRU Cache - 替换策略 + +- 两个要素:大小、替换策略 +- Hash Table + Double LinkedList +- + * O(1) 查询 + * Q(1) 修改 更新 + + +#### 排序算法 + +- 比较类排序 + * 通过比较来决定元素间相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序 +- 非比较类排序 + * 不通过比较来决定元素间的相对次序,他可以突破基于比较排序的时间下限,以线性时间运行,因此也称为线性时间非比较类排序 +- 初级排序 + * 选择排序 + - 每次找最小值,然后放到待排序数组的起始位置 + * 插入排序 + - 从前到后逐步构建有序序列,对于未排序数组,在已排序序列中从后向前扫描,找到相应位置并插入 + * 冒泡排序 + - 嵌套循环,每次查看相邻的元素如果逆序,则交换 +- 高级排序 + * 快速排序 + - 数组取标杆pivot,将小元素放pivot左边,大元素放右边,然后依次对右边和右边的子数组继续快排;已达到整个序列有序 + * 归并排序 + - 把长度为n的输入序列分为2个长度为n/2的序列 + - 对这2个子序列分别采用归并排序 + - 将2个排序好的子序列合并成一个最终的排序序列 + * 堆排序 - 堆插入O(logN),取最大/小值O(1) + - 数组元素依次建立小顶堆 + - 依次取顶堆元素,并删除 diff --git a/Week 07/id_313/LeetCode_1122_313.go b/Week 07/id_313/LeetCode_1122_313.go new file mode 100644 index 000000000..220a122e9 --- /dev/null +++ b/Week 07/id_313/LeetCode_1122_313.go @@ -0,0 +1,24 @@ +package id_313 + +func relativeSortArray(arr1 []int, arr2 []int) []int { + count := [1001]int{} + for _, a := range arr1 { + count[a]++ + } + + ret := make([]int, 0, len(arr1)) + for _, b := range arr2 { + for count[b] > 0 { + ret = append(ret, b) + count[b]-- + } + } + for i := 0; i < 1001; i++ { + for count[i] > 0 { + ret = append(ret, i) + count[i]-- + } + } + + return ret +} diff --git a/Week 07/id_313/LeetCode_164_313.go b/Week 07/id_313/LeetCode_164_313.go new file mode 100644 index 000000000..6fcf056e5 --- /dev/null +++ b/Week 07/id_313/LeetCode_164_313.go @@ -0,0 +1,76 @@ +package id_313 + +type LRUCache struct { + size int + HashMap map[int]*Node + head *Node + tail *Node +} + +type Node struct { + Key int + Value int + pre *Node + next *Node +} + +func Constructor(capacity int) LRUCache { + lru := LRUCache{size: capacity} + lru.HashMap = make(map[int]*Node, capacity) + return lru +} + +func (this *LRUCache) Get(key int) int { + if v, ok := this.HashMap[key]; ok { + this.refreshNode(v) + return v.Value + } + return -1 +} + +func (this *LRUCache) Put(key int, value int) { + if v, ok := this.HashMap[key]; !ok { + if len(this.HashMap) >= this.size { + old := this.removeNode(this.head) + delete(this.HashMap, old) + } + node := Node{Key: key, Value: value} + this.addNode(&node) + this.HashMap[key] = &node + } else { + v.Value = value + this.refreshNode(v) + } +} + +func (this *LRUCache) refreshNode(node *Node) { + if node == this.tail { + return + } + this.removeNode(node) + this.addNode(node) +} + +func (this *LRUCache) removeNode(node *Node) int { + if node == this.tail { + this.tail = this.tail.pre + } else if node == this.head { + this.head = this.head.next + } else { + node.pre.next = node.next + node.next.pre = node.pre + } + return node.Key +} + +func (this *LRUCache) addNode(node *Node) { + if this.tail != nil { + this.tail.next = node + node.pre = this.tail + node.next = nil + } + this.tail = node + if this.head == nil { + this.head = node + } +} diff --git a/Week 07/id_313/LeetCode_191_313.go b/Week 07/id_313/LeetCode_191_313.go new file mode 100644 index 000000000..1440f279c --- /dev/null +++ b/Week 07/id_313/LeetCode_191_313.go @@ -0,0 +1,14 @@ +package id_313 + +func hammingWeight(num uint32) int { + if num == 0 { + return 0 + } + ret := 0 + for num != 0 { + num = num & (num - 1) + ret++ + } + return ret + +} diff --git a/Week 07/id_313/LeetCode_231_313.go b/Week 07/id_313/LeetCode_231_313.go new file mode 100644 index 000000000..d7494a1a4 --- /dev/null +++ b/Week 07/id_313/LeetCode_231_313.go @@ -0,0 +1,8 @@ +package id_313 + +func isPowerOfTwo(n int) bool { + if n > 0 && n&(n-1) == 0 { + return true + } + return false +} diff --git a/Week 07/id_313/LeetCode_338_313.go b/Week 07/id_313/LeetCode_338_313.go new file mode 100644 index 000000000..ee74344c0 --- /dev/null +++ b/Week 07/id_313/LeetCode_338_313.go @@ -0,0 +1,9 @@ +package id_313 + +func countBits(num int) []int { + ret := make([]int, num+1) + for i := 1; i <= num; i++ { + ret[i] = ret[i&(i-1)] + 1 + } + return ret +} diff --git a/Week 07/id_318/LeetCode_190_318.cpp b/Week 07/id_318/LeetCode_190_318.cpp new file mode 100644 index 000000000..21d58d081 --- /dev/null +++ b/Week 07/id_318/LeetCode_190_318.cpp @@ -0,0 +1,21 @@ +/* + * @lc app=leetcode id=190 lang=cpp + * + * [190] Reverse Bits + */ + +// @lc code=start +class Solution { +public: + uint32_t reverseBits(uint32_t n) { + int i = 32; + uint32_t reverse_n = 0; + while(i--) { + reverse_n = (reverse_n << 1) + (n & 1); + n >>= 1; + } + return reverse_n; + } +}; +// @lc code=end + diff --git a/Week 07/id_318/LeetCode_190_318.js b/Week 07/id_318/LeetCode_190_318.js new file mode 100644 index 000000000..81b462566 --- /dev/null +++ b/Week 07/id_318/LeetCode_190_318.js @@ -0,0 +1,21 @@ +/* + * @lc app=leetcode id=190 lang=javascript + * + * [190] Reverse Bits + */ + +// @lc code=start +/** + * @param {number} n - a positive integer + * @return {number} - a positive integer + */ +var reverseBits = function(n) { + let reverse_n = 0; + for (let i = 0; i < 32; ++i) { + reverse_n = (reverse_n << 1) + (n & 1); + n >>= 1; + } + return reverse_n >>> 0; +}; +// @lc code=end + diff --git a/Week 07/id_318/LeetCode_191_318.cpp b/Week 07/id_318/LeetCode_191_318.cpp new file mode 100644 index 000000000..bfd5fd557 --- /dev/null +++ b/Week 07/id_318/LeetCode_191_318.cpp @@ -0,0 +1,15 @@ +/* + * @lc app=leetcode id=191 lang=cpp + * + * [191] Number of 1 Bits + */ + +// @lc code=start +class Solution { +public: + int hammingWeight(uint32_t n) { + return (n > 0) ? 1 + hammingWeight(n & (n - 1)) : 0; + } +}; +// @lc code=end + diff --git a/Week 07/id_318/LeetCode_191_318.java b/Week 07/id_318/LeetCode_191_318.java new file mode 100644 index 000000000..9113399c2 --- /dev/null +++ b/Week 07/id_318/LeetCode_191_318.java @@ -0,0 +1,20 @@ +/* + * @lc app=leetcode id=191 lang=java + * + * [191] Number of 1 Bits + */ + +// @lc code=start +public class Solution { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int num = 0; + while (n != 0) { + n &= n - 1; + ++num; + } + return num; + } +} +// @lc code=end + diff --git a/Week 07/id_318/LeetCode_231_318.java b/Week 07/id_318/LeetCode_231_318.java new file mode 100644 index 000000000..721101a8e --- /dev/null +++ b/Week 07/id_318/LeetCode_231_318.java @@ -0,0 +1,14 @@ +/* + * @lc app=leetcode id=231 lang=java + * + * [231] Power of Two + */ + +// @lc code=start +class Solution { + public boolean isPowerOfTwo(int n) { + return (n > 0) && (n & (n - 1)) == 0; + } +} +// @lc code=end + diff --git a/Week 07/id_318/LeetCode_231_318.py b/Week 07/id_318/LeetCode_231_318.py new file mode 100644 index 000000000..41c6ce9cd --- /dev/null +++ b/Week 07/id_318/LeetCode_231_318.py @@ -0,0 +1,12 @@ +# +# @lc app=leetcode id=231 lang=python3 +# +# [231] Power of Two +# + +# @lc code=start +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + return (n > 0) and (n & (n - 1)) == 0 +# @lc code=end + diff --git a/Week 07/id_328/LRUCache.java b/Week 07/id_328/LRUCache.java new file mode 100644 index 000000000..ee478dd8e --- /dev/null +++ b/Week 07/id_328/LRUCache.java @@ -0,0 +1,20 @@ +class LRUCache extends LinkedHashMap{ + private int capacity; + + public LRUCache(int capacity) { + super(capacity, 0.75F, true); + this.capacity = capacity; + } + + public int get(int key) { + return super.getOrDefault(key, -1); + } + + public void put(int key, int value) { + super.put(key, value); + } + + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > capacity; + } +} \ No newline at end of file diff --git a/Week 07/id_328/hammingWeight.java b/Week 07/id_328/hammingWeight.java new file mode 100644 index 000000000..822ee3432 --- /dev/null +++ b/Week 07/id_328/hammingWeight.java @@ -0,0 +1,10 @@ +public class Solution { + public int hammingWeight(int n) { + int sum = 0 ; + while(n!=0){ + sum++; + n &= n-1; + } + return sum; + } +} \ No newline at end of file diff --git a/Week 07/id_338/LeetCode_146_338.java b/Week 07/id_338/LeetCode_146_338.java new file mode 100644 index 000000000..f45641f58 --- /dev/null +++ b/Week 07/id_338/LeetCode_146_338.java @@ -0,0 +1,109 @@ +import java.util.Hashtable; + +/** + * @author Leesen + * @date 2019/12/1 23:30 + */ +public class LeetCode_146_338 { + class DLinkedNode { + int key; + int value; + DLinkedNode prev; + DLinkedNode next; + } + + private void addNode(DLinkedNode node) { + /** + * Always add the new node right after head. + */ + node.prev = head; + node.next = head.next; + + head.next.prev = node; + head.next = node; + } + + private void removeNode(DLinkedNode node){ + /** + * Remove an existing node from the linked list. + */ + DLinkedNode prev = node.prev; + DLinkedNode next = node.next; + + prev.next = next; + next.prev = prev; + } + + private void moveToHead(DLinkedNode node){ + /** + * Move certain node in between to the head. + */ + removeNode(node); + addNode(node); + } + + private DLinkedNode popTail() { + /** + * Pop the current tail. + */ + DLinkedNode res = tail.prev; + removeNode(res); + return res; + } + + private Hashtable cache = + new Hashtable(); + private int size; + private int capacity; + private DLinkedNode head, tail; + + public LRUCacheManu_146(int capacity) { + this.size = 0; + this.capacity = capacity; + + head = new DLinkedNode(); + // head.prev = null; + + tail = new DLinkedNode(); + // tail.next = null; + + head.next = tail; + tail.prev = head; + } + + public int get(int key) { + DLinkedNode node = cache.get(key); + if (node == null) return -1; + + // move the accessed node to the head; + moveToHead(node); + + return node.value; + } + + public void put(int key, int value) { + DLinkedNode node = cache.get(key); + + if(node == null) { + DLinkedNode newNode = new DLinkedNode(); + newNode.key = key; + newNode.value = value; + + cache.put(key, newNode); + addNode(newNode); + + ++size; + + if(size > capacity) { + // pop the tail + DLinkedNode tail = popTail(); + cache.remove(tail.key); + --size; + } + } else { + // update the value + node.value = value; + moveToHead(node); + } + } +} diff --git a/Week 07/id_338/LeetCode_190_338.java b/Week 07/id_338/LeetCode_190_338.java new file mode 100644 index 000000000..fe30e8214 --- /dev/null +++ b/Week 07/id_338/LeetCode_190_338.java @@ -0,0 +1,25 @@ +/** + * @author Leesen + * @date 2019/12/1 23:30 + */ +public class LeetCode_190_338 { + // 思路 +// 将给定的二进制数,由低到高位逐个取出 +// 然后通过位运算将其放置到反转后的位置 +// 将上述结果再次通过运算结合到一起 + public int reverseBits(int n) { + int result = 0; + for (int i = 0; i < 32; i++) { + // 1. 将给定的二进制数,由低到高位逐个取出 + // 1.1 右移 i 位, + int tmp = n >> i; //****右移>> + // 1.2 取有效位 + tmp = tmp & 1; + // 2. 然后通过位运算将其放置到反转后的位置. + tmp = tmp << (31 - i); + // 3. 将上述结果再次通过运算结合到一起 + result |= tmp; + } + return result; + } +} diff --git a/Week 07/id_338/LeetCode_338_338.java b/Week 07/id_338/LeetCode_338_338.java new file mode 100644 index 000000000..5d90c3665 --- /dev/null +++ b/Week 07/id_338/LeetCode_338_338.java @@ -0,0 +1,26 @@ +/** + * @author Leesen + * @date 2019/12/1 23:30 + */ +public class LeetCode_338_338 { + //x1=(1001011101) + //x2=(100101110) + //动态规划 + 最低有效位 + public int[] countBits(int num) { + //ans[i] 指int i 二进制数中1的个数 + //num+1 是因为ans[0]到ans[num] + int[] ans = new int[num + 1]; + for (int i = 1; i <= num; ++i) + ans[i] = ans[i >> 1] + (i & 1); // x / 2 is x >> 1 and x % 2 is x & 1 + return ans; + } + + //最后设置位的思路也很巧妙, 利用公式 x = x&(x-1) 清零最低位的1 + //https://leetcode-cn.com/problems/counting-bits/solution/bi-te-wei-ji-shu-by-leetcode/ + public int[] countBits1(int num) { + int[] ans = new int[num + 1]; + for (int i = 1; i <= num; ++i) + ans[i] = ans[i & (i - 1)] + 1; + return ans; + } +} diff --git a/Week 07/id_338/LeetCode_493_338.java b/Week 07/id_338/LeetCode_493_338.java new file mode 100644 index 000000000..7be5c6682 --- /dev/null +++ b/Week 07/id_338/LeetCode_493_338.java @@ -0,0 +1,33 @@ +/** + * @author Leesen + * @date 2019/12/1 23:31 + */ +public class LeetCode_493_338 { + public int reversePairs(int[] nums) { + return reversePairsSub(nums, 0, nums.length - 1); + } + + private int reversePairsSub(int[] nums, int l, int r) { + if (l >= r) return 0; + + int m = l + ((r - l) >> 1); + int res = reversePairsSub(nums, l, m) + reversePairsSub(nums, m + 1, r); + + int i = l, j = m + 1, k = 0, p = m + 1; + int[] merge = new int[r - l + 1]; + + while (i <= m) { + while (p <= r && nums[i] > 2L * nums[p]) p++; + res += p - (m + 1); + + while (j <= r && nums[i] >= nums[j]) merge[k++] = nums[j++]; + merge[k++] = nums[i++]; + } + + while (j <= r) merge[k++] = nums[j++]; + + System.arraycopy(merge, 0, nums, l, merge.length); + + return res; + } +} diff --git a/Week 07/id_338/LeetCode_56_338.java b/Week 07/id_338/LeetCode_56_338.java new file mode 100644 index 000000000..8152e4def --- /dev/null +++ b/Week 07/id_338/LeetCode_56_338.java @@ -0,0 +1,82 @@ +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; + +/** + * @author Leesen + * @date 2019/12/1 23:31 + */ +public class LeetCode_56_338 { + private static class Interval { + int start; + int end; + Interval(int[] interval) { + this.start = interval[0]; + this.end = interval[1]; + } + + int[] toArray() { + return new int[]{this.start, this.end}; + } + } + + private class IntervalComparator implements Comparator { + @Override + public int compare(Interval a, Interval b) { +// return a.start < b.start ? -1 : a.start == b.start ? 0 : 1; + return Integer.compare(a.start, b.start); + } + } + + + public int[][] merge(int[][] intervals) { + List intervalsList = new LinkedList<>(); + for (int[] interval : intervals) { + intervalsList.add(new Interval(interval)); + } + intervalsList.sort(new IntervalComparator()); + + LinkedList merged = new LinkedList(); + for (Interval interval : intervalsList) { + if (merged.isEmpty() || merged.getLast().end < interval.start) { + merged.add(interval); + } else { + merged.getLast().end = Math.max(merged.getLast().end, interval.end); + } + } + + int i = 0; + int[][] result = new int[merged.size()][2]; + for (Interval interval : merged) { + result[i++] = interval.toArray(); + } + return result; + } + + // 上面方法比较啰嗦, 下面这个解法很简练 + // https://leetcode.com/problems/relative-sort-array/discuss/335056/Java-in-place-solution-using-counting-sort + public int[][] merge1(int[][] intervals) { + if (intervals.length <= 1) { + return intervals; + } + + //**** Sort by ascending starting point + Arrays.sort(intervals, (i1, i2) -> Integer.compare(i1[0], i2[0])); + + int[][] result = new int[intervals.length][2]; + int[] newInterval = intervals[0]; + int i = 0; + result[0] = newInterval; + for (int[] interval : intervals) { + if (newInterval[1] >= interval[0]) { // Overlapping intervals, move the end if needed + result[i][1] = Math.max(result[i][1], interval[1]); + } else { // Disjoint intervals, add the new interval to the list + result[++i] = interval; + newInterval = interval; //**** 容易遗漏 + } + } + + return Arrays.copyOfRange(result, 0, i+1); //****要记住这种写法 + } +} diff --git a/Week 07/id_343/LeetCode_191.go b/Week 07/id_343/LeetCode_191.go new file mode 100644 index 000000000..de3b6c2ef --- /dev/null +++ b/Week 07/id_343/LeetCode_191.go @@ -0,0 +1,8 @@ +func hammingWeight(num uint32) int { + sum := 0 + for num > 0 { + sum++ + num &= (num-1) + } + return sum +} diff --git a/Week 07/id_343/LeetCode_231.go b/Week 07/id_343/LeetCode_231.go new file mode 100644 index 000000000..b6a1d9b08 --- /dev/null +++ b/Week 07/id_343/LeetCode_231.go @@ -0,0 +1,3 @@ +func isPowerOfTwo(n int) bool { + return n > 0 && (n & (n-1)) == 0 +} diff --git a/Week 07/id_353/Leetcode_191_353.cpp b/Week 07/id_353/Leetcode_191_353.cpp new file mode 100644 index 000000000..7175d3ece --- /dev/null +++ b/Week 07/id_353/Leetcode_191_353.cpp @@ -0,0 +1,20 @@ +/* + * @lc app=leetcode.cn id=191 lang=cpp + * + * [191] 位1的个数 + */ + +// @lc code=start +class Solution { +public: + int hammingWeight(uint32_t n) { + int count = 0; + while (n != 0) { + n &= (n - 1); + count++; + } + return count; + } +}; +// @lc code=end + diff --git a/Week 07/id_353/Leetcode_231_353.cpp b/Week 07/id_353/Leetcode_231_353.cpp new file mode 100644 index 000000000..1ab988a12 --- /dev/null +++ b/Week 07/id_353/Leetcode_231_353.cpp @@ -0,0 +1,15 @@ +/* + * @lc app=leetcode.cn id=231 lang=cpp + * + * [231] 2的幂 + */ + +// @lc code=start +class Solution { +public: + bool isPowerOfTwo(int n) { + return (n > 0) && (n & (n - 1)) == 0; + } +}; +// @lc code=end + diff --git a/Week 07/id_358/LRU_cache.js b/Week 07/id_358/LRU_cache.js new file mode 100644 index 000000000..ff8223a61 --- /dev/null +++ b/Week 07/id_358/LRU_cache.js @@ -0,0 +1,94 @@ +/** + * @param {number} capacity + */ + var LRUCache = function(capacity) { + this.capacity = capacity + this.hashTable = {} + this.size = 0; + this.head = new Node('head'); + this.tail = new Node('tail'); + this.head.prev = null; + this.head.next = this.tail; + this.tail.prev = this.head; + this.tail.next = null; +}; + +/** + * @param {number} key + * @return {number} + */ +LRUCache.prototype.get = function(key) { + if(this.hashTable[key]) { + this.moveToHead(key); + return this.hashTable[key].value; + } + return -1; +}; + +/** + * @param {number} key + * @param {number} value + * @return {void} + */ +LRUCache.prototype.put = function(key, value) { + if(this.hashTable[key]) { + this.hashTable[key].value = value; + this.moveToHead(key) + } else { + if(this.capacity === this.size) { + this.removeLRU() + } + this.add(key, value) + } + +}; + +LRUCache.prototype.moveToHead = function(key) { + debugger + const node = this.hashTable[key]; + node.prev.next = node.next; + node.next.prev = node.prev; + node.prev = this.head; + node.next = this.head.next; + this.head.next.prev = node; + this.head.next = node; +} + +/** + * @param {number} key + * @param {number} value + * @return {void} + */ +LRUCache.prototype.add = function(key, value) { + const node = new Node(key, value); + this.hashTable[key] = node; + this.size++; + node.prev = this.head; + node.next = this.head.next; + this.moveToHead(key) +} + +LRUCache.prototype.removeLRU = function() { + let node = this.tail.prev; + node.prev.next = this.tail; + this.tail.prev = node.prev; + this.size--; + delete this.hashTable[node.key] + node = null; +} + +/** + * Your LRUCache object will be instantiated and called as such: + * var obj = new LRUCache(capacity) + * var param_1 = obj.get(key) + * obj.put(key,value) + */ + +class Node { + constructor(key,value) { + this.key = key; + this.value = value; + this.next = null; + this.prev = null; + } +} \ No newline at end of file diff --git a/Week 07/id_358/NOTE.md b/Week 07/id_358/NOTE.md index a6321d6e2..d332eba6a 100644 --- a/Week 07/id_358/NOTE.md +++ b/Week 07/id_358/NOTE.md @@ -1,4 +1,260 @@ -# NOTE +# 位运算 - +主要有:与&,或|,非~,异或^ +## 异或的特点 + +重点需要知道异或的特征:可以看成“不进位加法” + +异或有以下特殊情况: + +1. x^0 = x +2. x^1s = ~x // 1s表示非零, x异或非零等于取反 +3. x^(~x) = 1s // x 异或本身取反等于非零 +4. x^x = 0 +5. c = a^b => a^c = b, b ^ c = a; // 用于交换两个数 +6. a^b^c = a^(b^c) = (a^b)^c // 结合律 + +## 指定位置的位运算 + +1. 将x最右边的n位清零: x & (~0 << n) +2. 获取x的第n位值:(x>>n) & 1 +3. 获取x的第n位的幂值: x&(1<<(n-1)) + +## 实际应用中位运算要的 + +1. 判断奇偶 + + x % 2 == 1 => (x&1) === 1 + + x % 2 == 0 => (x&1) === 0 + +2. x >>1 => x / 2 + +3. x = x & (x - 1) 清零最低位的1 + +4. x & -x => 得到最低位的1 + +5. x & ~x => 0 + +## 实战题 + +### 1. 位1 的个数 + +1. 位运算。利用 x & (x - 1) 不断清零最低位的1 + + ```js + sum = 0; + while(n > 0) { + sum ++; + n = n & (n -1) + } + ``` + +### 2. 给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 + + 判断符合条件的二进制数为:有且仅有一个1. + +即满足: n > 0 && (n & (n - 1)) === 0 + +### 3. 颠倒二进制 + +1. Int => string => reverse => int +2. 位运算 + +``` +var reverseBits = function(n) { + let result = 0; + // result从右往移动空出末位 + n从左往右移动获取末位 + n次 = 倒序 + for(let i = 0;i < 32;i++){ + // 左移空出一位 + result <<= 1 + // n&1获取n的末位,result的末位换成n的末位 + result |= n & 1; + // 右移1位 + n >>= 1; + } + return result >>> 0; +}; + +``` + +### 4. n皇后 + +使用二进制存储列,撇,捺 的皇后,开始时都是11111111. + +### 5. 比特位计数 + +DP + 位运算 + +DP 方程递推: + + 奇数:二进制表示中,奇数一定比前面那个偶数多一个 1,因为多的就是最低位的 1 二进制表示中,偶数:二进制表示中,偶数中 1 的个数一定和除以 2 之后的那个数一样多。因为最低位是 0,除以 2 就是右移一位,也就是把那个 0 抹掉而已,所以 1 的个数是不变的。 + +``` +result[0] = 0 +for (i --> n) + if i % 2 ==- 0 + result[i] = result[i - 1] + 1 + else + result[i] = result[i/2] +``` + +# 布隆过滤器 + + 与hashtable类似。 + +通过hashtable+ 双向列表实现 + +## BlooFilter + + 一个很长的二进制向量和一系列随机映射函数,可以用于检索一个元素是否在一个集合中。 + +优点 : 空间效率和查询时间都远超一般算法; + +缺点: 有一定误识别率和删除困难; + +## 实现 + +为每个元素分配一系列的二进制位,值为1. + +用于检索: 元素的所有位都是1 ==》可能存在; + +​ 只要有一位是 0 ==》不存在 + +## 实际应用 + +1. 比特币网络中的存储: transaction in node +2. 分布式系统map-reduce +3. redis缓存 +4. 垃圾邮件,评论等的过滤系统 + +## 实战题 + +### 1. LRU缓存实现 + +两个要素: 有固定大小,替换策略(LRU) + +hashtable + double linked list + +时间复杂度:查询O(1),修改更新O(1) + +最近使用的一定在最前,最少使用的淘汰。 + +实际上有很多算法实现,如今已发展为使用AI代替传统算法。 + +# 排序 + +参考链接: https://www.cnblogs.com/onepixel/p/7674659.html + +## 排序分类 + +### 1. 比较类排序 + +​ 通过比较决定相对顺序 O(nlongn) + +### 2. 非比较类排序 + +#### 1. 初级排序O (n^2) + +1. 选择排序 + + 每次找最小值,然后放到待排序的位置 + +2. 插入排序 + + 从前到后逐步建立有序序列,对于未排序的数据,在已排序的序列中从后向前扫描,找到相应的位置插入。 + +3. 冒泡排序 + + 嵌套循环,每次查看相邻的元素,如果逆序则交换位置。 + +#### 2. 高级排序 + + 1. 快速排序 + + 取标杆pivot,取小元素放到pivot左侧,较大的放到pivot右侧,对左右数据继续快排。 + + ```java + public static void quickSort(int[] array, int begin, int end) { + if (end <= begin) return; + int pivot = partition(array, begin, end); + quickSort(array, begin, pivot - 1); + quickSort(array, pivot + 1, end); + } + + static int partition(int[] a, int begin, int end) { + // pivot: 标杆位置,counter: 小于pivot的元素的个数 + int pivot = end, counter = begin; + for (int i = begin; i < end; i++) { + if (a[i] < a[pivot]) { + int temp = a[counter]; a[counter] = a[i]; a[i] = temp; + counter++; + } + } + int temp = a[pivot]; a[pivot] = a[counter]; a[counter] = temp; + return counter; + } + ``` + + + + 2. 归并排序 + + 1. 把长度为n的输入序列分成两个长度为n/2的子序列; + 2. 对子序列分别采用归并排序 + 3. 合并子序列() + + ```java + public static void mergeSort(int[] array, int left, int right) { + + if (right <= left) return; + int mid = (left + right) >> 1; // (left + right) / 2 + + mergeSort(array, left, mid); + mergeSort(array, mid + 1, right); + merge(array, left, mid, right); + + } + + public static void merge(int[] arr, int left, int mid, int right) { + + int[] temp = new int[right - left + 1]; // 中间数组 + int i = left, j = mid + 1, k = 0; + + while (i <= mid && j <= right) { + temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++]; + } + + while (i <= mid) temp[k++] = arr[i++]; + while (j <= right) temp[k++] = arr[j++]; + + for (int p = 0; p < temp.length; p++) { + arr[left + p] = temp[p]; + } + // 也可以用 System.arraycopy(a, start1, b, start2, length) + } + ``` + + > 快排 vs 归并 + > + > 归并: 先排序左右子数组,然后合并 + > + > 快速: 先调配处左右子数组,然后对左右子数组进行排序 + + + + 3. 堆排序 + + 堆插入O(logN), 取最大/小 O (1) + + 1. 数组元素一次建立小顶堆; + 2. 依次取堆顶,并删除; + + + +#### 特殊排序 + +1. 计数排序 +2. 桶排序 +3. 基数排序 \ No newline at end of file diff --git a/Week 07/id_358/counting-bits.js b/Week 07/id_358/counting-bits.js new file mode 100644 index 000000000..aefb1a8c8 --- /dev/null +++ b/Week 07/id_358/counting-bits.js @@ -0,0 +1,22 @@ +/** + * @param {number} num + * @return {number[]} + * 奇数:1 个数为前一个数的1 的个数+1 + * 偶数:1个数等于下一个乘以2的数的 1 的个数 + * DP方程 + * a[0] = 0 + * if n % 2 === 1 a[n] = a[n-1] + 1 + * if n % 2 === 0 a[n] = a[n/2] + */ + var countBits = function(num) { + let a = new Array(num+1) + a[0] = 0; + for(let i = 1; i <= num; i++) { + if(i%2 === 1) { + a[i] = a[i-1] + 1; + } else if(i % 2 === 0) { + a[i] = a[i/2] + } + } + return a +}; \ No newline at end of file diff --git a/Week 07/id_358/reverse_paris.js b/Week 07/id_358/reverse_paris.js new file mode 100644 index 000000000..0093a891f --- /dev/null +++ b/Week 07/id_358/reverse_paris.js @@ -0,0 +1,37 @@ +/**使用归并排序 + * 数组分成左右两个子数组,在merge过程中,设下标i,j 满足nums[i]/2 > nums[j],右侧移动j,统计循环i这一轮中满足的重要对个数为 j-mid+1(j从mid+1开始,i从0 开始) + * 最后累加个数。 + * @param {number[]} nums + * @return {number} + */ + var reversePairs = function(nums) { + const count = merge_sort(nums,0, nums.length - 1) + + function merge_sort(nums, start, end) { + if(start>=end) return 0; + let mid = start + Math.floor((end - start)/2) + let cnt = merge_sort(nums, start, mid) + merge_sort(nums, mid+1, end) + for(let i = start, j = mid+1; i<=mid; i++) { + while(j<=end && nums[i]/2 > nums[j]){ + j++; + } + cnt += j - (mid + 1); + } + myMerge(nums, start, mid, end) + return cnt; + } + + function myMerge(nums, start, mid, end) { + let temp = []; + let i = start, j = mid+1, k = 0; + while(i <= mid && j <= end) { + temp[k++] = nums[i] <= nums[j] ? nums[i++] : nums[j++] + } + while(i <= mid) temp[k++] = nums[i++] + while(j <= end) temp[k++] = nums[j++] + for(let p = 0; p < end - start +1; p++) { + nums[start+p] = temp[p] + } + } + return count; +}; \ No newline at end of file diff --git a/Week 07/id_363/LeetCode_1122_363.java b/Week 07/id_363/LeetCode_1122_363.java new file mode 100644 index 000000000..95a53a5ad --- /dev/null +++ b/Week 07/id_363/LeetCode_1122_363.java @@ -0,0 +1,41 @@ +package com.test.leetcode.week07; + +import org.junit.Test; + +import java.util.Arrays; + + +public class SolutionRelativeSort1122 { + + + @Test + public void test1() { + System.out.println(Arrays.toString(relativeSortArray(new int[]{2,3,1,3,2,4,6,7,9,2,19}, new int[]{2,1,4,3,9,6}))); + } + + // 计数排序 + public int[] relativeSortArray(int[] arr1, int[] arr2) { + int[] cnt = new int[1001]; + for (int a1 : arr1) { + cnt[a1] ++; + } + int[] res = new int[arr1.length]; + int k = 0; + for (int a2 : arr2) { + while (cnt[a2] -- > 0) { + res[k ++] = a2; + } + } + for (int i = 0; i < 1001; i ++) { + if (k == arr1.length) { + break; + } + while(cnt[i] > 0) { + res[k ++] = i; + cnt[i] --; + } + } + return res; + } + +} diff --git a/Week 07/id_363/LeetCode_146_363.java b/Week 07/id_363/LeetCode_146_363.java new file mode 100644 index 000000000..f29a15613 --- /dev/null +++ b/Week 07/id_363/LeetCode_146_363.java @@ -0,0 +1,171 @@ +package com.test.leetcode.week07; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + + + +public class SolutionLru146 { + + + @Test + public void test1() { + LRUCache cache = new LRUCache( 2 );/* 缓存容量 */ + + cache.put(1, 1); + cache.put(2, 2); + System.out.println(cache.get(1)); // 返回 1 + cache.put(3, 3); // 该操作会使得密钥 2 作废 + System.out.println(cache.get(2)); // 返回 -1 (未找到) + cache.put(4, 4); // 该操作会使得密钥 1 作废 + System.out.println(cache.get(1)); // 返回 -1 (未找到) + System.out.println(cache.get(3)); // 返回 3 + System.out.println(cache.get(4)); // 返回 4 + } + + + + +} + +class LRUCache extends LinkedHashMap { + private int MAX_CACHE_SIZE; + + public LRUCache(int capacity) { + super(capacity, 0.75f, true); + this.MAX_CACHE_SIZE = capacity; + } + + public int get(int key) { + return super.getOrDefault(key, -1); + } + + public void put(int key, int value) { + super.put(key, value); + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > MAX_CACHE_SIZE; + } +} + +class LRUCache3 { + private LinkedHashMap map; + private int MAX_CACHE_SIZE; + private final float LOAD_FACTOR = 0.75f; + + public LRUCache3 (int capacity) { + MAX_CACHE_SIZE = capacity; + map = new LinkedHashMap(capacity, LOAD_FACTOR, true) { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > MAX_CACHE_SIZE; + } + }; + } + + public int get(int key) { + Integer val = map.get(key); + return val == null ? -1 : val; + } + + public void put(int key, int value) { + map.put(key, value); + } +} + +class LRUCache2 { + + private Map map; // key 和 node + private DoubleList doubleList; + private int MAX_CACHE_SIZE; + + public LRUCache2(int capacity) { + MAX_CACHE_SIZE = capacity; + map = new HashMap<>(); + doubleList = new DoubleList(); + } + + public int get(int key) { + Node node = map.get(key); + if (node != null) { + doubleList.remove(node); + doubleList.addFirst(node); + return node.val; + } + return -1; + } + + public void put(int key, int value) { + Node cur = new Node(key, value); + if (map.containsKey(cur.key)) { + doubleList.remove(map.get(cur.key)); + doubleList.addFirst(cur); + map.put(key, cur); + } else { + if (doubleList.getSize() == MAX_CACHE_SIZE) { + Node last = doubleList.removeLast(); + map.remove(last.key); + } + doubleList.addFirst(cur); + map.put(key, cur); + } + } +} + +class Node { + public int key, val; + public Node pre, next; + + public Node(int key, int val) { + this.key = key; + this.val = val; + } +} + +class DoubleList { + private Node head, tail; + private int size; + + public DoubleList () { + this.head = new Node(0,0); + this.tail = new Node(0, 0); + head.next = tail; + tail.pre = head; + size = 0; + } + // 头部添加 + public void addFirst(Node node) { + Node next = head.next; + node.next = next; + next.pre = node; + node.pre = head; + head.next = node; + size ++; + } + // 尾部删除 + public Node removeLast() { + Node last = tail.pre; + if (last == head) { + return null; + } + remove(last); + return last; + } + + public void remove(Node node) { + Node nextNode = node.next; + Node preNode = node.pre; + preNode.next = node.next; + nextNode.pre = node.pre; + size --; + } + public int getSize() { + return this.size; + } +} + diff --git a/Week 07/id_363/LeetCode_190_363.java b/Week 07/id_363/LeetCode_190_363.java new file mode 100644 index 000000000..2e37d18b0 --- /dev/null +++ b/Week 07/id_363/LeetCode_190_363.java @@ -0,0 +1,39 @@ +package com.test.leetcode.week07; + +import org.junit.Test; + + +public class SolutionReverseBit190 { + + + @Test + public void test1() { + int n = Integer.parseUnsignedInt("43261596"); + System.out.println(reverseBits(n)); + } + + + + /** + * n 是无符号整数,表示n的最高为不是符号 + * 从0循环到31 0 和 第31位交换 + * @param n + * @return + */ + public int reverseBits(int n) { + int res = 0; + if (n == 0) { + return res; + } + for (int i = 0; i < 32; i ++) { + // 获取第i位 获取第31-i位 交换复制给n + res <<= 1; + res += n & 1; + n >>>= 1; + } + return res; + + } + + +} diff --git a/Week 07/id_363/LeetCode_191_363.java b/Week 07/id_363/LeetCode_191_363.java new file mode 100644 index 000000000..b55acc2d8 --- /dev/null +++ b/Week 07/id_363/LeetCode_191_363.java @@ -0,0 +1,49 @@ +package com.test.leetcode.week07; + +import org.junit.Test; + + + +public class SolutionNumOfBit191 { + + @Test + public void test1() { + System.out.println(hammingWeight(8)); + System.out.println(hammingWeight(5)); + + System.out.println(hammingWeight2(8)); + System.out.println(hammingWeight2(5)); + } + + /** + * 输入的是一个无符号整数,返回其二进制表达式中1的个数 + * 1.for循环 : 1.判断最后一位是否是1 然后向右移动一位 + * + * 2.位运算 x = x & (x - 1) 清零最后一位的1 + * @param n + * @return + */ + public int hammingWeight(int n) { + int count = 0; + while (n != 0){ + int b = n & 1; + if(b == 1) { + count ++; + } + n = n >>> 1; + } + return count; + } + + + public int hammingWeight2(int n) { + int count = 0; + while (n != 0) { + n = n & (n - 1); // 清零最后一位1 + count ++; + } + return count; + } + + +} diff --git a/Week 07/id_363/LeetCode_231_363.java b/Week 07/id_363/LeetCode_231_363.java new file mode 100644 index 000000000..4ff4de425 --- /dev/null +++ b/Week 07/id_363/LeetCode_231_363.java @@ -0,0 +1,28 @@ +package com.test.leetcode.week07; + +import org.junit.Test; + + +public class SolutionPowerOfTwo231 { + + + @Test + public void test1() { + System.out.println(isPowerOfTwo(8)); + } + + /** + * 如果二进制中只有一位,那么是2的次幂 + * n > 0 && n & (n - 1) == 0; + * @param n + * @return + */ + public boolean isPowerOfTwo(int n) { + return n > 0 && (n & n - 1) == 0; + } + + + + + +} diff --git a/Week 07/id_363/LeetCode_242_363.java b/Week 07/id_363/LeetCode_242_363.java new file mode 100644 index 000000000..7d9548351 --- /dev/null +++ b/Week 07/id_363/LeetCode_242_363.java @@ -0,0 +1,78 @@ +package com.test.leetcode.week02; + +import org.junit.Test; +import org.springframework.test.context.CacheAwareContextLoaderDelegate; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SolutionIsAnagram242 { + + + @Test + public void test1() { + + System.out.println(isAnagram_20191201_1("anagram", "nagaram")); + System.out.println(isAnagram_20191201_2("anagram", "nagaram")); + System.out.println(isAnagram_20191201_3("anagram", "nagaram")); + } + + /** + * 1.排序 + * 2.int[] 计数 + * 3.map计数 + * + * @param s + * @param t + * @return + */ + public boolean isAnagram_20191201_1(String s, String t) { + if (s == null && t == null) return true; + if (s == null || t == null || s.length() != t.length()) return false; + char[] schar = s.toCharArray(); + char[] tchar = t.toCharArray(); + Arrays.sort(schar); + Arrays.sort(tchar); + return Arrays.equals(schar, tchar); + } + + public boolean isAnagram_20191201_2(String s, String t) { + if (s == null && t == null) return true; + if (s == null || t == null || s.length() != t.length()) return false; + int[] cnt = new int[26]; + for (char sc : s.toCharArray()) { + cnt[sc - 'a'] ++; + } + for (char tc : t.toCharArray()) { + cnt[tc - 'a']--; + if (cnt[tc - 'a'] < 0) { + return false; + } + } + return true; + } + + + public boolean isAnagram_20191201_3(String s, String t) { + if (s == null && t == null) return true; + if (s == null || t == null || s.length() != t.length()) return false; + Map cnt = new HashMap<>(); + for (char sc : s.toCharArray()) { + cnt.put(sc, cnt.get(sc) == null ? 1 : cnt.get(sc) + 1); + } + + for (char tc : t.toCharArray()) { + Integer num = cnt.get(tc); + if (num == null || num == 0) { + return false; + } + cnt.put(tc, num - 1); + } + return true; + } + + + +} diff --git a/Week 07/id_363/LeetCode_338_363.java b/Week 07/id_363/LeetCode_338_363.java new file mode 100644 index 000000000..f12676fb4 --- /dev/null +++ b/Week 07/id_363/LeetCode_338_363.java @@ -0,0 +1,51 @@ +package com.test.leetcode.week07; + +import org.junit.Test; + +import java.util.Arrays; + + +public class SolutionCountBit338 { + + + @Test + public void test1() { + System.out.println(Arrays.toString(countBits(5))); + System.out.println(Arrays.toString(countBits2(5))); + System.out.println(Arrays.toString(countBits3(5))); + } + + + public int[] countBits(int num) { + int[] res = new int[num + 1]; + for (int i = 1; i <= num; i ++) { + if ((i & 1) == 1) { + res[i] = res[i - 1] + 1;// 奇数 + } else { + res[i] = res[i >> 1]; + } + } + return res; + } + + // 清除最后一个1 然后加1 + public int[] countBits2(int num) { + int[] res = new int[num + 1]; + for (int i = 1; i <= num; i ++) { + res[i] = res[i & (i - 1)] + 1; + } + return res; + } + + // num/2 + i & 1 + public int[] countBits3(int num) { + int[] res = new int[num + 1]; + for (int i = 1; i <= num; i ++) { + res[i] = res[i >> 1] + (i & 1); + } + return res; + } + + + +} diff --git a/Week 07/id_363/LeetCode_493_363.java b/Week 07/id_363/LeetCode_493_363.java new file mode 100644 index 000000000..22ca0bfbe --- /dev/null +++ b/Week 07/id_363/LeetCode_493_363.java @@ -0,0 +1,73 @@ +package com.test.leetcode.week07; + +import org.junit.Test; + + +public class SolutionReversePair493 { + + + @Test + public void test1() { + System.out.println(reversePairs(new int[]{2147483647,2147483647,2147483647,2147483647,2147483647,2147483647})); + System.out.println(reversePairs(new int[]{12,2,1,17,19,10,5,23,7,20,10,17,22,15,9,18,12,12,16,16,17,8,11,19,2,21,5,19,22,9,17,24,8,8,16,5,2,25,1,0,3,24,25,0,11,7,19,0,5,16,17,4,19,20,20,0,14,4,16,15,11,15,20,11,17,13,3,18,12,6,10,25,12,6,18,6,19,19,18,13,21,9,17,1,1,2,10,15,24,24,22,7,10,23,15,9,1,23,22,15,3,16,23,25,8,18,0,5,1,12,9,0,25,0,13,11,22,5,3,13,10,17,14,24,23,1,8,1,21,18,2,16,21,21,5,3,19,8,23,6,6,3,2,4,13,2,4,14,9,17,23,18,4,23,5,13,25,10,9,14,3,9,11,5,14,18,0,10,13,5,19,17,24,25,4,8,16,14,3,24,18,2,17,22,4,11,18,9,9,7,10,4,24,0,7,0,6,15,18,13,14,20,22,17,22,15,17,9,10,17,13,0,22,22,23,2,21,18,6,10,10,15,14,4,4,18,21,15,0,18,14,0,2,24,6,10,1,8,25,20,13,20,13,20,5,21,21,9,19,8,9,9,5,17,18,18,20,5,17,18,3,7,21,6,0,8,3,3,1,11,0,21,6,15,11,10,13,6,7,21,7,1,1,14,15,20,2,8,21,25,19,12,18,16,0,4,10,19,14,23,6,17,2,15,19,4,13,8,14,4,15,21,4,23,20,3,18,0,12,14,14,19,0,21,18,21,17,13,9,20,17,25,17,21,16,22,4,1,13,20,15,9,7,18,18,7,22,8,18,1,13,0,24,8,12,16,1,3,6,23,16,24,5,0,1,25,3,16,9,4,24,1,11,24,9,16,11,0,2,20,16,0,1,6,19,22,12,3,23,21,4,20,1,0,18,24,10,0,12,21,17,23,0,13,1,25,9,19,0,13,21,23,6,24,25,16,9,8,16,2,22,23,3,7,16,25,11,18,19,4,11,1,25,22,9,11,14,9,3,16,8,5,11,12,15,15,19,15,15,7,17,24,18,9,8,20,23,18,17,7,8,19,23,9,13,4,17,23,21,19,11,22,22,9,3,19,23,11,2,23,8,8,21,15,1,25,7,6,14,6,7,11,3,2,11,14,10,24,3,8,10,1,18,4,6,16,12,18,12,6,5,25,24,25,7,12,17,19,15,8,23,7,6,11,6,16,14,15,13,18,5,9,21,24,8,17,25,21,22,19,24,9,9,25,21,6,25,24,3,15,20,19,13,7,13,3,0,11,2,3,23,4,14,13,7,14,3,2,18,6,1,24,19,11,6,22,9,20,3,15,23,14,18,11,11,0,2,14,21,1,12,8,8,22,10,25,20,15,22,15,21,4,19,23,5,20,4,10,17,9,7,8,11,7,10,2,18,5,24,4,16,22,13,0,11,6,19,8,21,23,24,14,19,6,3,1,17,25,22,9,14,12,15,2,24,23,17,3,3,3,6,11,20,11,0,12,17,0,3,12,24,5,13,11,19,5,2,5,12,20,19,23,2,14,23,19,4,6,15,12,2,24,17,18,9,18,4,12,20,17,19,21,16,15,13,0,17,10,23,22,10,8,20,6,4,13,11,0,3,1,5,19,17,23,17,10,10,7,4,1,20,21,23,21,21,25,2,1,8,22,4,10,16,9,15,12,12,7,3,10,14,11,9,0,7,1,1,18,23,16,6,4,20,17,18,20,17,22,8,19,6,8,14,23,14,14,15,3,24,19,16,18,14,3,6,10,8,22,12,6,8,5,3,20,10,15,19,17,8,10,7,22,0,5,19,18,16,22,24,6,18,19,19,21,1,22,14,0,24,1,20,21,7,2,11,13,10,9,7,13,15,22,2,17,4,1,4,22,22,7,18,3,12,12,7,6,20,15,25,8,13,7,5,1,25,12,1,25,16,3,23,25,9,22,4,11,16,21,20,15,17,16,13,14,20,5,23,9,0,6,3,21,2,7,2,22,7,5,8,17,14,17,8,18,21,22,14,8,15,2,10,24,0,10,23,11,16,22,5,5,19,20,14,2,19,3,25,5,10,14,22,3,5,10,20,22,16,17,22,15,23,10,0,21,17,20,3,15,0,13,17,2,10,20,8,24,5,6,19,9,4,25,11,19,10,3,24,0,10,10,9,21,16,25,6,20,11,7,17,20,10,9,22,19,21,7,0,4,11,1,9,18,18,3,1,25,5,1,20,13,2,7,19,10,13,25,3,23,13,5,10,15,11,15,22,9,10,8,18,0})); + System.out.println(reversePairs(new int[]{1,3,2,3,1})); + System.out.println(reversePairs(new int[]{2,4,3,5,1})); + + } + + /** + * 归并: + * 拆分 合并有序的数组 + * 1. 计算中间索引 2.拆分 3. 合并有序数组 + * 返回重要翻转对的数量 + * i < j && nums[i] > 2 nums[j] + * @param nums + * @return + */ + public int reversePairs(int[] nums) { + if (nums == null || nums.length <= 1) { + return 0; + } + int cnt = doReversePairs(nums, 0, nums.length - 1); + return cnt; + } + + public int doReversePairs(int[] nums, int left, int right) { + if (left >= right) { + return 0; + } + int mid = (left + right) >> 1; + int leftCnt = doReversePairs(nums, left, mid); + int rightCnt = doReversePairs(nums, mid + 1, right); + int intervalCnt = merge(nums, left, mid, right); + return leftCnt + rightCnt + intervalCnt; + } + + private int merge(int[] nums, int left, int mid, int right) { + // 用来计数 + int cnt = 0, cj = mid + 1; + for (int c = left; c <= mid; c ++) { + while (cj <= right && (long)nums[c] > 2 * (long)nums[cj]) { + cj ++; + } + // 记录left那边的数组 每个元素可以贡献的count + cnt += cj - mid - 1; + } + + // 合并两个有序数组 三个while循环 + int[] temp = new int[right - left + 1]; + int i = left, j = mid + 1, k = 0; + while (i <= mid && j <= right) { + temp[k ++] = nums[i] <= nums[j] ? nums[i ++] : nums[j ++]; + } + while (i <= mid) { + temp[k++] = nums[i ++]; + } + while (j <= right) { + temp[k ++] = nums[j++]; + } + System.arraycopy(temp, 0, nums, left, right - left + 1); + return cnt; + } + +} diff --git a/Week 07/id_363/LeetCode_51_363.java b/Week 07/id_363/LeetCode_51_363.java new file mode 100644 index 000000000..8b42a8b3e --- /dev/null +++ b/Week 07/id_363/LeetCode_51_363.java @@ -0,0 +1,88 @@ +package com.test.leetcode.week02; + +import org.junit.Test; + +import java.util.LinkedList; +import java.util.List; + + +public class SolutionNQueens51 { + + + @Test + public void test1() { + System.out.println(solveNQueens_20191130(8)); + } + + + /** + * 从第0行开始递归 + * 递归:循环所有列,如果可以放皇后,那么递归下一行。如果不可以,循环下一列 + * 不能使用位运算的原因:不知道queens中数组怎么放值。 + * + * @param n + * @return + */ + int[] cols_20191130; + int[] pie_20191130; + int[] na_20191130; + int[] queen_20191130; + List> result_20191130; + int n_20191130; + public List> solveNQueens_20191130(int n) { + // 初始化 + n_20191130 = n; + queen_20191130 = new int[n]; + cols_20191130 = new int[n]; + pie_20191130 = new int[2 * n + 1]; + na_20191130 = new int[2 * n + 1]; + result_20191130 = new LinkedList<>(); + // 递归 + helper_20191130(0); + // 返回 + return result_20191130; + } + + private void helper_20191130(int r) { + // termination + if (r == n_20191130) { + result_20191130.add(genResult(queen_20191130)); + return; + } + // process + for (int c = 0; c < n_20191130; c ++) { + if (cols_20191130[c] + pie_20191130[r + c] + na_20191130[r - c + n_20191130] == 0) { + cols_20191130[c] = 1; + pie_20191130[r + c] = 1; + na_20191130[ r - c + n_20191130] = 1; + queen_20191130[r] = c; + + helper_20191130(r + 1); + + cols_20191130[c] = 0; + pie_20191130[r + c] = 0; + na_20191130[ r - c + n_20191130] = 0; + queen_20191130[r] = 0; + } + } + // drill down + // reverse + } + + private List genResult(int[] queen) { + List result = new LinkedList<>(); + for (int i = 0; i < queen.length; i ++) { + StringBuffer buffer = new StringBuffer(); + for (int j = 0; j < queen[i]; j ++) { + buffer.append("."); + } + buffer.append("Q"); + for (int j = queen[i] + 1; j < queen.length; j ++) { + buffer.append("."); + } + result.add(buffer.toString()); + } + return result; + } + +} diff --git a/Week 07/id_363/LeetCode_52_363.java b/Week 07/id_363/LeetCode_52_363.java new file mode 100644 index 000000000..0b642894e --- /dev/null +++ b/Week 07/id_363/LeetCode_52_363.java @@ -0,0 +1,98 @@ +package com.test.leetcode.week07; + +import org.junit.Test; + + +public class SolutionNQueensii52 { + + + @Test + public void test1() { + System.out.println(totalNQueens(4)); + System.out.println(totalNQueens(8)); + System.out.println(totalNQueens_2(4)); + System.out.println(totalNQueens_2(8)); + + } + + + /** + * 返回n皇后不同解法的数量 + * 位运算 + * ld rd 中的1 表示左斜线/右斜线 哪些位置不能放 + * @param n + * @return + */ + int res; + public int totalNQueens(int n) { + int col = 0, ld = 0, rd = 0; + res = 0; + helper(0, n, col, ld, rd); + return res; + } + + private void helper(int r, int n, int col, int ld, int rd) { + // termiantion + if (r == n) { + res ++; + return; + } + // process + int bits = ~(col | ld | rd) & ((1 << n) - 1); + // drill down + while (bits != 0) { + int pick = bits & (-bits); // 保留最后一个1,其他1变为0 + helper(r + 1, n, col | pick, (ld | pick) << 1, (rd | pick) >> 1); + bits &= (bits - 1); // 最后一个1赋值为0 + } + // reverse + } + + + + int[] cols; + int[] pie; + int[] na; + int[] queens; + int count; + public int totalNQueens_2(int n) { + // 初始化 + count = 0; + queens = new int[n]; + cols = new int[n]; + pie = new int[2 * n + 1]; + na = new int[2 * n + 1]; + // 递归 + helper_20191130(0, n); + // 返回 + return count; + + } + + private void helper_20191130(int r, int n) { + // termiantion + if (r == n) { + count ++; + return; + } + // process + for (int c = 0; c < n; c ++) { + if (cols[c] + pie[r + c] + na[ r - c + n] == 0) { + cols[c] = 1; + pie[r + c] = 1; + na[r - c + n] = 1; + queens[r] = c; + helper_20191130(r + 1, n); + cols[c] = 0; + pie[r + c] = 0; + na[r - c + n] = 0; + queens[r] = 0; + } + } + // drill down + // reverse + + } + + +} diff --git a/Week 07/id_363/LeetCode_56_363.java b/Week 07/id_363/LeetCode_56_363.java new file mode 100644 index 000000000..896430031 --- /dev/null +++ b/Week 07/id_363/LeetCode_56_363.java @@ -0,0 +1,59 @@ +package com.test.leetcode.week07; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; + + +public class SolutionMergeIntervals56 { + + + @Test + public void test1() { + int[][] intervals = { + {8,10}, + {2,6}, + {15,18}, + {1,3} + }; + printIntArr(merge(intervals)); + } + + + // 获取第一个元素 排序 + // a=[1, 4] b = [2, 3] + // 区间重叠的条件:a[1] >= b[0] 左边值:a[0] 右边值=Math.max(a[1], b[1]) + public int[][] merge(int[][] intervals) { + if (intervals == null || intervals.length <= 1) { + return intervals; + } + // 排序 + List res = new LinkedList<>(); + Arrays.sort(intervals, Comparator.comparing((a) -> a[0])); + // 循环 + int len = intervals.length, i = 0; + while (i < len) { + int left = intervals[i][0]; + int right = intervals[i][1]; + while (i < len - 1 && intervals[i + 1][0] <= right) { + right = Math.max(right, intervals[i + 1][1]); + i ++; + } + res.add(new int[]{left, right}); + i ++; + } + return res.toArray(new int[0][]); + } + + + public void printIntArr(int[][] arr) { + for (int[] a : arr) { + System.out.println(Arrays.toString(a)); + } + } + + +} diff --git a/Week 07/id_363/NOTE.md b/Week 07/id_363/NOTE.md index a6321d6e2..b61cf6099 100644 --- a/Week 07/id_363/NOTE.md +++ b/Week 07/id_363/NOTE.md @@ -1,4 +1,23 @@ -# NOTE +#### 位运算 +1.位运算符: 与 或 异或 取反 +2.为什么需要位运算?计算机里面数字表示方式和存储格式是二进制。 +####布隆过滤器 +BloomFilter vs HashTable : +1.HashTable 存储元素以及其他属性值。bloomFilter 只能查询元素是否存在 +2.bloomFilter 的内存占用和查询比hashmap快很多。 +3.hashMap可以允许删除且不会误判。bloomFilter 存在误判概率以及删除困难。 +bloomFilter:一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用来检查一个元素是否在一个集合中。 +优点:空间效率和查询效率远远超过一版的算法。 +缺点:有一定的误识别率和删除困难。如果blommFilter 认为数据不存在,那么一定不存在,如果blooFilter认为数据存在,可能这个数据并不存在。 +bloomFilter 一般用来快速判断数据是否不存在,这样不用查后面db。 +####LRU缓存 +要求:加入元素和取出元素的时间复杂度都是O(1) +1.使用LinkedHashMap实现 +2.使用Map和双向链表 +#### 排序 +初级排序: 冒泡排序、插入排序、选择排序 +高级排序: 快排、归并排序、堆排序 (重要) +特殊排序:桶排序、计数排序、基数排序 \ No newline at end of file diff --git a/Week 07/id_373/week07_373_homework/1122.cpp b/Week 07/id_373/week07_373_homework/1122.cpp new file mode 100644 index 000000000..c5aeb06e5 --- /dev/null +++ b/Week 07/id_373/week07_373_homework/1122.cpp @@ -0,0 +1,34 @@ +class Solution +{ +public: + vector relativeSortArray(vector& arr1, vector& arr2) + { + int a_size = arr1.size(), b_size = arr2.size(); + vector Res, Left; + + map m_a, m_b; + for (int i = 0; i < a_size; i++) + m_a[arr1[i]]++; + + for (int i = 0; i < b_size; i++) + m_b[arr2[i]]++; + + + for (int i = 0; i < b_size; i++) + if (m_a.count(arr2[i]) != 0) + for (int j = 0; j < m_a[arr2[i]]; j++) + Res.push_back(arr2[i]); + + + for (int i = 0; i < a_size; i++) + if (m_b.count(arr1[i]) == 0) + Left.push_back(arr1[i]); + sort(Left.begin(), Left.end()); + + + for (int i = 0; i < Left.size(); i++) + Res.push_back(Left[i]); + + return Res; + } +}; diff --git a/Week 07/id_373/week07_373_homework/146.cpp b/Week 07/id_373/week07_373_homework/146.cpp new file mode 100644 index 000000000..a64845252 --- /dev/null +++ b/Week 07/id_373/week07_373_homework/146.cpp @@ -0,0 +1,32 @@ +class LRUCache { +public: + LRUCache(int capacity) : capacity_(capacity) {} + + int get(int key) { + if (hash_.find(key) == hash_.end()) + return -1; + else { + int value = hash_[key]->second; + ls_.erase(hash_[key]); + ls_.push_front(make_pair(key, value)); + hash_[key] = ls_.begin(); + return value; + } + } + + void put(int key, int value) { + if (hash_.find(key) != hash_.end()) + ls_.erase(hash_[key]); + else if (ls_.size() >= capacity_) { + hash_.erase(ls_.back().first); + ls_.pop_back(); + } + ls_.push_front(make_pair(key, value)); + hash_[key] = ls_.begin(); + } + +private: + int capacity_; + list> ls_; + unordered_map>::iterator> hash_; +}; \ No newline at end of file diff --git a/Week 07/id_383/LeetCode_1122_383.java b/Week 07/id_383/LeetCode_1122_383.java new file mode 100644 index 000000000..d99c3bb08 --- /dev/null +++ b/Week 07/id_383/LeetCode_1122_383.java @@ -0,0 +1,25 @@ +public class LeetCode_1122_383 { + + public int[] relativeSortArray(int[] arr1, int[] arr2) { + int[] counter = new int[1001]; + int len = arr1.length; + for(int i = 0; i < len; i++) { + counter[arr1[i]]++; + } + int[] arr3 = new int[len]; + int index = 0; + for(int i = 0; i < arr2.length; i++) { + while(counter[arr2[i]] > 0) { + arr3[index++] = arr2[i]; + counter[arr2[i]]--; + } + } + for(int i = 0; i < 1001; i++) { + while(counter[i] > 0) { + arr3[index++] = i; + counter[i]--; + } + } + return arr3; + } +} diff --git a/Week 07/id_383/LeetCode_242_383.java b/Week 07/id_383/LeetCode_242_383.java new file mode 100644 index 000000000..936b9ea68 --- /dev/null +++ b/Week 07/id_383/LeetCode_242_383.java @@ -0,0 +1,15 @@ +import java.util.Arrays; + +public class LeetCode_242_383 { + + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + char[] chars1 = s.toCharArray(); + char[] chars2 = t.toCharArray(); + Arrays.sort(chars1); + Arrays.sort(chars2); + return Arrays.equals(chars1, chars2); + } +} diff --git a/Week 07/id_388/LeeCode_388_190.java b/Week 07/id_388/LeeCode_388_190.java new file mode 100644 index 000000000..cadda83ed --- /dev/null +++ b/Week 07/id_388/LeeCode_388_190.java @@ -0,0 +1,59 @@ +package com.company.leetcode.editor.cn; +//颠倒给定的 32 位无符号整数的二进制位。 +// +// +// +// 示例 1: +// +// 输入: 00000010100101000001111010011100 +//输出: 00111001011110000010100101000000 +//解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596, +// 因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。 +// +// 示例 2: +// +// 输入:11111111111111111111111111111101 +//输出:10111111111111111111111111111111 +//解释:输入的二进制串 11111111111111111111111111111101 表示无符号整数 4294967293, +//  因此返回 3221225471 其二进制表示形式为 10101111110010110010011101101001。 +// +// +// +// 提示: +// +// +// 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 +// 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 2 中,输入表示有符号整数 -3,输出表示有符号整数 -1073741825。 +// +// +// +// +// 进阶: +//如果多次调用这个函数,你将如何优化你的算法? +// Related Topics 位运算 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + // you need treat n as an unsigned value + public int reverseBits(int n) { + int result = 0; + + for (int i = 0; i < 32; i++) { + result += n & 1; + n >>>= 1; // CATCH: must do unsigned shift + if (i < 31) // CATCH: for last digit, don't shift! + result <<= 1; + } + + return result; + } + + public static void main(String[] args) { + Solution s = new Solution(); + //Wrong Answer: input:00000010100101000001111010011100 Output: 15065253 (00000000111001011110000010100101) Expected: 964176192 (00111001011110000010100101000000) + System.out.println(s.reverseBits(1)); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 07/id_388/LeeCode_388_191.java b/Week 07/id_388/LeeCode_388_191.java new file mode 100644 index 000000000..4d320fbb6 --- /dev/null +++ b/Week 07/id_388/LeeCode_388_191.java @@ -0,0 +1,62 @@ +package com.company.leetcode.editor.cn; +//编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。 +// +// +// +// 示例 1: +// +// 输入:00000000000000000000000000001011 +//输出:3 +//解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。 +// +// +// 示例 2: +// +// 输入:00000000000000000000000010000000 +//输出:1 +//解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。 +// +// +// 示例 3: +// +// 输入:11111111111111111111111111111101 +//输出:31 +//解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。 +// +// +// +// 提示: +// +// +// 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 +// 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。 +// +// +// +// +// 进阶: +//如果多次调用这个函数,你将如何优化你的算法? +// Related Topics 位运算 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_191 { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int count = 0; + + while (n != 0) { + count++; + n &= n - 1; + } + + return count; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// System.out.println(s.hammingWeight(11)); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 07/id_388/LeeCode_388_231.java b/Week 07/id_388/LeeCode_388_231.java new file mode 100644 index 000000000..9bb4375d6 --- /dev/null +++ b/Week 07/id_388/LeeCode_388_231.java @@ -0,0 +1,29 @@ +package com.company.leetcode.editor.cn;//给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 +// +// 示例 1: +// +// 输入: 1 +//输出: true +//解释: 20 = 1 +// +// 示例 2: +// +// 输入: 16 +//输出: true +//解释: 24 = 16 +// +// 示例 3: +// +// 输入: 218 +//输出: false +// Related Topics 位运算 数学 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_231 { + public boolean isPowerOfTwo(int n) { + return n > 0 && ((n & (n - 1)) == 0); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 07/id_388/NOTE.md b/Week 07/id_388/NOTE.md index a6321d6e2..d33ca03f7 100644 --- a/Week 07/id_388/NOTE.md +++ b/Week 07/id_388/NOTE.md @@ -1,4 +1,82 @@ # NOTE - +#### 1、位操作 + - 将x最右边的n位清零: x&(~0<>n)&1 + - 获取x的第n位幂值:x&(1<<(n - 1) + - 仅将第n位置为1:x|(1<{ + private int capacity; + + public LRUCache(int capacity) { + super(capacity, 0.75F, true); + this.capacity = capacity; + } + + public int get(int key) { + return super.getOrDefault(key, -1); + } + + public void put(int key, int value) { + super.put(key, value); + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > capacity; + } + } + + ``` + +#### 4、 + 归并排序 + ``` + public static void mergeSort(int[] array, int left, int right) { + + if (right <= left) return; + int mid = (left + right) >> 1; // (left + right) / 2 + + mergeSort(array, left, mid); + mergeSort(array, mid + 1, right); + merge(array, left, mid, right); + + } + + public static void merge(int[] arr, int left, int mid, int right) { + + int[] temp = new int[right - left + 1]; // 中间数组 + int i = left, j = mid + 1, k = 0; + + while (i <= mid && j <= right) { + temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++]; + } + + while (i <= mid) temp[k++] = arr[i++]; + while (j <= right) temp[k++] = arr[j++]; + + for (int p = 0; p < temp.length; p++) { + arr[left + p] = temp[p]; + } + // 也可以用 System.arraycopy(a, start1, b, start2, length) + } + ``` \ No newline at end of file diff --git a/Week 07/id_393/LeetCode_191_393.java b/Week 07/id_393/LeetCode_191_393.java new file mode 100644 index 000000000..2f10efe8e --- /dev/null +++ b/Week 07/id_393/LeetCode_191_393.java @@ -0,0 +1,19 @@ +package no191; + +/** + * @author boyiren + * @date 2019-12-01 + */ +public class Solution { + public int hammingWeight(int n) { + int bits = 0; + int mask = 1; + for (int i = 0; i < 32; i++) { + if ((n & mask) != 0) { + bits++; + } + mask <<= 1; + } + return bits; + } +} diff --git a/Week 07/id_393/LeetCode_231_393.java b/Week 07/id_393/LeetCode_231_393.java new file mode 100644 index 000000000..959a5449c --- /dev/null +++ b/Week 07/id_393/LeetCode_231_393.java @@ -0,0 +1,11 @@ +package no231; + +/** + * @author boyiren + * @date 2019-12-01 + */ +public class Solution { + public boolean isPowerOfTwo(int n) { + return n > 0 && (n & (n - 1)) == 0; + } +} diff --git a/Week 07/id_408/LeetCode_191_408.c++ b/Week 07/id_408/LeetCode_191_408.c++ new file mode 100644 index 000000000..1d1c6208a --- /dev/null +++ b/Week 07/id_408/LeetCode_191_408.c++ @@ -0,0 +1,12 @@ +class Solution { +public: + int hammingWeight(uint32_t n) { + int an=0; + for(int i=1;i<=32;i++) + { + if(n&1) an++; + n=n>>1; + } + return an; + } +}; diff --git a/Week 07/id_408/LeetCode_242_408.c++ b/Week 07/id_408/LeetCode_242_408.c++ new file mode 100644 index 000000000..970227ad6 --- /dev/null +++ b/Week 07/id_408/LeetCode_242_408.c++ @@ -0,0 +1,16 @@ +class Solution { +public: + bool isAnagram(string s, string t) { + mapmp1; + mapmp2; + for (int i = 0; i < s.size(); ++i) + { + mp1[s[i]]++; + } + for (int i = 0; i < t.size(); ++i) + { + mp2[t[i]]++; + } + return mp1 == mp2; + } +}; diff --git a/Week 07/id_418/LeetCode_146_418.java b/Week 07/id_418/LeetCode_146_418.java new file mode 100644 index 000000000..88ce6addf --- /dev/null +++ b/Week 07/id_418/LeetCode_146_418.java @@ -0,0 +1,138 @@ +package com.ljg.leetcode.week07.a05; + +/** + * LruCache + */ +public class LRUCache { + + public static void main(String[] args) { + LRUCache cache = new LRUCache(2); + + // cache.put(1, 1); + // cache.put(2, 2); + // cache.get(1); // 返回 1 + // cache.put(3, 3); // 该操作会使得密钥 2 作废 + // cache.get(2); // 返回 -1 (未找到) + // cache.put(4, 4); // 该操作会使得密钥 1 作废 + // cache.get(1); // 返回 -1 (未找到) + // cache.get(3); // 返回 3 + // cache.get(4); // 返回 4 + + cache.put(2, 1); + cache.put(1,1); + cache.put(2, 3); + cache.put(4, 1); + cache.get(1); + cache.get(2); + +// ["LRUCache","put","put","put","put","get","get"] +// [[2],[2,1],[1,1],[2,3],[4,1],[1],[2]] + + } + + private int capacity; + private Node head; + + private int size; + + private static class Node { + private int key; + private int value; + private Node next; + } + + public LRUCache(int capacity) { + this.capacity = capacity; + this.size = 0; + Node node = new Node(); + this.head = node; + } + + public int get(int key) { + if (head == null) { + return -1; + } + int count = 0; + Node temp = head.next; + Node preNode = head; + while (temp != null) { + if (temp.key == key) { + + // 将当前node置顶,并返回 + preNode.next = temp.next; + temp.next = head.next; + head.next = temp; + + return temp.value; + } + count++; + + preNode = temp; + temp = temp.next; + } + return -1; + } + + public void put(int key, int value) { + int count = 0; + + Node temp = head.next; + + if (capacity == 1) { + if (head.next == null) { + // 添加 + Node node = new Node(); + node.key = key; + node.value = value; + head.next = node; + } else { + head.next.key = key; + head.next.value = value; + } + return; + } + + Node preNode = head; + + while (temp != null) { + if (temp.key == key) { + temp.value = value; + + preNode.next = temp.next; + temp.next = head.next; + head.next = temp; + return; + } + count++; + + if (count == (capacity - 1)) { + if (temp.next == null) { + continue; + } else if (temp.next.key == key) { + temp.next.value = value; + Node tailNode = temp.next; + temp.next = null; + tailNode.next = head.next; + head.next = tailNode; + return; + } else { + // 删除最后一个元素 + temp.next = null; + count++; + break; + } + } + + preNode = temp; + temp = temp.next; + } + + Node node = new Node(); + node.key = key; + node.value = value; + node.next = head.next; + + head.next = node; + size++; + } +} \ No newline at end of file diff --git a/Week 07/id_418/LeetCode_190_418.java b/Week 07/id_418/LeetCode_190_418.java new file mode 100644 index 000000000..b958d58eb --- /dev/null +++ b/Week 07/id_418/LeetCode_190_418.java @@ -0,0 +1,28 @@ +package com.ljg.leetcode.week07.a03; + +/** + * ReverseBit + */ +public class ReverseBits { + + public static void main(String[] args) { + int n = -3; + + ReverseBits reverseBits = new ReverseBits(); + int n2 = reverseBits.reverseBits(n); + + System.out.println("n2=" + n2); + } + + public int reverseBits(int n) { + int n2 = 0; + int temp; + for (int i = 0; i < 32; i++) { + temp = (n >> i) & 1; + if (temp == 1) { + n2 ^= (temp << (31-i)); + } + } + return n2; + } +} \ No newline at end of file diff --git a/Week 07/id_418/LeetCode_191_418.java b/Week 07/id_418/LeetCode_191_418.java new file mode 100644 index 000000000..c2df447e0 --- /dev/null +++ b/Week 07/id_418/LeetCode_191_418.java @@ -0,0 +1,44 @@ +package com.ljg.leetcode.week07.a01; + +/** + * NumberOfOneBits + */ +public class NumberOfOneBits { + + public static void main(String[] args) { + NumberOfOneBits numberOfOneBits = new NumberOfOneBits(); + int n = 3; + int num = numberOfOneBits.hammingWeight(n); + System.out.println("num:" + num); + } + + /** + * 只支持正整数 + * + * @param n + * @return + */ + public int hammingWeight(int n) { + int num = 0; + while (n > 0) { + num++; + n &= (n - 1); + } + return num; + } + + /** + * 支持正数和负数 + */ + public int hammingWeight2(int n) { + int num = 0; + int temp; + for (int i = 0; i < 32; i++) { + temp = n >> i; + if ((temp & 1) == 1) { + num++; + } + } + return num; + } +} \ No newline at end of file diff --git a/Week 07/id_418/LeetCode_231_418.java b/Week 07/id_418/LeetCode_231_418.java new file mode 100644 index 000000000..c04e751d8 --- /dev/null +++ b/Week 07/id_418/LeetCode_231_418.java @@ -0,0 +1,59 @@ +package com.ljg.leetcode.week07.a02; + +import java.util.ArrayList; +import java.util.List; + +/** + * PowerOfTwo + */ +public class PowerOfTwo { + + public static void main(String[] args) { + PowerOfTwo powerOfTwo = new PowerOfTwo(); + List list = new ArrayList<>(); + list.add(0); + list.add(1); + list.add(2); + list.add(4); + list.add(16); + list.add(218); + list.add(-2147483648); + list.add(-16); + for (int n : list) { + boolean bln = powerOfTwo.isPowerOfTwo(n); + System.out.println("n=" + n + ", bln=" + bln); + } + + } + + public boolean isPowerOfTwo(int n) { + if (n > 0) { + n &= n - 1; + return n == 0; + } else { + return false; + } + } + + public boolean isPowerOfTwo2(int n) { + if (n >= 0) { + int num = hammingWeight(n); + return num == 1; + } else { + return false; + } + + } + + private int hammingWeight(int n) { + int num = 0; + int temp; + for (int i = 0; i < 32; i++) { + temp = n >> i; + if ((temp & 1) == 1) { + num++; + } + } + return num; + } +} \ No newline at end of file diff --git a/Week 07/id_418/LeetCode_493_418.java b/Week 07/id_418/LeetCode_493_418.java new file mode 100644 index 000000000..79ad7e4f1 --- /dev/null +++ b/Week 07/id_418/LeetCode_493_418.java @@ -0,0 +1,86 @@ +package com.ljg.leetcode.week07.a06; + +/** + * ReversePairs + */ +public class ReversePairs { + + public static void main(String[] args) { + + ReversePairs reversePairs = new ReversePairs(); + // int[] nums = new int[] { 1, 3, 2, 3, 1 }; + // int[] nums = new int[] { 2, 4, 3, 5, 1 }; + int[] nums = new int[] { 2147483647, 2147483647, 2147483647, 2147483647, 2147483647, 2147483647 }; + int count = reversePairs.reversePairs(nums); + System.out.println("count:" + count); + int i = 2147483647; + int j = 2 * i; + System.out.println("i=" + i); + System.out.println("j=" + j); + System.out.println("-1 >> 30 = " + (-1 >> 30)); + System.out.println("-1 >>> 30 = " + (-1 >>> 30)); + + System.out.println("-4 >> 1 = " + (-4 >> 1)); + System.out.println("-4 >>> 1 = " + (-4 >>> 1)); + + System.out.println("5 >> 1 = " + (5 >> 1)); + System.out.println("5 / 2.0 = " + (5 / 2.0)); + + } + + public int reversePairs(int[] nums) { + if (nums == null || nums.length < 2) { + return 0; + } + + int count = mergeSort(nums, 0, nums.length - 1); + + return count; + } + + private int mergeSort(int[] nums, int start, int end) { + if (start >= end) { + return 0; + } + + int mid = (start + end) >> 1; + + int count = mergeSort(nums, start, mid) + mergeSort(nums, mid + 1, end); + + int len = nums.length; + int[] arr = new int[len]; + + int i = start; + int j = mid + 1; + + while (i <= mid && j <= end) { + if (nums[i] / 2.0 > nums[j]) { + count += (mid - i + 1); + j++; + } else { + i++; + } + } + + // 排序 + int index = start; + i = start; + j = mid + 1; + while (i <= mid && j <= end) { + arr[index++] = nums[i] < nums[j] ? nums[i++] : nums[j++]; + } + + while (i <= mid) { + arr[index++] = nums[i++]; + } + while (j <= end) { + arr[index++] = nums[j++]; + } + + for (int k = start; k <= end; k++) { + nums[k] = arr[k]; + } + + return count; + } +} \ No newline at end of file diff --git a/Week 07/id_423/LeetCode_191_423.java b/Week 07/id_423/LeetCode_191_423.java new file mode 100644 index 000000000..1288cf334 --- /dev/null +++ b/Week 07/id_423/LeetCode_191_423.java @@ -0,0 +1,13 @@ +class Solution { + public int hammingWeight(int n) { + int bits = 0; + int mask = 1; + for (int i = 0; i < 32; i++) { + if ((n & mask) != 0) { + bits++; + } + mask <<= 1; + } + return bits; + } +} \ No newline at end of file diff --git a/Week 07/id_423/LeetCode_242_423.java b/Week 07/id_423/LeetCode_242_423.java new file mode 100644 index 000000000..0bbaef1ea --- /dev/null +++ b/Week 07/id_423/LeetCode_242_423.java @@ -0,0 +1,18 @@ +class Solution { + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + int[] counter = new int[26]; + for (int i = 0; i < s.length(); i++) { + counter[s.charAt(i) - 'a']++; + counter[t.charAt(i) - 'a']--; + } + for (int count : counter) { + if (count != 0) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/Week 07/id_428/LeeCode_146_428.java b/Week 07/id_428/LeeCode_146_428.java new file mode 100644 index 000000000..da3b31589 --- /dev/null +++ b/Week 07/id_428/LeeCode_146_428.java @@ -0,0 +1,113 @@ +import java.util.Hashtable; +class LRUCache { + + class DLinkedNode { + int key; + int value; + DLinkedNode prev; + DLinkedNode next; + } + + private void addNode(DLinkedNode node) { + /** + * Always add the new node right after head. + */ + node.prev = head; + node.next = head.next; + + head.next.prev = node; + head.next = node; + } + + private void removeNode(DLinkedNode node){ + /** + * Remove an existing node from the linked list. + */ + DLinkedNode prev = node.prev; + DLinkedNode next = node.next; + + prev.next = next; + next.prev = prev; + } + + private void moveToHead(DLinkedNode node){ + /** + * Move certain node in between to the head. + */ + removeNode(node); + addNode(node); + } + + private DLinkedNode popTail() { + /** + * Pop the current tail. + */ + DLinkedNode res = tail.prev; + removeNode(res); + return res; + } + + private Hashtable cache = + new Hashtable(); + private int size; + private int capacity; + private DLinkedNode head, tail; + + public LRUCache(int capacity) { + this.size = 0; + this.capacity = capacity; + + head = new DLinkedNode(); + // head.prev = null; + + tail = new DLinkedNode(); + // tail.next = null; + + head.next = tail; + tail.prev = head; + } + + public int get(int key) { + DLinkedNode node = cache.get(key); + if (node == null) return -1; + + // move the accessed node to the head; + moveToHead(node); + + return node.value; + } + + public void put(int key, int value) { + DLinkedNode node = cache.get(key); + + if(node == null) { + DLinkedNode newNode = new DLinkedNode(); + newNode.key = key; + newNode.value = value; + + cache.put(key, newNode); + addNode(newNode); + + ++size; + + if(size > capacity) { + // pop the tail + DLinkedNode tail = popTail(); + cache.remove(tail.key); + --size; + } + } else { + // update the value. + node.value = value; + moveToHead(node); + } + } + +} + +/** + * Your LRUCache object will be instantiated and called as such: + * LRUCache obj = new LRUCache(capacity); + * int param_1 = obj.get(key); + * obj.put(key,value); + */ \ No newline at end of file diff --git a/Week 07/id_428/LeeCode_191_428.java b/Week 07/id_428/LeeCode_191_428.java new file mode 100644 index 000000000..6883439db --- /dev/null +++ b/Week 07/id_428/LeeCode_191_428.java @@ -0,0 +1,13 @@ + +class Solution { + public int hammingWeight(int n) { + int num=0; + for (int i= 0;i<32;i++) { + if ((n >> i & 0x01) == 1) { + num++; + } + } + return num; + } + +} \ No newline at end of file diff --git a/Week 07/id_428/NOTE.md b/Week 07/id_428/NOTE.md index a6321d6e2..3ff13a168 100644 --- a/Week 07/id_428/NOTE.md +++ b/Week 07/id_428/NOTE.md @@ -1,4 +1,375 @@ -# NOTE +# Week_07_学习总结 - +## 1、学习过程 +​ 1)位运算 2)算法题目演练 + +## 2、本周学习内容(待细化) + +​ 1)位运算 + + + +​ 2)布隆过滤器 + +​ 3)LRU缓存 + +​ 4)排序算法 + +## 3、布隆过滤器 + +本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。 + +相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少,但是缺点是其返回的结果是概率性的,而不是确切的。 + +## 实现原理 + +### HashMap 的问题 + +讲述布隆过滤器的原理之前,我们先思考一下,通常你判断某个元素是否存在用的是什么?应该蛮多人回答 HashMap 吧,确实可以将值映射到 HashMap 的 Key,然后可以在 O(1) 的时间复杂度内返回结果,效率奇高。但是 HashMap 的实现也有缺点,例如存储容量占比高,考虑到负载因子的存在,通常空间是不能被用满的,而一旦你的值很多例如上亿的时候,那 HashMap 占据的内存大小就变得很可观了。 + +还比如说你的数据集存储在远程服务器上,本地服务接受输入,而数据集非常大不可能一次性读进内存构建 HashMap 的时候,也会存在问题。 + +### 布隆过滤器数据结构 + +布隆过滤器是一个 bit 向量或者说 bit 数组,长这样: + + + + + +![img](https:////upload-images.jianshu.io/upload_images/2785001-07e149c32a2608fa.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/600/format/webp) + +image + +如果我们要映射一个值到布隆过滤器中,我们需要使用多个不同的哈希函数生成多个哈希值,并对每个生成的哈希值指向的 bit 位置 1,例如针对值 “baidu” 和三个不同的哈希函数分别生成了哈希值 1、4、7,则上图转变为: + + + + + +![img](https:////upload-images.jianshu.io/upload_images/2785001-12449becdb038afd.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/600/format/webp) + +image + +Ok,我们现在再存一个值 “tencent”,如果哈希函数返回 3、4、8 的话,图继续变为: + + + + + +![img](https:////upload-images.jianshu.io/upload_images/2785001-802577f6332d76b4.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/600/format/webp) + +image + +值得注意的是,4 这个 bit 位由于两个值的哈希函数都返回了这个 bit 位,因此它被覆盖了。现在我们如果想查询 “dianping” 这个值是否存在,哈希函数返回了 1、5、8三个值,结果我们发现 5 这个 bit 位上的值为 0,说明没有任何一个值映射到这个 bit 位上,因此我们可以很确定地说 “dianping” 这个值不存在。而当我们需要查询 “baidu” 这个值是否存在的话,那么哈希函数必然会返回 1、4、7,然后我们检查发现这三个 bit 位上的值均为 1,那么我们可以说 “baidu” 存在了么?答案是不可以,只能是 “baidu” 这个值可能存在。 + +这是为什么呢?答案跟简单,因为随着增加的值越来越多,被置为 1 的 bit 位也会越来越多,这样某个值 “taobao” 即使没有被存储过,但是万一哈希函数返回的三个 bit 位都被其他值置位了 1 ,那么程序还是会判断 “taobao” 这个值存在。 + +## 支持删除么 + +目前我们知道布隆过滤器可以支持 add 和 isExist 操作,那么 delete 操作可以么,答案是不可以,例如上图中的 bit 位 4 被两个值共同覆盖的话,一旦你删除其中一个值例如 “tencent” 而将其置位 0,那么下次判断另一个值例如 “baidu” 是否存在的话,会直接返回 false,而实际上你并没有删除它。 + +如何解决这个问题,答案是计数删除。但是计数删除需要存储一个数值,而不是原先的 bit 位,会增大占用的内存大小。这样的话,增加一个值就是将对应索引槽上存储的值加一,删除则是减一,判断是否存在则是看值是否大于0。 + +## 如何选择哈希函数个数和布隆过滤器长度 + +很显然,过小的布隆过滤器很快所有的 bit 位均为 1,那么查询任何值都会返回“可能存在”,起不到过滤的目的了。布隆过滤器的长度会直接影响误报率,布隆过滤器越长其误报率越小。 + +另外,哈希函数的个数也需要权衡,个数越多则布隆过滤器 bit 位置位 1 的速度越快,且布隆过滤器的效率越低;但是如果太少的话,那我们的误报率会变高。 + + + + + +![img](https:////upload-images.jianshu.io/upload_images/2785001-76dccfbdc9d7bdb1.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/600/format/webp) + +image + +k 为哈希函数个数,m 为布隆过滤器长度,n 为插入的元素个数,p 为误报率。 + 至于如何推导这个公式,我在知乎发布的[文章](https://zhuanlan.zhihu.com/p/43263751)有涉及,感兴趣可以看看,不感兴趣的话记住上面这个公式就行了。 + +## 最佳实践 + +常见的适用常见有,利用布隆过滤器减少磁盘 IO 或者网络请求,因为一旦一个值必定不存在的话,我们可以不用进行后续昂贵的查询请求。 + +另外,既然你使用布隆过滤器来加速查找和判断是否存在,那么性能很低的哈希函数不是个好选择,推荐 MurmurHash、Fnv 这些。 + +### 大Value拆分 + +Redis 因其支持 setbit 和 getbit 操作,且纯内存性能高等特点,因此天然就可以作为布隆过滤器来使用。但是布隆过滤器的不当使用极易产生大 Value,增加 Redis 阻塞风险,因此生成环境中建议对体积庞大的布隆过滤器进行拆分。 + +拆分的形式方法多种多样,但是本质是不要将 Hash(Key) 之后的请求分散在多个节点的多个小 bitmap 上,而是应该拆分成多个小 bitmap 之后,对一个 Key 的所有哈希函数都落在这一个小 bitmap 上。 + +参考: + +https://www.jianshu.com/p/2104d11ee0a2 + +https://blog.csdn.net/a308601801/article/details/87074273 + +https://blog.csdn.net/maizi1045/article/details/80652351 + +## 4、LRU缓存 + +[LRU缓存原理](https://www.cnblogs.com/Jackie-zhang/p/9869652.html) + +LRU(Least Recently Used) LRU是近期最少使用的算法,它的核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象。 + +采用LRU算法的缓存有两种:**LrhCache和DisLruCache**,分别用于实现内存缓存和硬盘缓存,其核心思想都是LRU缓存算法。 + +##### 1.LruCache的介绍 + +LruCache是个泛型类,主要算法原理是把最近使用的对象用强引用(即我们平常使用的对象引用方式)存储在 LinkedHashMap 中。当缓存满时, + +把最近最少使用的对象从内存中移除,并提供了get和put方法来完成缓存的获取和添加操作。 + +##### 2.LruCache的使用 + +LruCache的使用非常简单,我们就已图片缓存为例。 + +[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0);) + +``` + int maxMemory = (int) (Runtime.getRuntime().totalMemory()/1024); + int cacheSize = maxMemory/8; + mMemoryCache = new LruCache(cacheSize){ + @Override + protected int sizeOf(String key, Bitmap value) { + return value.getRowBytes()*value.getHeight()/1024; + } + }; +``` + +[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0);) + +①设置LruCache缓存的大小,一般为当前进程可用容量的1/8。 + +②重写sizeOf方法,计算出要缓存的每张图片的大小。 + +**注意:**缓存的总容量和每个缓存对象的大小所用单位要一致。 + +### 三、LruCache的实现原理 + +LruCache的核心思想很好理解,就是要维护一个缓存对象列表,其中对象列表的排列方式是按照访问顺序实现的, + +即一直没访问的对象,将放在队首,即将被淘汰。而最近访问的对象将放在队头,最后被淘汰。 + +![img](https://img2018.cnblogs.com/blog/891527/201810/891527-20181029112357695-129541770.png) + +**这个队列是由LinkedHashMap来维护。** + +LinkedHashMap是由数组+双向链表 + +其中双向链表的结构可以实现访问顺序和插入顺序,使得LinkedHashMap中的对按照一定顺序排列起来。 + + + +通过下面构造函数来指定LinkedHashMap中双向链表的结构是访问顺序还是插入顺序。 + +``` +public LinkedHashMap(int initialCapacity, + float loadFactor, + boolean accessOrder) { + super(initialCapacity, loadFactor); + this.accessOrder = accessOrder; + } +``` + +其中accessOrder设置为true则为访问顺序,为false,则为插入顺序。LruCache的构造函数中可以看到**正是用了LinkedHashMap的访问顺序**。 + +以具体例子解释: +当设置为true时 + +[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0);) + +``` +public static final void main(String[] args) { + LinkedHashMap map = new LinkedHashMap<>(0, 0.75f, true); + map.put(0, 0); + map.put(1, 1); + map.put(2, 2); + map.put(3, 3); + map.put(4, 4); + map.put(5, 5); + map.put(6, 6); + map.get(1); + map.get(2); + + for (Map.Entry entry : map.entrySet()) { + System.out.println(entry.getKey() + ":" + entry.getValue()); + + } + } +``` + +[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0);) + +输出结果: + +> 0:0 +> 3:3 +> 4:4 +> 5:5 +> 6:6 +> 1:1 +> 2:2 + +即最近访问的最后输出,那么这就正好满足的LRU缓存算法的思想。可见**LruCache巧妙实现,就是利用了LinkedHashMap的这种数据结构。**下面我们在LruCache源码中具体看看,怎么应用LinkedHashMap来实现缓存的添加,获得和删除的。 + +[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0);) + +``` +public LruCache(int maxSize) { + if (maxSize <= 0) { + throw new IllegalArgumentException("maxSize <= 0"); + } + this.maxSize = maxSize; + this.map = new LinkedHashMap(0, 0.75f, true); + } +``` + +[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0);) + +从LruCache的构造函数中可以看到**正是用了LinkedHashMap的访问顺序**。 + +### 3.1 put() 方法 + +[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0);) + +``` +public final V put(K key, V value) { + //不可为空,否则抛出异常 + if (key == null || value == null) { + throw new NullPointerException("key == null || value == null"); + } + V previous; + synchronized (this) { + //插入的缓存对象值加1 + putCount++; + //增加已有缓存的大小 + size += safeSizeOf(key, value); + //向map中加入缓存对象 + previous = map.put(key, value); + //如果已有缓存对象,则缓存大小恢复到之前 + if (previous != null) { + size -= safeSizeOf(key, previous); + } + } + //entryRemoved()是个空方法,可以自行实现 + if (previous != null) { + entryRemoved(false, key, previous, value); + } + //调整缓存大小(关键方法) + trimToSize(maxSize); + return previous; + } +``` + +[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0);) + +可以看到put()方法并没有什么难点,重要的就是在添加过缓存对象后,调用 trimToSize()方法,来判断缓存是否已满,如果满了就要删除近期最少使用的算法。 + +trimToSize()方法(LRU的核心算法) + +[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0);) + +``` +public void trimToSize(int maxSize) { + //死循环 + while (true) { + K key; + V value; + synchronized (this) { + //如果map为空并且缓存size不等于0或者缓存size小于0,抛出异常 + if (size < 0 || (map.isEmpty() && size != 0)) { + throw new IllegalStateException(getClass().getName() + + ".sizeOf() is reporting inconsistent results!"); + } + //如果缓存大小size小于最大缓存,或者map为空,不需要再删除缓存对象,跳出循环 + if (size <= maxSize || map.isEmpty()) { + break; + } + //迭代器获取第一个对象,即队尾的元素,近期最少访问的元素 + Map.Entry toEvict = map.entrySet().iterator().next(); + key = toEvict.getKey(); + value = toEvict.getValue(); + //删除该对象,并更新缓存大小 + map.remove(key); + size -= safeSizeOf(key, value); + evictionCount++; + } + entryRemoved(true, key, value, null); + } + } +``` + +[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0);) + +trimToSize()方法不断地删除LinkedHashMap中队尾的元素,即近期最少访问的,直到缓存大小小于最大值。 + +当调用LruCache的get()方法获取集合中的缓存对象时,就代表访问了一次该元素,将会更新队列,保持整个队列是按照访问顺序排序。这个更新过程就是在LinkedHashMap中的get()方法中完成的。 + +### 3.2 get() 方法 + +[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0);) + +``` +public final V get(K key) { + //key为空抛出异常 + if (key == null) { + throw new NullPointerException("key == null"); + } + + V mapValue; + synchronized (this) { + //获取对应的缓存对象 + //get()方法会实现将访问的元素更新到队列头部的功能 + mapValue = map.get(key); + if (mapValue != null) { + hitCount++; + return mapValue; + } + missCount++; + } +``` + +[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0);) + +其中LinkedHashMap的get()方法如下: + +[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0);) + +``` +public V get(Object key) { + LinkedHashMapEntry e = (LinkedHashMapEntry)getEntry(key); + if (e == null) + return null; + //实现排序的关键方法 + e.recordAccess(this); + return e.value; + } +``` + + **recordAccess()方法**如下: + +``` +void recordAccess(HashMap m) { + LinkedHashMap lm = (LinkedHashMap)m; + //判断是否是访问排序 + if (lm.accessOrder) { + lm.modCount++; + //删除此元素 + remove(); + //将此元素移动到队列的头部 + addBefore(lm.header); + } + } +``` + +**总结**:由此可见LruCache中维护了一个集合LinkedHashMap,该LinkedHashMap是以访问顺序排序的。当调用put()方法时,就会在结合中添加元素,并调用trimToSize()判断缓存是否已满,如果满了就用LinkedHashMap的迭代器删除队尾元素,即近期最少访问的元素。当调用get()方法访问缓存对象时,就会调用LinkedHashMap的get()方法获得对应集合元素,同时会更新该元素到队头。 + +参考: + +https://www.cnblogs.com/Jackie-zhang/p/9869652.html + +https://www.jb51.net/article/134282.htm \ No newline at end of file diff --git a/Week 07/id_433/LeetCode_338_433.py b/Week 07/id_433/LeetCode_338_433.py new file mode 100644 index 000000000..f6f7052f7 --- /dev/null +++ b/Week 07/id_433/LeetCode_338_433.py @@ -0,0 +1,6 @@ +class Solution: + def countBits(self, num: int) -> List[int]: + dp = [0]*(num+1) + for i in range(1, num+1): + dp[i] = dp[i & (i-1)]+1 + return dp \ No newline at end of file diff --git a/Week 07/id_433/LeetCode_52_433.py b/Week 07/id_433/LeetCode_52_433.py new file mode 100644 index 000000000..9b27adec2 --- /dev/null +++ b/Week 07/id_433/LeetCode_52_433.py @@ -0,0 +1,17 @@ +class Solution: + def totalNQueens(self, n: int) -> int: + + def dfs(row, cols, pie, na): + if row == n: + self.res += 1 + return + + bits = (~(cols | pie | na)) & ((1 << n)-1) + while bits: + p = bits & -bits + bits = bits & (bits-1) + dfs(row+1, cols | p, (pie | p) << 1, (na | p) >> 1) + + self.res = 0 + dfs(0, 0, 0, 0) + return self.res \ No newline at end of file diff --git a/Week 07/id_443/LeetCode_190_443.java b/Week 07/id_443/LeetCode_190_443.java new file mode 100644 index 000000000..f4b0c6a30 --- /dev/null +++ b/Week 07/id_443/LeetCode_190_443.java @@ -0,0 +1,57 @@ +//颠倒给定的 32 位无符号整数的二进制位。 +// +// +// +// 示例 1: +// +// 输入: 00000010100101000001111010011100 +//输出: 00111001011110000010100101000000 +//解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596, +// 因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。 +// +// 示例 2: +// +// 输入:11111111111111111111111111111101 +//输出:10111111111111111111111111111111 +//解释:输入的二进制串 11111111111111111111111111111101 表示无符号整数 4294967293, +//  因此返回 3221225471 其二进制表示形式为 10101111110010110010011101101001。 +// +// +// +// 提示: +// +// +// 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 +// 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 2 中,输入表示有符号整数 -3,输出表示有符号整数 -1073741825。 +// +// +// +// +// 进阶: +//如果多次调用这个函数,你将如何优化你的算法? +// Related Topics 位运算 + +package com.modds.alltest.leetcode.editor.cn; + +public class LeetCode_190_443_ReverseBits { + public static void main(String[] args) { + Solution solution = new LeetCode_190_443_ReverseBits().new Solution(); + } + + + //leetcode submit region begin(Prohibit modification and deletion) + public class Solution { + // you need treat n as an unsigned value + public int reverseBits(int n) { + int result = 0; + + for (int i = 0; i < 32; i++) { + result = ((result + (n & 1)) << 1); + n = n >>> 1; + } + return result + n; + } + } +//leetcode submit region end(Prohibit modification and deletion) + +} \ No newline at end of file diff --git a/Week 07/id_443/LeetCode_191_443.java b/Week 07/id_443/LeetCode_191_443.java new file mode 100644 index 000000000..a12c804b1 --- /dev/null +++ b/Week 07/id_443/LeetCode_191_443.java @@ -0,0 +1,61 @@ + //编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。 +// +// +// +// 示例 1: +// +// 输入:00000000000000000000000000001011 +//输出:3 +//解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。 +// +// +// 示例 2: +// +// 输入:00000000000000000000000010000000 +//输出:1 +//解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。 +// +// +// 示例 3: +// +// 输入:11111111111111111111111111111101 +//输出:31 +//解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。 +// +// +// +// 提示: +// +// +// 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 +// 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。 +// +// +// +// +// 进阶: +//如果多次调用这个函数,你将如何优化你的算法? +// Related Topics 位运算 + +package com.modds.alltest.leetcode.editor.cn; + public class LeetCode_191_443_NumberOf1Bits{ + public static void main(String[] args) { + Solution solution = new LeetCode_191_443_NumberOf1Bits().new Solution(); + } + + +//leetcode submit region begin(Prohibit modification and deletion) +public class Solution { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int count = 0; + while( n !=0 ){ + n = n & n-1; + count++; + } + return count; + } +} +//leetcode submit region end(Prohibit modification and deletion) + + } \ No newline at end of file diff --git a/Week 07/id_468/leetcode1122_7_468.java b/Week 07/id_468/leetcode1122_7_468.java new file mode 100644 index 000000000..87f4cf3f2 --- /dev/null +++ b/Week 07/id_468/leetcode1122_7_468.java @@ -0,0 +1,35 @@ +package week7; + +/** + * @program: leetcode + * @description: 1122. Relative Sort Array + * @author: 王瑞全 + * @create: 2019-12-0122:26 + **/ + + +public class leetcode1122_7_468 { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + //count frequency + int[] f = new int[1001]; + for (int i : arr1) f[i]++; + + //arrange arr1 based on arr2 + int index = 0; + for (int i : arr2) { + while(f[i] > 0) { + arr1[index++] = i; + f[i]--; + } + } + + //sort the rest of the numbers not in arr2 + for (int i = 0; i < 1001; i++){ + while(f[i] > 0) { + arr1[index++] = i; + f[i]--; + } + } + return arr1; + } +} diff --git a/Week 07/id_468/leetcode146_7_468.java b/Week 07/id_468/leetcode146_7_468.java new file mode 100644 index 000000000..9aeba935b --- /dev/null +++ b/Week 07/id_468/leetcode146_7_468.java @@ -0,0 +1,55 @@ +package week7; + +import java.util.HashMap; +import java.util.Map; + +/** + * @program: leetcode + * @description: 146. LRU缓存机制 + * @author: 王瑞全 + * @create: 2019-12-0122:15 + **/ + + +public class leetcode146_7_468 { + class LRUCache { + private Map KmapV; + private Map KmapTime; + private int capacity; + private int time; + + public LRUCache(int capacity) { + KmapV = new HashMap<>(capacity); + KmapTime = new HashMap<>(capacity); + this.capacity = capacity; + time = 0; + } + + public int get(int key) { + int value = KmapV.getOrDefault(key, -1); + if(value != -1) { + time++; + KmapTime.put(key, time); + } + return value; + } + + public void put(int key, int value) { + time++; + if(KmapV.size() == capacity && !KmapV.containsKey(key)) { + int t = time; + int lcu = 0; + for(int k : KmapTime.keySet()) { + if(KmapTime.get(k) < t) { + t = KmapTime.get(k); + lcu = k; + } + } + KmapTime.remove(lcu); + KmapV.remove(lcu); + } + KmapV.put(key, value); + KmapTime.put(key, time); + } + } +} diff --git a/Week 07/id_468/leetcode190_7_468.java b/Week 07/id_468/leetcode190_7_468.java new file mode 100644 index 000000000..d5e4d5643 --- /dev/null +++ b/Week 07/id_468/leetcode190_7_468.java @@ -0,0 +1,25 @@ +package week7; + +/** + * @program: leetcode + * @description: + * @author: 王瑞全 + * @create: 2019-12-0122:11 + **/ + + +public class leetcode190_7_468 { + public int reverseBits(int n) { + int num = 0; + int mask = 1; + for (int i = 0 ; i < 32; i++) { + num |= (mask & n); + n >>= 1; + if (i < 31) { + num <<= 1; + } + } + + return num; + } +} diff --git a/Week 07/id_468/leetcode191_7_468.java b/Week 07/id_468/leetcode191_7_468.java new file mode 100644 index 000000000..922e1a3cc --- /dev/null +++ b/Week 07/id_468/leetcode191_7_468.java @@ -0,0 +1,21 @@ +package week7; + +/** + * @program: leetcode + * @description: 位1的个数 + * @author: 王瑞全 + * @create: 2019-12-0121:01 + **/ + + +public class leetcode191_7_468 { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int count=0; + while (n!=0){ + count+=n&1; + n>>>=1; + } + return count; + } +} diff --git a/Week 07/id_468/leetcode231_7_468.java b/Week 07/id_468/leetcode231_7_468.java new file mode 100644 index 000000000..d642035b9 --- /dev/null +++ b/Week 07/id_468/leetcode231_7_468.java @@ -0,0 +1,21 @@ +package week7; + +/** + * @program: leetcode + * @description: + * @author: 王瑞全 + * @create: 2019-12-0121:31 + **/ + + +public class leetcode231_7_468 { + public boolean isPowerOfTwo(int n) { + if(n <= 0) { + return false; + } + while((n & 1) != 1) { + n = n >> 1; + } + return n == 1; + } +} diff --git a/Week 07/id_468/leetcode242_7_468.java b/Week 07/id_468/leetcode242_7_468.java new file mode 100644 index 000000000..feafdb2e4 --- /dev/null +++ b/Week 07/id_468/leetcode242_7_468.java @@ -0,0 +1,19 @@ +package week7; + +/** + * @program: leetcode + * @description: + * @author: 王瑞全 + * @create: 2019-12-0122:52 + **/ + + +public class leetcode242_7_468 { + public boolean isAnagram(String s, String t) { + int[] alphabet = new int[26]; + for (int i = 0; i < s.length(); i++) alphabet[s.charAt(i) - 'a']++; + for (int i = 0; i < t.length(); i++) alphabet[t.charAt(i) - 'a']--; + for (int i : alphabet) if (i != 0) return false; + return true; + } +} diff --git a/Week 07/id_468/leetcode439_7_468.java b/Week 07/id_468/leetcode439_7_468.java new file mode 100644 index 000000000..d645524f2 --- /dev/null +++ b/Week 07/id_468/leetcode439_7_468.java @@ -0,0 +1,32 @@ +package week7; + +/** + * @program: leetcode + * @description: + * @author: 王瑞全 + * @create: 2019-12-0123:14 + **/ + + +public class leetcode439_7_468 { + public int reversePairs(int[] nums) { + if (nums == null || nums.length == 0) return 0; + return mergeSort(nums, 0, nums.length - 1); + } + private int mergeSort(int[] nums, int l, int r) { + if (l >= r) return 0; + int mid = l + (r - l)/2; + int count = mergeSort(nums, l, mid) + mergeSort(nums, mid + 1, r); + int[] cache = new int[r - l + 1]; + int i = l, t = l, c = 0; + for (int j = mid + 1; j <= r; j++, c++) { + while (i <= mid && nums[i] <= 2 * (long)nums[j]) i++; + while (t <= mid && nums[t] < nums[j]) cache[c++] = nums[t++]; + cache[c] = nums[j]; + count += mid - i + 1; + } + while (t <= mid) cache[c++] = nums[t++]; + System.arraycopy(cache, 0, nums, l, r - l + 1); + return count; + } +} diff --git a/Week 07/id_468/leetcode51_7_468.java b/Week 07/id_468/leetcode51_7_468.java new file mode 100644 index 000000000..db5a0d5ee --- /dev/null +++ b/Week 07/id_468/leetcode51_7_468.java @@ -0,0 +1,79 @@ +package week7; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @program: leetcode + * @description: + * @author: 王瑞全 + * @create: 2019-12-0122:18 + **/ + + +public class leetcode51_7_468 { + public List> solveNQueens(int n) { + List> result = new ArrayList<>(n); + solve(result, new int[n], 0, new char[n]); + return result; + } + + // rescursive solution + void solve(List> result, int[] board, int x, char[] buffer) { + int n = board.length; + if (x == n) { + result.add(print(board, buffer)); + return; + } + for (int y=0; y < n; y++) { + if (valid(board, x, y)) { + board[x] = y; + solve(result, board, x + 1, buffer); + } + } + } + + // checks if the x,y is valid (only consideres left half of board) + boolean valid(int[] board, int x, int y) { + + // horizontal going to the left + for (int i=0; i < x; i++) { + if (board[i] == y) + return false; + } + + // diagonal going up and to the left + int cx = x-1, cy = y-1; + while (cx >= 0 && cy >= 0) { + if (board[cx] == cy) + return false; + cx--; + cy--; + } + + // diagonal going down and to the left + cx = x-1; cy = y + 1; + while (cx >= 0 && cy < board.length) { + if (board[cx] == cy) + return false; + cx--; + cy++; + } + + return true; + } + + // prints the board + List print(int[] board, char[] buffer) { + int n = board.length; + ArrayList list = new ArrayList<>(n); + for (int y=0; y < n; y++) { + for (int x=0; x < n; x++) { + buffer[x] = board[x] == y ? 'Q' : '.'; + } + list.add(new String(buffer)); + } + return list; + } +} diff --git a/Week 07/id_468/leetcode56_7_468.java b/Week 07/id_468/leetcode56_7_468.java new file mode 100644 index 000000000..a7851296e --- /dev/null +++ b/Week 07/id_468/leetcode56_7_468.java @@ -0,0 +1,38 @@ +package week7; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @program: leetcode + * @description: 56. Merge Intervals + * @author: 王瑞全 + * @create: 2019-12-0122:58 + **/ + + +public class leetcode56_7_468 { + public int[][] merge(int[][] intervals) { + if (intervals.length <= 1) + return intervals; + + // Sort by ascending starting point + Arrays.sort(intervals, (i1, i2) -> Integer.compare(i1[0], i2[0])); + + List result = new ArrayList<>(); + int[] newInterval = intervals[0]; + result.add(newInterval); + for (int[] interval : intervals) { + if (interval[0] <= newInterval[1]) // Overlapping intervals, move the end if needed + newInterval[1] = Math.max(newInterval[1], interval[1]); + else { // Disjoint intervals, add the new interval to the list + newInterval = interval; + result.add(newInterval); + } + } + + return result.toArray(new int[result.size()][]); + + } +} diff --git a/Week 07/id_473/LeetCode_1122.java b/Week 07/id_473/LeetCode_1122.java new file mode 100644 index 000000000..a2712d0b2 --- /dev/null +++ b/Week 07/id_473/LeetCode_1122.java @@ -0,0 +1,47 @@ +package LeetCode; + +import java.util.HashMap; +import java.util.Map; + +/** + * 1122.数组的相对排序 + * + * @author CJ + * @date 2019年12月01日 19:18 + */ +public class LeetCode_1122 { + public static void main(String[] args) { + int[] arr1 = new int[]{2,3,1,3,2,4,6,7,9,2,19}; + int[] arr2 = new int[]{2,1,4,3,9,6}; + int[] ints = relativeSortArray(arr1, arr2); + for (int i: + ints) { + System.out.println(i); + } + } + public static int[] relativeSortArray(int[] arr1, int[] arr2) { + int[] map = new int[1001]; + + int[] result = new int[arr1.length]; + //在m数组中给对应的数字位置计数, + for(int i = 0; i < arr1.length; i++) { + map[arr1[i]]++; + } + + int cnt = 0;//创建从0开始从左到右的指针,用来进行指定赋值 + for(int i = 0; i < arr2.length; i++) { + while(map[arr2[i]] > 0) { + result[cnt++] = arr2[i]; + map[arr2[i]]--; + } + } + //将剩余的值按顺序放到result后面 + for(int i = 0; i < 1001; i++) { + while(map[i] > 0) { + result[cnt++] = i; + map[i]--; + } + } + return result; + } +} diff --git a/Week 07/id_473/LeetCode_242.java b/Week 07/id_473/LeetCode_242.java new file mode 100644 index 000000000..ec45e4fd3 --- /dev/null +++ b/Week 07/id_473/LeetCode_242.java @@ -0,0 +1,27 @@ +package LeetCode; + +import java.util.Arrays; + +/** + * 242.有效的字母异位词 + * + * @author CJ + * @date 2019年12月02日 2:21 + */ +public class LeetCode_242 { + public static void main(String[] args) { + String s = "anagram",t ="nagaram"; + System.out.println(isAnagram(s, t)); + } + public static boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + char[] str1 = s.toCharArray(); + char[] str2 = t.toCharArray(); + //将两个字符数组进行排序后通过equals函数进行比较 + Arrays.sort(str1); + Arrays.sort(str2); + return Arrays.equals(str1, str2); + } +} diff --git a/Week 07/id_473/NOTE.md b/Week 07/id_473/NOTE.md index a6321d6e2..aa88d43e9 100644 --- a/Week 07/id_473/NOTE.md +++ b/Week 07/id_473/NOTE.md @@ -1,4 +1,95 @@ -# NOTE +# 第七周学习总结 +#### 位运算 + + +- 位运算符 + - 左移 << (0110 << 1100) + - 右移 >> (0110 >> 0011) + - 与 & (0011 & 1011 = 0011) + - 或 | (0011 | 1011 = 1011) + - 取反 ~ (~0011 = 1100) + - 异或 ^ (0011 ^ 1011 = 1000) +- 算数移位与逻辑移位 +- 位运算的应用 + + +#### 布隆过滤器 + +优点:空间效率和查询时间都远远超过一般的算法。 + +缺点:有一定的误识别率和删除困哪。 + +#### LRUCache +LRU : least recent use(最近最少使用的) +- 两个要素:大小、替换策略 +- HashTable + Double LinkedList +- O(1)查询, + O(1)修改、更新 + +#### 排序 + +初级排序实现(JAVA): + +冒泡排序: +``` + public static int[] bubbleSort(int[] array) { + if (array.length == 0){ + return array; + } + + for (int i = 0; i < array.length; i++){ + for (int j = 0; j < array.length - 1 - i; j++){ + if (array[j + 1] < array[j]) { + int temp = array[j + 1]; + array[j + 1] = array[j]; + array[j] = temp; + } + } + } + return array; + } +``` + +选择排序: +``` + public static int[] selectionSort(int[] array) { + if (array.length == 0) + return array; + for (int i = 0; i < array.length; i++) { + int minIndex = i; + for (int j = i; j < array.length; j++) { + if (array[j] < array[minIndex]) //找到最小的数 + minIndex = j; //将最小数的索引保存 + } + int temp = array[minIndex]; + array[minIndex] = array[i]; + array[i] = temp; + } + return array; + } + +``` + +插入排序: +``` + + public static int[] insertionSort(int[] array) { + if (array.length == 0) { + return array; + } + int current; + for (int i = 0; i < array.length - 1; i++) { + current = array[i + 1]; + int preIndex = i; + while (preIndex >= 0 && current < array[preIndex]) { + array[preIndex + 1] = array[preIndex]; + preIndex--; + } + array[preIndex + 1] = current; + } + return array; + } +``` diff --git "a/Week 07/id_493/1122.\346\225\260\347\273\204\347\232\204\347\233\270\345\257\271\346\216\222\345\272\217.js" "b/Week 07/id_493/1122.\346\225\260\347\273\204\347\232\204\347\233\270\345\257\271\346\216\222\345\272\217.js" new file mode 100644 index 000000000..5c4ed0c1e --- /dev/null +++ "b/Week 07/id_493/1122.\346\225\260\347\273\204\347\232\204\347\233\270\345\257\271\346\216\222\345\272\217.js" @@ -0,0 +1,31 @@ +/* + * @lc app=leetcode.cn id=1122 lang=javascript + * + * [1122] 数组的相对排序 + */ + +// @lc code=start +/** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ +var relativeSortArray = function(arr1, arr2) { + arr1.sort((a, b) => { + let indexa = arr2.indexOf(a); + let indexb = arr2.indexOf(b); + if (indexa != -1 && indexb != -1) { + return indexa - indexb; + } + if (indexa != -1) { + return -1; + } + if (indexb != -1) { + return 1; + } + return a - b; + }); + return arr1; +}; +// @lc code=end + diff --git "a/Week 07/id_493/191.\344\275\215-1-\347\232\204\344\270\252\346\225\260.js" "b/Week 07/id_493/191.\344\275\215-1-\347\232\204\344\270\252\346\225\260.js" new file mode 100644 index 000000000..49e3c5c6a --- /dev/null +++ "b/Week 07/id_493/191.\344\275\215-1-\347\232\204\344\270\252\346\225\260.js" @@ -0,0 +1,21 @@ +/* + * @lc app=leetcode.cn id=191 lang=javascript + * + * [191] 位1的个数 + */ + +// @lc code=start +/** + * @param {number} n - a positive integer + * @return {number} + */ +var hammingWeight = function(n) { + let rs = 0; + while(n != 0) { + rs = rs + 1; + n = n & (n - 1); + } + return rs; +}; +// @lc code=end + diff --git "a/Week 07/id_493/231.2-\347\232\204\345\271\202.js" "b/Week 07/id_493/231.2-\347\232\204\345\271\202.js" new file mode 100644 index 000000000..d8400c6ed --- /dev/null +++ "b/Week 07/id_493/231.2-\347\232\204\345\271\202.js" @@ -0,0 +1,16 @@ +/* + * @lc app=leetcode.cn id=231 lang=javascript + * + * [231] 2的幂 + */ + +// @lc code=start +/** + * @param {number} n + * @return {boolean} + */ +var isPowerOfTwo = function(n) { + return n > 0 && (n & (n - 1)) == 0; +}; +// @lc code=end + diff --git a/Week 07/id_498/LeetCode_191_498.py b/Week 07/id_498/LeetCode_191_498.py new file mode 100644 index 000000000..9a10a14b5 --- /dev/null +++ b/Week 07/id_498/LeetCode_191_498.py @@ -0,0 +1,12 @@ +class Solution(object): + def hammingWeight(self, n): + count = 0 + while n != 0: + # 清除最低位的1 + n &= (n - 1) + count += 1 + return count + + +s = Solution() +print(s.hammingWeight(0b00000000000000000000000000001011)) diff --git a/Week 07/id_498/LeetCode_231_498.py b/Week 07/id_498/LeetCode_231_498.py new file mode 100644 index 000000000..bb47dcf1a --- /dev/null +++ b/Week 07/id_498/LeetCode_231_498.py @@ -0,0 +1,7 @@ +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + return n != 0 and (n & (n - 1)) == 0 + + +s = Solution() +print(s.isPowerOfTwo(47)) diff --git a/Week 07/id_503/NOTE.md b/Week 07/id_503/NOTE.md index a6321d6e2..f2d59cc19 100644 --- a/Week 07/id_503/NOTE.md +++ b/Week 07/id_503/NOTE.md @@ -1,4 +1,73 @@ -# NOTE +# 位运算 +* 位运算符 - + |含义 |运算符 |示例 | + |-- |-- |-- | + |右移 |>> |0011 >> 1 = 0001 | + |左移 |<< |0011 << 1 = 0110 | + |按位或 |\| |0011 \| 1011 = 1011| + |按位与 |& |0011 & 1011 = 0011 | + |按位取反 |~ |~0011 = 1100 | + |按位异或(相同位 0,不同位 1) |^ |0011^1011 = 1000 | +* 异或操作 XOR(^) + * 相同为0,不同为1;可用【不进位加法】理解 + * 操作的一些特点 + * x^0 = x + * x^1s = ~x // 1s表示全 1 + * x^(~x) = 1s + * x^x = 0 + * c=a^b => a^c = b; b^c = a //交换两个数 + * a^b^c = a^(b^c) = (a^b)^c //结合性 +* 指定位置的位运算 + * 将最右边的 n 位清零:x & (~0 << n) + * 获取 x 的第 n 位值(0 或者 1):(x >> n)& 1 + * 获取 x 的第 n 位的幂值:x & (1 << (n-1) + * 仅将第 n 位设置为 1:x | (1 << n) + * 仅将第 n 位设置为 0:x & (~(1 << n)) + * 将 x 最高位至第 n 位(含)清零:x & ((1 << n) - 1) + * 将 n 位至第 0 位(含)清零:x & (~((1 << (n+1)) - 1)) + +LRU Cache(最近最少使用缓存) +* 代码模板 + ```js + class LRUCache { + /** + * + * @param {number}} capacity + */ + constructor(capacity) { + this.cap = capacity; + this.cache = new Map(); // 有序字典 + } + /** + * @param {number} key + * @return {number} + */ + get(key) { + if (!this.cache.has(key)) { + return -1; + } + const val = this.cache.get(key); + this.put(key, val); + return val; + } + /** + * @param {number} key + * @param {number} value + * @return {void} + */ + put(key, value) { + if (this.cache.has(key)) { + this.cache.delete(key); + } + else { + if (this.cache.size === this.cap) { // 容量已满,删除最后一元素 + const lastKey = this.cache.keys().next().value; + this.cache.delete(lastKey); + } + } + this.cache.set(key, value); + } + } + ``` \ No newline at end of file diff --git a/Week 07/id_503/leetcode_146_503.js b/Week 07/id_503/leetcode_146_503.js new file mode 100644 index 000000000..3d050e46f --- /dev/null +++ b/Week 07/id_503/leetcode_146_503.js @@ -0,0 +1,47 @@ +class LRUCache { + + /** + * + * @param {number}} capacity + */ + constructor(capacity) { + this.cap = capacity; + this.cache = new Map(); // 有序字典 + } + /** + * @param {number} key + * @return {number} + */ + get(key) { + if (!this.cache.has(key)) { + return -1; + } + const val = this.cache.get(key); + this.put(key, val); + return val; + } + /** + * @param {number} key + * @param {number} value + * @return {void} + */ + put(key, value) { + if (this.cache.has(key)) { + this.cache.delete(key); + } + else { + if (this.cache.size === this.cap) { // 容量已满,删除最后一元素 + const lastKey = this.cache.keys().next().value; + this.cache.delete(lastKey); + } + } + this.cache.set(key, value); + } +} + +/** + * Your LRUCache object will be instantiated and called as such: + * var obj = new LRUCache(capacity) + * var param_1 = obj.get(key) + * obj.put(key,value) + */ \ No newline at end of file diff --git a/Week 07/id_503/leetcode_191_503.js b/Week 07/id_503/leetcode_191_503.js new file mode 100644 index 000000000..8e87b33fe --- /dev/null +++ b/Week 07/id_503/leetcode_191_503.js @@ -0,0 +1,15 @@ +/** + * @param {number} n - a positive integer + * @return {number} + */ +var hammingWeight = function (n) { + + let count = 0; + while (n !== 0) { + count++; + + n &= (n - 1); + } + + return count; +}; \ No newline at end of file diff --git a/Week 07/id_503/leetcode_231_503.js b/Week 07/id_503/leetcode_231_503.js new file mode 100644 index 000000000..dda9214bd --- /dev/null +++ b/Week 07/id_503/leetcode_231_503.js @@ -0,0 +1,7 @@ +/** + * @param {number} n + * @return {boolean} + */ +var isPowerOfTwo = function (n) { + return n > 0 && (n & (n - 1)) === 0; +}; \ No newline at end of file diff --git a/Week 07/id_508/LeetCode_1122_508.cpp b/Week 07/id_508/LeetCode_1122_508.cpp new file mode 100644 index 000000000..441b757cc --- /dev/null +++ b/Week 07/id_508/LeetCode_1122_508.cpp @@ -0,0 +1,24 @@ +class Solution { +public: + vector relativeSortArray(vector& arr1, vector& arr2) { + unordered_map table; + for(auto i:arr1) { + if(table.find(i)==table.end()) table[i] = 1; + else table[i]+=1; + } + vector result; + for(auto i:arr2) { + for(int j = 0;j tail; + for (auto i:arr1) { + if(table[i]!=0) + tail.push_back(i); + } + sort(tail.begin(),tail.end()); + for(auto i:tail) result.push_back(i); + return result; + } +}; diff --git a/Week 07/id_508/LeetCode_242_508.cpp b/Week 07/id_508/LeetCode_242_508.cpp new file mode 100644 index 000000000..66511f76d --- /dev/null +++ b/Week 07/id_508/LeetCode_242_508.cpp @@ -0,0 +1,11 @@ +class Solution { +public: + bool isAnagram(string s, string t) { + int counter[26]; + for(auto &i:counter) i = 0; + for(auto i:s) counter[i-'a']++; + for(auto i:t) counter[i-'a']--; + for(auto i:counter) if(i!=0) return false; + return true; + } +}; diff --git a/Week 07/id_523/LeetCode_231_523.cs b/Week 07/id_523/LeetCode_231_523.cs new file mode 100644 index 000000000..6eb9bdad3 --- /dev/null +++ b/Week 07/id_523/LeetCode_231_523.cs @@ -0,0 +1,26 @@ +public class Solution +{ + public bool IsPowerOfTwo(int n) + { + while (n > 0) + { + if (n == 1 || n == 2) return true; + + if (n % 2 == 1) + { + return false; + } + else + { + n = n / 2; + } + } + + return false; + } + + public bool IsPowerOfTwo2(int n) + { + return n > 0 && (n & (n - 1)) == 0; + } +} \ No newline at end of file diff --git a/Week 07/id_523/LeetCode_56.523.cs b/Week 07/id_523/LeetCode_56.523.cs new file mode 100644 index 000000000..06b7e994e --- /dev/null +++ b/Week 07/id_523/LeetCode_56.523.cs @@ -0,0 +1,40 @@ +/** + * Definition for an interval. + * public class Interval { + * public int start; + * public int end; + * public Interval() { start = 0; end = 0; } + * public Interval(int s, int e) { start = s; end = e; } + * } + */ +public class Solution +{ + public IList Merge(IList intervals) + { + var result = new List(); + if (intervals.Count == 0) return result; + + intervals = intervals.OrderBy(q => q.start).ToList(); + + int start = intervals[0].start; + int end = intervals[0].end; + + foreach (var interval in intervals) + { + if (interval.start <= end) + { + end = Math.Max(interval.end, end); + } + else + { + result.Add(new Interval(start, end)); + start = interval.start; + end = interval.end; + } + } + + result.Add(new Interval(start, end)); + + return result; + } +} \ No newline at end of file diff --git a/Week 07/id_533/LeetCode_1122.js b/Week 07/id_533/LeetCode_1122.js new file mode 100644 index 000000000..d57117d75 --- /dev/null +++ b/Week 07/id_533/LeetCode_1122.js @@ -0,0 +1,28 @@ +// https://leetcode-cn.com/problems/relative-sort-array/ + +/** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ +var relativeSortArray = function(arr1, arr2) { + var bucket = {}, + result = []; + for (var i = 0; i < arr1.length; i++) + bucket[arr1[i]] = bucket[arr1[i]] ? ++bucket[arr1[i]] : 1; + for (var j = 0; j < arr2.length; j++) { + while (bucket[arr2[j]]) { + result.push(arr2[j]); + bucket[arr2[j]]--; + } + } + for(let key in bucket){ + while(bucket[key]){ + result.push(key); + bucket[key]--; + } + } + return result; +}; + +console.log(relativeSortArray([2, 3, 1, 3, 2, 4, 6, 7, 9, 2, 19], [2, 1, 4, 3, 9, 6])); \ No newline at end of file diff --git a/Week 07/id_533/LeetCode_146.js b/Week 07/id_533/LeetCode_146.js new file mode 100644 index 000000000..efe0c3f34 --- /dev/null +++ b/Week 07/id_533/LeetCode_146.js @@ -0,0 +1,47 @@ +// https://leetcode-cn.com/problems/lru-cache/ + +/** + * @param {number} capacity + */ +var LRUCache = function (capacity) { + this.capacity = capacity; + this.map = new Map(); +}; + +/** + * @param {number} key + * @return {number} + */ +LRUCache.prototype.get = function (key) { + let value = this.map.get(key); + if (value === undefined) return -1; + this.map.delete(key); + this.map.set(key, value); + return value; +}; + +/** + * @param {number} key + * @param {number} value + * @return {void} + */ +LRUCache.prototype.put = function (key, value) { + this.map.delete(key); + this.map.set(key, value); + var keys = this.map.keys(); + if (this.map.size > this.capacity) { + this.map.delete(keys.next().value); + } +}; + +var cache = new LRUCache(2); + +cache.put(1, 1); +cache.put(2, 2); +console.log(cache.get(1)); // 返回 1 +cache.put(3, 3); // 该操作会使得密钥 2 作废 +console.log(cache.get(2)); // -1 (未找到) +cache.put(4, 4); // 该操作会使得密钥 1 作废 +console.log(cache.get(1)); // 返回返回 -1 (未找到) +console.log(cache.get(3)); // 返回 3 +console.log(cache.get(4)); // 返回 4 diff --git a/Week 07/id_533/LeetCode_190.js b/Week 07/id_533/LeetCode_190.js new file mode 100644 index 000000000..efd590e04 --- /dev/null +++ b/Week 07/id_533/LeetCode_190.js @@ -0,0 +1,15 @@ +// https://leetcode-cn.com/problems/reverse-bits/ +// JS对于整数的位运算仅支持32位有符号整数 + +/** + * @param {number} n - a positive integer + * @return {number} - a positive integer + */ +var reverseBits = function(n) { + var ans = 0; + for (var i = 0; i < 32; i++) { + ans = (ans << 1) | (n & 1); + n >>= 1; + } + return ans >>> 0; +}; diff --git a/Week 07/id_533/LeetCode_191.js b/Week 07/id_533/LeetCode_191.js new file mode 100644 index 000000000..ac76a7062 --- /dev/null +++ b/Week 07/id_533/LeetCode_191.js @@ -0,0 +1,34 @@ +// https://leetcode-cn.com/problems/number-of-1-bits/ + +/** + * @param {number} n + * @return {number} + */ +// 取模判断再左移 +var hammingWeight = function(n) { + var count = 0; + while (n) { + if (n & 1 == 1) + count++; + n = n >>> 1; + } + return count; +}; + +/** + * @param {number} n + * @return {number} + */ +// 打掉最低位的1 +var hammingWeight = function(n) { + var count = 0; + while (n) { + count++; + n = n & (n-1); + } + return count; +}; + +console.log(hammingWeight(00000000000000000000000000001011)) +console.log(hammingWeight(00000000000000000000000010000000)) +console.log(hammingWeight(11111111111111111111111111111101)) \ No newline at end of file diff --git a/Week 07/id_533/LeetCode_231.js b/Week 07/id_533/LeetCode_231.js new file mode 100644 index 000000000..388f60f7b --- /dev/null +++ b/Week 07/id_533/LeetCode_231.js @@ -0,0 +1,13 @@ +// https://leetcode-cn.com/problems/power-of-two/ + +/** + * @param {number} n + * @return {boolean} + */ +var isPowerOfTwo = function(n) { + return n > 0 && (n & (n - 1)) == 0; +}; + +console.log(isPowerOfTwo(1)); +console.log(isPowerOfTwo(16)); +console.log(isPowerOfTwo(218)); \ No newline at end of file diff --git a/Week 07/id_533/LeetCode_338.js b/Week 07/id_533/LeetCode_338.js new file mode 100644 index 000000000..200a28770 --- /dev/null +++ b/Week 07/id_533/LeetCode_338.js @@ -0,0 +1,25 @@ +// https://leetcode-cn.com/problems/counting-bits/ + +/** + * @param {number} num + * @return {number[]} + */ +var countBits = function(num) { + var countArr = [] + for (var i = 0; i <= num; i++) { + countArr.push(count(i)) + } + return countArr; +}; + +function count(num) { + var count = 0; + while (num) { + count++; + num = num & (num - 1); + } + return count; +} + +console.log(countBits(2)) +console.log(countBits(5)) \ No newline at end of file diff --git a/Week 07/id_533/LeetCode_493.js b/Week 07/id_533/LeetCode_493.js new file mode 100644 index 000000000..716bb6998 --- /dev/null +++ b/Week 07/id_533/LeetCode_493.js @@ -0,0 +1,37 @@ +// https://leetcode-cn.com/problems/reverse-pairs/ + +/** + * @param {number[]} nums + * @return {number} + */ +var reversePairs = function (nums) { + if (!nums || !nums.length) return 0; + var count = 0; + mergeSort(nums, 0, nums.length - 1); + return count; + function mergeSort (nums, begin, end) { + if (begin >= end) return; + var mid = (begin + end) >> 1; + mergeSort(nums, begin, mid); + mergeSort(nums, mid + 1, end); + var tmp = [], + i1 = begin, + i2 = begin, + k = 0; + for (var j = mid + 1; j <= end; j++) { + while (i1 <= mid && (nums[i1] / 2) <= nums[j]) + i1++; + while (i2 <= mid && nums[i2] < nums[j]) + tmp[k++] = nums[i2++]; + tmp[k++] = nums[j]; + count += mid - i1 + 1; + } + while (i2 <= mid) + tmp[k++] = nums[i2++]; + for (var p = 0; p < tmp.length; p++) + nums[begin+p] = tmp[p]; + } +}; + +console.log(reversePairs([1, 3, 2, 3, 1])); +console.log(reversePairs([2, 4, 3, 5, 1])); \ No newline at end of file diff --git a/Week 07/id_533/LeetCode_56.js b/Week 07/id_533/LeetCode_56.js new file mode 100644 index 000000000..484e3e702 --- /dev/null +++ b/Week 07/id_533/LeetCode_56.js @@ -0,0 +1,26 @@ +/** + * @param {number[][]} intervals + * @return {number[][]} + */ +var merge = function(intervals) { + if (!intervals || !intervals.length) return []; + let len = intervals.length, + i = 0, + result =[]; + intervals.sort(function (a, b) { + return a[0] - b[0]; + }); + while (i < len){ + let currLeft = intervals[i][0]; + let currRight = intervals[i][1]; + while(i < len - 1 && intervals[i+1][0] <= currRight){ + i++; + currRight = Math.max(intervals[i][1], currRight); + } + result.push([currLeft,currRight]); + i++; + } + return result; +}; + +console.log(merge([[1, 3], [2, 6], [8, 10], [15, 18]])); \ No newline at end of file diff --git a/Week 07/id_543/LeetCode_146_543.java b/Week 07/id_543/LeetCode_146_543.java new file mode 100644 index 000000000..77395ea15 --- /dev/null +++ b/Week 07/id_543/LeetCode_146_543.java @@ -0,0 +1,111 @@ +//运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。 +// +// 获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 +//写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。 +// +// 进阶: +// +// 你是否可以在 O(1) 时间复杂度内完成这两种操作? +// +// 示例: +// +// LRUCache cache = new LRUCache( 2 /* 缓存容量 */ ); +// +//cache.put(1, 1); +//cache.put(2, 2); +//cache.get(1); // 返回 1 +//cache.put(3, 3); // 该操作会使得密钥 2 作废 +//cache.get(2); // 返回 -1 (未找到) +//cache.put(4, 4); // 该操作会使得密钥 1 作废 +//cache.get(1); // 返回 -1 (未找到) +//cache.get(3); // 返回 3 +//cache.get(4); // 返回 4 +// +// Related Topics 设计 + +import java.util.Hashtable; + +class LRUCache { + + + + class DLinkNode{ + int key; + int value; + DLinkNode pre; + DLinkNode next; + } + + private Hashtable hashtable = new Hashtable(); + int capacity; + int size; + private DLinkNode head,tail; + + public LRUCache(int capacity) { + this.size = 0; + this.capacity = capacity; + head = new DLinkNode(); + tail = new DLinkNode(); + head.next = tail; + tail.pre = head; + } + + public int get(int key) { + DLinkNode node = hashtable.get(key); + if(node == null) return -1; + moveToHead(node); + return node.value; + } + + + public void put(int key, int value) { + DLinkNode node = hashtable.get(key); + if(node == null){ + DLinkNode newNode = new DLinkNode(); + newNode.key = key; + newNode.value = value; + hashtable.put(key,newNode); + addNode(newNode); + size ++; + if(size > capacity){ + DLinkNode tail = popTail(); + hashtable.remove(tail.key); + remove(tail); + size--; + } + }else { + node.value = value; + moveToHead(node); + + } + } + + private void moveToHead(DLinkNode node) { + remove(node); + addNode(node); + } + + private void remove(DLinkNode node) { + node.pre.next = node.next; + node.next.pre = node.pre; + } + + private void addNode(DLinkNode node) { + node.next = head.next; + head.next = node; + node.pre = head; + node.next.pre = node; + } + + private DLinkNode popTail(){ + return tail.pre; + } + +} + +/** + * Your LRUCache object will be instantiated and called as such: + * LRUCache obj = new LRUCache(capacity); + * int param_1 = obj.get(key); + * obj.put(key,value); + */ \ No newline at end of file diff --git a/Week 07/id_543/LeetCode_191_543.java b/Week 07/id_543/LeetCode_191_543.java new file mode 100644 index 000000000..a12506158 --- /dev/null +++ b/Week 07/id_543/LeetCode_191_543.java @@ -0,0 +1,11 @@ +public class Solution { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int count = 0; + while(n!=0){ + n = n & n-1; + count++; + } + return count; + } +} \ No newline at end of file diff --git a/Week 07/id_543/LeetCode_231_543.java b/Week 07/id_543/LeetCode_231_543.java new file mode 100644 index 000000000..6587ed980 --- /dev/null +++ b/Week 07/id_543/LeetCode_231_543.java @@ -0,0 +1,8 @@ +class Solution { + public boolean isPowerOfTwo(int n) { + if(n > 0 && (n & n-1) == 0){ + return true; + } + return false; + } +} \ No newline at end of file diff --git a/Week 07/id_543/Leet_190_543.java b/Week 07/id_543/Leet_190_543.java new file mode 100644 index 000000000..9e7b681cb --- /dev/null +++ b/Week 07/id_543/Leet_190_543.java @@ -0,0 +1,12 @@ +public class Solution { + // you need treat n as an unsigned value + public int reverseBits(int n) { + int result = 0; + for(int i = 0;i < 32;i++){ + result <<= 1; + result |= n & 1; + n >>= 1; + } + return result; + } +} \ No newline at end of file diff --git a/Week 07/id_558/LeetCode_146_558.java b/Week 07/id_558/LeetCode_146_558.java new file mode 100644 index 000000000..46681283b --- /dev/null +++ b/Week 07/id_558/LeetCode_146_558.java @@ -0,0 +1,33 @@ +package Week06; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @see https://leetcode-cn.com/problems/lru-cache/solution/lru-huan-cun-ji-zhi-by-leetcode/ + * LRU缓存机制 + */ +public class LeetCode_146_558 { + class LRUCache extends LinkedHashMap { + private int capacity; + + public LRUCache(int capacity) { + super(capacity, 0.75F, true); + this.capacity = capacity; + } + + public int get(int key) { + return super.getOrDefault(key, -1); + } + + public void put(int key, int value) { + super.put(key, value); + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > capacity; + } + } + +} diff --git a/Week 07/id_558/LeetCode_190_558.java b/Week 07/id_558/LeetCode_190_558.java new file mode 100644 index 000000000..b6035f79b --- /dev/null +++ b/Week 07/id_558/LeetCode_190_558.java @@ -0,0 +1,31 @@ +package Week06; + +/** + * @see https://leetcode-cn.com/problems/reverse-bits/ + * 颠倒二进制位 + */ +public class LeetCode_190_558 { + public static int reverseBits_1(int n) { + String s = Integer.toBinaryString(n); + int zeroCount = 32 - s.length(); + for (int i = 0; i < zeroCount; i++) { + s = "0" + s; + } + s = new StringBuilder(s).reverse().toString(); + return (int) Long.parseLong(s, 2); + } + + public static int reverseBits(int n) { + int ans = 0; + for (int i = 0; i < 32; i++) { + ans = (ans << 1) + (n & 1); + n >>= 1; + } + return ans; + } + + public static void main(String[] args) { + int n = 0b11111111111111111111111111111101; + System.out.println(reverseBits(n)); + } +} diff --git a/Week 07/id_558/LeetCode_191_558.java b/Week 07/id_558/LeetCode_191_558.java new file mode 100644 index 000000000..aa1a260ff --- /dev/null +++ b/Week 07/id_558/LeetCode_191_558.java @@ -0,0 +1,57 @@ +package Week06; + +/** + * @see https://leetcode-cn.com/problems/number-of-1-bits/submissions/ + * 位1的个数 + */ +public class LeetCode_191_558 { + public static int hammingWeight(int n) { + return Integer.bitCount(n); + } + + /** + * 正整数 + */ + public static int hammingWeight_1(int n) { + int count = 0; + while (n / 2 != 0) { + if ((n & 1) == 1) { + count++; + } + n /= 2; + } + if ((n & 1) == 1) { + count++; + } + return count; + } + + public static int hammingWeight_2(int n) { + int count = 0; + for (int i = 0; i < 32; i++) { + if ((n % 1) == 1) { + count++; + } + n = n >> 1; + } + return count; + } + + /** + * 去掉最低位1 + */ + public static int hammingWeight_3(int n) { + int count = 0; + while (n != 0) { + count++; + n &= (n - 1); + } + return count; + } + + public static void main(String[] args) { + int n = 92108; + // System.out.println(hammingWeight(n)); + System.out.println(hammingWeight_3(0b11111111111111111111111111111101)); + } +} diff --git a/Week 07/id_558/LeetCode_231_558.java b/Week 07/id_558/LeetCode_231_558.java new file mode 100644 index 000000000..1a4b2b8b3 --- /dev/null +++ b/Week 07/id_558/LeetCode_231_558.java @@ -0,0 +1,11 @@ +package Week06; + +/** + * @see https://leetcode-cn.com/problems/number-of-1-bits/submissions/ + * 2的幂 + */ +public class LeetCode_231_558 { + public boolean isPowerOfTwo(int n) { + return n > 0 && ((n & (n - 1)) == 0); + } +} diff --git a/Week 07/id_558/LeetCode_242_558.java b/Week 07/id_558/LeetCode_242_558.java new file mode 100644 index 000000000..bfd374dc6 --- /dev/null +++ b/Week 07/id_558/LeetCode_242_558.java @@ -0,0 +1,65 @@ +package Week06; + + +/** + * @see https://leetcode-cn.com/problems/valid-anagram/ + * 有效的字母异位词 + */ +public class LeetCode_242_558 { +// public static void quickSort(char[] arr, int begin, int end) { +// if (end <= begin) return; +// int pivot = partition(arr, begin, end); +// quickSort(arr, begin, pivot - 1); +// quickSort(arr, pivot + 1, end); +// +// } +// +// private static int partition(char[] arr, int begin, int end) { +// int counter = begin; +// for (int i = begin; i < end; i++) { +// if (arr[i] < arr[end - 1]) { +// char temp = arr[i]; +// arr[i] = arr[counter]; +// arr[counter] = temp; +// counter++; +// } +// } +// char temp = arr[counter]; +// arr[counter] = arr[end - 1]; +// arr[end - 1] = temp; +// return counter; +// } + + public boolean isAnagram(String s, String t) { + if (s == null || t == null) { + return false; + } + if (s.length() != t.length()) { + return false; + } + int arr[] = new int[256]; + for (int i = 0; i < s.length(); i++) { + arr[s.charAt(i)] += 1; + + } + for (int i = 0; i < t.length(); i++) { + arr[t.charAt(i)] -= 1; + if (arr[t.charAt(i)] < 0) { + return false; + } + } + return true; + } + +// public static void main(String[] args) { +// String s1 = "eat"; +// String s2 = "tea"; +// char[] c1 = s1.toCharArray(); +// // char[] c2 = s2.toCharArray(); +// quickSort(c1, 0, c1.length); +// // quickSort(c2, 0, c2.length); +// System.out.println(String.valueOf(c1)); +// } + + +} diff --git a/Week 07/id_558/NOTE.md b/Week 07/id_558/NOTE.md index a6321d6e2..1a65f1d67 100644 --- a/Week 07/id_558/NOTE.md +++ b/Week 07/id_558/NOTE.md @@ -1,4 +1,36 @@ # NOTE +#### 知识回顾 +* 位运算 + * 清零最低位的1: X = X & (X-1) + * 得到最低位的1: X & -X +* 布隆过滤器 + * 一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索 一个元素是否在一个集合中。 + * 优点:空间效率和查询时间都远远超过一般的算法 + * 缺点:有一定的误识别率和删除困难 + * 应用: + * 比特币网络 + * 分布式系统 + * Redis缓存 + * 垃圾邮件、评论等的过滤 +* LRU Cache + * 两个要素: 大小 、替换策略 + * Hash Table + Double LinkedList + * O(1) 查询 O(1) 修改、更新 +* 排序 + * 比较类排序 + * 非比较类排序 +#### 处理输出 +* [LRU缓存机制](https://leetcode-cn.com/problems/lru-cache/solution/lru-huan-cun-ji-zhi-by-leetcode/) +* [颠倒二进制位](https://leetcode-cn.com/problems/reverse-bits/) +* [位1的个数](https://leetcode-cn.com/problems/number-of-1-bits/submissions/) +* [2的幂](https://leetcode-cn.com/problems/number-of-1-bits/submissions/) +* [有效的字母异位词](https://leetcode-cn.com/problems/valid-anagram/) +#### 遗留问题 + * n皇后问题(位运算实现) + * 非比较类排序 + +#### 总结改进 +* 重点练习遗留问题题目 diff --git a/Week 07/id_563/Leecode_1122_563.go b/Week 07/id_563/Leecode_1122_563.go new file mode 100644 index 000000000..809de96da --- /dev/null +++ b/Week 07/id_563/Leecode_1122_563.go @@ -0,0 +1,22 @@ +func relativeSortArray(arr1 []int, arr2 []int) []int { + stack, temp, exemp := []int{}, []int{}, make(map[int][]int) + + for i, i2 := range arr2 { + for _, i1 := range arr1 { + if i == 0 { + exemp[i1] = append(exemp[i1], i1) + } + if i2 == i1 { + delete(exemp, i1) + stack = append(stack, i2) + } + } + } + + for _, num := range exemp { + temp = append(temp, num...) + } + sort.Ints(temp) + + return append(stack, temp...) +} \ No newline at end of file diff --git a/Week 07/id_563/Leecode_190_563.go b/Week 07/id_563/Leecode_190_563.go new file mode 100644 index 000000000..97c266f03 --- /dev/null +++ b/Week 07/id_563/Leecode_190_563.go @@ -0,0 +1,8 @@ +func reverseBits(num uint32) (res uint32) { + for i := 32; i > 0; i-- { + res <<= 1 + res += num&1 + num >>= 1 + } + return res +} \ No newline at end of file diff --git a/Week 07/id_563/Leecode_191_563.go b/Week 07/id_563/Leecode_191_563.go new file mode 100644 index 000000000..1764e29d6 --- /dev/null +++ b/Week 07/id_563/Leecode_191_563.go @@ -0,0 +1,9 @@ +func hammingWeight(num uint32) int { + sum := 0 + for num != 0 { + sum++ + num &= num-1 + } + + return sum +} \ No newline at end of file diff --git a/Week 07/id_563/Leecode_231_563.go b/Week 07/id_563/Leecode_231_563.go new file mode 100644 index 000000000..c44134049 --- /dev/null +++ b/Week 07/id_563/Leecode_231_563.go @@ -0,0 +1,3 @@ +func isPowerOfTwo(n int) bool { + return n != 0 && n & (n-1) == 0 +} \ No newline at end of file diff --git a/Week 07/id_563/Leecode_338_563.go b/Week 07/id_563/Leecode_338_563.go new file mode 100644 index 000000000..cbbde1017 --- /dev/null +++ b/Week 07/id_563/Leecode_338_563.go @@ -0,0 +1,13 @@ +func countBits(num int) []int { + ans, i, b := make([]int, num+1), 0, 1 + + for b <= num { + for i < b && i + b <= num { + ans[i+b] = ans[i] + 1 + i++ + } + i = 0 + b <<= 1 + } + return ans +} diff --git a/Week 07/id_563/Leecode_51_563.go b/Week 07/id_563/Leecode_51_563.go new file mode 100644 index 000000000..a764be50f --- /dev/null +++ b/Week 07/id_563/Leecode_51_563.go @@ -0,0 +1,35 @@ +/** 这题不太懂 */ +func solveNQueens(n int) [][]string { + result = make([][]string, 0) + board := make([][]byte, n) + for i := 0; i < n; i++ { + board[i] = make([]byte, n) + for j := 0; j < n; j++ { + board[i][j] = '.' + } + } + helper(board, 0, 0, 0, 0) + return result +} + +func helper(board [][]byte, row int, colBit, leftBit, rightBit int) { + n := len(board) + if row == n { + dst := make([]string, n) + for i, v := range board { + dst[i] = string(v) + } + result = append(result, dst) + return + } + for i := 0; i < n; i++ { + bit := 1 << uint(n-i-1) + if colBit&bit == 0 && leftBit&bit == 0 && rightBit&bit == 0 { + board[row][i] = 'Q' + helper(board, row+1, colBit|bit, (leftBit|bit)<<1, (rightBit|bit)>>1) + board[row][i] = '.' + } + } +} + +var result [][]string \ No newline at end of file diff --git a/Week 07/id_568/LeetCode_1122_568.java b/Week 07/id_568/LeetCode_1122_568.java new file mode 100644 index 000000000..ab66c9df1 --- /dev/null +++ b/Week 07/id_568/LeetCode_1122_568.java @@ -0,0 +1,31 @@ +/** + * @author kelvin + * @date 2019/12/1 10:15 PM + */ +public class LeetCode_1122_568 { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + int[] m = new int[1001]; + + int[] ref = new int[arr1.length]; + + for (int i = 0; i < arr1.length; i++) { + m[arr1[i]]++; + } + + int cnt = 0; + for (int i = 0; i < arr2.length; i++) { + while (m[arr2[i]] > 0) { + ref[cnt++] = arr2[i]; + m[arr2[i]]--; + } + } + + for (int i = 0; i < 1001; i++) { + while (m[i] > 0) { + ref[cnt++] = i; + m[i]--; + } + } + return ref; + } +} diff --git a/Week 07/id_568/LeetCode_146_568.java b/Week 07/id_568/LeetCode_146_568.java new file mode 100644 index 000000000..4789b3e4f --- /dev/null +++ b/Week 07/id_568/LeetCode_146_568.java @@ -0,0 +1,33 @@ +package algorithom; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author kelvin + * @date 2019/12/1 10:20 PM + */ +public class LeetCode_146_568 { + +} +class LRUCache extends LinkedHashMap { + private int capacity; + + public LRUCache(int capacity) { + super(capacity, 0.75F, true); + this.capacity = capacity; + } + + public int get(int key) { + return super.getOrDefault(key, -1); + } + + public void put(int key, int value) { + super.put(key, value); + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > capacity; + } +} diff --git a/Week 07/id_568/LeetCode_52_568.java b/Week 07/id_568/LeetCode_52_568.java new file mode 100644 index 000000000..bfd9eed1e --- /dev/null +++ b/Week 07/id_568/LeetCode_52_568.java @@ -0,0 +1,38 @@ + + +/** + * @author kelvin + * @date 2019/12/1 10:40 PM + */ +public class LeetCode_52_568 { + public int backtrack(int row, int hills, int next_row, int dales, int count, int n) { + int columns = (1 << n) - 1; + + if (row == n) { + count++; + } else { + + int free_columns = columns & ~(hills | next_row | dales); + + while (free_columns != 0) { + + int curr_column = -free_columns & free_columns; + + free_columns ^= curr_column; + + count = backtrack(row + 1, + (hills | curr_column) << 1, + next_row | curr_column, + (dales | curr_column) >> 1, + count, n); + } + } + + return count; + } + + public int totalNQueens(int n) { + return backtrack(0, 0, 0, 0, 0, n); + } + +} diff --git a/Week 07/id_573/NOTE.md b/Week 07/id_573/NOTE.md index a6321d6e2..f461aaa9a 100644 --- a/Week 07/id_573/NOTE.md +++ b/Week 07/id_573/NOTE.md @@ -1,4 +1,79 @@ # NOTE +### 算法分类 + +十种常见排序算法可以分为两大类: + + 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。 + 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。 + + + #### 概念 + 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。 + 不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。 + 时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。 + 空间复杂度:是指算法在计算机 + 内执行时所需存储空间的度量,它也是数据规模n的函数。 + + +#### 1、冒泡排序(Bubble Sort) + + 冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。 + +##### 1.1 算法描述 + + 比较相邻的元素。如果第一个比第二个大,就交换它们两个; + 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数; + 针对所有的元素重复以上的步骤,除了最后一个; + 重复步骤1~3,直到排序完成。 + +``` +function bubbleSort(arr) { + var len = arr.length; + for (var i = 0; i < len - 1; 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、选择排序(Selection Sort) + 选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。 + +##### 算法描述 + + n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下: + + 初始状态:无序区为R[1..n],有序区为空; + 第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1..i]和R[i+1..n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区; + n-1趟结束,数组有序化了。 + + +``` +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; +} +``` +##### 分析 + 表现最稳定的排序算法之一,因为无论什么数据进去都是O(n2)的时间复杂度,所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。理论上讲,选择排序可能也是平时排序一般人想到的最多的排序方法了吧。 \ No newline at end of file diff --git a/Week 07/id_573/leetcode_1122_573.go b/Week 07/id_573/leetcode_1122_573.go new file mode 100644 index 000000000..45fd4303b --- /dev/null +++ b/Week 07/id_573/leetcode_1122_573.go @@ -0,0 +1,30 @@ +package main + +import "fmt" + +func relativeSortArray(arr1 []int, arr2 []int) []int { + m := make(map[int]int) + for i := 0; i < len(arr1); i++ { + m[arr1[i]]++ + } + ret := make([]int, 0, len(arr1)) + for i := 0; i < len(arr2); i++ { + for m[arr2[i]] > 0 { + ret = append(ret, arr2[i]) + m[arr2[i]]-- + } + } + for i := 0; i <= 1000; i++ { + for m[i] > 0 { + ret = append(ret, i) + m[i]-- + } + } + return ret +} + +func main() { + arr1 := []int{2,3,1,3,2,4,6,7,9,2,19} + arr2 := []int{2,1,4,3,9,6} + fmt.Println(relativeSortArray(arr1,arr2)) +} \ No newline at end of file diff --git a/Week 07/id_573/leetcode_242_573.java b/Week 07/id_573/leetcode_242_573.java new file mode 100644 index 000000000..63834aa60 --- /dev/null +++ b/Week 07/id_573/leetcode_242_573.java @@ -0,0 +1 @@ +class Solution { public boolean isAnagram(String s, String t) { if (s.length() != t.length()) { return false; } int[] table = new int[26]; for (int i = 0; i < s.length(); i++) { table[s.charAt(i) - 'a']++; } for (int i = 0; i < t.length(); i++) { table[t.charAt(i) - 'a']--; if (table[t.charAt(i) - 'a'] < 0) { return false; } } return true; } public static void main(String[] args) { Solution solution = new Solution(); String s = "anagram"; String t = "nagaram"; System.out.println(solution.isAnagram(s, t)); s = "rat"; t = "car"; System.out.println(solution.isAnagram(s, t)); } \ No newline at end of file diff --git a/Week 07/id_573/leetcode_56_573.java b/Week 07/id_573/leetcode_56_573.java new file mode 100644 index 000000000..50d5f31c8 --- /dev/null +++ b/Week 07/id_573/leetcode_56_573.java @@ -0,0 +1 @@ +class Solution { private class IntervalComparator implements Comparator { @Override public int compare(Interval a, Interval b) { return a.start < b.start ? -1 : a.start == b.start ? 0 : 1; } } public List merge(List intervals) { Collections.sort(intervals, new IntervalComparator()); LinkedList merged = new LinkedList(); for (Interval interval : intervals) { if (merged.isEmpty() || merged.getLast().end < interval.start) { merged.add(interval); } else { merged.getLast().end = Math.max(merged.getLast().end, interval.end); } } return merged; } } \ No newline at end of file diff --git a/Week 07/id_578/LeetCode_146_578.java b/Week 07/id_578/LeetCode_146_578.java new file mode 100644 index 000000000..41b127c39 --- /dev/null +++ b/Week 07/id_578/LeetCode_146_578.java @@ -0,0 +1,35 @@ +package com.hand.week7; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/12/1 + */ +public class LeetCode_146_578 { + class LRUCache extends LinkedHashMap { + private int capacity; + + public LRUCache(int capacity) { + //initialCapacity:初始化容量 loadFactor:负载因子 accessOrder:访问顺序(true代表使用LRU/false代表使用插入的顺序) + super(capacity, 0.75F, true); + this.capacity = capacity; + } + + public int get(int key) { + return super.getOrDefault(key, -1); + } + + public void put(int key, int value) { + super.put(key, value); + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > capacity; + } + } +} diff --git a/Week 07/id_578/LeetCode_190_578.java b/Week 07/id_578/LeetCode_190_578.java new file mode 100644 index 000000000..751d1520c --- /dev/null +++ b/Week 07/id_578/LeetCode_190_578.java @@ -0,0 +1,20 @@ +package com.hand.week7; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/12/1 + */ +public class LeetCode_190_578 { + public int reverseBits(int n) { + int result = 0; + for (int i = 1; i <= 32; ++i) { + int tmp = n >> i; + tmp = tmp & 1; + tmp = tmp << (31 - i); + result |= tmp; + } + return result; + } +} diff --git a/Week 07/id_578/LeetCode_191_578.java b/Week 07/id_578/LeetCode_191_578.java new file mode 100644 index 000000000..27478ae5d --- /dev/null +++ b/Week 07/id_578/LeetCode_191_578.java @@ -0,0 +1,18 @@ +package com.hand.week7; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/12/1 + */ +public class LeetCode_191_578 { + public int hammingWeight(int n) { + int count = 0; + while (n != 0) { + n &= (n - 1); + count++; + } + return count; + } +} diff --git a/Week 07/id_578/LeetCode_231_578.java b/Week 07/id_578/LeetCode_231_578.java new file mode 100644 index 000000000..4e614563b --- /dev/null +++ b/Week 07/id_578/LeetCode_231_578.java @@ -0,0 +1,13 @@ +package com.hand.week7; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/12/1 + */ +public class LeetCode_231_578 { + public boolean isPowerOfTwo(int n) { + return n > 0 & ((n & (n - 1)) == 0); + } +} diff --git a/Week 07/id_578/LeetCode_338_578.java b/Week 07/id_578/LeetCode_338_578.java new file mode 100644 index 000000000..94d359a7d --- /dev/null +++ b/Week 07/id_578/LeetCode_338_578.java @@ -0,0 +1,19 @@ +package com.hand.week7; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/12/1 + */ +public class LeetCode_338_578 { + public int[] countBits(int num) { + int[] result = new int[num + 1]; + result[0] = 0; + for (int i = 0; i <= num; ++i) { + if ((i & 1) == 1) result[i] = result[i - 1] + 1; + else result[i] = result[i >> 1]; + } + return result; + } +} diff --git a/Week 07/id_578/LeetCode_51_578.java b/Week 07/id_578/LeetCode_51_578.java new file mode 100644 index 000000000..410c4b685 --- /dev/null +++ b/Week 07/id_578/LeetCode_51_578.java @@ -0,0 +1,56 @@ +package com.hand.week7; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/12/1 + */ +public class LeetCode_51_578 { + boolean[] col; + boolean[] pie; + boolean[] na; + List> result = new ArrayList<>(); + + public List> solveNQueens(int n) { + col = new boolean[n]; + pie = new boolean[2 * n - 1]; + na = new boolean[2 * n - 1]; + char[][] board = new char[n][n]; + dfs(board, 0, n); + return result; + } + + private void dfs(char[][] board, int row, int n) { + if (row == n) { + result.add(construct(board)); + return; + } + Arrays.fill(board[row], '.'); + for (int i = 0; i < n; ++i) { + if (!col[i] && !pie[row + i] && !na[i - row + n - 1]) { + col[i] = true; + pie[row + i] = true; + na[i - row + n - 1] = true; + board[row][i] = 'Q'; + dfs(board, row + 1, n); + col[i] = false; + pie[row + i] = false; + na[i - row + n - 1] = false; + board[row][i] = '.'; + } + } + } + + private List construct(char[][] board) { + List list = new ArrayList<>(); + for (int i = 0; i < board.length; ++i) { + list.add(new String(board[i])); + } + return list; + } +} diff --git a/Week 07/id_578/LeetCode_52_578.java b/Week 07/id_578/LeetCode_52_578.java new file mode 100644 index 000000000..2dea3b58a --- /dev/null +++ b/Week 07/id_578/LeetCode_52_578.java @@ -0,0 +1,32 @@ +package com.hand.week7; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/12/1 + */ +public class LeetCode_52_578 { + int size; + int count; + + public int totalNQueens(int n) { + size = n; + count = 0; + dfs(0, 0, 0, 0); + return count; + } + + private void dfs(int row, int col, int pie, int na) { + if (row == size) { + count++; + return; + } + int bits = (~(col | pie | na) & ((1 << size) - 1)); + while (bits != 0) { + int p = bits & (-bits); + dfs(row + 1, col | p, (pie | p) << 1, (na | p) >> 1); + bits &= (bits - 1); + } + } +} diff --git a/Week 07/id_583/LeetCode_231_583.php b/Week 07/id_583/LeetCode_231_583.php new file mode 100644 index 000000000..93f90bc29 --- /dev/null +++ b/Week 07/id_583/LeetCode_231_583.php @@ -0,0 +1,25 @@ + 0) && ($n & ($n - 1)) == 0; + } +} + +$s = new Solution(); +$n = '161'; + +$result = $s->isPowerOfTwo($n); + +echo $result; diff --git a/Week 07/id_583/QuickSort.php b/Week 07/id_583/QuickSort.php new file mode 100644 index 000000000..1d8ca9574 --- /dev/null +++ b/Week 07/id_583/QuickSort.php @@ -0,0 +1,31 @@ + cache = new Hashtable<>(); + private int size; + private int capacity; + private DLinkedNode head, tail; + + class DLinkedNode { + int key; + int value; + DLinkedNode prev; + DLinkedNode next; + } + + // 插入节点(将节点插在head标兵的后面) + private void addNode(DLinkedNode node) { + node.prev = head; + node.next = head.next; + + head.next.prev = node; + head.next = node; + } + + // 删除某个节点 + private void removeNode(DLinkedNode node) { + DLinkedNode prev = node.prev; + DLinkedNode next = node.next; + + prev.next = next; + next.prev = prev; + } + + // 将某个节点移动到前面 + private void moveToHead(DLinkedNode node) { + removeNode(node); + addNode(node); + } + + // 删除最后一个节点(即tail标兵前面一个节点) + private DLinkedNode popTail() { + DLinkedNode res = tail.prev; + removeNode(res); + return res; + } + + public LeetCode_146_588(int capacity) { + this.size = 0; + this.capacity = capacity; + + head = new DLinkedNode(); + tail = new DLinkedNode(); + + head.next = tail; + tail.prev = head; + } + + public int get(int key) { + // 先判断是否在hashtable中存在 + DLinkedNode node = cache.get(key); + if (null == node) { + return -1; + } + + // 将节点移动到最前面 + moveToHead(node); + + return node.value; + } + + + public void put(int key, int value) { + DLinkedNode node = cache.get(key); + // 如果hashTable没找到相应元素,则插入 + if (null == node) { + DLinkedNode newNode = new DLinkedNode(); + newNode.key = key; + newNode.value = value; + + cache.put(key, newNode); + addNode(newNode); + + size ++; + + // 如果缓存已满,则移除最后的节点 + if (size > capacity) { + DLinkedNode tail = popTail(); + cache.remove(tail.key); + size --; + } + } else { + // 如果找到了,则将其移动到最前面 + node.value = value; + moveToHead(node); + } + } +} diff --git a/Week 07/id_588/LeetCode_231_588.java b/Week 07/id_588/LeetCode_231_588.java new file mode 100644 index 000000000..f2f849276 --- /dev/null +++ b/Week 07/id_588/LeetCode_231_588.java @@ -0,0 +1,13 @@ +/** + * 2的幂 + * https://leetcode-cn.com/problems/power-of-two/ + */ +public class LeetCode_231_588 { + + public boolean isPowerOfTwo(int n) { + // 2的幂则只有一个1,其余都是0 + // 使用位运算去除最低位的1之后判断是否等于0,若等于,则为2的幂 + // 还要注意n必须是一个大于0的数 + return 0 == (n & (n - 1)) && n > 0; + } +} diff --git a/Week 07/id_588/LeetCode_338_588.java b/Week 07/id_588/LeetCode_338_588.java new file mode 100644 index 000000000..1707dc307 --- /dev/null +++ b/Week 07/id_588/LeetCode_338_588.java @@ -0,0 +1,30 @@ +/** + * 比特位计数 + * https://leetcode-cn.com/problems/counting-bits/description/ + */ +public class LeetCode_338_588 { + + /** + * 对于所有的数字,只有两类: + * + * 奇数:二进制表示中,奇数一定比前面那个偶数多一个1,因为多的就是最低位的1。 + * 偶数:二进制表示中,偶数中1的个数一定和除以2之后的那个数一样多。因为最低位是0,除以2就是右移一位,也就是把那个0抹掉而已,所以1的个数是不变的。 + * @param num + * @return + */ + public int[] countBits(int num) { + int[] result = new int[num + 1]; + result[0] = 0; + for (int i = 1; i <= num; i ++) { + // 奇数 + if (1 == (i & 1)) { + result[i] = result[i - 1] + 1; + } else { + // 偶数 + result[i] = result[i / 2]; + } + } + + return result; + } +} diff --git a/Week 07/id_588/LeetCode_493_588.java b/Week 07/id_588/LeetCode_493_588.java new file mode 100644 index 000000000..10003a28d --- /dev/null +++ b/Week 07/id_588/LeetCode_493_588.java @@ -0,0 +1,28 @@ +import java.util.Arrays; + +/** + * 逆序对 + * https://leetcode-cn.com/problems/reverse-pairs/ + */ +public class LeetCode_493_588 { + + public int reversePairs(int[] nums) { + return mergeSort(nums, 0, nums.length - 1); + } + + private int mergeSort(int[] nums, int s, int e) { + if (s >= e) { + return 0; + } + int mid = s + (e - s) / 2; + int count = mergeSort(nums, s, mid) + mergeSort(nums, mid + 1, e); + for (int i = s, j = mid + 1; i <= mid; i ++) { + while (j <= e && ((long)nums[i] > (long)nums[j] * 2)) { + j ++; + } + count += j - (mid + 1); + } + Arrays.sort(nums, s, e + 1); + return count; + } +} diff --git a/Week 07/id_588/LeetCode_56_588.java b/Week 07/id_588/LeetCode_56_588.java new file mode 100644 index 000000000..3c020fc35 --- /dev/null +++ b/Week 07/id_588/LeetCode_56_588.java @@ -0,0 +1,35 @@ +import java.util.*; + +/** + * 合并区间 + * https://leetcode-cn.com/problems/merge-intervals + */ +public class LeetCode_56_588 { + + public int[][] merge(int[][] intervals) { + List result = new ArrayList<>(); + if (null == intervals || 0 == intervals.length) { + return result.toArray(new int[0][]); + } + + Arrays.sort(intervals, new Comparator() { + @Override + public int compare(int[] o1, int[] o2) { + return o1[0] - o2[0]; + } + }); + int i = 0; + while (i < intervals.length) { + int left = intervals[i][0]; + int right = intervals[i][1]; + while (i < intervals.length - 1 && right >= intervals[i + 1][0]) { + i ++; + right = Math.max(right, intervals[i][1]); + } + result.add(new int[]{left, right}); + i ++; + } + return result.toArray(new int[0][]); + } + +} diff --git a/Week 07/id_588/NOTE.md b/Week 07/id_588/NOTE.md index a6321d6e2..9a2c32f93 100644 --- a/Week 07/id_588/NOTE.md +++ b/Week 07/id_588/NOTE.md @@ -1,4 +1,114 @@ -# NOTE +# 第七周学习总结 +## 位运算 + +异或操作的一些特点: + +x ^ 0 = x + +x ^ 1s = ~x + +x ^ (~x) = 1s + +x ^ x = 0 + +c = a ^ b => a ^ c = b, b ^ c = a + +a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c + +## 布隆过滤器 + +- 作用: + + - 判断一个元素是否存在于集合中, 但是只能确定元素一定不在集合, 不能确定元素一定在集合 + +- 优点: + + - 空间效率和查询时间远远超过一半的算法 + +- 缺点: + + - 有一定的误识别率和删除困难 + + +## 初级排序算法 +### 选择排序 + +``` + private void selectionSort(int a[]) { + + int n = a.length; + if (n <= 1) { + return; + } + + for (int i = 0; i < n-1; i ++) { + int leftLast = a[i]; + int j = i + 1; + int tempUnsortedIndex = j; + for (; j < n; j ++) { + if (a[j] < a[tempUnsortedIndex]) { + tempUnsortedIndex = j; + } + } + a[i] = a[tempUnsortedIndex]; + a[tempUnsortedIndex] = leftLast; + } + } +``` + +### 插入排序 + +``` + private void insertSort(int[] a) { + int n = a.length; + if (n <= 1) { + return; + } + + for (int i = 1; i < n; i ++) { + int tempValue = a[i]; + int j = i - 1; + for (; j >= 0; j --) { + if (tempValue < a[j]) { + a[j+1] = a[j]; + } else { + break; + } + } + a[j+1] = tempValue; + } + } +``` + +### 冒泡排序 + +``` + private void bubbleSort(int[] valueArray) { + + // 如果数组个数小于等于1,则无需排序 + if (valueArray.length <= 1) { + return; + } + + for (int i = 0 ; i < valueArray.length; i ++) { + boolean flag = false; + for (int j = 0; j < valueArray.length - i - 1; j ++) { + if (valueArray[j] > valueArray[j + 1]) { + int temp = valueArray[j]; + valueArray[j] = valueArray[j + 1]; + valueArray[j + 1] = temp; + + flag = true; + } + } + + // 当某个冒泡没有交换的时候,说明已经达到完全有序 + if ( !flag) { + break; + } + } + } +``` \ No newline at end of file diff --git a/Week 07/id_593/LeetCode_146_593.java b/Week 07/id_593/LeetCode_146_593.java new file mode 100644 index 000000000..6a5d4ebbe --- /dev/null +++ b/Week 07/id_593/LeetCode_146_593.java @@ -0,0 +1,118 @@ +import java.util.Hashtable; + +/** + * 146. LRU缓存机制 + * 运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。 + *

+ * 获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 + * 写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。 + *

+ * 进阶: + *

+ * 你是否可以在 O(1) 时间复杂度内完成这两种操作? + *

+ * 示例: + *

+ * LRUCache cache = new LRUCache( 2 (缓存容量) ); + *

+ * cache.put(1, 1); + * cache.put(2, 2); + * cache.get(1); // 返回 1 + * cache.put(3, 3); // 该操作会使得密钥 2 作废 + * cache.get(2); // 返回 -1 (未找到) + * cache.put(4, 4); // 该操作会使得密钥 1 作废 + * cache.get(1); // 返回 -1 (未找到) + * cache.get(3); // 返回 3 + * cache.get(4); // 返回 4 + *

+ *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/lru-cache + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + */ +class LRUCache { + + private DLinkedNode head; + private DLinkedNode tail; + + class DLinkedNode { + int key; + int value; + private DLinkedNode pre; + private DLinkedNode next; + } + + private void addNode(DLinkedNode node) { + node.pre = head; + node.next = head.next; + + head.next.pre = node; + head.next = node; + } + + private void removeNode(DLinkedNode node) { + DLinkedNode pre = node.pre; + DLinkedNode next = node.next; + + pre.next = next; + next.pre = pre; + } + + private void moveToHead(DLinkedNode node) { + removeNode(node); + addNode(node); + } + + private DLinkedNode popTail() { + DLinkedNode node = tail.pre; + removeNode(node); + return node; + } + + private int capacity; + private int size; + private Hashtable cache; + + public LRUCache(int capacity) { + this.size = 0; + this.capacity = capacity; + cache = new Hashtable<>(capacity); + head = new DLinkedNode(); + tail = new DLinkedNode(); + head.next = tail; + tail.pre = head; + } + + public int get(int key) { + DLinkedNode node = cache.get(key); + if (node == null) { + return -1; + } + moveToHead(node); + return node.value; + } + + public void put(int key, int value) { + DLinkedNode node = cache.get(key); + if (node == null) { + node = new DLinkedNode(); + node.value = value; + // 1 bug + node.key = key; + cache.put(key, node); + size++; + if (size > capacity) { + DLinkedNode tailNode = popTail(); + cache.remove(tailNode.key); + size--; + } + addNode(node); + } else { + // 2 bug + node.value = value; + moveToHead(node); + } + } +} diff --git a/Week 07/id_593/LeetCode_190_593.java b/Week 07/id_593/LeetCode_190_593.java new file mode 100644 index 000000000..f92d38590 --- /dev/null +++ b/Week 07/id_593/LeetCode_190_593.java @@ -0,0 +1,49 @@ +/** + * 190. 颠倒二进制位 + * 颠倒给定的 32 位无符号整数的二进制位。 + *

+ *   + *

+ * 示例 1: + *

+ * 输入: 00000010100101000001111010011100 + * 输出: 00111001011110000010100101000000 + * 解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596, + * 因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。 + * 示例 2: + *

+ * 输入:11111111111111111111111111111101 + * 输出:10111111111111111111111111111111 + * 解释:输入的二进制串 11111111111111111111111111111101 表示无符号整数 4294967293, + *   因此返回 3221225471 其二进制表示形式为 10101111110010110010011101101001。 + *   + *

+ * 提示: + *

+ * 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 + * 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 2 中,输入表示有符号整数 -3,输出表示有符号整数 -1073741825。 + *   + *

+ * 进阶: + * 如果多次调用这个函数,你将如何优化你的算法? + *

+ *

+ *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/reverse-bits + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + */ +public class LeetCode_190_593 { + + public int reverseBits(int n) { + int result = 0; + for (int i = 0; i < 32; i++) { + result = (result << 1) + (n & 1); + n >>= 1; + } + return result; + } + +} diff --git a/Week 07/id_593/LeetCode_191_593.java b/Week 07/id_593/LeetCode_191_593.java new file mode 100644 index 000000000..401268399 --- /dev/null +++ b/Week 07/id_593/LeetCode_191_593.java @@ -0,0 +1,49 @@ +/** + * 191. 位1的个数 + *

+ * 编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。 + *

+ *   + *

+ * 示例 1: + *

+ * 输入:00000000000000000000000000001011 + * 输出:3 + * 解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。 + * 示例 2: + *

+ * 输入:00000000000000000000000010000000 + * 输出:1 + * 解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。 + * 示例 3: + *

+ * 输入:11111111111111111111111111111101 + * 输出:31 + * 解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。 + *   + *

+ * 提示: + *

+ * 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 + * 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。 + *   + *

+ * 进阶: + * 如果多次调用这个函数,你将如何优化你的算法? + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/number-of-1-bits + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + */ +public class LeetCode_191_593 { + public int hammingWeight(int n) { + int count = 0; + while (n != 0) { + count++; + n = n & (n - 1); + } + return count; + } +} diff --git a/Week 07/id_593/LeetCode_52_593.java b/Week 07/id_593/LeetCode_52_593.java new file mode 100644 index 000000000..8fd9d4867 --- /dev/null +++ b/Week 07/id_593/LeetCode_52_593.java @@ -0,0 +1,68 @@ +/** + * 52. N皇后 II + * n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。 + *

+ *

+ *

+ * 上图为 8 皇后问题的一种解法。 + *

+ * 给定一个整数 n,返回 n 皇后不同的解决方案的数量。 + *

+ * 示例: + *

+ * 输入: 4 + * 输出: 2 + * 解释: 4 皇后问题存在如下两个不同的解法。 + * [ + *  [".Q..",  // 解法 1 + *   "...Q", + *   "Q...", + *   "..Q."], + *

+ *  ["..Q.",  // 解法 2 + *   "Q...", + *   "...Q", + *   ".Q.."] + * ] + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/n-queens-ii + * 链接:https://leetcode.com/problems/n-queens-ii + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + *

+ * 撇 / + *

+ * 捺 \ + * + * @author jaryoung + */ +public class LeetCode_52_593 { + public int totalNQueens(int n) { + return dfs(n, 0, 0, 0, 0, 0); + } + + private int dfs(int n, int row, int col, int pie, int na, int count) { + if (row == n) { + return count + 1; + } + int bits = (~(col | pie | na)) & ((1 << n) - 1); + // ~(col | pie | na) -> 1111 1111 这里很好理解,与操作,就是 列 、撇 和 捺 各自来站位 + // (1 << n) -> 0001 0000 + // ((1 << n) - 1) -> 0000 1111 清除因为取反后的前四位的1111 + // 1111 1111 + // 0000 1111 + // bits 0000 1111 最后,得到能站的位置 + while (bits != 0) { + // 0000 1111 + // & 1111 0001 + // position 0000 0001 + // 取得当前能站的位置 + int position = bits & -bits; + // 不断打掉 bits 的最后一位 1 + bits = bits & (bits - 1); + count = dfs(n, row + 1, col | position, (pie | position) << 1, (na | position) >> 1, count); + } + return count; + } + +} diff --git a/Week 07/id_593/NOTE.md b/Week 07/id_593/NOTE.md index a6321d6e2..5c4495557 100644 --- a/Week 07/id_593/NOTE.md +++ b/Week 07/id_593/NOTE.md @@ -1,4 +1,243 @@ -# NOTE +# 【593-Week 07】学习总结+初级排序代码 - +### 学习总结 + + + +#### 总结 + +#### 位运算 + +- X = X & (X-1) 清零最低位的 1 +- X & -X => 得到最低位的 1 +- jdk 代码很有意思的代码 + + - private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); + private static final int COUNT_BITS = Integer.SIZE - 3; + private static final int CAPACITY = (1 << COUNT_BITS) - 1; + + // runState is stored in the high-order bits + private static final int RUNNING = -1 << COUNT_BITS; + private static final int SHUTDOWN = 0 << COUNT_BITS; + private static final int STOP = 1 << COUNT_BITS; + private static final int TIDYING = 2 << COUNT_BITS; + private static final int TERMINATED = 3 << COUNT_BITS; + + // Packing and unpacking ctl + private static int runStateOf(int c) { return c & ~CAPACITY; } + private static int workerCountOf(int c) { return c & CAPACITY; } + private static int ctlOf(int rs, int wc) { return rs | wc; } + +#### LRU Cache + +- 结构 + + - hashMap + 双端链表 + +#### 排序算法 + +- 初级 + + - 选择 + + - 每次找最小值,然后放到待排序数组的起始位置。 + + - 插入 + + - 从前到后逐步构建有序序列;对于未排序数据,在已排序序列中从后 向前扫描,找到相应位置并插入。 + + - 冒泡 + + - 嵌套循环,每次查看相邻的元素如果逆序,则交换。 + +- 高级 + + - 快排 + + - 数组取标杆 pivot,将小元素放 pivot左边,大元素放右侧,然后依次 对右边和右边的子数组继续快排;以达到整个序列有序。 + + - 归并排序 + + - 1. 把长度为n的输入序列分成两个长度为n/2的子序列; + - 2. 对这两个子序列分别采用归并排序; + - 3. 将两个排序好的子序列合并成一个最终的排序序列。 + +#### 排序代码 + +```java + +/** + * 初级排序 + * + * @author jaryoung + */ +public class PrimarySort { + + /** + * 选择排序: + * 每次找最小值,然后放到待排序数组的起始位置。 + * + * @param array 待排序 数组 + * @return 已经排好序的 数组 + */ + public static int[] selectionSort(int[] array) { + for (int i = 0; i < array.length; i++) { + int mid = i; + for (int j = i + 1; j < array.length; j++) { + if (array[mid] > array[j]) { + mid = j; + } + } + int temp = array[i]; + array[i] = array[mid]; + array[mid] = temp; + } + return array; + } + + /** + * 插入排序: + * 从前到后逐步构建有序序列;对于未排序数据,在已排序序列中从后 向前扫描,找到相应位置并插入。 + * + * @param array 待排序 数组 + * @return 已经排好序的 数组 + */ + public static int[] insertSort(int[] array) { + for (int i = 1; i < array.length; i++) { + int pre = i - 1; + int current = array[i]; + while (pre >= 0 && current < array[pre]) { + array[pre + 1] = array[pre]; + pre--; + } + array[pre + 1] = current; + } + return array; + } + + /** + * 冒泡排序: + * 嵌套循环,每次查看相邻的元素如果逆序,则交换。 + * + * @param array 待排序 数组 + * @return 已经排好序的 数组 + */ + public static int[] bubbleSort(int[] array) { + int length = array.length; + for (int i = 0; i < length; i++) { + for (int j = i + 1; j < length; j++) { + if (array[i] > array[j]) { + int temp = array[j]; + array[j] = array[i]; + array[i] = temp; + } + } + } + return array; + } + + +} + +``` + + + +```java + +/** + * 高级一点的排序 + * + * @author jaryoung + */ +public class AdvancedSort { + + /** + * 快速排序: + * 数组取标杆 pivot,将小元素放 pivot左边,大元素放右侧,然后依次 对右边和右边的子数组继续快排;以达到整个序列有序。 + * + * @param array 待排序 数组 + * @return 已经排好序的 数组 + */ + public static int[] quickSort(int[] array) { + quickSort(array, 0, array.length - 1); + return array; + } + + private static void quickSort(int[] array, int begin, int end) { + if (begin >= end) { + return; + } + int pivot = partition(array, begin, end); + quickSort(array, begin, pivot - 1); + quickSort(array, pivot + 1, end); + } + + private static int partition(int[] array, int begin, int end) { + int count = begin; + for (int i = begin; i < end; i++) { + // 小于 end位置的数,都往左边移动 + if (array[i] < array[end]) { + int temp = array[count]; + array[count] = array[i]; + array[i] = temp; + // 作用一:不断记录和更新,小于end位置的数的下一个位置(小于end位置的数都在count之前了) + // 作用二:为最后,跟定义的中间值,交换位置作准备 + count++; + } + } + // end位置的数,跟count交换位置(count 之前的数都是小于end位置的数了) + int temp = array[end]; + array[end] = array[count]; + array[count] = temp; + return count; + } + + /** + * 1. 把长度为n的输入序列分成两个长度为n/2的子序列; + * 2. 对这两个子序列分别采用归并排序; + * 3. 将两个排序好的子序列合并成一个最终的排序序列。 + * + * @param array 待排序 数组 + * @return 已经排好序的 数组 + */ + public static int[] mergeSort(int[] array) { + if (array == null || array.length < 1) { + return new int[0]; + } + mergeSort(array, 0, array.length - 1); + return array; + } + + private static void mergeSort(int[] array, int left, int right) { + if (left >= right) { + return; + } + int mid = (left + right) >> 1; + mergeSort(array, left, mid); + mergeSort(array, mid + 1, right); + merge(array, left, mid, right); + } + + private static void merge(int[] array, int left, int mid, int right) { + int[] temp = new int[right - left + 1]; + int i = left; + int j = mid + 1; + int k = 0; + while (i <= mid && j <= right) { + temp[k++] = array[i] < array[j] ? array[i++] : array[j++]; + } + while (i <= mid) { + temp[k++] = array[i++]; + } + while (j <= right) { + temp[k++] = array[j++]; + } + System.arraycopy(temp, 0, array, left, temp.length); + } + + +} + +``` diff --git a/Week 07/id_598/LeetCode_146_598.java b/Week 07/id_598/LeetCode_146_598.java new file mode 100644 index 000000000..357d4b2a4 --- /dev/null +++ b/Week 07/id_598/LeetCode_146_598.java @@ -0,0 +1,174 @@ +import java.util.HashMap; +import java.util.Map; + +/** + * @author northleaf + * @create 2019年11月27日 + */ +public class LeetCode_146_598 { + class LRUCache { + /** + * 元素数量 + */ + int size = 0; + + /** + * 容量所容纳元素上限 + */ + int capacity = 0 ; + + + + private class DLinkNode{ + int var; + DLinkNode prev; + DLinkNode next; + + public DLinkNode(int var) { + this(var,null,null); + } + + public DLinkNode(int var, DLinkNode prev, DLinkNode next) { + this.var = var; + this.prev = prev; + this.next = next; + } + } + /** + * hashMap + */ + Map map; + + /** + * 头结点 + */ + DLinkNode head; + + /** + * 尾结果 + */ + DLinkNode tail; + + + public LRUCache(int capacity) { + + this.capacity = capacity; + this.size = 0; + //为了不让map扩容,直接设定它的初始容量 + this.map = new HashMap<>((capacity * 4)/3 + (capacity*4)%3); + + //头尾指针 + head = new DLinkNode(-1); + tail = new DLinkNode(-1); + head.next = tail; + tail.prev = head; + + } + + + public int get(int key) { + + DLinkNode node = map.get(key); + //不为null + if (node != null) { + moveTohead(node); + return node.var; + } + + return -1; + + + } + + + public void put(int key, int value) { + + DLinkNode node = map.get(key); + if (node != null) { + node.var = value; + moveTohead(node); + }else{ + //添加元素 + node = new DLinkNode(value); + map.put(key,node); + addNode(node); + } + } + + /** + * 移动到队道 + * @param node + */ + private void moveTohead(DLinkNode node) { + + //第一步移除这个node + removeNode(node); + //第二步添加到队首 + addNode(node); + + } + + /** + * 在队首添加元素 + * @param node + */ + private void addNode(DLinkNode node) { + if (node == null) { + return; + } + + node.next = head.next; + head.next.prev = node; + + head.next = node; + node.prev = head; + + + size++; + //判断要不要移动队尾 + removeLast(); + + } + + /** + * 移除队尾前的元素 + */ + private void removeLast() { + + if(size > capacity){ + DLinkNode node = tail.prev; + removeNode(node); + for(Integer key: map.keySet()){ + if(map.get(key) == node){ + map.remove(key); + return; + } + } + } + + } + + /** + * 移除节点 + * @param node + */ + private void removeNode(DLinkNode node) { + if (node == null) { + return; + } + DLinkNode pre = node.prev; + DLinkNode next = node.next; + + pre.next = next; + next.prev = pre; + + node.prev = null; + node.next = null; + + size --; + return; + } + + } + +} diff --git a/Week 07/id_598/LeetCode_190_598.java b/Week 07/id_598/LeetCode_190_598.java new file mode 100644 index 000000000..91fded4f0 --- /dev/null +++ b/Week 07/id_598/LeetCode_190_598.java @@ -0,0 +1,25 @@ +/** + * @author northleaf + * @create 2019年11月26日 + */ +public class LeetCode_190_598 { + // you need treat n as an unsigned value + public int reverseBits(int n) { + + int res = 0; + + //移动32次 + for (int i = 0; i < 32; i++) { + //左移一位 + res = res << 1; + //取最后一个有效位,与1相与后,再与res 相加 + //n与1的的意思是保留最后一移 + res =res + (n & 1); + //将n右移一位,也即舍弃最后一痊 + n = n >> 1; + } + + return res; + + } +} diff --git a/Week 07/id_598/LeetCode_191_598.java b/Week 07/id_598/LeetCode_191_598.java new file mode 100644 index 000000000..1a2e45097 --- /dev/null +++ b/Week 07/id_598/LeetCode_191_598.java @@ -0,0 +1,17 @@ +/** + * @author northleaf + * @create 2019年11月26日 + */ +public class LeetCode_191_598 { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int count = 0 ; + + while (n!=0){ + count++; + n = n & (n - 1); + } + + return count; + } +} diff --git a/Week 07/id_598/LeetCode_231_598.java b/Week 07/id_598/LeetCode_231_598.java new file mode 100644 index 000000000..015d1abeb --- /dev/null +++ b/Week 07/id_598/LeetCode_231_598.java @@ -0,0 +1,10 @@ +/** + * @author northleaf + * @create 2019年11月26日 + */ +public class LeetCode_231_598 { + + public boolean isPowerOfTwo(int n) { + return n > 0 && (n & ( n -1)) == 0 ; + } +} \ No newline at end of file diff --git a/Week 07/id_598/LeetCode_338_598.java b/Week 07/id_598/LeetCode_338_598.java new file mode 100644 index 000000000..13ca1f1ca --- /dev/null +++ b/Week 07/id_598/LeetCode_338_598.java @@ -0,0 +1,36 @@ +/** + * @author northleaf + * @create 2019年11月26日 + */ +public class LeetCode_338_598 { + public int[] countBits(int num) { + int[] nums = new int[num+1]; + + for(int i = 0;i>1; + } + + return count; + } + + public static void main(String[] args) { + LeetCode_338_598 leetCode_338_598 = new LeetCode_338_598(); +// leetCode_338_598.countBits(2); + System.out.println(leetCode_338_598.getBits(2)); + } +} diff --git a/Week 07/id_598/LeetCode_51_598.java b/Week 07/id_598/LeetCode_51_598.java new file mode 100644 index 000000000..57c09b5dd --- /dev/null +++ b/Week 07/id_598/LeetCode_51_598.java @@ -0,0 +1,79 @@ +import java.util.ArrayList; +import java.util.List; + +/** + * @author northleaf + * @create 2019年11月25日 + */ +public class LeetCode_51_598 { + boolean[][] board; + List> ans; + + public List> solveNQueens1(int n) { + board = new boolean[n][n]; + ans = new ArrayList<>(); + + solveNQueens1(0,n, board); + + return ans; + + } + + private void solveNQueens1(int row, int n, boolean[][] board) { + //终止条件 + if(row>=n){ + addSolution1(board,n); + return; + } + //遍历处理 + for(int col = 0;col list = new ArrayList<>(); + for(int i = 0;i=0&& j=0&&j>=0;i--,j--){ + if(board[i][j]){ + return false; + } + } + + return true; + } + +} diff --git a/Week 07/id_598/LeetCode_52_598.java b/Week 07/id_598/LeetCode_52_598.java new file mode 100644 index 000000000..2739cf1f8 --- /dev/null +++ b/Week 07/id_598/LeetCode_52_598.java @@ -0,0 +1,44 @@ +/** + * @author northleaf + * @create 2019年11月26日 + */ +public class LeetCode_52_598 { + int size; + int count; + + public int totalNQueens(int n) { + count = 0 ; + //size的意思就是:1向右移到n位,再减1 + //比中n=8,1向右移动8位,再减1,就是:11111111 + size = (1<>1); + } + + } +} diff --git a/Week 07/id_598/NOTE.md b/Week 07/id_598/NOTE.md index a6321d6e2..1b5054b03 100644 --- a/Week 07/id_598/NOTE.md +++ b/Week 07/id_598/NOTE.md @@ -1,4 +1,73 @@ -# NOTE +## 指定位置的位运算 - +1. 将x最右边的n位清零:x&(~0<>n)&1 +3. 获取x的第n位的幂值:x&(1<<(n-1)) +4. 仅将第n位置为1:x|(1<(x&1) ==1 + * x%2==2 ->(x&1) ==0 + +2. x>>1 ->x/2 + + 即x=x/2 -> x=x>>1; + +3. x=x&(x-1)清零最低位的1 + +4. x&-x = > 得到最低位的1 + +5. x&~x =>0 + + + + + +## BloomFilter + +一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。 + +优点是空间效率和查询时间都远远超过一般的算法 + +缺点是有一定的误识别率和删除困难 + + + +在布隆过滤器中查询: + +1. 如果查到,结果是不一定存在 +2. 如果没有查到,说明结果一定不存在 + +布隆过滤器一般用于缓存,当查到的时候,会再去DB中查询。 + + + +## LRU cache + +两个要素:大小、替换策略(LFU-least frequently used; LRU - least recently used) + +hash table + double linked list + +o(1)查询 + +O(1) 修改更新 + + + +## 排序算法 + +第一类:比较类排序 + +通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序 + +第二类:非比较类排序 + +不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以纯属时间运行,因此也称为线性时间非比较类排序。 \ No newline at end of file diff --git a/Week 07/id_613/NOTE.md b/Week 07/id_613/NOTE.md index a6321d6e2..8e8ea5573 100644 --- a/Week 07/id_613/NOTE.md +++ b/Week 07/id_613/NOTE.md @@ -1,4 +1,88 @@ # NOTE +# 位运算 +## XOR - 异或 +异或:相同为0,不同为1。也可以用"不进位加法"来理解。 +异或操作的一些特点: +- x ^ 0 = x +- x ^ 1s = ~x (1s = ~0,也就是全1) +- x ^ (~x) = 1s +- x ^ x = 0 +- c = a ^ b ==> a ^ c = b, b ^ c = a // 交换两个数 +- a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c + +## 指定位置的位运算 +- 将x最右边的n位清零:x&(~0<>n)&1 +- 获取x的第n位的幂值:x&(1<<(n-1)) +- 仅将第n位置为1:x|(1< (x&1)==1 + x%2==0 --> (x&1)==0 +- x>>1 --> x/2 + 即x=x/2 --> x=x>>1 + mid=(left+right)/2 --> mid=(left+right)>>1 +- X=X&(X-1)清零最低位的1 +- X&-X==>得到最低位的1 +- X&~X==>0 + +# bloom filter +bloom filter是一个很长的二进制向量和一系列的随机映射函数。 +bloom filter可以用于检索一个元素是否在一个集合中。 +- 优点:空间效率和查询时间都远远超过一般的算法 +- 缺点:有一定的误识别率和删除困难 + +## bloom filter的原理及实现 +https://www.cnblogs.com/cpselvis/p/6265825.html + +## bloom filter:只能精确的判"否",而不能精确的判"是" +- bloom filter可以精确的判断一个元素没有存在于bloom filter之中 +- bloom filter不能精确的判断一个元素存在于bloom filter之中, 真正 +要判断元素是否存在,必须另外访问一个完整的存储数据结构(比如DB, redis等) + +## 使用布隆过滤器解决缓存击穿、垃圾邮件识别、集合判重 +https://blog.csdn.net/tianyaleixiaowu/article/details/74721877 + +## bloom filter实现 +### python +- 代码示例:https://shimo.im/docs/xKwrcwrDxRv3QpKG/read +- 实现示例:https://www.geeksforgeeks.org/bloom-filters-introduction-and-python-implementation/ +- 高性能bloom filter实现:https://github.com/jhgg/pybloof + +### java +- https://github.com/lovasoa/bloomfilter/blob/master/src/main/java/BloomFilter.java +- https://github.com/Baqend/Orestes-Bloomfilter + +# LRU(least recent use) Cache +- 两个要素:大小、替换策略 +- Hash Table + Double LinkedList +- O(1)查询 + O(1)修改、更新 +## 替换算法 +https://en.wikipedia.org/wiki/Cache_replacement_policies + +# 排序算法 +## 分类 +- 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破 +O(nlogn), 因此也称为非线性时间比较类排序 +- 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序 +的时间下界,以线性时间运行,因此也称为线性时间非比较类排序 + + + + + + + + + + + + diff --git a/Week 07/id_613/java/src/main/LRUCache.java b/Week 07/id_613/java/src/main/LRUCache.java new file mode 100644 index 000000000..b5bfa724a --- /dev/null +++ b/Week 07/id_613/java/src/main/LRUCache.java @@ -0,0 +1,110 @@ +import java.util.HashMap; + +/** + * LRU Cache + * + * 执行用时 : * 31 ms * , 在所有 java 提交中击败了 * 86.81% * 的用户 + * 内存消耗 : * 59 MB * , 在所有 java 提交中击败了 * 90.57% * 的用户 + */ +public class LRUCache { + + class LRUNode { + int key; + int value; + LRUNode prev; + LRUNode next; + } + + private LRUNode head; + private LRUNode tail; + private int capcity; + + private HashMap cache; + + public LRUCache(int capacity) { + this.capcity = capacity; + + // 哨兵 + this.head = new LRUNode(); + this.head.prev = null; + this.tail = new LRUNode(); + this.tail.next = null; + + this.head.next = this.tail; + this.tail.prev = this.head; + + cache = new HashMap<>(); + } + + private void addNode(LRUNode node) { + node.prev = head; + node.next = head.next; + + head.next.prev = node; + head.next = node; + } + + private void removeNode(LRUNode node) { + LRUNode prev = node.prev; + LRUNode next = node.next; + + prev.next = node.next; + next.prev = node.prev; + } + + private void moveToHead(LRUNode node) { + removeNode(node); + addNode(node); + } + + private LRUNode popTail() { + LRUNode node = tail.prev; + removeNode(node); + + return node; + } + + // get value from map + public int get(int key) { + if (!cache.containsKey(key)) { + return -1; + } + + LRUNode node = cache.get(key); + moveToHead(node); + return node.value; + } + + public void put(int key, int value) { + LRUNode node = cache.get(key); + if (node == null) { + if (cache.size() >= capcity) { + LRUNode n = popTail(); + cache.remove(n.key); + } + + LRUNode newNode = new LRUNode(); + newNode.key = key; + newNode.value = value; + addNode(newNode); + cache.put(key, newNode); + } else { + node.value = value; + moveToHead(node); + } + } + + public static void main(String[] args) { + LRUCache cache = new LRUCache( 2 /* 缓存容量 */ ); + cache.put(1, 1); + cache.put(2, 2); + System.out.println(cache.get(2)); // 返回2 + System.out.println(cache.get(1)); // 返回 1 + cache.put(3, 3); // 该操作会使得密钥 2 作废 + System.out.println(cache.get(2)); // 返回 -1 (未找到) + cache.put(4, 4); // 该操作会使得密钥 1 作废 + System.out.println(cache.get(1)); // 返回 -1 (未找到) + System.out.println(cache.get(3)); // 返回 3 + System.out.println(cache.get(4)); // 返回 4 + } +} diff --git a/Week 07/id_613/java/src/main/Leetcode191.java b/Week 07/id_613/java/src/main/Leetcode191.java new file mode 100644 index 000000000..92084c506 --- /dev/null +++ b/Week 07/id_613/java/src/main/Leetcode191.java @@ -0,0 +1,22 @@ +/** + * 位1的个数 + */ +public class Leetcode191 { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + // 边界条件 + if (n == 0) { + return 0; + } + + // 右移计算1的个数 + int count = 0; + for (int i = 0; i < 32; i++) { + if (((n >> i) & 1) == 1) { + count++; + } + } + + return count; + } +} diff --git a/Week 07/id_613/java/src/main/Leetcode231One.java b/Week 07/id_613/java/src/main/Leetcode231One.java new file mode 100644 index 000000000..463e5ea02 --- /dev/null +++ b/Week 07/id_613/java/src/main/Leetcode231One.java @@ -0,0 +1,19 @@ +/** + * 2的幂 + */ +public class Leetcode231One { + public boolean isPowerOfTwo(int n) { + if (n <= 0) { + return false; + } + + while (n > 1) { + if (n % 2 != 0) { + return false; + } + n = n / 2; + } + + return true; + } +} diff --git a/Week 07/id_613/java/src/main/Leetcode231Two.java b/Week 07/id_613/java/src/main/Leetcode231Two.java new file mode 100644 index 000000000..f2cacb4ec --- /dev/null +++ b/Week 07/id_613/java/src/main/Leetcode231Two.java @@ -0,0 +1,19 @@ +/** + * 2的幂 + */ +public class Leetcode231Two { + public boolean isPowerOfTwo(int n) { + if (n <= 0) { + return false; + } + + // first, cal bit count + int count = 0; + while (n != 0) { + count++; + n = n & (n - 1); + } + + return count == 1; + } +} diff --git a/Week 07/id_613/java/src/main/QuickSort.java b/Week 07/id_613/java/src/main/QuickSort.java new file mode 100644 index 000000000..c2f975380 --- /dev/null +++ b/Week 07/id_613/java/src/main/QuickSort.java @@ -0,0 +1,43 @@ +import java.util.Arrays; + +/** + * 位1的个数 + */ +public class QuickSort { + public static void quickSort(int[] array, int begin, int end) { + // terminator + if (end <= begin) { + return; + } + + int pivot = _partition(array, begin, end); + quickSort(array, begin, pivot - 1); + quickSort(array, pivot + 1, end); + } + + private static int _partition(int[] array, int begin, int end) { + int pivot = end; + int pivotIndex = begin; + for (int i = begin; i < end; i++) { + if (array[i] < array[pivot]) { + _swap(array, i, pivotIndex); + pivotIndex++; + } + } + + _swap(array, pivot, pivotIndex); + return pivotIndex; + } + + private static void _swap(int[] array, int left, int right) { + int temp = array[left]; + array[left] = array[right]; + array[right] = temp; + } + + public static void main(String[] args) { + int[] array = new int[]{5, 3, 8, 1, 6, 80, 99, -2}; + QuickSort.quickSort(array, 0, array.length - 1); + System.out.println(Arrays.toString(array)); + } +} diff --git a/Week 07/id_613/java/src/test/Leetcode191Test.java b/Week 07/id_613/java/src/test/Leetcode191Test.java new file mode 100644 index 000000000..e85fa2286 --- /dev/null +++ b/Week 07/id_613/java/src/test/Leetcode191Test.java @@ -0,0 +1,22 @@ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +/** + * + */ +public class Leetcode191Test { + @Test + public void testSolution1() { + Leetcode191 solution = new Leetcode191(); + + assertEquals(solution.hammingWeight(11), 3); + } + + @Test + public void testSolution2() { + Leetcode191 solution = new Leetcode191(); + + assertEquals(solution.hammingWeight(-3), 31); + } +} + diff --git a/Week 07/id_613/java/src/test/Leetcode231Test.java b/Week 07/id_613/java/src/test/Leetcode231Test.java new file mode 100644 index 000000000..2a2765f24 --- /dev/null +++ b/Week 07/id_613/java/src/test/Leetcode231Test.java @@ -0,0 +1,26 @@ +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * + */ +public class Leetcode231Test { + @Test + public void testSolution1() { + Leetcode231One solution = new Leetcode231One(); + + assertTrue(solution.isPowerOfTwo(16)); + assertFalse(solution.isPowerOfTwo(218)); + } + + @Test + public void testSolution2() { + Leetcode231Two solution = new Leetcode231Two(); + + assertTrue(solution.isPowerOfTwo(16)); + assertFalse(solution.isPowerOfTwo(218)); + } +} + diff --git a/Week 07/id_618/LeetCode_1122_618.java b/Week 07/id_618/LeetCode_1122_618.java new file mode 100644 index 000000000..11d896af5 --- /dev/null +++ b/Week 07/id_618/LeetCode_1122_618.java @@ -0,0 +1,30 @@ +class Solution { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + // 声明桶的个数 + int[] bucket = new int[1001]; + + // 计数每个桶有多少个数,也就是每个值在数组中有几个 + for (int num : arr1) { + bucket[num]++; + } + + int i = 0; + // 由于排序是按照相对顺序进行排序,所以,首先根据arr2中的桶的顺序,依次从对应的桶中取数到arr1中 + // 并注意,每拿出一个数,需要将对桶中的数的个数进行-1操作 + for (int num : arr2) { + while (bucket[num]-- > 0) { + arr1[i++] = num; + } + } + // 未在arr2中的桶中的数,按照桶号升序进行输出,所以进行二次遍历 + for (int j = 0; j < 1001; ++j) { + while (bucket[j]-- > 0) { + arr1[i++] = j; + } + } + + return arr1; + + } + +} \ No newline at end of file diff --git a/Week 07/id_618/LeetCode_146_618.java b/Week 07/id_618/LeetCode_146_618.java new file mode 100644 index 000000000..bd25a6094 --- /dev/null +++ b/Week 07/id_618/LeetCode_146_618.java @@ -0,0 +1,112 @@ +class LRUCache1 extends LinkedHashMap { + + private static final long serialVersionUID = -8670003437340362842L; + private int capacity; + + public LRUCache1(int capacity) { + super(capacity, 0.75F, true); + this.capacity = capacity; + } + + public int get(int key) { + return super.getOrDefault(key, -1); + } + + public void put(int key, int value) { + super.put(key, value); + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > capacity; + } +} + +class LRUCache2 { + + class Node { + int key; + int value; + Node prev; + Node next; + } + + private void addNode(Node node) { + node.prev = head; + node.next = head.next; + + head.next.prev = node; + head.next = node; + } + + private void removeNode(Node node) { + Node prev = node.prev; + Node next = node.next; + + prev.next = next; + next.prev = prev; + } + + private void moveToHead(Node node) { + removeNode(node); + addNode(node); + } + + private Node popTail() { + Node res = tail.prev; + removeNode(res); + return res; + } + + private HashMap cache = new HashMap<>(); + private int size; + private int capacity; + private Node head; + private Node tail; + + public LRUCache2(int capacity) { + this.size = 0; + this.capacity = capacity; + + head = new Node(); + + tail = new Node(); + + head.next = tail; + tail.prev = head; + } + + public int get(int key) { + Node node = cache.get(key); + if (node == null) { + return -1; + } + + moveToHead(node); + + return node.value; + } + + public void put(int key, int value) { + Node node = cache.get(key); + + if (node == null) { + Node newNode = new Node(); + newNode.key = key; + newNode.value = value; + + cache.put(key, newNode); + addNode(newNode); + + if (size + 1 > capacity) { + Node tail = popTail(); + cache.remove(tail.key); + } else { + size++; + } + } else { + node.value = value; + moveToHead(node); + } + } +} \ No newline at end of file diff --git a/Week 07/id_618/LeetCode_242_618.java b/Week 07/id_618/LeetCode_242_618.java new file mode 100644 index 000000000..6fec7c94b --- /dev/null +++ b/Week 07/id_618/LeetCode_242_618.java @@ -0,0 +1,41 @@ +class Solution { + public boolean isAnagram(String s, String t) { + // 长度不一致,肯定不是 + if (s.length() != t.length()) { + return false; + } + + // 利用字符的asc2编码作为索引 + int[] map = new int[256]; + + // 遍历s,记录每个字符出现的次数 + for (char c : s.toCharArray()) { + int num = map[c]; + num++; + map[c] = num; + } + + // 遍历t,从map中找到对应字符,找到则减去出现的次数 + for (char c : t.toCharArray()) { + int num = map[c]; + + if (num == 0) { + // 不存在直接说明肯定不匹配 + return false; + } else { + num--; + } + + map[c] = num; + } + + // map中都是0,说明匹配,反之不匹配 + for (int i = 0; i < map.length; i++) { + if (map[i] != 0) { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/Week 07/id_618/LeetCode_338_618.java b/Week 07/id_618/LeetCode_338_618.java new file mode 100644 index 000000000..248933b58 --- /dev/null +++ b/Week 07/id_618/LeetCode_338_618.java @@ -0,0 +1,19 @@ +class Solution { + /** + * 对于奇数,1的数量比前一个偶数多一个,对于偶数,与其右移一位(即除以2)的数一致,如11100和1110的1数量一致 + */ + public int[] countBits(int num) { + int[] result = new int[num + 1]; + result[0] = 0; + + for (int i = 1; i <= num; i++) { + if ((i & 1) == 1) { + result[i] = result[i - 1] + 1; + } else { + result[i] = result[i >> 1]; + } + } + + return result; + } +} \ No newline at end of file diff --git a/Week 07/id_628/LeetCode_191_628.java b/Week 07/id_628/LeetCode_191_628.java new file mode 100644 index 000000000..9e0e5dc10 --- /dev/null +++ b/Week 07/id_628/LeetCode_191_628.java @@ -0,0 +1,55 @@ +//编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。 +// +// +// +// 示例 1: +// +// 输入:00000000000000000000000000001011 +//输出:3 +//解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。 +// +// +// 示例 2: +// +// 输入:00000000000000000000000010000000 +//输出:1 +//解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。 +// +// +// 示例 3: +// +// 输入:11111111111111111111111111111101 +//输出:31 +//解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。 +// +// +// +// 提示: +// +// +// 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 +// 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。 +// +// +// +// +// 进阶: +//如果多次调用这个函数,你将如何优化你的算法? +// Related Topics 位运算 + + + +//leetcode submit region begin(Prohibit modification and deletion) +public class LeetCode_191_628 { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + if(n == 0)return 0; + int count = 0 ; + while(n != 0){ + n = n & (n-1); + count ++ ; + } + return count; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 07/id_628/LeetCode_231_628.java b/Week 07/id_628/LeetCode_231_628.java new file mode 100644 index 000000000..e477bbf35 --- /dev/null +++ b/Week 07/id_628/LeetCode_231_628.java @@ -0,0 +1,29 @@ +//给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 +// +// 示例 1: +// +// 输入: 1 +//输出: true +//解释: 20 = 1 +// +// 示例 2: +// +// 输入: 16 +//输出: true +//解释: 24 = 16 +// +// 示例 3: +// +// 输入: 218 +//输出: false +// Related Topics 位运算 数学 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class LeetCode_231_628 { + public boolean isPowerOfTwo(int n) { + return n > 0 && ((n&(n-1)) == 0); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 07/id_628/LeetCode_338_628.java b/Week 07/id_628/LeetCode_338_628.java new file mode 100644 index 000000000..55a6b36a0 --- /dev/null +++ b/Week 07/id_628/LeetCode_338_628.java @@ -0,0 +1,44 @@ +//һǸ num 0 i num Χеÿ i е 1 ĿΪ鷵ء +// +// ʾ 1: +// +// : 2 +//: [0,1,1] +// +// ʾ 2: +// +// : 5 +//: [0,1,1,2,1,2] +// +// : +// +// +// ʱ临ӶΪO(n*sizeof(integer))ĽdzסʱO(n)һɨ +// Ҫ㷨Ŀռ临ӶΪO(n) +// ܽһƽⷨҪC++κвʹκú C++ е __builtin_popcountִд˲ +// +// Related Topics λ ̬滮 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class LeetCode_338_628 { + public int[] countBits(int num) { + int[] arr = new int[num+1]; + for (int i = 0; i <= num ; i++) { + arr[i] = hammingWeight(i); + } + return arr; + } + + public int hammingWeight(int n) { + if(n == 0)return 0; + int count = 0 ; + while(n != 0){ + n = n & (n-1); + count ++ ; + } + return count; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 07/id_628/NOTE.md b/Week 07/id_628/NOTE.md index a6321d6e2..83420a7b0 100644 --- a/Week 07/id_628/NOTE.md +++ b/Week 07/id_628/NOTE.md @@ -2,3 +2,557 @@ +# 第七周总结 + +## 【本周知识点】 + +1、位运算 + +2、布隆过滤器 + +3、LRU + +4、排序 + +## 【本周学习总结】 + +### 位运算 + +| 功能 | 示例 | 位运算 | +| --------------------- | --------------------------------------------- | ---------------------- | +| 去掉最后一位 | (101101->10110) | x >> 1 | +| 在最后加一个0 | (101101->1011010) | x << 1 | +| 在最后加一个1 | (101101->1011011) | x << 1+1 | +| 把最后一位变成1 | (101100->101101) | x | +| 把最后一位变成0 | (101101->101100) | x | +| 最后一位取反 | (101101->101100) | x ^ 1 | +| 把右数第k位变成1 | (101001->101101,k=3) | x | +| 把右数第k位变成0 | (101101->101001,k=3) | x & ~ (1 << (k-1)) | +| 右数第k位取反 | (101001->101101,k=3) | x ^ (1 << (k-1)) | +| 取末三位 | (1101101->101) | x & 7 | +| 取末k位 | (1101101->1101,k=5) | x & (1 << k-1) | +| 取右数第k位 | (1101101->1,k=4) | x >> (k-1) & 1 | +| 把末k位变成1 | (101001->101111,k=4) | x | +| 末k位取反 | (101001->100110,k=4) | x ^ (1 << k-1) | +| 把右边连续的1变成0 | (100101111->100100000) | x & (x+1) | +| 把右起第一个0变成1 | (100101111->100111111) | x | +| 把右边连续的0变成1 | (11011000->11011111) | x | +| 取右边连续的1 | (100101111->1111) | (x ^ (x+1)) >> 1 | +| 去掉右起第一个1的左边 | (100101000->1000) | x & (x ^ (x-1)) | +| 取int max | 2147483647-> 1111111111111111111111111111111 | (1<<31)-1 | +| 取int min | -2147483648->10000000000000000000000000000000 | 1<<31 | +| 判断奇偶数 | 11-> 1 10-> 0 | x&1 | +| 求绝对值 | 11(3)->11(3) 11111101(-3)->11(3) | (x^(x>>31))-(x>>31) | +| 求相反数 | 11->11111101 11111101->11 | ~x+1 | +| 判断是不是2的幂次方 | | ((x&(x-1))==0)&&(x!=0) | + + + + + +### 布隆过滤器 + +> 用于查找元素不存在与过滤器中。常用于缓存的最外层,如果判断在,那么是可能存在的,需要再去实际存储中查找一下是否真正存在。 +> +> - 一个很长的二进制向量和一系列随机映射函数。 +> - 用于检索一个元素是否在一个集合中 +> - 优点: +> - 空间效率和查询时间都远远超过一般算法 +> - 缺点 +> - 有一定的误识别率和删除困难 + +### LRU + +> 最近最少使用,当缓存空间被打满时,剔除最少使用元素。实现方式是哈希表+双向链表。可以用java原生api中LInkedHashMap实现 + +### 排序 + +#### 比较类排序 + +##### 交换排序 + +###### 【冒泡排序】 + +>时间复杂度(平均/最坏/最好):O(n2) /O(n2) /O(n) +> +>空间复杂度:O(1) +> +>稳定性:稳定 + +```java +/** + * 冒泡排序方法 + * @param array + * @return + */ +public static void BubbleSort(int [] arr){ + int temp;//临时变量 + boolean flag;//是否交换的标志 + for(int i=0; ii; j--){ + if(arr[j] < arr[j-1]){ + temp = arr[j]; + arr[j] = arr[j-1]; + arr[j-1] = temp; + flag = true; + } + } + if(!flag) break; + } +} +``` + + + +###### 【快速排序】 + +>时间复杂度(平均/最坏/最好):O(nlogn) /O(n2) /O(nlogn) +> +>空间复杂度:O(nlogn) +> +>稳定性:不稳定 + +```java +/** + * 快速排序方法 + * @param array + * @param start + * @param end + * @return + */ +public static int[] QuickSort(int[] array, int start, int end) { + if (array.length < 1 || start < 0 || end >= array.length || start > end) return null; + int smallIndex = partition(array, start, end); + if (smallIndex > start) + QuickSort(array, start, smallIndex - 1); + if (smallIndex < end) + QuickSort(array, smallIndex + 1, end); + return array; +} +/** + * 快速排序算法——partition + * @param array + * @param start + * @param end + * @return + */ +public static int partition(int[] array, int start, int end) { + int pivot = (int) (start + Math.random() * (end - start + 1)); + int smallIndex = start - 1; + swap(array, pivot, end); + for (int i = start; i <= end; i++) + if (array[i] <= array[end]) { + smallIndex++; + if (i > smallIndex) + swap(array, i, smallIndex); + } + return smallIndex; +} + +/** + * 交换数组内两个元素 + * @param array + * @param i + * @param j + */ +public static void swap(int[] array, int i, int j) { + int temp = array[i]; + array[i] = array[j]; + array[j] = temp; +} +``` + + + +##### 插入排序 + +###### 【简单插入排序】 + +>时间复杂度(平均/最坏/最好):O(n2) /O(n2) /O(n) +> +>空间复杂度:O(1) +> +>稳定性:稳定 + +```java +/** + * 简单插入排序方法 + * @param array + * @param lenth + * @return + */ +public static void insert_sort(int array[],int lenth){ + int temp; + for(int i=0;i0;j--){ + if(array[j] < array[j-1]){ + temp = array[j-1]; + array[j-1] = array[j]; + array[j] = temp; + }else{ //不需要交换 + break; + } + } + } +} +``` + + + +###### 【希尔排序】 + +>时间复杂度(平均/最坏/最好):O(n1.3) /O(n2) /O(n) +> +>空间复杂度:O(1) +> +>稳定性:不稳定 + +```java +/** + * 希尔排序方法 + * @param array + * @param lenth + * @return + */ +public static void shell_sort(int array[],int lenth){ + + int temp = 0; + int incre = lenth; + + while(true){ + incre = incre/2; + + for(int k = 0;kk;j-=incre){ + if(array[j]时间复杂度(平均/最坏/最好):O(n2) /O(n2) /O(n2) +> +>空间复杂度:O(1) +> +>稳定性:不稳定 + +```java +/** + * 简单选择排序方法 + * @param array + * @param lenth + * @return + */ +public static void select_sort(int array[],int lenth){ + + for(int i=0;i时间复杂度(平均/最坏/最好):O(nlogn) /O(nlogn) /O(nlogn) +> +>空间复杂度:O(1) +> +>稳定性:不稳定 + +```java + //声明全局变量,用于记录数组array的长度; + public static int len; + /** + * 堆排序算法 + * + * @param array + * @return + */ + public static int[] HeapSort(int[] array) { + len = array.length; + if (len < 1) return array; + //1.构建一个最大堆 + buildMaxHeap(array); + //2.循环将堆首位(最大值)与末位交换,然后在重新调整最大堆 + while (len > 0) { + swap(array, 0, len - 1); + len--; + adjustHeap(array, 0); + } + return array; + } + /** + * 建立最大堆 + * + * @param array + */ + public static void buildMaxHeap(int[] array) { + //从最后一个非叶子节点开始向上构造最大堆 + for (int i = (len/2 - 1); i >= 0; i--) { //感谢 @让我发会呆 网友的提醒,此处应该为 i = (len/2 - 1) + adjustHeap(array, i); + } + } + /** + * 调整使之成为最大堆 + * + * @param array + * @param i + */ + public static void adjustHeap(int[] array, int i) { + int maxIndex = i; + //如果有左子树,且左子树大于父节点,则将最大指针指向左子树 + if (i * 2 < len && array[i * 2] > array[maxIndex]) + maxIndex = i * 2; + //如果有右子树,且右子树大于父节点,则将最大指针指向右子树 + if (i * 2 + 1 < len && array[i * 2 + 1] > array[maxIndex]) + maxIndex = i * 2 + 1; + //如果父节点不是最大值,则将父节点与最大值交换,并且递归调整与父节点交换的位置。 + if (maxIndex != i) { + swap(array, maxIndex, i); + adjustHeap(array, maxIndex); + } + } +``` + + + +##### 归并排序 + +###### 【二路归并排序】 + +>时间复杂度(平均/最坏/最好):O(nlogn) /O(nlogn) /O(nlogn) +> +>空间复杂度:O(n) +> +>稳定性:稳定 + +```java + /** + * 归并排序 + * + * @param array + * @return + */ + public static int[] MergeSort(int[] array) { + if (array.length < 2) return array; + int mid = array.length / 2; + int[] left = Arrays.copyOfRange(array, 0, mid); + int[] right = Arrays.copyOfRange(array, mid, array.length); + return merge(MergeSort(left), MergeSort(right)); + } + /** + * 归并排序——将两段排序好的数组结合成一个排序数组 + * + * @param left + * @param right + * @return + */ + public static int[] merge(int[] left, int[] right) { + int[] result = new int[left.length + right.length]; + for (int index = 0, i = 0, j = 0; index < result.length; index++) { + if (i >= left.length) + result[index] = right[j++]; + else if (j >= right.length) + result[index] = left[i++]; + else if (left[i] > right[j]) + result[index] = right[j++]; + else + result[index] = left[i++]; + } + return result; + } +``` + + + +###### 【多路归并排序】【外部排序】 + +>时间复杂度(平均/最坏/最好):O(nlogn) /O(nlogn) /O(nlogn) +> +>空间复杂度:O(n) +> +>稳定性:稳定 + + + +#### 非比较类排序 + +###### 【计数排序】 + +>时间复杂度(平均/最坏/最好):O(n+k) /O(n+k) /O(n+k) +> +>空间复杂度:O(n+k) +> +>稳定性:稳定 + +```java + /** + * 计数排序 + * + * @param array + * @return + */ + public static int[] CountingSort(int[] array) { + if (array.length == 0) return array; + int bias, min = array[0], max = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] > max) + max = array[i]; + if (array[i] < min) + min = array[i]; + } + bias = 0 - min; + int[] bucket = new int[max - min + 1]; + Arrays.fill(bucket, 0); + for (int i = 0; i < array.length; i++) { + bucket[array[i] + bias]++; + } + int index = 0, i = 0; + while (index < array.length) { + if (bucket[i] != 0) { + array[index] = i - bias; + bucket[i]--; + index++; + } else + i++; + } + return array; + } +``` + + + +###### 【桶排序】 + +>时间复杂度(平均/最坏/最好):O(n+k) /O(n2) /O(n) +> +>空间复杂度:O(n+k) +> +>稳定性:稳定 + +```java + /** + * 桶排序 + * + * @param array + * @param bucketSize + * @return + */ + public static ArrayList BucketSort(ArrayList array, int bucketSize) { + if (array == null || array.size() < 2) + return array; + int max = array.get(0), min = array.get(0); + // 找到最大值最小值 + for (int i = 0; i < array.size(); i++) { + if (array.get(i) > max) + max = array.get(i); + if (array.get(i) < min) + min = array.get(i); + } + int bucketCount = (max - min) / bucketSize + 1; + ArrayList> bucketArr = new ArrayList<>(bucketCount); + ArrayList resultArr = new ArrayList<>(); + for (int i = 0; i < bucketCount; i++) { + bucketArr.add(new ArrayList()); + } + for (int i = 0; i < array.size(); i++) { + bucketArr.get((array.get(i) - min) / bucketSize).add(array.get(i)); + } + for (int i = 0; i < bucketCount; i++) { + if (bucketSize == 1) { // 如果带排序数组中有重复数字时 感谢 @见风任然是风 朋友指出错误 + for (int j = 0; j < bucketArr.get(i).size(); j++) + resultArr.add(bucketArr.get(i).get(j)); + } else { + if (bucketCount == 1) + bucketSize--; + ArrayList temp = BucketSort(bucketArr.get(i), bucketSize); + for (int j = 0; j < temp.size(); j++) + resultArr.add(temp.get(j)); + } + } + return resultArr; + } +``` + + + +###### 【基数排序】 + +>时间复杂度(平均/最坏/最好):O(n*k) /O(n*k) /O(n*k) +> +>空间复杂度:O(n+k) +> +>稳定性:稳定 + +```java + /** + * 基数排序 + * @param array + * @return + */ + public static int[] RadixSort(int[] array) { + if (array == null || array.length < 2) + return array; + // 1.先算出最大数的位数; + int max = array[0]; + for (int i = 1; i < array.length; i++) { + max = Math.max(max, array[i]); + } + int maxDigit = 0; + while (max != 0) { + max /= 10; + maxDigit++; + } + int mod = 10, div = 1; + ArrayList> bucketList = new ArrayList>(); + for (int i = 0; i < 10; i++) + bucketList.add(new ArrayList()); + for (int i = 0; i < maxDigit; i++, mod *= 10, div *= 10) { + for (int j = 0; j < array.length; j++) { + int num = (array[j] % mod) / div; + bucketList.get(num).add(array[j]); + } + int index = 0; + for (int j = 0; j < bucketList.size(); j++) { + for (int k = 0; k < bucketList.get(j).size(); k++) + array[index++] = bucketList.get(j).get(k); + bucketList.get(j).clear(); + } + } + return array; + } + +``` + diff --git a/Week 07/id_633/LeetCode_242_633.java b/Week 07/id_633/LeetCode_242_633.java new file mode 100644 index 000000000..ba2aa0d43 --- /dev/null +++ b/Week 07/id_633/LeetCode_242_633.java @@ -0,0 +1,20 @@ +package lesson_16_bit; + +public class LeetCode_242_633 { + + public boolean isAnagram(String s, String t) { + int[] alphabet = new int[26]; + for (int i = 0; i < s.length(); i++) { + alphabet[s.charAt(i) - 'a']++; + } + for (int i = 0; i < t.length(); i++) { + alphabet[t.charAt(i) - 'a']--; + } + for (int i : alphabet) { + if (i != 0) { + return false; + } + } + return true; + } +} diff --git a/Week 07/id_633/LeetCode_338_633.java b/Week 07/id_633/LeetCode_338_633.java new file mode 100644 index 000000000..0c1c923ee --- /dev/null +++ b/Week 07/id_633/LeetCode_338_633.java @@ -0,0 +1,13 @@ +package lesson_16_bit; + +public class LeetCode_338_633 { + + public class Solution { + public int[] countBits(int num) { + int[] res = new int[num + 1]; + for (int i = 1; i <= num; ++i) + res[i] = res[i & (i - 1)] + 1; + return res; + } + } +} diff --git a/Week 07/id_638/Leetcode_190_638.java b/Week 07/id_638/Leetcode_190_638.java new file mode 100644 index 000000000..5fa06d2de --- /dev/null +++ b/Week 07/id_638/Leetcode_190_638.java @@ -0,0 +1,16 @@ +package test1.Week7; + +public class Leetcode_190_638 { + /** + * 翻转二进制 + * @param n + * @return + */ + public int reverseBits(int n) { + int res = 0; + for(int i = 31;i >= 0;i--){ + res = res |((n >> i) & 1) << (31 - i) ; + } + return res; + } +} diff --git a/Week 07/id_638/Leetcode_52_638.java b/Week 07/id_638/Leetcode_52_638.java new file mode 100644 index 000000000..6349b3ef6 --- /dev/null +++ b/Week 07/id_638/Leetcode_52_638.java @@ -0,0 +1,31 @@ +package test1.Week7; + +public class Leetcode_52_638 { + + /** + * 八皇后2 + * @param n + * @return + */ + int count = 0; + public int totalNQueens(int n) { + if (n < 1) return 0; + dfs(0,n,0,0,0); + return count; + } + + public void dfs(int row,int n,int col,int pie,int na){ + if (row == n) { + count ++; + return; + } + //当前可以考虑放皇后的位置 + int bits = (~(col | pie | na)) & ((1 << n) - 1); + while (bits != 0){ + int p = bits & (-bits);//取出最低位的1 + bits = bits & (bits -1);//最低位清零 + dfs(row +1,n,col | p,(pie | p) << 1,(na | p) >> 1); + + } + } +} diff --git a/Week 07/id_643/LeetCode_191_643.java b/Week 07/id_643/LeetCode_191_643.java new file mode 100644 index 000000000..4cff96708 --- /dev/null +++ b/Week 07/id_643/LeetCode_191_643.java @@ -0,0 +1,13 @@ +public class Solution { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int count = 0; + + while(n !=0) { + n = n&(n-1); + count++; + } + return count; + } + +} diff --git a/Week 07/id_643/LeetCode_231_643.java b/Week 07/id_643/LeetCode_231_643.java new file mode 100644 index 000000000..9b633c732 --- /dev/null +++ b/Week 07/id_643/LeetCode_231_643.java @@ -0,0 +1,11 @@ +class Solution { + public boolean isPowerOfTwo(int n) { + boolean flag = false; + if(n > 0) { + n = n & n - 1; + if(n == 0) + flag = true; + } + return flag; + } +} diff --git a/Week 07/id_648/LeetCode_1122_648.java b/Week 07/id_648/LeetCode_1122_648.java new file mode 100644 index 000000000..e7372ee43 --- /dev/null +++ b/Week 07/id_648/LeetCode_1122_648.java @@ -0,0 +1,46 @@ +import com.sun.deploy.util.ArrayUtil; + +import java.util.*; + +/** + * @Date 2019/12/1 + **/ +public class LeetCode_1122_648 { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + int maxNum = 0; + for(int i=0;i result = new ArrayList(); + for(int i=0;i0){ + result.add(arr2[i]); + bucket[arr2[i]]--; + } + } + for(int i=0;i<=maxNum;i++){ + while(bucket[i]>0){ + result.add(i); + bucket[i]--; + } + } + int[] s = new int[arr1.length]; + for(int i=0;i map; + class LinkedCappedHashMap extends LinkedHashMap + { + int maxCapacity; + LinkedCappedHashMap(int capacityNum){ + super(16,0.75f,true); + this.maxCapacity = capacityNum; + } + protected boolean removeEldestEntry(Map.Entry eldest){ + return size()>maxCapacity; + } + } + + public LeetCode_146_648(int capacity) { + map = new LinkedCappedHashMap<>(capacity); + } + public int get(int key) { + if(!map.containsKey(key)){return -1;} + return map.get(key); + } + + public void put(int key, int value) { + map.put(key,value); + } +} diff --git a/Week 07/id_648/LeetCode_191_648.java b/Week 07/id_648/LeetCode_191_648.java new file mode 100644 index 000000000..4dccf4b39 --- /dev/null +++ b/Week 07/id_648/LeetCode_191_648.java @@ -0,0 +1,20 @@ +/** + * @Date 2019/12/1 + **/ +public class LeetCode_191_648 { + public int hammingWeight(int n) { + int sum = 0 ; + while(n!=0){ + sum++; + n&=n-1; + } + System.out.println(sum); + return sum; + } + + public static void main(String[] args) { + LeetCode_191_648 leetCode_191_648 = new LeetCode_191_648(); + leetCode_191_648.hammingWeight(5); + } + +} diff --git a/Week 07/id_648/LeetCode_231_648.java b/Week 07/id_648/LeetCode_231_648.java new file mode 100644 index 000000000..cd947b043 --- /dev/null +++ b/Week 07/id_648/LeetCode_231_648.java @@ -0,0 +1,14 @@ +/** + * @Date 2019/12/1 21:44 + **/ +public class LeetCode_231_648 { + public boolean isPowerOfTwo(int n) { + return n>0&&(n & (n-1))==0; + } + + public static void main(String[] args) { + LeetCode_231_648 leetCode_231_648 = new LeetCode_231_648(); + System.out.println(leetCode_231_648.isPowerOfTwo(16)); + + } +} diff --git a/Week 07/id_653/LeetCode_493_653.java b/Week 07/id_653/LeetCode_493_653.java new file mode 100644 index 000000000..8712c765b --- /dev/null +++ b/Week 07/id_653/LeetCode_493_653.java @@ -0,0 +1,23 @@ +class Solution { + public int reversePairs(int[] nums) { + return mergeSort(nums,0,nums.length-1); + } + + private int mergeSort(int [] nums,int s,int e) { + if(s>=e) { + return 0; + } + int mid = (s+e)/2; + int count = mergeSort(nums,s,mid)+mergeSort(nums,mid+1,e); + + for (int i = s,j=mid+1; i < mid+1; i++) { + while (j<=e&&nums[i]/2.0>nums[j]) + j++; + count = count+(j-mid-1); + } + + Arrays.sort(nums,s,e+1); + return count; + + } +} \ No newline at end of file diff --git a/Week 07/id_653/LeetCode_51_653.java b/Week 07/id_653/LeetCode_51_653.java new file mode 100644 index 000000000..4218116ac --- /dev/null +++ b/Week 07/id_653/LeetCode_51_653.java @@ -0,0 +1,50 @@ +class Solution { + private int size; + private int count; + private int n; + List> lists = new ArrayList<>(); + + private void solve(int row, int ld, int rd,String str) { + if (row == size) { + count++; + String [] res = str.split(","); + + ArrayList temps = new ArrayList(); + for (int i = 0; i < res.length; i++) { + + String format = String.format("%016d", Long.parseLong(res[i])); + + format = format.substring(format.length() - n).replace('1', 'Q').replace('0', '.'); + + temps.add(format); + } + + lists.add(temps); + return; + } + int pos = size & (~(row | ld | rd)); +// System.out.println("pos: " + Integer.toBinaryString(pos)); + while (pos != 0) { +// System.out.println("-pos: " + Integer.toBinaryString(-pos)); + + int p = pos & (-pos); + String s = Integer.toBinaryString(p); + + pos -= p; // pos &= pos - 1; +// System.out.println("pos: " + Integer.toBinaryString(pos)); + if (str.length()>0) { + s = s+","+str; + } + solve(row | p, (ld | p) << 1, (rd | p) >> 1,s); + + } + } + + public List> solveNQueens(int n) { + count = 0; + size = (1 << n) - 1; + this.n = n; + solve(0, 0, 0,""); + return lists; + } +} \ No newline at end of file diff --git a/Week 07/id_658/LeetCode_146_658.java b/Week 07/id_658/LeetCode_146_658.java new file mode 100644 index 000000000..e9dd272e8 --- /dev/null +++ b/Week 07/id_658/LeetCode_146_658.java @@ -0,0 +1,40 @@ +import java.util.LinkedHashMap; + +/* + * @lc app=leetcode.cn id=146 lang=java + * + * [146] LRU缓存机制 + */ + +// @lc code=start +class LRUCache extends LinkedHashMap { + + private static final long serialVersionUID = 1L; + private int capacity; + + public LRUCache(int capacity) { + super(capacity, 0.75f, true); + this.capacity = capacity; + } + + public int get(int key) { + return super.getOrDefault(key, -1); + } + + public void put(int key, int value) { + super.put(key, value); + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > capacity; + } +} + +/** + * Your LRUCache object will be instantiated and called as such: + * LRUCache obj = new LRUCache(capacity); + * int param_1 = obj.get(key); + * obj.put(key,value); + */ +// @lc code=end diff --git a/Week 07/id_658/LeetCode_190_658.java b/Week 07/id_658/LeetCode_190_658.java new file mode 100644 index 000000000..56510d733 --- /dev/null +++ b/Week 07/id_658/LeetCode_190_658.java @@ -0,0 +1,22 @@ +/* + * @lc app=leetcode.cn id=190 lang=java + * + * [190] 颠倒二进制位 + */ + +// @lc code=start +public class Solution { + // you need treat n as an unsigned value + public int reverseBits(int n) { + if (n == 0) + return 0; + int result = 0; + for (int i = 0; i < 32; i++) { + result <<= 1; + result ^= (n & 1); + n >>= 1; + } + return result; + } +} +// @lc code=end diff --git a/Week 07/id_658/LeetCode_191_658.java b/Week 07/id_658/LeetCode_191_658.java new file mode 100644 index 000000000..c323932bf --- /dev/null +++ b/Week 07/id_658/LeetCode_191_658.java @@ -0,0 +1,19 @@ +/* + * @lc app=leetcode.cn id=191 lang=java + * + * [191] 位1的个数 + */ + +// @lc code=start +public class Solution { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int sum = 0; + while (n != 0) { + sum++; + n &= (n - 1); + } + return sum; + } +} +// @lc code=end diff --git a/Week 07/id_658/LeetCode_231_658.java b/Week 07/id_658/LeetCode_231_658.java new file mode 100644 index 000000000..612cbc64f --- /dev/null +++ b/Week 07/id_658/LeetCode_231_658.java @@ -0,0 +1,13 @@ +/* + * @lc app=leetcode.cn id=231 lang=java + * + * [231] 2的幂 + */ + +// @lc code=start +class Solution { + public boolean isPowerOfTwo(int n) { + return n > 0 && (n & (n - 1)) == 0; + } +} +// @lc code=end diff --git a/Week 07/id_658/LeetCode_242_658.java b/Week 07/id_658/LeetCode_242_658.java new file mode 100644 index 000000000..effe0c1bb --- /dev/null +++ b/Week 07/id_658/LeetCode_242_658.java @@ -0,0 +1,26 @@ +/* + * @lc app=leetcode.cn id=242 lang=java + * + * [242] 有效的字母异位词 + */ + +// @lc code=start +class Solution { + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + int[] counts = new int[26]; + for (int i = 0; i < s.length(); i++) { + counts[s.charAt(i) - 'a']++; + counts[t.charAt(i) - 'a']--; + } + for (int count : counts) { + if (count != 0) { + return false; + } + } + return true; + } +} +// @lc code=end diff --git a/Week 07/id_658/NOTE.md b/Week 07/id_658/NOTE.md index a6321d6e2..f221c4b89 100644 --- a/Week 07/id_658/NOTE.md +++ b/Week 07/id_658/NOTE.md @@ -1,4 +1,225 @@ -# NOTE +# 第七周学习总结 - +## 位运算 +- 计算机里的数字表示方式和存储格式就是二进制 + +### 位运算符 + +- 左移 << +- 右移 >> +- 按位或 | +- 按位与 & +- 按位取反 ~ +- 按位异或 ^ + - 相同为0 不同为1 + - 不进位加法 + +### 位运算要点 + +- 判断奇偶 + - 奇数 x % 2 == 1 位运算:(x & 1) == 1 + - 偶数 x % 2 == 0 位运算:(x & 1) == 0 +- x / 2 位运算:x >> 1 +- 清零最低位的1 位运算:x = x & (x - 1) +- 得到最低位的1 位运算:x & -x +- x & ~x == 0 + +## 布隆过滤器 Bloom Filter + +- 一个很长的二进制向量和一系列随机映射函数构成 +- 可以用于检索一个元素是否在一个集合中 +- 优点是空间效率和查询效率都远超过一般的算法 +- 缺点是有一定的误识别率和删除困难 + +```java +package com.github.lovasoa.bloomfilter; + +import java.util.BitSet; +import java.util.Random; +import java.util.Iterator; + +public class BloomFilter implements Cloneable { + private BitSet hashes; + private RandomInRange prng; + private int k; // Number of hash functions + private static final double LN2 = 0.6931471805599453; // ln(2) + + /** + * Create a new bloom filter. + * + * @param n Expected number of elements + * @param m Desired size of the container in bits + **/ + public BloomFilter(int n, int m) { + k = (int) Math.round(LN2 * m / n); + if (k <= 0) + k = 1; + this.hashes = new BitSet(m); + this.prng = new RandomInRange(m, k); + } + + /** + * Create a bloom filter of 1Mib. + * + * @param n Expected number of elements + **/ + public BloomFilter(int n) { + this(n, 1024 * 1024 * 8); + } + + /** + * Add an element to the container + **/ + public void add(Object o) { + prng.init(o); + for (RandomInRange r : prng) + hashes.set(r.value); + } + + /** + * Returns true if the element is in the container. + * Returns false with a probability ≈ 1-e^(-ln(2)² * m/n) + * if the element is not in the container. + **/ + public boolean contains(Object o) { + prng.init(o); + for (RandomInRange r : prng) + if (!hashes.get(r.value)) + return false; + return true; + } + + /** + * Removes all of the elements from this filter. + **/ + public void clear() { + hashes.clear(); + } + + /** + * Create a copy of the current filter + **/ + public BloomFilter clone() throws CloneNotSupportedException { + return (BloomFilter) super.clone(); + } + + /** + * Generate a unique hash representing the filter + **/ + public int hashCode() { + return hashes.hashCode() ^ k; + } + + /** + * Test if the filters have equal bitsets. + * WARNING: two filters may contain the same elements, but not be equal + * (if the filters have different size for example). + */ + public boolean equals(BloomFilter other) { + return this.hashes.equals(other.hashes) && this.k == other.k; + } + + /** + * Merge another bloom filter into the current one. + * After this operation, the current bloom filter contains all elements in + * other. + **/ + public void merge(BloomFilter other) { + if (other.k != this.k || other.hashes.size() != this.hashes.size()) { + throw new IllegalArgumentException("Incompatible bloom filters"); + } + this.hashes.or(other.hashes); + } + + private class RandomInRange + implements Iterable, Iterator { + + private Random prng; + private int max; // Maximum value returned + 1 + private int count; // Number of random elements to generate + private int i = 0; // Number of elements generated + public int value; // The current value + + RandomInRange(int maximum, int k) { + max = maximum; + count = k; + prng = new Random(); + } + + public void init(Object o) { + prng.setSeed(o.hashCode()); + } + + public Iterator iterator() { + i = 0; + return this; + } + + public RandomInRange next() { + i++; + value = prng.nextInt() % max; + if (value < 0) + value = -value; + return this; + } + + public boolean hasNext() { + return i < count; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } +} +``` + +## LRU 缓存 + +- 最近最少使用 +- 两个要素:大小、替换策略 +- HashTable + Double LinkedList +- 查询 **O(1)** 修改更新 **O(1)** +- Java 中 LinkedHashMap + +## 排序算法 + +### 比较类排序 + +- 通过比较来决定元素间的相对次序,由于其时间复杂度不能突破 **O(nlogn)**,因此也称为非线性时间比较类排序 +- 交换排序 + - 冒泡排序 `Bubble Sort` **O(n^2)** + - 嵌套循环,每次查看相邻的元素如果逆序,则交换 + - 快速排序 `Quick Sort` **O(nlogn)** + - 数组取标杆 pivot,将小元素放在 pivot 左边,大元素放在右侧,然后依次对左边和右边的子数组继续快排,以达到整个序列有序 +- 插入排序 `Insertion Sort` **O(n^2)** + - 从前到后逐步构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入 + - 简单插入排序 + - 希尔排序 +- 选择排序 `Selection Sort` **O(n^2)** + - 每次找最小值,然后放到待排序数组的起始位置 + - 简单选择排序 + - 堆排序 `Heep Sort` **O(nlogn)** + - 堆插入 **O(logn)** + - 取最大/小值 **O(1)** + - 数组元素依次建立小顶堆,依次取堆顶元素,并删除 +- 归并排序 `Merge Sort` **O(nlogn)** `分治` + - 把长度为 n 的输入序列分成两个长度为 n / 2 的子序列,对两个子序列分别采用归并排序,将两个排序好的子序列合并成一个最终的排序序列 + - 二路归并排序 + - 多路归并排序 +- `归并排序`和`快速排序`具有相似性,但步骤顺序相反 + - 归并:先排序左右子数组,然后合并两个有序子数组 + - 快排:先调配出左右子数组,然后对于左右子数组进行排序 + +### 非比较类排序 + +- 不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序 +- 计数排序 `Counting Sort` **O(n)** + - 计数排序要求输入的数据必须是有确定范围的整数,将输入的数据值转化为键存储在额外开辟的数组空间中,然后依次把计数大于1的填充回原数组 +- 桶排序 `Bucket Sort` **O(n)** + - 假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序 + - 有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序 +- 基数排序 `Radix Sort` **O(n)** + - 先按照低位排序,然后收集,再按照高位排序,然后再收集,依此类推,直到最高位。 + - 有时候有些属性是有优先级顺序的,先按照低优先级排序,再按照高优先级排序 diff --git a/Week 07/id_668/leetcode_146_668.py b/Week 07/id_668/leetcode_146_668.py new file mode 100644 index 000000000..25e51bda5 --- /dev/null +++ b/Week 07/id_668/leetcode_146_668.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: 146_lru_cache.py + @time: 2019/12/1 12:41 +""" +import collections + + +class LRUCache(object): + """ + 运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。 + + 获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 + 写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值, + 从而为新的数据值留出空间。 + + 进阶: + 你是否可以在 O(1) 时间复杂度内完成这两种操作? + + 示例: + LRUCache cache = new LRUCache( 2 /* 缓存容量 */ ); + + cache.put(1, 1); + cache.put(2, 2); + cache.get(1); // 返回 1 + cache.put(3, 3); // 该操作会使得密钥 2 作废 + cache.get(2); // 返回 -1 (未找到) + cache.put(4, 4); // 该操作会使得密钥 1 作废 + cache.get(1); // 返回 -1 (未找到) + cache.get(3); // 返回 3 + cache.get(4); // 返回 4 + + """ + + def __init__(self, capacity): + """ + :type capacity: int + """ + self.dict = collections.OrderedDict() + self.remain = capacity # remain表示可装入的缓存元素数量,当值为0时,表示已满;此时若加入新的缓存元素,则需要删除老的缓存元素 + + def get(self, key): + """ + :type key: int + :rtype: int + + 策略:获取缓存元素,如果不存在则返回-1,若存在,则需要将其从原有位置移动到最新的位置,因为这个缓存元素是最新被访问过 + """ + if key not in self.dict: + return -1 + + v = self.dict.pop(key) + self.dict[key] = v + + return v + + def put(self, key, value): + """ + :type key: int + :type value: int + :rtype: None + + 添加缓存元素:如果已在缓存池中,则将其从原有位置移动到最新位置,这里的做法是将原有位置的缓存元素pop,然后在最新位置加入 + 如果不存在,这个时候需要判断缓存池是否已满,若没满,则直接添加同时缓存剩余量-1;若已满,则需要将老的缓存元素pop出一个, + 然后加入新的元素 + """ + if key in self.dict: + self.dict.pop(key) + else: + if self.remain > 0: + self.remain -= 1 + else: + # 参数last=False以FIFO order pop element + self.dict.popitem(last=False) # 缓存池已满,需要将最先进来且没被使用的缓存元素给清理掉 + + self.dict[key] = value + diff --git a/Week 07/id_668/leetcode_493_668.py b/Week 07/id_668/leetcode_493_668.py new file mode 100644 index 000000000..10bccd6aa --- /dev/null +++ b/Week 07/id_668/leetcode_493_668.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: 493_reverse_pairs.py + @time: 2019/11/30 20:46 +""" + + +class Solution(object): + """ + 给定一个数组 nums ,如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对。 + 你需要返回给定数组中的重要翻转对的数量。 + + 示例 1: + 输入: [1,3,2,3,1] + 输出: 2 + + 示例 2: + 输入: [2,4,3,5,1] + 输出: 3 + + 注意: + 给定数组的长度不会超过50000。 + 输入数组中的所有数字都在32位整数的表示范围内。 + """ + + def reverse_pairs(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + if len(nums) == 0: + return 0 + + self.counter = 0 + self.merge_sort(nums, 0, len(nums) - 1) + + return self.counter + + def merge_sort(self, arr, left, right): + if left >= right: + return + + mid = (left + right) >> 1 + + self.merge_sort(arr, left, mid) + self.merge_sort(arr, mid + 1, right) + self.merge(arr, left, mid, right) + + def merge(self, arr, left, mid, right): + # 计算counter值 + l, r = left, mid + 1 + + while l <= mid and r <= right: + if arr[l] > 2 * arr[r]: + self.counter += (mid + 1 - l) + r += 1 + else: + l += 1 + + # 合并有序数组 + temp, i, j = [], left, mid + 1 + + while i <= mid and j <= right: + if arr[i] <= arr[j]: + temp.append(arr[i]) + i += 1 + else: + temp.append(arr[j]) + j += 1 + + temp += arr[i: mid + 1] + temp += arr[j: right + 1] + arr[left: right + 1] = temp + + # 或直接使用sorted排序,不再需要合并有序数组的操作 + # arr[left: right + 1] = sorted(arr[left: right + 1]) + diff --git a/Week 07/id_668/leetcode_56_668.py b/Week 07/id_668/leetcode_56_668.py new file mode 100644 index 000000000..dcab6a6ba --- /dev/null +++ b/Week 07/id_668/leetcode_56_668.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: 56_merge_intervals.py + @time: 2019/11/30 17:08 +""" + + +class Solution(object): + """ + 给出一个区间的集合,请合并所有重叠的区间。 + + 示例 1: + 输入: [[1,3],[2,6],[8,10],[15,18]] + 输出: [[1,6],[8,10],[15,18]] + 解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]. + + 示例 2: + 输入: [[1,4],[4,5]] + 输出: [[1,5]] + 解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。 + + """ + + def merge(self, intervals): + """ + :type intervals: List[List[int]] + :rtype: List[List[int]] + """ + if len(intervals) == 0: + return [] + + intervals = sorted(intervals, key=lambda e: e[0]) # 因为每个区间并不是按照区间首元素递增的,因此这里首先按区间首字母排序 + res = [intervals[0]] + + for interval in intervals[1:]: + if interval[0] <= res[-1][-1]: # 如果当前区间首元素<=已有序结果res中最后一个区间的尾元素,则此时需要合并区间 + res[-1][-1] = max(interval[-1], res[-1][-1]) + else: + res.append(interval) + + return res + diff --git a/Week 07/id_673/lru-cache.cpp b/Week 07/id_673/lru-cache.cpp new file mode 100644 index 000000000..4b81fc990 --- /dev/null +++ b/Week 07/id_673/lru-cache.cpp @@ -0,0 +1,53 @@ +class LRUCache { +public: + LRUCache(int capacity) : max_c(capacity), c(0) {} + + int get(int key) { + if (reg.find(key) != reg.end()) { + auto valReg = reg[key]; + auto val = std::get<0>(valReg); + auto valP = std::get<1>(valReg); + if (valP != lru_queue.begin()) { + lru_queue.erase(valP); + lru_queue.emplace_front(key); + auto newValReg = make_tuple(val, lru_queue.begin()); + reg[key] = newValReg; + } + return val; + } else { + return -1; + } + } + + void put(int key, int value) { + if (reg.find(key) != reg.end()) { + auto valReg = reg[key]; + auto valP = std::get<1>(valReg); + if (valP != lru_queue.begin()) { + lru_queue.erase(valP); + lru_queue.emplace_front(key); + } + auto newValReg = make_tuple(value, lru_queue.begin()); + reg[key] = newValReg; + } else { + lru_queue.emplace_front(key); + auto newValReg = make_tuple(value, lru_queue.begin()); + reg[key] = newValReg; + for (++c; c > max_c; --c) { + auto finalK = lru_queue.back(); + reg.erase(finalK); + lru_queue.pop_back(); + } + } + } + +private: + using key_type = int; + using value_type = int; + using it_type = list::iterator; + using reg_type = tuple; + + unordered_map reg; + list lru_queue; + int c, max_c; +}; diff --git a/Week 07/id_673/maximal-rectangle.cpp b/Week 07/id_673/maximal-rectangle.cpp new file mode 100644 index 000000000..397d6255d --- /dev/null +++ b/Week 07/id_673/maximal-rectangle.cpp @@ -0,0 +1,39 @@ +class Solution { +public: + void update( vector< vector< vector>>& record, int begin_i, int begin_j){ + int line_min = record[begin_i][begin_j][0]; + int row = record[begin_i][begin_j][1]; + + for( int count = 0; count < row; count++){ + line_min = min( line_min, record[ begin_i-count][begin_j][0]); + record[begin_i][begin_j][2] = max( record[begin_i][begin_j][2], line_min * (count + 1)); + } + } + + int maximalRectangle(vector>& matrix) { + if( !matrix.size()) + return 0; + vector< vector< vector>> record( matrix.size(), vector>( matrix[0].size(), {0,0,0})); + int res = 0; + //dp 过程 + for( int i = 0; i < matrix.size(); i++) + for( int j = 0; j < matrix[0].size(); j++){ + if( matrix[i][j] == '0') + ; + else + if( i == 0 && j == 0) + record[i][j] = { 1, 1, 1}; + else if( i == 0) + record[i][j] = {record[i][j-1][0] + 1, 1, record[i][j-1][2] + 1}; + else if( j == 0) + record[i][j] = {1, record[i-1][j][1] + 1, record[i-1][j][2] + 1}; + else{ + record[i][j][0] = record[i][j-1][0] + 1, record[i][j][1] = record[i-1][j][1] + 1; + update( record, i, j); + } + res = max( res, record[i][j][2]); + } + + return res; + } +}; diff --git a/Week 07/id_673/reverse-bits.cpp b/Week 07/id_673/reverse-bits.cpp new file mode 100644 index 000000000..91ad8b557 --- /dev/null +++ b/Week 07/id_673/reverse-bits.cpp @@ -0,0 +1,14 @@ +class Solution { +public: + unordered_map memo; + uint32_t reverseBits(uint32_t n) { + if (memo.count(n) > 0) + return memo[n]; + int res = 0; + for (int i = 0; i < 32; ++i) + if (n & (1 << i)) + res |= 1 << (31 - i); + memo[n] = res; + return res; + } +}; diff --git a/Week 07/id_683/LeetCode_190_683.java b/Week 07/id_683/LeetCode_190_683.java new file mode 100644 index 000000000..611aa68da --- /dev/null +++ b/Week 07/id_683/LeetCode_190_683.java @@ -0,0 +1,12 @@ +public class Solution { + // you need treat n as an unsigned value + public int reverseBits(int n) { + int res = 0; + + for (int i = 0; i < 32; ++i) { + res = (res << 1) + (n & 1); + n >>= 1; + } + return res; + } +} \ No newline at end of file diff --git a/Week 07/id_683/LeetCode_191_683.java b/Week 07/id_683/LeetCode_191_683.java new file mode 100644 index 000000000..ac17d75db --- /dev/null +++ b/Week 07/id_683/LeetCode_191_683.java @@ -0,0 +1,23 @@ +public class Solution { + // you need to treat n as an unsigned value + + public int hammingWeight(int n) { + int count = 0; + while (n != 0) { + count++; + // 清除n最后一位1 + n = n & (n - 1); + } + return count; + } + + public int hammingWeight1(int n) { + int count = 0; + for (int i = 0; i < 32; i++) { + if ((n & (1 << i)) != 0) { + count++; + } + } + return count; + } +} \ No newline at end of file diff --git a/Week 07/id_683/LeetCode_231_683.java b/Week 07/id_683/LeetCode_231_683.java new file mode 100644 index 000000000..7177e5db5 --- /dev/null +++ b/Week 07/id_683/LeetCode_231_683.java @@ -0,0 +1,5 @@ +class Solution { + public boolean isPowerOfTwo(int n) { + return (n != 0) && (n & (n - 1)) == 0; + } +} \ No newline at end of file diff --git a/Week 07/id_683/LeetCode_56_683.java b/Week 07/id_683/LeetCode_56_683.java new file mode 100644 index 000000000..af2a82307 --- /dev/null +++ b/Week 07/id_683/LeetCode_56_683.java @@ -0,0 +1,38 @@ +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +class Solution { + + public int[][] merge(int[][] intervals) { + + if (intervals == null || intervals.length < 2) { + return intervals; + } + + List res = new ArrayList<>(); + + Collections.sort(intervals, new Comparator() { + @Override + public int compare(int[] o1, int[] o2) { + return o1[0] - o2[0]; + } + }); + + int index = 0; + + while (index < intervals.length) { + int left = intervals[index][0]; + int right = intervals[index][1]; + + while (index < intervals.length - 1 && right <= intervals[index + 1][0]) { + index++; + right = Math.max(right, intervals[index][1]); + } + res.add(new int[] {left, right}); + index++; + } + + return res.toArray(new int[0][]); + } +} \ No newline at end of file diff --git a/Week 07/id_683/NOTE.md b/Week 07/id_683/NOTE.md index a6321d6e2..5ccd62264 100644 --- a/Week 07/id_683/NOTE.md +++ b/Week 07/id_683/NOTE.md @@ -1,4 +1,46 @@ -# NOTE +# 第七周学习总结 - +## 位运算 + +### XOR异或 + +- x ^ 0 = x +- x ^ 1s = ~x +- x ^ (~x) = 1s +- x ^ x = 0 +- c = a ^ b => a ^ c = b, b ^ c = a +- a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c + +### 指定位置的位运算 + +1. 将x最右边的n位清零: x & (~0 << n) +2. 获取x第n位的值(0或1): (x >> n) & 1 +3. 获取x的第n位的幂值: x & (1 << (n-1)) +4. 仅将第n位位置为1: x | (1 << n) +5. 仅将第n位位置为0: x & (~(1 << n)) +6. 将x最高位至第n位(含第n位)清零: x & ((1 << n) - 1) +7. 将第n位至第0位(含第0位)清零: x & (~((1 << (n + 1)) - 1)) + +### 实战位运算要点 + +- 判断奇偶 + - x % 2 == 1 --> (x & 1) == 1 + x % 2 == 0 --> (x & 1) == 0 +- x >> 1 --> x / 2 + - 即 x = x / 2; --> x = x >> 1; + - mid = (left + right) / 2 --> mid = (left + right) >> 1; +- x & (x - 1) 清零最低位的1 +- x & -x => 得到最低位的1 +- x & (~x) => 0 + + +## 排序 + +### 比较类排序算法 + +通过比较来决定元素的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。 + +### 非比较类排序算法 + +不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。 \ No newline at end of file diff --git a/Week 07/id_693/LeetCode_1122_693.java b/Week 07/id_693/LeetCode_1122_693.java new file mode 100644 index 000000000..bfea8c234 --- /dev/null +++ b/Week 07/id_693/LeetCode_1122_693.java @@ -0,0 +1,34 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 1122. 数组的相对排序 https://leetcode-cn.com/problems/relative-sort-array/ + * @Date 2019/11/27 + */ +public class LeetCode_1122_693 { + /** + * 计数排序:1、设定计数数组 2、放入计数数组 3、根据arr2取出计数数组数据,取出计数数组剩余的 + */ + class Solution { + public int[] relativeSortArray(int[] arr1,int[] arr2) { + int[] counts = new int[1001]; + int[] res = new int[arr1.length]; + for (int val : arr1) { + counts[val]++; + } + int i = 0; + for (int val : arr2) { + while (counts[val]-- > 0) { + res[i++] = val; + } + } + + for (int j = 0; j < 1001; j++) { + while (counts[j]-- > 0) { + res[i++] = j; + } + } + return res; + } + } +} diff --git a/Week 07/id_693/LeetCode_1244_693.java b/Week 07/id_693/LeetCode_1244_693.java new file mode 100644 index 000000000..a77106d09 --- /dev/null +++ b/Week 07/id_693/LeetCode_1244_693.java @@ -0,0 +1,48 @@ +package id_693; + +import java.util.*; + +/** + * @Author 李雷(KyLin) + * @Desc 1244. 力扣排行榜 https://leetcode-cn.com/problems/design-a-leaderboard/ + * @Date 2019/11/27 + */ +public class LeetCode_1244_693 { + /** + * HashMap + */ + class Leaderboard { + Map map; + + public Leaderboard() { + map = new HashMap<>(); + } + + public void addScore(int playerId,int score) { + map.put(playerId,map.getOrDefault(playerId,0) + score); + } + + public int top(int K) { + List list = new ArrayList<>(map.values()); + Collections.sort(list); + int res = 0; + int size = list.size(); + for (int i = size - K; i < size; i++) { + res += list.get(i); + } + return res; + } + + public void reset(int playerId) { + map.remove(playerId); + } + } +} + +/** + * Your Leaderboard object will be instantiated and called as such: + * Leaderboard obj = new Leaderboard(); + * obj.addScore(playerId,score); + * int param_2 = obj.top(K); + * obj.reset(playerId); + */ \ No newline at end of file diff --git a/Week 07/id_693/LeetCode_146_693.java b/Week 07/id_693/LeetCode_146_693.java new file mode 100644 index 000000000..a7a8ab206 --- /dev/null +++ b/Week 07/id_693/LeetCode_146_693.java @@ -0,0 +1,104 @@ +package id_693; + + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; + +/** + * @Author 李雷(KyLin) + * @Desc 146. LRU缓存机制 https://leetcode-cn.com/problems/lru-cache/ + * @Date 2019/11/27 + */ +public class LeetCode_146_693 { + /** + * LinkedHashMap + 重写removeEldestEntry + */ + public class LRUCache { + LinkedHashMap elements; + + public LRUCache(int capacity) { + //考虑已经限定了容量,所以直接设定加载因子(负荷因子)为1 + this.elements = new LinkedHashMap(capacity,1,true) { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > capacity; + } + }; + + } + + public int get(int key) { + Integer e = elements.get(key); + return e == null ? -1 : e; + } + + public void put(int key,int value) { + elements.put(key,value); + } + } + + public static void main(String[] args) { + LRUCache lruCache = new LeetCode_146_693().new LRUCache(2); + lruCache.put(1,1); + lruCache.put(2,2); + lruCache.get(1); + lruCache.put(3,3); + lruCache.get(2); + lruCache.put(4,4); + lruCache.get(1); + lruCache.get(3); + lruCache.get(4); + + } + + /** + * 双向链表 + HashMap + */ + public class LRUCache2 { + LinkedList order; + Map elements; + int size; + int capacity; + + public LRUCache2(int capacity) { + this.order = new LinkedList<>(); + this.elements = new HashMap<>(capacity,1); + this.capacity = capacity; + this.size = 0; + } + + public int get(int key) { + if (!elements.containsKey(key)) { + return -1; + } else { + order.addFirst(order.remove(order.indexOf(key))); + return elements.get(key); + } + } + + public void put(int key,int value) { + if (elements.containsKey(key)) { + order.addFirst(order.remove(order.indexOf(key))); + elements.put(key,value); + } else { + if (size == capacity) { + int t = order.removeLast(); + elements.remove(t); + } else { + size++; + } + order.addFirst(key); + elements.put(key,value); + } + } + } +} + +/** + * Your LRUCache object will be instantiated and called as such: + * LRUCache obj = new LRUCache(capacity); + * int param_1 = obj.get(key); + * obj.put(key,value); + */ diff --git a/Week 07/id_693/LeetCode_190_693.java b/Week 07/id_693/LeetCode_190_693.java new file mode 100644 index 000000000..af0203c63 --- /dev/null +++ b/Week 07/id_693/LeetCode_190_693.java @@ -0,0 +1,42 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 190. 颠倒二进制位 https://leetcode-cn.com/problems/reverse-bits/ + * @Date 2019/11/26 + */ +public class LeetCode_190_693 { + /** + * 位运算1 + */ + class Solution { + // you need treat n as an unsigned value + + //输入: 00000010100101000001111010011100 + //输出: 00111001011110000010100101000000 + public int reverseBits(int n) { + int res = 0; + for (int i = 0; i < 32; i++) { + res = (res << 1) | (n & 1); + n = n >> 1; + } + return res; + } + } + + /** + * 位运算2 + */ + class Solution2 { + // you need treat n as an unsigned value + public int reverseBits(int n) { + int res = 0; + for (int i = 0; i < 32; i++) { + res = res << 1; + res = res + (n & 1); + n = n >> 1; + } + return res; + } + } +} diff --git a/Week 07/id_693/LeetCode_191_693.java b/Week 07/id_693/LeetCode_191_693.java new file mode 100644 index 000000000..bd6d36021 --- /dev/null +++ b/Week 07/id_693/LeetCode_191_693.java @@ -0,0 +1,40 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 191. 位1的个数 https://leetcode-cn.com/problems/number-of-1-bits/ + * @Date 2019/11/26 + */ +public class LeetCode_191_693 { + /** + * 位运算1 :(x >> n) & 1 获取x的n位的值(0或1) + */ + public class Solution { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int count = 0; + for (int i = 1; i <= 32; i++) { + if (((n >> i) & 1) == 1) { + count++; + } + } + return count; + } + + } + + /** + * 位运算2 :n & (n - 1) 清零最低位的1 + */ + public class Solution2 { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int count = 0; + while (n != 0) { + count++; + n = n & (n - 1); + } + return count; + } + } +} diff --git a/Week 07/id_693/LeetCode_231_693.java b/Week 07/id_693/LeetCode_231_693.java new file mode 100644 index 000000000..1f7f3d38b --- /dev/null +++ b/Week 07/id_693/LeetCode_231_693.java @@ -0,0 +1,32 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 231. 2的幂 https://leetcode-cn.com/problems/power-of-two/ + * @Date 2019/11/26 + */ +public class LeetCode_231_693 { + /** + * 位运算1:直接套用计算1的数量, 2的幂只有一个1 + */ + class Solution { + public boolean isPowerOfTwo(int n) { + if (n < 0) return false; + int count = 0; + while (n != 0) { + count++; + n &= (n - 1); + } + return count == 1; + } + } + + /** + * 位运算2:直接和自身-1 比较,如果是0 那么肯定就是2的倍数了 + */ + class Solution2 { + public boolean isPowerOfTwo(int n) { + return n > 0 && (n & (n - 1)) == 0; + } + } +} diff --git a/Week 07/id_693/LeetCode_338_693.java b/Week 07/id_693/LeetCode_338_693.java new file mode 100644 index 000000000..2ad30aacf --- /dev/null +++ b/Week 07/id_693/LeetCode_338_693.java @@ -0,0 +1,61 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 338. 比特位计数 https://leetcode-cn.com/problems/counting-bits/ + * @Date 2019/11/26 + */ +public class LeetCode_338_693 { + /** + * 位运算:直接利用位1计数来用,时间复杂度是O(n ^ 32) + */ + class Solution { + public int[] countBits(int num) { + int[] result = new int[num + 1]; + for (int i = 0; i <= num; i++) { + result[i] = hammingWeight(i); + } + return result; + } + + private int hammingWeight(int n) { + int count = 0; + for (int i = 1; i <= 32; i++) { + if (((n >> i) & 1) == 1) { + count++; + } + } + return count; + } + } + + /** + * 位运算2:看的通过的最高效率 + */ + class Solution2 { + public int[] countBits(int num) { + int[] result = new int[num + 1]; + for (int i = 1; i <= num; i++) { + result[i] = result[i & (i - 1)] + 1; + } + return result; + } + } + + /** + * 位运算3: 自己思考 2 的幂 - 1 + */ + class Solution3 { + /* + 1 1 2 + 11 3 4 + 111 7 8 + 1111 15 16 + 11111 31 32 + */ + public int[] countBits(int num) { + int[] result = new int[num + 1]; + return result; + } + } +} diff --git a/Week 07/id_698/LeetCode_1122_698.java b/Week 07/id_698/LeetCode_1122_698.java new file mode 100644 index 000000000..a992ef7e2 --- /dev/null +++ b/Week 07/id_698/LeetCode_1122_698.java @@ -0,0 +1,31 @@ +/** + * @author gning (id=698) + */ + + public class LeetCode_1122_698 { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + int[] tmp = new int[1001]; + int[] result = new int[arr1.length]; + + for(int i=0; i 0) { + result[cur++] = arr2[i]; + tmp[arr2[i]] --; + } + } + + for(int i=0; i < tmp.length; i++) { + while(tmp[i] > 0) { + result[cur++] = i; + tmp[i]--; + } + } + + return result; + } + } \ No newline at end of file diff --git a/Week 07/id_698/LeetCode_146_698.java b/Week 07/id_698/LeetCode_146_698.java new file mode 100644 index 000000000..251f8a884 --- /dev/null +++ b/Week 07/id_698/LeetCode_146_698.java @@ -0,0 +1,31 @@ +import java.util.LinkedHashMap; + +/** + * 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。 + * + * @author gning (id=698) + */ + + public class LeetCode_146_698 extends LinkedHashMap { + + private int capacity; + + public LeetCode_146_698(int cap) { + super(cap,0.75F,true); + this.capacity = cap; + } + + public int get(int key) { + return super.getOrDefault(key, -1); + } + + public void put(int key, int value) { + super.put(key, value); + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > capacity; + } + + } \ No newline at end of file diff --git a/Week 07/id_698/LeetCode_190_698.java b/Week 07/id_698/LeetCode_190_698.java new file mode 100644 index 000000000..8f47b2892 --- /dev/null +++ b/Week 07/id_698/LeetCode_190_698.java @@ -0,0 +1,19 @@ +/** + * 颠倒给定的 32 位无符号整数的二进制位。 + * + * @author gning (id=698) + */ + + public class LeetCode_190_698 { + + public int reverseBits(int n) { + int result = 0; + for (int i=0; i < 32; i++) { + result = (result << 1) + (n & 1); + n >>= 1; + } + + return result; + + } + } \ No newline at end of file diff --git a/Week 07/id_698/LeetCode_191_698.java b/Week 07/id_698/LeetCode_191_698.java new file mode 100644 index 000000000..e9046f2be --- /dev/null +++ b/Week 07/id_698/LeetCode_191_698.java @@ -0,0 +1,22 @@ +/** + * 编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。 + * + * @author gning (id=698) + */ + +public class LeetCode_191_698 { + + public int hammingWeight(int n) { + int count = 0; + + while(n!=0) { + count ++; + n &= (n-1); + } + + return count; + + } + + +} \ No newline at end of file diff --git a/Week 07/id_698/LeetCode_231_698.java b/Week 07/id_698/LeetCode_231_698.java new file mode 100644 index 000000000..2ec8b7d4c --- /dev/null +++ b/Week 07/id_698/LeetCode_231_698.java @@ -0,0 +1,13 @@ +/** + * 给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 + * + * @author gning (id=698) + */ + +public class LeetCode_231_698 { + + public boolean isPowerOfTwo(int n) { + return (n>0) && ((n & (n-1))==0); + } + +} \ No newline at end of file diff --git a/Week 07/id_703/LeetCode_493_703.py b/Week 07/id_703/LeetCode_493_703.py new file mode 100644 index 000000000..b8a143fa9 --- /dev/null +++ b/Week 07/id_703/LeetCode_493_703.py @@ -0,0 +1,36 @@ +# +# @lc app=leetcode.cn id=493 lang=python3 +# +# [493] 翻转对 +# + +# @lc code=start +class Solution: + def reversePairs(self, nums: List[int]) -> int: + def helper(nums,l,r): + if r == l: + return 0 + m = l + 1 + (r-l)//2 + L = helper(nums,l,m-1) + R = helper(nums,m,r) + + i = l + j = m + ans = L + R + nums[l:m] = sorted(nums[l:m]) + nums[m:r+1] = sorted(nums[m:r+1]) + while i < m and j <= r: + if nums[i] > 2*nums[j]: + ans += m - i + j += 1 + else: + i += 1 + return ans + if not nums: + return 0 + else: + return helper(nums,0,len(nums)-1) + + +# @lc code=end + diff --git a/Week 07/id_703/LeetCode_56_703.py b/Week 07/id_703/LeetCode_56_703.py new file mode 100644 index 000000000..80dff5e04 --- /dev/null +++ b/Week 07/id_703/LeetCode_56_703.py @@ -0,0 +1,25 @@ +# +# @lc app=leetcode.cn id=56 lang=python3 +# +# [56] 合并区间 +# + +# @lc code=start +class Solution: + def merge(self, intervals: List[List[int]]) -> List[List[int]]: + intervals = sorted(intervals) + res = [] + n = len(intervals) + i = 0 + while i < n: + left = intervals[i][0] + right = intervals[i][1] + while i < n - 1 and intervals[i+1][0] <= right: + i += 1 + right = max(intervals[i][1], right) + res.append([left, right]) + i += 1 + return res + +# @lc code=end + diff --git a/Week 07/id_708/LeetCode146_708.java b/Week 07/id_708/LeetCode146_708.java new file mode 100644 index 000000000..63c37db8b --- /dev/null +++ b/Week 07/id_708/LeetCode146_708.java @@ -0,0 +1,94 @@ +class LRUCache { + private class DLinkNode { + int key; + int value; + DLinkNode prev; + DLinkNode next; + public DLinkNode(int key, int value) { + this.key = key; + this.value = value; + } + public DLinkNode() {} + + @Override + public String toString() { + return "[key=" + key + ", value=" + value + ", prev=" + prev.key + ", next=" + next.key + "]"; + } + } + + private void removeNode(DLinkNode node) { + DLinkNode prev = node.prev; + DLinkNode next = node.next; + + prev.next = next; + next.prev = prev; + } + + private void addLast(DLinkNode node) { + node.prev = tail.prev; + node.next = tail; + tail.prev.next = node; + tail.prev = node; + } + + private void moveToLast(DLinkNode node) { + removeNode(node); + addLast(node); + } + + private void removeFirst() { + removeNode(head.next); + } + + private Map cache; + private DLinkNode head; + private DLinkNode tail; + private int capacity; + private int size; + + public LRUCache(int capacity) { + this.capacity = capacity; + size = 0; + cache = new HashMap<>(); + // 使用两个哨兵节点,使插入删除操作不需要特殊处理 + head = new DLinkNode(); + tail = new DLinkNode(); + head.next = tail; + tail.prev = head; + } + + public int get(int key) { + DLinkNode node = cache.getOrDefault(key, null); + // System.out.println(node); + if (node == null) return -1; + moveToLast(node); + return node.value; + } + + public void put(int key, int value) { + DLinkNode newNode = new DLinkNode(key, value); + DLinkNode oldNode = cache.getOrDefault(key, null); + if (oldNode != null) { + removeNode(oldNode); + addLast(newNode); + cache.put(key, newNode); + } else { + if (size == capacity) { + cache.remove(head.next.key); + removeFirst(); + size--; + } + addLast(newNode); + // System.out.println(newNode); + cache.put(key, newNode); + size++; + } + } +} + +/** + * Your LRUCache object will be instantiated and called as such: + * LRUCache obj = new LRUCache(capacity); + * int param_1 = obj.get(key); + * obj.put(key,value); + */ \ No newline at end of file diff --git a/Week 07/id_708/LeetCode_190_708.java b/Week 07/id_708/LeetCode_190_708.java new file mode 100644 index 000000000..edc3d2ead --- /dev/null +++ b/Week 07/id_708/LeetCode_190_708.java @@ -0,0 +1,13 @@ +public class Solution { + // you need treat n as an unsigned value + public int reverseBits(int n) { + int result = 0; + // 不断去 n 的末尾填充到 result 的末尾 + for (int i = 0; i < 32; i++) { + result <<= 1; + result |= (n&1); + n >>>= 1; + } + return result; + } +} \ No newline at end of file diff --git a/Week 07/id_708/LeetCode_191_708.java b/Week 07/id_708/LeetCode_191_708.java new file mode 100644 index 000000000..bde359222 --- /dev/null +++ b/Week 07/id_708/LeetCode_191_708.java @@ -0,0 +1,11 @@ +public class Solution { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int count = 0; + while (n != 0) { + n &= n-1; + count++; + } + return count; + } +} \ No newline at end of file diff --git a/Week 07/id_708/LeetCode_231_708.java b/Week 07/id_708/LeetCode_231_708.java new file mode 100644 index 000000000..4a6a23d3f --- /dev/null +++ b/Week 07/id_708/LeetCode_231_708.java @@ -0,0 +1,6 @@ +class Solution { + // 1 的位只能有一个 + public boolean isPowerOfTwo(int n) { + return n > 0 && (n & n-1) == 0; + } +} \ No newline at end of file diff --git a/Week 07/id_708/LeetCode_338_708.java b/Week 07/id_708/LeetCode_338_708.java new file mode 100644 index 000000000..cc4ea9ca4 --- /dev/null +++ b/Week 07/id_708/LeetCode_338_708.java @@ -0,0 +1,14 @@ +class Solution { + public int[] countBits(int num) { + int[] ans = new int[num+1]; + while (num >= 0) { + int temp = num; + while (temp != 0) { + temp &= temp-1; + ans[num]++; + } + num--; + } + return ans; + } +} \ No newline at end of file diff --git a/Week 07/id_713/LeetCode_146_LruCache.java b/Week 07/id_713/LeetCode_146_LruCache.java new file mode 100644 index 000000000..da2f7dc09 --- /dev/null +++ b/Week 07/id_713/LeetCode_146_LruCache.java @@ -0,0 +1,163 @@ +package id_713; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * 146. LRU缓存机制 + */ +public class LeetCode_146_LruCache { + + /* + + 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。 + + 获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 + 写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。 + + 进阶: + + 你是否可以在 O(1) 时间复杂度内完成这两种操作? + + 示例: + + LRUCache cache = new LRUCache( 2 ); // 缓存容量 + + cache.put(1, 1); + cache.put(2, 2); + cache.get(1); // 返回 1 + cache.put(3, 3); // 该操作会使得密钥 2 作废 + cache.get(2); // 返回 -1 (未找到) + cache.put(4, 4); // 该操作会使得密钥 1 作废 + cache.get(1); // 返回 -1 (未找到) + cache.get(3); // 返回 3 + cache.get(4); // 返回 4 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/lru-cache + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + + */ + + + /** + * 实现双向链表 + */ + class LRUCache { + + private Map map = new HashMap<>(); + private int size; + private int capacity; + private DLinkedNode head, tail; + + public LRUCache(int capacity) { + this.size = 0; + this.capacity = capacity; + + head = new DLinkedNode(); + tail = new DLinkedNode(); + + head.next = tail; + tail.prev = head; + } + + public int get(int key) { + DLinkedNode node = map.get(key); + if (node == null) return -1; + + moveToHead(node); + + return node.value; + } + + public void put(int key, int value) { + DLinkedNode node = map.get(key); + + if (node == null) { + DLinkedNode newNode = new DLinkedNode(); + newNode.key = key; + newNode.value = value; + + map.put(key, newNode); + addNode(newNode); + + ++size; + + if (size > capacity) { + DLinkedNode tail = popTail(); + map.remove(tail.key); + --size; + } else { + node.value = value; + moveToHead(node); + } + } + } + + + class DLinkedNode { + int key; + int value; + DLinkedNode prev; + DLinkedNode next; + } + + private void addNode(DLinkedNode node) { + node.prev = head; + node.next = head.next; + + head.next.prev = node; + head.next = node; + } + + private void removeNode(DLinkedNode node) { + DLinkedNode prev = node.prev; + DLinkedNode next = node.next; + + prev.next = next; + next.prev = prev; + } + + private void moveToHead(DLinkedNode node) { + removeNode(node); + addNode(node); + } + + private DLinkedNode popTail() { + DLinkedNode res = tail.prev; + removeNode(res); + return res; + } + + + } + + + /** + * 借助 封装类 + */ + class LRUCacheBaseJDK extends LinkedHashMap { + private int capacity; + + public LRUCacheBaseJDK(int capacity) { + super(capacity, 0.75F, true); + this.capacity = capacity; + } + + public int get(int key) { + return super.getOrDefault(key, -1); + } + + public void put(int key, int value) { + super.put(key, value); + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > capacity; + } + } + + +} diff --git a/Week 07/id_713/LeetCode_190_ReverseBits.java b/Week 07/id_713/LeetCode_190_ReverseBits.java new file mode 100644 index 000000000..ce8e4c59e --- /dev/null +++ b/Week 07/id_713/LeetCode_190_ReverseBits.java @@ -0,0 +1,64 @@ +package id_713; + +/** + * 190. 颠倒二进制位 + */ +public class LeetCode_190_ReverseBits { + + + /* + 颠倒给定的 32 位无符号整数的二进制位。 + + 示例 1: + + 输入: 00000010100101000001111010011100 + 输出: 00111001011110000010100101000000 + 解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596, + 因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。 + + 示例 2: + + 输入:11111111111111111111111111111101 + 输出:10111111111111111111111111111111 + 解释:输入的二进制串 11111111111111111111111111111101 表示无符号整数 4294967293, + 因此返回 3221225471 其二进制表示形式为 10101111110010110010011101101001。 + + + + 提示: + + 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 + 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 2 中,输入表示有符号整数 -3,输出表示有符号整数 -1073741825。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/reverse-bits + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + public int reverseBits(int n) { + /* + + 1. 将给定的二进制数,由低到高位逐个取出 + 2. 然后通过位运算将其放置到反转后的位置. + 3. 将上述结果再次通过运算结合到一起 + + */ + + + int result = 0; + + for (int i = 0; i < 32; i++) { + + // 1. 右移动 i 位 + int tmp = n >> i; + // 2. 取有效位 + tmp = tmp & 1; + // 3. 通过位运算将其放置到反转后的位置 + tmp = tmp << (31 - i); + // 4. 将上述结果再次通过位运算结合到一起 + result = result | tmp; + } + + return result; + } +} diff --git a/Week 07/id_713/LeetCode_191_NumberOf1Bits.java b/Week 07/id_713/LeetCode_191_NumberOf1Bits.java new file mode 100644 index 000000000..35cfdcd6a --- /dev/null +++ b/Week 07/id_713/LeetCode_191_NumberOf1Bits.java @@ -0,0 +1,50 @@ +package id_713; + +/** + * 191. 位1的个数 + */ +public class LeetCode_191_NumberOf1Bits { + /* + 编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。 + + 示例 1: + + 输入:00000000000000000000000000001011 + 输出:3 + 解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。 + + 示例 2: + + 输入:00000000000000000000000010000000 + 输出:1 + 解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。 + + 示例 3: + + 输入:11111111111111111111111111111101 + 输出:31 + 解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。 + + + + 提示: + + 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 + 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/number-of-1-bits + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + public int hammingWeight(int n) { + int cnt = 0; + + while (n != 0) { + n = n & (n - 1); //打掉最右的一个1 + cnt++; + } + + return cnt; + } +} diff --git a/Week 07/id_713/LeetCode_231_PowerOfTwo.java b/Week 07/id_713/LeetCode_231_PowerOfTwo.java new file mode 100644 index 000000000..455994b17 --- /dev/null +++ b/Week 07/id_713/LeetCode_231_PowerOfTwo.java @@ -0,0 +1,37 @@ +package id_713; + +/** + * 231. 2的幂 + */ +public class LeetCode_231_PowerOfTwo { + + /* + 给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 + + 示例 1: + + 输入: 1 + 输出: true + 解释: 20 = 1 + + 示例 2: + + 输入: 16 + 输出: true + 解释: 24 = 16 + + 示例 3: + + 输入: 218 + 输出: false + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/power-of-two + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + public boolean isPowerOfTwo(int n) { + return n > 0 && (n & (n - 1)) == 0; + } + +} diff --git a/Week 07/id_713/LeetCode_493_ReversePairs.java b/Week 07/id_713/LeetCode_493_ReversePairs.java new file mode 100644 index 000000000..40b2fa133 --- /dev/null +++ b/Week 07/id_713/LeetCode_493_ReversePairs.java @@ -0,0 +1,53 @@ +package id_713; + +import java.util.Arrays; + +/** + * 493. 翻转对 + */ +public class LeetCode_493_ReversePairs { + /* + 给定一个数组 nums ,如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对。 + + 你需要返回给定数组中的重要翻转对的数量。 + + 示例 1: + + 输入: [1,3,2,3,1] + 输出: 2 + + 示例 2: + + 输入: [2,4,3,5,1] + 输出: 3 + + 注意: + + 给定数组的长度不会超过50000。 + 输入数组中的所有数字都在32位整数的表示范围内。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/reverse-pairs + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + public int reversePairs(int[] nums) { + return mergeSort(nums, 0, nums.length - 1); + } + + private int mergeSort(int[] nums, int start, int end) { + if (start >= end) return 0; + + int mid = start + (end - start) >> 1; + + int cnt = mergeSort(nums, start, mid) + mergeSort(nums, mid + 1, end); + + for (int i = 0, j = mid + 1; i <= mid; i++) { + while (j <= end && nums[i] / 2.0 > nums[j]) j++; + cnt += j - (mid + 1); + } + + Arrays.sort(nums, start, end + 1); + return cnt; + } +} diff --git a/Week 07/id_713/LeetCode_56_MergeIntervals.java b/Week 07/id_713/LeetCode_56_MergeIntervals.java new file mode 100644 index 000000000..2f58e80a3 --- /dev/null +++ b/Week 07/id_713/LeetCode_56_MergeIntervals.java @@ -0,0 +1,53 @@ +package id_713; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 56. 合并区间 + */ +public class LeetCode_56_MergeIntervals { + /* + 给出一个区间的集合,请合并所有重叠的区间。 + + 示例 1: + + 输入: [[1,3],[2,6],[8,10],[15,18]] + 输出: [[1,6],[8,10],[15,18]] + 解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]. + + 示例 2: + + 输入: [[1,4],[4,5]] + 输出: [[1,5]] + 解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/merge-intervals + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + public int[][] merge(int[][] intervals) { + if (intervals.length <= 1) + return intervals; + + // 升序排序 + Arrays.sort(intervals, (i1, i2) -> Integer.compare(i1[0], i2[1])); + + List result = new ArrayList<>(); + int[] newInterval = intervals[0]; + result.add(newInterval); + + for (int[] interval : intervals) { + if (interval[0] <= newInterval[1]) { + newInterval[1] = Math.max(newInterval[1], interval[1]); + } else { + newInterval = interval; + result.add(newInterval); + } + } + + return result.toArray(new int[result.size()][]); + } +} diff --git a/Week 07/id_713/NOTE.md b/Week 07/id_713/NOTE.md index a6321d6e2..07ff29e01 100644 --- a/Week 07/id_713/NOTE.md +++ b/Week 07/id_713/NOTE.md @@ -2,3 +2,149 @@ +# 位运算 + +## 位运算符 + +| 含义 | 运算符 | 示例 | 备注 | +| -------- | ------ | -------------------- | ---------------- | +| 左移 | << | 0011 -> 0110 | X2 | +| 右移 | >> | 0110 -> 0011 | /2 | +| 按位或 | \| | 0011 \| 1011 -> 1011 | | +| 按位与 | & | 0011 & 1011 -> 0011 | | +| 取反 | ~ | 0011 -> 1100 | | +| 按位异或 | ^ | 0011 ^ 1011 -> 1000 | 相异为1, 相同为0 | + +### 异或运算特点 + +* x ^ 0 = x +* x ^ 1s = ~x // 1s为全1, 全1相当于0取反(~0) +* x ^ (~x) = 1s +* x ^ x = 0 +* c = a ^ b => a ^ c = b, b ^ c = a // 交换两个数 +* a ^ b ^ c = a ^ ( b ^ c ) = ( a ^ b ) ^ c // 结合律 + +### 指定位置的位运算 + +| 描述 | 公式 | +| -------------------------- | -------------------------- | +| 将x最右边的n位清零 | x & (~0 << n) | +| 获取x的第n个位的值(0或者1) | (x >> n) & 1 | +| 获取x的第n次幂 | x & (1 << (n - 1)) | +| 仅将第n位, 置为1 | x \| (1 << n) | +| 仅将第n为, 置为0 | x & ( ~(1 << n)) | +| 将x最高位至第n位(含)清零 | x & ((1 << n) - 1) | +| 将第n位至第0位(含)清零 | x & (~((1<< (n + 1)) - 1)) | + +### 实战运算要点 + +* 奇偶判断 + * x % 2 == 1 -> (x & 1) == 1 + * x % 2 == 0 -> (x & 1) == 0 +* 除2 + * x / 2 -> x >> 1 + * mid = (left + right) / 2 -> mid = (left + right) >> 1 +* 清零最低位的1 + * x = x & (x - 1) +* 得到最低位的1 + * x & (x - 1) +* x & ~x = 0 + + + + + +# 排序 + +## 快排 Quick Sort + +* 数组取标杆pivot, 将小元素放在pivot左边, 大元素放右侧, 然后依次对左边和右边的子数组进行快排, 从而达到整个数组有序 + +### 代码 + +```java +public static void quickSort(int[] arr, int begin, int end) { + if (end <= begin) return; + + int pivot = partition(arr, begin, end); + quickSort(arr, begin, pivot - 1); + quickSort(arr, pivot + 1, end); +} + +private static int partition(int[] arr, int begin, int end) { + // 标杆位置, 计数器:小于pivot的个数 + int pivot = end, counter = begin; + for (int i = begin; i < end; i++) { + if (arr[i] < a[pivot]) { + int tmp = arr[counter]; + a[counter] = a[i]; + a[i] = tmp; + counter++; + } + int tmp = a[pivot]; + a[pivot] = a[counter]; + a[counter] = tmp; + } + return counter; +} +``` + +## 归并排序 Merge Sort + +* 归并排序(Merge Sort) 分治 + * 把长度为n的输入序列, 分成2个长度为 n/2的子序列 + * 对这2个子序列分别采用归并排序 + * 将2个排序好的子序列合并成一个最终的排序序列 + +### 代码 + +```java +public static void mergeSort(int[] arr, int left, int right) { + if (right <= left) return; + + int mid = (left + right) >> 1; // (right - left) >> 1 + left; + + mergeSort(arr, left, mid); + mergeSort(arr, mid + 1, right); + merge(arr, left, mid, right); +} + +private static void merge(int[] arr, int left, int mid, int right) { + int[] tmp = new int[right - left - 1]; // 中间数组 + int i = left, j = mid + 1, k = 0; + + while (i <= mid && j <= right) { + tmp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++]; + } + + while (i <= mid) tmp[k++] = arr[i++]; + while (j <= right) tmp[k++] = arr[j++]; + + for (int p = 0; p < tmp.length; p++) { + arr[left + p] = tmp[p]; + } + + // 也可以用 System.arraycopy(a, start1, b, start2, length); + // System.arraycopy(tmp, 0, arr, left, tmp.length); +} +``` + + + +## 归并&快排总结 + +* 归并: 先排序左右子数组, 然后合并2个有序数组 +* 快排: 先调配出左右子数组, 然后对于左右子数组进行排序 + + + +## 堆排序 Heap Sort + +* 插入 O(longN), 取最大/最小值 O(1) +* 数组元素依次建立小顶堆 +* 依次取堆顶元素, 并删除 + + + + + diff --git a/Week 07/id_718/190.reverse-bits.py b/Week 07/id_718/190.reverse-bits.py new file mode 100644 index 000000000..b9d9a3089 --- /dev/null +++ b/Week 07/id_718/190.reverse-bits.py @@ -0,0 +1,9 @@ +class Solution: + # @param n, an integer + # @return an integer + def reverseBits(self, n): + ret = 0 + for i in range(32): + ret = (ret << 1) + (n & 1) + n >>= 1 + return ret \ No newline at end of file diff --git a/Week 07/id_718/191.number-of-1-bits.py b/Week 07/id_718/191.number-of-1-bits.py new file mode 100644 index 000000000..c28c8361e --- /dev/null +++ b/Week 07/id_718/191.number-of-1-bits.py @@ -0,0 +1,20 @@ +class Solution(object): + def hammingWeight(self, n): + """ + :type n: int + :rtype: int + """ + cnt = 0 + while n: + n &= (n-1) + cnt += 1 + return cnt +''' +class Solution(object): + def hammingWeight(self, n): + """ + :type n: int + :rtype: int + """ + return bin(n).count('1') +''' \ No newline at end of file diff --git a/Week 07/id_718/231.power-of-two.py b/Week 07/id_718/231.power-of-two.py new file mode 100644 index 000000000..0077c2fc9 --- /dev/null +++ b/Week 07/id_718/231.power-of-two.py @@ -0,0 +1,13 @@ +# +# @lc app=leetcode id=231 lang=python3 +# +# [231] Power of Two +# + +# @lc code=start +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + return n != 0 and n & (n-1) == 0 + +# @lc code=end + diff --git a/Week 07/id_718/338.counting-bits.py b/Week 07/id_718/338.counting-bits.py new file mode 100644 index 000000000..711a99743 --- /dev/null +++ b/Week 07/id_718/338.counting-bits.py @@ -0,0 +1,16 @@ +# +# @lc app=leetcode id=338 lang=python3 +# +# [338] Counting Bits +# + +# @lc code=start +class Solution: + def countBits(self, num: int) -> List[int]: + dp = [0] * (num+1) + for i in range(1, num+1): + dp[i] = dp[i & (i - 1)] + 1 + return dp + +# @lc code=end + diff --git a/Week 07/id_733/LeetCode_1122_733.go b/Week 07/id_733/LeetCode_1122_733.go new file mode 100644 index 000000000..14834beb7 --- /dev/null +++ b/Week 07/id_733/LeetCode_1122_733.go @@ -0,0 +1,25 @@ +func relativeSortArray(arr1 []int, arr2 []int) []int { + counter := make([]int, 1001) + for _, num := range arr1 { + counter[num]++ + } + + idx := 0 + for _, num := range arr2 { + for counter[num] > 0 { + arr1[idx] = num + idx++ + counter[num]-- + } + } + + for num := range counter { + for counter[num] > 0 { + arr1[idx] = num + idx++ + counter[num]-- + } + } + + return arr1 +} diff --git a/Week 07/id_733/LeetCode_146_733.go b/Week 07/id_733/LeetCode_146_733.go new file mode 100644 index 000000000..e45b14135 --- /dev/null +++ b/Week 07/id_733/LeetCode_146_733.go @@ -0,0 +1,92 @@ +type LRUCache struct { + dict map[int]*cacheNode + list *cacheList + cap int +} + + +func Constructor(capacity int) LRUCache { + cache := LRUCache{} + cache.dict = make(map[int]*cacheNode, capacity) + cache.list = newCacheList() + cache.cap = capacity + return cache +} + + +func (this *LRUCache) Get(key int) int { + node, ok := this.dict[key] + if !ok { + return -1 + } + + this.list.Remove(node) + this.list.Insert(node) + return node.Val +} + + +func (this *LRUCache) Put(key int, value int) { + node, ok := this.dict[key] + if ok { + this.list.Remove(node) + } else { + if len(this.dict) == this.cap { + least := this.list.RemoveEnd() + delete(this.dict, least.Key) + } + + node = &cacheNode{Key: key} + this.dict[key] = node + } + + node.Val = value + this.list.Insert(node) +} + +type cacheList struct { + head *cacheNode + rear *cacheNode +} + +func newCacheList() *cacheList { + l := &cacheList{} + l.head = &cacheNode{} + l.rear = &cacheNode{} + l.head.Next = l.rear + l.rear.Prev = l.head + return l +} + +func (l *cacheList) Remove(c *cacheNode) { + c.Next.Prev = c.Prev + c.Prev.Next = c.Next +} + +func (l *cacheList) Insert(c *cacheNode) { + c.Next = l.head.Next + c.Next.Prev = c + c.Prev = l.head + l.head.Next = c +} + +func (l *cacheList) RemoveEnd() *cacheNode { + node := l.rear.Prev + l.Remove(node) + return node +} + +type cacheNode struct { + Key int + Val int + Prev *cacheNode + Next *cacheNode +} + + +/** + * Your LRUCache object will be instantiated and called as such: + * obj := Constructor(capacity); + * param_1 := obj.Get(key); + * obj.Put(key,value); + */ diff --git a/Week 07/id_733/LeetCode_190_733.go b/Week 07/id_733/LeetCode_190_733.go new file mode 100644 index 000000000..5acedaabe --- /dev/null +++ b/Week 07/id_733/LeetCode_190_733.go @@ -0,0 +1,18 @@ +func reverseBits(num uint32) uint32 { + res := uint32(0) + for i := 0; i < 32; i++ { + res = (res << 1) | (num & 1) + num = num >> 1 + } + + return res +} + +func reverseBitsV2(num uint32) uint32 { + num = (num >> 16) | (num << 16) + num = ((num >> 8) & 0x00FF00FF) | ((num & 0x00FF00FF) << 8) + num = ((num >> 4) & 0x0F0F0F0F) | ((num & 0x0F0F0F0F) << 4) + num = ((num >> 2) & 0x33333333) | ((num & 0x33333333) << 2) + num = ((num >> 1) & 0x55555555) | ((num & 0x55555555) << 1) + return num +} diff --git a/Week 07/id_733/LeetCode_191_733.go b/Week 07/id_733/LeetCode_191_733.go new file mode 100644 index 000000000..6c2874445 --- /dev/null +++ b/Week 07/id_733/LeetCode_191_733.go @@ -0,0 +1,9 @@ +func hammingWeight(num uint32) int { + cnt := 0 + for num != 0 { + num &= num - 1 + cnt++ + } + + return cnt +} diff --git a/Week 07/id_733/LeetCode_231_733.go b/Week 07/id_733/LeetCode_231_733.go new file mode 100644 index 000000000..18ac21e69 --- /dev/null +++ b/Week 07/id_733/LeetCode_231_733.go @@ -0,0 +1,3 @@ +func isPowerOfTwo(n int) bool { + return n > 0 && (n&(n-1) == 0) +} diff --git a/Week 07/id_733/LeetCode_338_733.go b/Week 07/id_733/LeetCode_338_733.go new file mode 100644 index 000000000..6b1e61662 --- /dev/null +++ b/Week 07/id_733/LeetCode_338_733.go @@ -0,0 +1,7 @@ +func countBits(num int) []int { + res := make([]int, num+1) + for i := 1; i <= num; i++ { + res[i] = res[i>>1] + i&1 + } + return res +} diff --git a/Week 07/id_733/LeetCode_493_733.go b/Week 07/id_733/LeetCode_493_733.go new file mode 100644 index 000000000..26df9dce7 --- /dev/null +++ b/Week 07/id_733/LeetCode_493_733.go @@ -0,0 +1,97 @@ +func reversePairs(nums []int) int { + if len(nums) <= 1 { + return 0 + } + + cnt := 0 + mid := len(nums) >> 1 + cnt += reversePairs(nums[:mid]) + reversePairs(nums[mid:]) + + newNums := make([]int, len(nums)) + i, idx, k := 0, 0, 0 + for j := mid; j < len(nums); j++ { + for idx < mid && float64(nums[idx])/2 <= float64(nums[j]) { + idx++ + } + + for i < mid && nums[i] <= nums[j] { + newNums[k] = nums[i] + i++ + k++ + } + + cnt += mid - idx + newNums[k] = nums[j] + k++ + } + + for i < mid { + newNums[k] = nums[i] + i++ + k++ + } + + copy(nums, newNums) + return cnt +} + +func reversePairsV2(nums []int) int { + if len(nums) <= 1 { + return 0 + } + + numsCopy := make([]int, len(nums)) + copy(numsCopy, nums) + sort.Ints(numsCopy) + + cnt := 0 + bit := NewBIT(len(nums)) + for i := len(nums) - 1; i >= 0; i-- { + cnt += bit.Search(index(numsCopy, float64(nums[i])/2)) + bit.Insert(index(numsCopy, float64(nums[i])) + 1) + } + + return cnt +} + +func index(nums []int, num float64) int { + start, end := 0, len(nums)-1 + for start <= end { + mid := start + (end-start)>>1 + val := float64(nums[mid]) + if val >= num { + end = mid - 1 + } else { + start = mid + 1 + } + } + + return start +} + +type BIT struct { + arr []int +} + +func NewBIT(cap int) *BIT { + b := &BIT{} + b.arr = make([]int, cap+1) + return b +} + +func (b *BIT) Insert(idx int) { + for idx < len(b.arr) { + b.arr[idx]++ + idx += idx & (-idx) + } +} + +func (b *BIT) Search(idx int) int { + sum := 0 + for idx > 0 { + sum += b.arr[idx] + idx -= idx & (-idx) + } + + return sum +} diff --git a/Week 07/id_733/LeetCode_51_733.go b/Week 07/id_733/LeetCode_51_733.go new file mode 100644 index 000000000..5822d72f0 --- /dev/null +++ b/Week 07/id_733/LeetCode_51_733.go @@ -0,0 +1,45 @@ +const ( + queen = 'Q' + empty = '.' +) + +func solveNQueens(n int) [][]string { + if n <= 0 { + return nil + } + + board := make([][]rune, n) + for i := 0; i < n; i++ { + board[i] = make([]rune, n) + for j := 0; j < n; j++ { + board[i][j] = empty + } + } + + res := make([][]string, 0) + doSolveNQueens(board, &res, n, 0, 0, 0, 0) + return res +} + +func doSolveNQueens(board [][]rune, res *[][]string, n, row, col, leftSlash, rightSlash int) { + if row >= n { + r := make([]string, n) + for i := 0; i < n; i++ { + r[i] = string(board[i]) + } + + *res = append(*res, r) + } + + pos := col | leftSlash | rightSlash + for i := 0; i < n; i++ { + mask := 1 << uint(i) + if pos&mask > 0 { + continue + } + + board[row][i] = queen + doSolveNQueens(board, res, n, row+1, col|mask, (leftSlash|mask)<<1, (rightSlash|mask)>>1) + board[row][i] = empty + } +} diff --git a/Week 07/id_733/LeetCode_52_733.go b/Week 07/id_733/LeetCode_52_733.go new file mode 100644 index 000000000..3e3e4a3ce --- /dev/null +++ b/Week 07/id_733/LeetCode_52_733.go @@ -0,0 +1,24 @@ +func totalNQueens(n int) int { + if n <= 0 { + return 0 + } + + size := (1 << uint(n)) - 1 + return countNQueens(n, size, 0, 0, 0, 0) +} + +func countNQueens(n, size, row, col, leftSlash, rightSlash int) int { + if row >= n { + return 1 + } + + poses := (^(col | leftSlash | rightSlash)) & size + cnt := 0 + for poses != 0 { + pos := poses & (-poses) + cnt += countNQueens(n, size, row+1, col|pos, (leftSlash|pos)<<1, (rightSlash|pos)>>1) + poses = poses & (poses - 1) + } + + return cnt +} diff --git a/Week 07/id_733/LeetCode_56_733.go b/Week 07/id_733/LeetCode_56_733.go new file mode 100644 index 000000000..58ab560e5 --- /dev/null +++ b/Week 07/id_733/LeetCode_56_733.go @@ -0,0 +1,45 @@ +func merge(intervals [][]int) [][]int { + if len(intervals) == 0 { + return nil + } + + quickSort(intervals) + res := make([][]int, 1) + res[0] = intervals[0] + last := res[0] + for i := 1; i < len(intervals); i++ { + if intervals[i][0] <= last[1] { + if intervals[i][1] > last[1] { + last[1] = intervals[i][1] + } + } else { + res = append(res, intervals[i]) + last = intervals[i] + } + } + + return res +} + +func quickSort(intervals [][]int) { + if len(intervals) <= 1 { + return + } + + pivot := partition(intervals) + quickSort(intervals[:pivot]) + quickSort(intervals[pivot+1:]) +} + +func partition(intervals [][]int) int { + idx, pivot := 0, len(intervals)-1 + for i := 0; i < pivot; i++ { + if intervals[i][0] < intervals[pivot][0] { + intervals[idx], intervals[i] = intervals[i], intervals[idx] + idx++ + } + } + + intervals[idx], intervals[pivot] = intervals[pivot], intervals[idx] + return idx +} diff --git a/Week 07/id_738/LeetCode_1122_738.py b/Week 07/id_738/LeetCode_1122_738.py new file mode 100644 index 000000000..bae7ec75b --- /dev/null +++ b/Week 07/id_738/LeetCode_1122_738.py @@ -0,0 +1,36 @@ +class Solution(object): + def relativeSortArray(self, arr1, arr2): + """ + 数组的相对排序:https://leetcode-cn.com/problems/relative-sort-array/ + + :type arr1: List[int] + :type arr2: List[int] + :rtype: List[int] + """ + # 使用计数排序 + tmp = [0 for _ in range(1001)] + result = [] + # 通过计数法,将arr1中的元素进行计数 + for num in arr1: + tmp[num] += 1 + # 按照arr2的元素的顺序,依次输出tmp中的元素 + for num in arr2: + while tmp[num] > 0: + result.append(num) + tmp[num] -= 1 + # 对不在arr2中的元素进行输出 + for i in range(1001): + while tmp[i] > 0: + result.append(i) + tmp[i] -= 1 + return result + + + + + + + + + + diff --git a/Week 07/id_738/LeetCode_146_2_738.py b/Week 07/id_738/LeetCode_146_2_738.py new file mode 100644 index 000000000..7a500969a --- /dev/null +++ b/Week 07/id_738/LeetCode_146_2_738.py @@ -0,0 +1,88 @@ +class Node(object): + def __init__(self, key = None, value = None): + self.key = key + self.value = value + self.prev = None + self.next = None + +class LRUCache(object): + """ + LRU缓存机制:https://leetcode-cn.com/problems/lru-cache/submissions/ + + 1. 要实现O(1)完成get操作,比如需要用hash直接定位到元素的位置 + 2. LRU的get操作,如果元素在Cache中,那么需要将元素提到Cache的头部;如果不在里面则返回-1 + 3. LRU的put操作,如果元素在Cache中,那么直接将元素提到Cache头部;如果不在里面,则有下面两种可能: + 3.1. Cache元素没满,则直接将元素插入到Cache的头部 + 3.2. Cache元素已满,则将Cache尾部(最不常用的)元素移除,并将元素插入到Cache头部 + 上的2和3操作涉及到元素的删除添加,添加元素O(1)用链表,删除元素O(1)则要将链表进化为双端链表 + 所以最终的数据结构为: hashmap + 双端链表 实现 + """ + + def __init__(self, capacity): + """ + :type capacity: int + """ + self.remain = capacity + self.cache = {} + self.head, self.tail = Node(), Node() + self.head.next = self.tail + self.tail.prev = self.head + + def get(self, key): + """ + :type key: int + :rtype: int + """ + if key not in self.cache: + return -1 + else: + node = self.cache[key] + # 将node提到队列最前面:先删除后添加到头部 + self._remove(node) + self._append_to_head(node) + return node.value + + def put(self, key, value): + """ + :type key: int + :type value: int + :rtype: None + """ + if key in self.cache: + # 将node提到队列最前面:先删除后添加到头部 + self._remove(self.cache[key]) + self._append_to_head(self.cache[key]) + # 这里注意,如果put是一个更新操作的话,那么需要重新赋值 + self.cache[key].value = value + else: + node = Node(key, value) + if self.remain > 0: # 还有容量则直接插入到头部 + self.cache[key] = node + self.remain -= 1 + else: # 没有容量了,那么将队列末尾的移除(并删除cache中的该元素)),将新元素插入到队列头部 + tail = self._remove_tail() + del self.cache[tail.key] + self.cache[key] = node + self._append_to_head(node) + + # 下面的三个操作都是对双端队列的操作 + def _remove(self, node): + pre = node.prev + nex = node.next + pre.next = nex + nex.prev = pre + + def _append_to_head(self, node): + nex = self.head.next + self.head.next = node + node.prev = self.head + node.next = nex + nex.prev = node + + def _remove_tail(self): + tail = self.tail.prev + self._remove(tail) + # pre = tail.prev + # pre.next = self.tail + # self.tail.prev = pre + return tail diff --git a/Week 07/id_738/LeetCode_146_738.py b/Week 07/id_738/LeetCode_146_738.py new file mode 100644 index 000000000..4b691255d --- /dev/null +++ b/Week 07/id_738/LeetCode_146_738.py @@ -0,0 +1,50 @@ +class LRUCache(object): + """ + LRU缓存机制:https://leetcode-cn.com/problems/lru-cache/submissions/ + + 利用Python的collections.OrderedDict实现 + """ + + def __init__(self, capacity): + """ + :type capacity: int + """ + self.dic = collections.OrderedDict() + self.remain = capacity + + def get(self, key): + """ + :type key: int + :rtype: int + """ + if key not in self.dic: + return -1 + v = self.dic.pop(key) + self.dic[key] = v + return v + + + def put(self, key, value): + """ + :type key: int + :type value: int + :rtype: None + """ + if key in self.dic: + self.dic.pop(key) + else: + if self.remain > 0: + self.remain -= 1 + else: + self.dic.popitem(last = False) + self.dic[key] = value + + + + + + +# Your LRUCache object will be instantiated and called as such: +# obj = LRUCache(capacity) +# param_1 = obj.get(key) +# obj.put(key,value) \ No newline at end of file diff --git a/Week 07/id_738/LeetCode_190_738.py b/Week 07/id_738/LeetCode_190_738.py new file mode 100644 index 000000000..12c53d1fd --- /dev/null +++ b/Week 07/id_738/LeetCode_190_738.py @@ -0,0 +1,13 @@ +class Solution: + # 颠倒二进制位: https://leetcode-cn.com/problems/reverse-bits/ + # @param n, an integer + # @return an integer + def reverseBits(self, n): + # 目标:设最右边为第一位,将第i位移动到第 31 - i 位 + # 那么,循环i = {0...31},取n的第i位加到结果值的第1位即可 + res = 0 + for _ in range(32): + res <<= 1 + res += n & 1 + n >>= 1 + return res \ No newline at end of file diff --git a/Week 07/id_738/LeetCode_191_738.py b/Week 07/id_738/LeetCode_191_738.py new file mode 100644 index 000000000..e3d072fe7 --- /dev/null +++ b/Week 07/id_738/LeetCode_191_738.py @@ -0,0 +1,12 @@ +class Solution(object): + def hammingWeight(self, n): + """ + 位1的个数:https://leetcode-cn.com/problems/number-of-1-bits/submissions/ + :type n: int + :rtype: int + """ + count = 0 + while n: + n &= n - 1 + count += 1 + return count \ No newline at end of file diff --git a/Week 07/id_738/LeetCode_231_738.py b/Week 07/id_738/LeetCode_231_738.py new file mode 100644 index 000000000..283cbb65a --- /dev/null +++ b/Week 07/id_738/LeetCode_231_738.py @@ -0,0 +1,11 @@ +class Solution(object): + def isPowerOfTwo(self, n): + """ + 2的幂:https://leetcode-cn.com/problems/power-of-two/ + + :type n: int + :rtype: bool + """ + # 2的幂次的二进制,只有一位为1,其他位0 + return n != 0 and n & (n - 1) == 0 + \ No newline at end of file diff --git a/Week 07/id_738/LeetCode_242_738.py b/Week 07/id_738/LeetCode_242_738.py new file mode 100644 index 000000000..61e5ea32a --- /dev/null +++ b/Week 07/id_738/LeetCode_242_738.py @@ -0,0 +1,39 @@ +class Solution(object): + def isAnagram(self, s, t): + """ + 有效的字母异位词:https://leetcode-cn.com/problems/valid-anagram/ + + :type s: str + :type t: str + :rtype: bool + """ + #判断t和t中的单词分类及其数量是否相等 + dict_d = {} + for i in s: + dict_d[i] = dict_d.get(i, 0) + 1 + dict_t = {} + for i in t: + dict_t[i] = dict_t.get(i, 0) + 1 + return dict_d == dict_t + + #利用python api: + #先去重,再比较各个字母在s和t中的数量 + # if len(s) != len(t): + # return False + # s_single = set(s) + # for i in s_single: + # if s.count(i) != t.count(i): + # return False + # return True + + # 对s中的每个字母进行计数,对t中的每个字母进行计数 + # 如果都相等则true + dic = collections.defaultdict(int) + for w in s: + dic[w] += 1 + for w in t: + dic[w] -= 1 + for key in dic: + if dic[key] != 0: + return False + return True \ No newline at end of file diff --git a/Week 07/id_738/LeetCode_338_738.py b/Week 07/id_738/LeetCode_338_738.py new file mode 100644 index 000000000..044f7789b --- /dev/null +++ b/Week 07/id_738/LeetCode_338_738.py @@ -0,0 +1,60 @@ +class Solution(object): + def countBits(self, num): + """ + 比特位计数:https://leetcode-cn.com/problems/counting-bits/description/ + + :type num: int + :rtype: List[int] + """ + # 解法1:比较难推算出来 + # 本题如果要实现O(n)复杂度,必然需要通过前面的数推算后面的数 + # 所以涉及到动态规划 + # 观察规律:设一个数 i 的1的位数为 n. + # 如果在 i 的二进制最左边的1位置的左边任意一位加多一个1,那么在二进制里面,这个数必然是 i + b {b = 2 ^ k , 且 b > i} + # 所以得出DP方程: dp[i + b] = dp[i] + 1 {i + b < nums and b = 2 ^ k and b > i} + # 程序的写法:让b = 2^i {i = 0,1,2....} 对于每个b,通过循环已有的dp数组中的所有数(比如t个))推算出下面t个dp的元素 + # 直到i + b > num为止 + dp = [0] * (num + 1) + b = 1 + while b <= num: + i = 0 + while i < b and b + i <= num: + dp[i + b] = dp[i] + 1 + i += 1 + b <<= 1 + return dp + +############################################################################### + + # 解法2:比较容易看出来,也是DP + # 对于一个数 i , 其二进制向右移动一位后,所得到的数正好为:i // 2(这是很明显的,因为移掉最低位表示除以2) + # 对于移除的最低位,可能为0,也可能为1,这取决于i为奇数或者为偶数,如果i为奇数,那么i肯定比i//2多一个1,否则等于。 + # 那么通过上面分析可以得出DP方程: + # dp[i] = dp[i // 2] + i % 2 + dp = [0 for _ in range(num + 1)] + for i in range(1, num + 1): + dp[i] = dp[i >> 1] + (i & 1) + return dp + + +############################################################################### + + # 解法3,我们可以将数i的最低位1变换为0,那么中1的个数就是变换后的数的1的个数 + 1 + # 循环过程中,变换后的数必然 < i,已经计算出来了,所以可以递推 + dp = [0 for _ in range(num + 1)] + for i in range(1, num + 1): + dp[i] = dp[i & (i - 1)] + 1 + return dp + + + + + + + + + + + + + diff --git a/Week 07/id_738/LeetCode_493_738.py b/Week 07/id_738/LeetCode_493_738.py new file mode 100644 index 000000000..78945e109 --- /dev/null +++ b/Week 07/id_738/LeetCode_493_738.py @@ -0,0 +1,145 @@ +class Solution(object): + # 解法1 + def reversePairs(self, nums): + """ + 翻转对:https://leetcode-cn.com/problems/reverse-pairs/ + 逆序对:对于(i, j), i < j 并且 nums[i] > nums[j]的对,称为逆序对 + 对逆序对求解出所需要的东西,是在算法中非常经典的问题,需要重视 + + :type nums: List[int] + :rtype: int + """ + # 对于逆序对的问题,我们使用归并排序进行解决,时间复杂度O(nlogn) + # 归并排序是解决逆序对的最高效手段 + self.result = 0 + self.mergeSort(nums, 0, len(nums) - 1) + return self.result + + def mergeSort(self, nums, start, end): + if start >= end: + return + mid = (start + end) >> 1 + self.mergeSort(nums, start, mid) + self.mergeSort(nums, mid + 1, end) + # 由于左右都有序了,这里就可以利用这个特点来统计逆序对 + j = mid + 1 + for i in range(start, mid + 1): + while j <= end and nums[i] > nums[j] * 2: + j += 1 + self.result += (j - (mid + 1)) + self.merge(nums, start, mid, end) + + def merge(self, nums, start, mid, end): + tmp = [0 for _ in range(end - start + 1)] + k, i, j = 0, start, mid + 1 + while i <= mid and j <= end: + if nums[i] <= nums[j]: + tmp[k] = nums[i] + i += 1 + else: + tmp[k] = nums[j] + j += 1 + k += 1 + if i <= mid: + tmp[k:] = nums[i: mid + 1] + if j <= end: + tmp[k:] = nums[j: end + 1] + nums[start: end + 1] = tmp + + +################################################### + #解法2 + def reversePairs(self, nums): + self.count = 0 + self.MergeSort(nums, 0, len(nums)-1) + return self.count + + def MergeSort(self, array, first, last): + cnt = [] + if first >= last: + return + mid = first + int((last - first)/2) + self.MergeSort(array, first, mid) + self.MergeSort(array, mid+1, last) + self.Merge(array, first, mid, last) + + def Merge(self, array, first, mid, last): + i = first + j = mid + 1 + while (i<=mid and j<=last): + if (array[i] <= 2*array[j]): + i += 1 + else: + j += 1 + self.count += mid-i+1 + array[first:last+1] = sorted(array[first:last+1]) + +#################################################### + + #解法3 + def reversePairs(self, nums): + self.result = 0 + self.mergeSort(nums, 0, len(nums) - 1) + return self.result + + def mergeSort(self, nums, start, end): + if start >= end: + return + mid = (start + end) >> 1 + self.mergeSort(nums, start, mid) + self.mergeSort(nums, mid + 1, end) + self.merge(nums, start, mid, end) + + def merge(self, nums, start, mid, end): + cache = [0 for _ in range(end - start + 1)] + c = 0 + j = mid + 1 + t = mid + 1 + for i in range(start, mid + 1): + # 统计逆序对 + while t <= end and nums[i] > nums[t] * 2: + t += 1 + self.result += (t - (mid + 1)) + # 归并排序部分 + while j <= end and nums[j] < nums[i]: + cache[c] = nums[j] + c += 1 + j += 1 + cache[c] = nums[i] + c += 1 + + # 对剩下的j进行加入到cache中 + # if j <= end: + # cache[c:] = nums[j: end + 1] + while j <= end: + cache[c] = nums[j] + c += 1 + j += 1 + # 写回主数组nums + # nums[start: end + 1] = cache + for i in range(len(cache)): + nums[start + i] = cache[i] + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Week 07/id_738/LeetCode_51_738.py b/Week 07/id_738/LeetCode_51_738.py new file mode 100644 index 000000000..594e7c56b --- /dev/null +++ b/Week 07/id_738/LeetCode_51_738.py @@ -0,0 +1,54 @@ +class Solution(object): + def solveNQueens(self, n): + """ + n皇后:https://leetcode-cn.com/problems/n-queens/description/ + + :type n: int + :rtype: List[List[str]] + """ + # 位运算解决 + self.res = [] + self.dfs(n, [], 0, 0, 0) + f = '{:0' + str(n) + 'b}' + return [[("{:0" + str(n) + "b}").format(b).replace('0','.').replace('1','Q') for b in l] for l in self.res] + + + # 转化为输出矩阵 + # self.result = [] + # for i in range(len(self.res)): + # l = [] + # for j in range(len(self.res[0])): + # s = "" + # for k in range(n): + # if self.res[i][j] & 1: + # s = 'Q' + s + # else: + # s = '.' + s + # self.res[i][j] >>= 1 + # l.append(s) + # self.result.append(l) + # return self.result + + def dfs(self, n, solution, col, pie, na): + if n == len(solution): + self.res.append(solution) + bits = (~(col | pie | na)) & ((1 << n) - 1) + while bits: + p = bits & -bits + bits &= (bits - 1) + self.dfs(n, solution + [p], col | p, (pie | p) << 1, (na | p) >> 1) + + + + # 分治,剪枝 + result = [] + def dfs(solution, pie, na): + row = len(solution) + if row == n: + result.append(solution) + return + for col in range(n): + if col not in solution and col + row not in na and col - row not in pie: + dfs(solution + [col], pie + [col - row], na + [col + row]) + dfs([], [], []) + return [ ['.' * i + 'Q' + '.' * (n - i - 1) for i in l ] for l in result] \ No newline at end of file diff --git a/Week 07/id_738/LeetCode_52_738.py b/Week 07/id_738/LeetCode_52_738.py new file mode 100644 index 000000000..b91c7ae48 --- /dev/null +++ b/Week 07/id_738/LeetCode_52_738.py @@ -0,0 +1,38 @@ +class Solution(object): + def totalNQueens(self, n): + """ + N皇后二:https://leetcode-cn.com/problems/n-queens-ii/submissions/ + + :type n: int + :rtype: int + """ + self.res = 0 + self.dfs(n, 0, 0, 0, 0) + return self.res + + def dfs(self, n, row , col, pie, na): + if n == row: + self.res += 1 + return + # 二进制位,为1的位置表示可以放置皇后 + # 这里设为1的位置表示可以放置皇后是因为方便后的while判断是否还有空位(只需判断bits是否为0就可以了,为0表示没空位) + # (如果设为0的位置可以放置皇后,那么只有当bits为11111才表示没有空位,那么wihle循环难以判断bits是否全为1) + # 下面操作计算出一个二进制,为1的位置表示可以放皇后,为0表示不能放皇后 + bits = (~(col | pie | na)) & ((1 << n) - 1) + # 如果bits中还有1,表示还有空位可以放置皇后,循环所有可能 + while bits: + # 从最低位1的位置开始放置皇后 + # 将最低位的1取出来,比如1010取出来就是0010 + # bits & -bits效果就是取出最低位的1 + p = bits & -bits + # 先将最低位1置为1表示放置了皇后,以便下次while判断 + bits = bits & (bits - 1) + # 下探下一行,继续放皇后 + # 在p的二进制位放置皇后,那么下一层: + # 不能在当前col为1的位置和col的p位置放置皇后,所以col | p + # 不能在当前pie为1的位置和pie的p位置的左移一位放置皇后,所以(pie | p) << 1 + # 不能在当前na为1的位置和na的p位置的右移一位放置皇后,所以(na | p) >> 1 ) + self.dfs(n, row + 1, col | p, (pie | p) << 1, (na | p) >> 1 ) + + + diff --git a/Week 07/id_738/LeetCode_56_738.py b/Week 07/id_738/LeetCode_56_738.py new file mode 100644 index 000000000..b7ff69e47 --- /dev/null +++ b/Week 07/id_738/LeetCode_56_738.py @@ -0,0 +1,31 @@ +class Solution(object): + def merge(self, intervals): + """ + 合并区间:https://leetcode-cn.com/problems/merge-intervals/ + + :type intervals: List[List[int]] + :rtype: List[List[int]] + """ + if len(intervals) <= 1: + return intervals + # 对intervals进行排序 + intervals.sort(key = lambda x: x[0]) + # 排序完后,扫描intervals,如果扫描元素和前一个元素有交集,则扩大前一个元素的区间,否则则加入到结果中 + result = [] + k = 0 + for i in range(1, len(intervals)): + if intervals[i][0] <= intervals[k][1]: + if intervals[i][1] > intervals[k][1]: + intervals[k][1] = intervals[i][1] + else: + result.append(intervals[k]) + k = i + # 最后的一次不在循环内判断,需要单独加入 + result.append(intervals[k]) + return result + + + + + + diff --git a/Week 07/id_738/NOTE.md b/Week 07/id_738/NOTE.md index a6321d6e2..ffbd7d52d 100644 --- a/Week 07/id_738/NOTE.md +++ b/Week 07/id_738/NOTE.md @@ -1,4 +1,281 @@ -# NOTE +# 第七周学习总结 +## 位运算 +- 为什么需要位运算 + - 计算机的数字表示方式是二进制方式 +- 位运算符 + - XOR - 异或 + - 相同为0,不同为1.也可用"不进位加法"来理解 + - 操作特点 + - x ^ 0 = x + - x ^ 1s = ~x //注意1s =~ 0,表示全1 + - x ^ (~x) = 1s + - x ^ x = 0 + - c = a ^ b => a ^ c = b, b ^ c = a //交换两数 + - a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c + - 指定位置的位运算 + - 将x最右边的n为清零:x & (~0 << n) + - 获取x的第n位值(0或者1): (x >> n) & 1 + - 获取x的第n位的幂值:x & (1 << (n - 1)) + - 仅将第n位置为1:x | (1 << n) + - 仅将第n位置为0:x & (~(1 << n)) + - 将x最高位至第n位(含)清零:x & ((1 << n) - 1) + - 将第n位至第0位(含)清零:x & (~((1 << (n + 1)) - 1)) +- 实战位运算要点(面试最多)(布隆过滤器,n皇后问题) + - 判断奇偶: + - x % 2 == 1 -> (x & 1) == 1 + - x % 2 == 0 -> (x & 1) == 0 + - x >> 1 -> x / 2 + - 即:x = x / 2 -> x = x >>1 + - X = X & (X - 1)清零最低位的1 + - X & -X => 得到最低位的1 + - X & ~X => 0 - +## 布隆过滤器 +- 高级数据结构 + - 在面试中和工业应用中较为广泛 +- 布隆过滤器 + - 一个很长的二进制向量和一系列随机映射函数。 + - 用于检索一个元素是否在一个集合中 + - 优点: + - 空间效率和查询时间都远远超过一般算法 + - 缺点 + - 有一定的误识别率和删除困难 + - 操作方法: + - 添加元素: + - 将要添加的元素给K个哈希函数 + - 得到对应于位数组上的K个位置 + - 将K个位置设置为1 + - 查询元素: + - 将要查询的元素给K个哈希函数 + - 得到对应于位数组上的K个元素 + - 如果K个位置有一个为0,则肯定不在集合中 + - 如果K个位置全部为1,则可能在集合中 + - 实际应用: + - 因为布隆过滤器只能判断肯定不在集合中的元素,但是不能判断元素必定在集合中。但是由于布隆过滤器的空间和时间复杂度很低,所以在工业中,布隆过滤器是位于最前线的过滤器,只有通过过滤器判断True,才传递到下一层去查询真正的源数据看是否在。 + - 比特币网络 + - 分布式系统 + - Redis缓存 + - 垃圾邮件、评论等过滤 + - 实现代码 + +```python +from bitarray import bitarray +import mmh3 + +class BloomFilter: + def __init__(self, size, hash_num): + self.size = size + self.hash_num = hash_num + self.bit_array = bitarray(size) + self.bit_array.setall(0) + + def add(self, s): + for seed in range(self.hash_num): + result = mmh3.hash(s, seed) % self.size + self.bit_array[result] = 1 + + def lookup(self, s): + for seed in range(self.hash_num): + result = mmh3.hash(s, seed) % self.size + if self.bit_array[result] == 0: + return "Nope" + return "Probably" + +# bf = BloomFilter(500000, 7) +# bf.add("dantezhao") +# print (bf.lookup("dantezhao")) +# print (bf.lookup("yyj")) +``` + +## LRU Cache +- LRU - least reently used + - 最近最少使用 + - Hash + 双向链表 + - O(1)查询 + - O(1)修改、更新 + - java中可以用LinkedHashMap + +## 初级排序和高级排序 +### 比较类排序(最常用) +- 通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序 +- 有一个compare之类的方法来比较两个数的次序 +- 不局限于整型类型 +- 类别(重点是nlogn的三种排序) +#### 交换排序 +- 冒泡排序:O(n^2),初级排序 + - 嵌套循环,每次查看相邻的元素,如果逆序,则交换 +- 快速排序:O(nlogn),高级排序 + - 数组取标杆pivot,将小元素放pivot左边,大元素放右侧,然后依次对左边和右边的子数组继续快排;以达到整个序列有序 + - 先调配出左右子数组,然后怼左右子数组进行快速排序 + - 代码 + +```java +public static void quickSort(int[] array, int begin, int end) { + if (end <= begin) return; + int pivot = partition(array, begin, end); + quickSort(array, begin, pivot - 1); + quickSort(array, pivot + 1, end); + +} + +static int partition(int[] a, int begin, int end) { + // pivot: 标杆位置,counter: counter下标前面的元素都小于pivot + int pivot = end, counter = begin; + // 循环数组,将小于pivot的元素挪动到counter的前面 + // 每挪动一个元素后,counter往后移动一个位置 + // 循环结果就是:counter前面的元素都小于pivot,后面的元素(包括counter位置,不包括pivot位置)都大于pivot + // 所以数组挪动后最后的counter位置就是应该存放pivot值的位置 + for (int i = begin; i < end; i++) { + if (a[i] < a[pivot]) { + int temp = a[counter]; a[counter] = a[i]; a[i] = temp; + counter++; + } + } + int temp = a[pivot]; a[pivot] = a[counter]; a[counter] = temp; + return counter; +} +``` +- 插入排序 + - 简单插入排序:O(n^2),初级排序 + - 从前到后逐步构建有序序列;对未排序数据,在已排序序列中从后向前扫描(扫描过程中,在插入位置之后的元素都要往后挪动一个位置),找到相应位置并插入 +- 希尔排序 +- 选择排序 + - 简单选择排序:O(n^2),初级排序 + - 每次找最小值,然后放到待排序数组的起始位置(和待排序数组的起始位置的元素进行交换) +- 堆排序:插入O(nlogn),取最大/小O(1) + - 数组元素一次建立小顶堆 + - 然后每次取出堆顶的元素 + - 代码: + +```java +static void heapify(int[] array, int length, int i) { + int left = 2 * i + 1, right = 2 * i + 2; + int largest = i; + + if (left < length && array[left] > array[largest]) { + largest = left; + } + if (right < length && array[right] > array[largest]) { + largest = right; + } + + if (largest != i) { + int temp = array[i]; array[i] = array[largest]; array[largest] = temp; + // 由于调整了堆的元素位置,所以需要对调整了的位置的子树进行重新建堆 + heapify(array, length, largest); + } +} + +public static void heapSort(int[] array) { + if (array.length == 0) return; + + int length = array.length; + // 从最后一个非叶子节点开始,逐步建立堆 + for (int i = length / 2 - 1; i >= 0; i--) + heapify(array, length, i); + + // 建立完堆,依次取出堆顶元素,即是最大值放到数组最后。 + // 操作完成后,重新对堆顶的子树进行重新建堆,建堆数组长度逐次减一(排除掉已经排序好的数组尾元素) + for (int i = length - 1; i >= 0; i--) { + int temp = array[0]; array[0] = array[i]; array[i] = temp; + heapify(array, i, 0); + } +} +``` + +- 归并排序:O(nlogn),二路,多路 + - 需要额外的内存空间n + - 先排序左右子数组,然后合并两个有序子数组 + - 分治 + - 1. 把长度为n的输入序列分成两个长度为n/2的子序列; + - 2. 对这两个子序列分别采用归并排序 + - 3. 将两个排序好的子序列合并成一个最终的排序序列 + - 代码: + +```java +public static void mergeSort(int[] array, int left, int right) { + if (right <= left) return; + int mid = (left + right) >> 1; // (left + right) / 2 + mergeSort(array, left, mid); + mergeSort(array, mid + 1, right); + merge(array, left, mid, right); +} + +// 这个方法完成的任何:如何把两个有序的数组合并成一个有序的数组 +// 需要借助一个额外空间n +public static void merge(int[] arr, int left, int mid, int right) { + int[] temp = new int[right - left + 1]; // 中间数组 + int i = left, j = mid + 1, k = 0; + while (i <= mid && j <= right) { + temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++]; + } + while (i <= mid) temp[k++] = arr[i++]; + while (j <= right) temp[k++] = arr[j++]; + for (int p = 0; p < temp.length; p++) { + arr[left + p] = temp[p]; + } + // 也可以用 System.arraycopy(a, start1, b, start2, length) +} +``` + +## 特殊排序 +- 非比较类排序 + - 不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序 + - 局限于整型相关的数据类型,对字符串,对象无能为力 + - 时间复杂度:O(n) + - 计数排序 + - 元素必须是整数 + - 计数排序要求输入的数据必须是有确定范围的整数。将输入的数据值转化为键存储在额外开辟的数组空间中;然后依次把计数大于1的填充回原数组 + - 桶排序(不做重点要求) + - 计数排序的升级版,解决计数排序元素过多会爆掉的问题 + - 假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序) +- 基数排序 + - 只能排整型数据 + - 按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是由优先级顺序的,先按低优先级排序,再按高优先级排序。 + +## 选择排序,冒泡排序,插入排序的代码 +- 冒泡排序 + +```python +def sort(nums): + # 每趟冒泡过程决定了数组最后一位的值,所以从后往前扫描 + for i in range(len(nums))[::-1]: + # 每趟冒泡需要比较相邻两个数之间的大小并交换,直到该趟的末尾i - 1(因为过程涉及到j+1,所以j最大必须为i - 1,否则j+1会越界) + for j in range(i): + if nums[j] > nums[j + 1]: + nums[j], nums[j + 1] = nums[j + 1], nums[j] +``` + +- 选择排序 + +```python +# 每次扫描未排序部分,选择其最小值 +# 插入到已排序部分的后面一个位置(交换操作) +def sort(nums): + size = len(nums) + for i in range(size): + mini_index = i + for j in range(i + 1, size): + if nums[mini_index] > nums[j]: + mini_index = j + nums[i], nums[mini_index] = nums[mini_index], nums[i] +``` + +- 插入排序 + +```python +# 每次对未排序部分的值,查找在已排序部分的位置并插入 +# 插入需要将已排序部分在插入位置后面的值向后挪动一位 +# nums=[2,3,1,5,0,10,11,4] +def sort(nums): + size = len(nums) + for i in range(1, size): + # 对j = 0...i-1部分的值进行查找位置插入nums[i] + # 操作是每次看j和j + 1部分的值,如果后者小于前者,表示前者必须在后者后面 + # 所以交换值 + # 然后继续比较下一对值 + for j in range(0, i)[::-1]: + if nums[j + 1] < nums[j]: + nums[j], nums[j + 1] = nums[j + 1], nums[j] +``` diff --git a/Week 07/id_748/LeetCode_190_748_reverseBits.java b/Week 07/id_748/LeetCode_190_748_reverseBits.java new file mode 100644 index 000000000..fec60d9cd --- /dev/null +++ b/Week 07/id_748/LeetCode_190_748_reverseBits.java @@ -0,0 +1,30 @@ +package com.code.week5; + + +/** + * 颠倒给定的 32 位无符号整数的二进制位。 + */ + + +public class LeetCode_191_748_hammingWeight { + + + public int reverseBits1(int n) { + int result = 0; + for (int i = 0; i <= 32; i++) { + // 1. 将给定的二进制数,由低到高位逐个取出 + // 1.1 右移 i 位, + int tmp = n >> i; + // 1.2 取有效位 + tmp = tmp & 1; + // 2. 然后通过位运算将其放置到反转后的位置. + tmp = tmp << (31 - i); + // 3. 将上述结果再次通过运算结合到一起 + result |= tmp; + } + return result; + } + + + +} diff --git a/Week 07/id_748/LeetCode_191_748_hammingWeight.java b/Week 07/id_748/LeetCode_191_748_hammingWeight.java new file mode 100644 index 000000000..403801b76 --- /dev/null +++ b/Week 07/id_748/LeetCode_191_748_hammingWeight.java @@ -0,0 +1,25 @@ +package com.code.week5; + + +/** + * 编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。 + */ + + +public class LeetCode_191_748_hammingWeight { + + + public int hammingWeight(int n) { + int bits = 0; + int mask = 1; + for (int i = 0; i < 32; i++) { + if ((n & mask) != 0) { + bits++; + } + mask <<= 1; + } + return bits; + } + + +} diff --git a/Week 07/id_748/LeetCode_231_748_isPowerOfTwo.java b/Week 07/id_748/LeetCode_231_748_isPowerOfTwo.java new file mode 100644 index 000000000..6612e94d9 --- /dev/null +++ b/Week 07/id_748/LeetCode_231_748_isPowerOfTwo.java @@ -0,0 +1,16 @@ +package com.code.week5; + + +/** + * 给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 + */ + + +public class LeetCode_191_748_hammingWeight { + + + public boolean isPowerOfTwo(int n) { + return n > 0 && (n & (n - 1)) == 0; + } + +} diff --git a/Week 07/id_748/NOTE.md b/Week 07/id_748/NOTE.md index a6321d6e2..89ca7e9b2 100644 --- a/Week 07/id_748/NOTE.md +++ b/Week 07/id_748/NOTE.md @@ -1,4 +1,14 @@ -# NOTE +**位运算** +程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算就是直接对整数在内存中的二进制位进行操作。比如,and运算本来是一个逻辑运算符,但整数与整数之间也可以进行and运算。举个例子,6的二进制是110,11的二进制是1011,那么6 and 11的结果就是2,它是二进制对应位进行逻辑运算的结果(0表示False,1表示True,空位都当0处理)。 - + +**布隆过滤器** +本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。 +相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少,但是缺点是其返回的结果是概率性的,而不是确切的。 + + +**LRU Cache** +LRU(Least Recently Used) 表示最近最少使用 +LRU内部维护的是一个LInkedHashMap +LinkedHashMap与HashMap的区别:LinkedHashMap保持了元素的插入顺序,使得遍历顺序可按插入顺序输出 \ No newline at end of file diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_178/NOTE.md" b/Week 08/id_003/NOTE.md old mode 100644 new mode 100755 similarity index 100% rename from "Week \351\242\204\344\271\240\345\221\250/id_178/NOTE.md" rename to Week 08/id_003/NOTE.md diff --git a/Week 08/id_008/LeetCode_14_008.js b/Week 08/id_008/LeetCode_14_008.js new file mode 100644 index 000000000..ca83303cb --- /dev/null +++ b/Week 08/id_008/LeetCode_14_008.js @@ -0,0 +1,31 @@ +/** + * @param {string[]} strs + * @return {string} + */ +var longestCommonPrefix = function(strs) { + if (strs.length === 0 || strs[0].length === 0) { + return ""; + } + + var arr = []; + var k = 0; + + search: while (true) { + if (k >= strs[0].length) { + break; + } + + var word = strs[0][k]; + + for (var i = 1; i < strs.length; ++i) { + if (strs[i][k] !== word) { + break search; + } + } + + arr.push(word); + ++k; + } + + return arr.join(""); +}; diff --git a/Week 08/id_008/LeetCode_151_008.js b/Week 08/id_008/LeetCode_151_008.js new file mode 100644 index 000000000..2be9a4c93 --- /dev/null +++ b/Week 08/id_008/LeetCode_151_008.js @@ -0,0 +1,7 @@ +/** + * @param {string} s + * @return {string} + */ +var reverseWords = function(s) { + return s.split(" ").reverse().join(" ").trim().replace(/([\s]+)/g, " "); +}; diff --git a/Week 08/id_008/LeetCode_32_008.js b/Week 08/id_008/LeetCode_32_008.js new file mode 100644 index 000000000..9e7c3ffc2 --- /dev/null +++ b/Week 08/id_008/LeetCode_32_008.js @@ -0,0 +1,24 @@ +/** + * @param {string} s + * @return {number} + */ +var longestValidParentheses = function(s) { + var max = -Infinity; + var n = s.length; + var dp = new Array(n).fill(0); + + for (var i = 1; i < n; ++i){ + if (s[i] === ')') { + if (s[i - 1] === '(') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + } + else if (i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] === '(') { + dp[i] = dp[i - 1] + ((i - dp[i - 1] >= 2) ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + + dp[i] > max && (max = dp[i]); + } + } + + return max > -Infinity ? max : 0; +}; diff --git a/Week 08/id_008/LeetCode_344_008.js b/Week 08/id_008/LeetCode_344_008.js new file mode 100644 index 000000000..ca6581547 --- /dev/null +++ b/Week 08/id_008/LeetCode_344_008.js @@ -0,0 +1,25 @@ +/** + * @param {character[]} s + * @return {void} Do not return anything, modify s in-place instead. + */ +var reverseString = function(s) { + var k = s.length >> 1; + var temp = ""; + + for (var i = 0; i < k; ++i) { + temp = s[i]; + s[i] = s[s.length - i - 1]; + s[s.length - i - 1] = temp; + } + + return s; +}; + +/** + * @param {character[]} s + * @return {void} Do not return anything, modify s in-place instead. + */ +var reverseString = function(s) { + s.reverse(); + return s; +}; diff --git a/Week 08/id_008/LeetCode_387_008.js b/Week 08/id_008/LeetCode_387_008.js new file mode 100644 index 000000000..6c9dfc809 --- /dev/null +++ b/Week 08/id_008/LeetCode_387_008.js @@ -0,0 +1,29 @@ +/** + * @param {string} s + * @return {number} + */ +var firstUniqChar = function(s) { + for (var i = 0; i < s.length; ++i) { + if (count(s[i]) <= 1) { + return i; + } + } + + return -1; + + function count(w){ + var total = 0; + + for (var i = 0; i < s.length; ++i) { + if (s[i] === w) { + ++total; + } + + if (total >= 2) { + return 2; + } + } + + return total; + } +}; diff --git a/Week 08/id_008/LeetCode_557_008.js b/Week 08/id_008/LeetCode_557_008.js new file mode 100644 index 000000000..e15edca5b --- /dev/null +++ b/Week 08/id_008/LeetCode_557_008.js @@ -0,0 +1,13 @@ +/** + * @param {string} s + * @return {string} + */ +var reverseWords = function(s) { + var arr = s.split(" "); + + for (var i = 0; i < arr.length; ++i) { + arr[i] = arr[i].split("").reverse().join(""); + } + + return arr.join(" "); +}; diff --git a/Week 08/id_008/LeetCode_58_008.js b/Week 08/id_008/LeetCode_58_008.js new file mode 100644 index 000000000..effbacb4e --- /dev/null +++ b/Week 08/id_008/LeetCode_58_008.js @@ -0,0 +1,23 @@ +/** + * @param {string} s + * @return {number} + */ +var lengthOfLastWord = function(s) { + var arr = s.split(" "); + + for (var i = arr.length - 1; i >= 0; --i) { + if (arr[i].length > 0) { + return arr[i].length; + } + } + + return 0; +}; + +/** + * @param {string} s + * @return {number} + */ +var lengthOfLastWord = function(s) { + return s.trim().split(" ").pop().length; +}; diff --git a/Week 08/id_008/LeetCode_709_008.js b/Week 08/id_008/LeetCode_709_008.js new file mode 100644 index 000000000..7083522a3 --- /dev/null +++ b/Week 08/id_008/LeetCode_709_008.js @@ -0,0 +1,20 @@ +/** + * @param {string} str + * @return {string} + */ +var toLowerCase = function(str) { + var s = []; + + for (var i = 0; i < str.length; ++i) { + var a = str.charCodeAt(i); + + if (a >= 65 && a <= 90) { + s.push(String.fromCharCode(a ^ 32)); + } + else { + s.push(str.charAt(i)); + } + } + + return s.join(""); +}; diff --git a/Week 08/id_008/LeetCode_771_008.js b/Week 08/id_008/LeetCode_771_008.js new file mode 100644 index 000000000..86283d1dd --- /dev/null +++ b/Week 08/id_008/LeetCode_771_008.js @@ -0,0 +1,14 @@ +/** + * @param {string} J + * @param {string} S + * @return {number} + */ +var numJewelsInStones = function(J, S) { + var total = 0; + + for (var i = 0; i < S.length; ++i) { + ~J.indexOf(S[i]) && ++total; + } + + return total; +}; diff --git a/Week 08/id_008/LeetCode_8_008.c b/Week 08/id_008/LeetCode_8_008.c new file mode 100644 index 000000000..ad06c1bfa --- /dev/null +++ b/Week 08/id_008/LeetCode_8_008.c @@ -0,0 +1,39 @@ +int myAtoi(char * str){ + if (str == NULL) { + return 0; + } + + int i = 0; + long total = 0; + int is_neg = 0; + + while (isspace(str[i])) { + ++i; + } + + if (str[i] == '-') { + is_neg = 1; + ++i; + } + else if (str[i] == '+') { + ++i; + } + + while (str[i] >= '0' && str[i] <= '9') { + total = total * 10 + (str[i++] - '0'); + + if (total > 2147483648) { + break; + } + } + + if (is_neg && total > 2147483648) { + return -2147483648; + } + + if (!is_neg && total > 2147483647) { + return 2147483647; + } + + return is_neg ? -total : total; +} diff --git a/Week 08/id_008/LeetCode_8_008.js b/Week 08/id_008/LeetCode_8_008.js new file mode 100644 index 000000000..18210fe5c --- /dev/null +++ b/Week 08/id_008/LeetCode_8_008.js @@ -0,0 +1,43 @@ +/** + * @param {string} str + * @return {number} + */ +var myAtoi = function(str) { + if (!str) { + return 0; + } + + var i = 0; + var total = 0; + var is_neg = 0; + + while (str[i] === " " || str[i] === "\r" || str[i] === "\n" || str[i] === "\t") { + ++i; + } + + if (str[i] === '-') { + is_neg = 1; + ++i; + } + else if (str[i] === '+') { + ++i; + } + + while (str.charCodeAt(i) >= 48 && str.charCodeAt(i) <= 57) { + total = total * 10 + (str.charCodeAt(i++) - 48); + + if (total > 2147483648) { + break; + } + } + + if (is_neg && total > 2147483648) { + return -2147483648; + } + + if (!is_neg && total > 2147483647) { + return 2147483647; + } + + return is_neg ? -total : total; +}; diff --git a/Week 08/id_008/LeetCode_91_008.js b/Week 08/id_008/LeetCode_91_008.js new file mode 100644 index 000000000..8f8cb6d5b --- /dev/null +++ b/Week 08/id_008/LeetCode_91_008.js @@ -0,0 +1,69 @@ +/** + * @param {string} s + * @return {number} + */ +var numDecodings = function(s) { + var cache = {}; + return gen(s); + + function gen(str){ + if (typeof cache[str] === "number") { + return cache[str]; + } + + if (str.length <= 0) { + cache[str] = 0; + return 0; + } + + if (str.charAt(0) === "0") { + cache[str] = 0; + return 0; + } + + if (str.length === 1) { + if (str === "0") { + cache[str] = 0; + return 0; + } + + return 1; + } + + if (str.length === 2) { + var k = +str; + + if (k <= 26) { + if (k % 10 === 0) { + cache[str] = 1; + return 1; + } + else { + cache[str] = 2; + return 2; + } + } + else { + if (k % 10 === 0) { + cache[str] = 0; + return 0; + } + else { + cache[str] = 1; + return 1; + } + } + } + + var total = gen(str.substring(1)); + var two = str.charAt(0) * 10 + +str.charAt(1); + + if (two <= 26) { + cache[str] = total + gen(str.substring(2)); + return cache[str] + } + + cache[str] = total; + return total; + } +}; diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_218/NOTE.md" b/Week 08/id_008/NOTE.md old mode 100644 new mode 100755 similarity index 100% rename from "Week \351\242\204\344\271\240\345\221\250/id_218/NOTE.md" rename to Week 08/id_008/NOTE.md diff --git a/Week 08/id_013/LeetCode_01_013.py b/Week 08/id_013/LeetCode_01_013.py new file mode 100644 index 000000000..6dd1f9b67 --- /dev/null +++ b/Week 08/id_013/LeetCode_01_013.py @@ -0,0 +1,11 @@ +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + if nums == []: + return 0 + cell = [1] + for i in range(1,len(nums)): + cell.append(1) + for j in range(i): + if(nums[j] < nums[i]): + cell[i] = max(cell[i], cell[j]+1) + return max(cell) diff --git a/Week 08/id_013/LeetCode_07_013.py b/Week 08/id_013/LeetCode_07_013.py new file mode 100644 index 000000000..0c5d01ac9 --- /dev/null +++ b/Week 08/id_013/LeetCode_07_013.py @@ -0,0 +1,17 @@ +class Solution: + def firstUniqChar(self, s): + """ + :type s: str + :rtype: int + """ + # build hash map : character and how often it appears + count = collections.Counter(s) + + # find the index + index = 0 + for ch in s: + if count[ch] == 1: + return index + else: + index += 1 + return -1 diff --git a/Week 08/id_013/NOTE.md b/Week 08/id_013/NOTE.md new file mode 100755 index 000000000..38f22b311 --- /dev/null +++ b/Week 08/id_013/NOTE.md @@ -0,0 +1,274 @@ +# NOTE +动态规划算法: +是一种解决棘手问题的方法,它将问题分成小问题,并先着手解决这些小问题。但仅当每个子问题都是离散的,即不依赖于其他子问题时,动态规划才管用。 + +在问题可分解为彼此独立且离散的子问题时,就可使用动态规划来解决。 + +设计动态规划解决方案的通用小贴士: +1、每个动态规划算法都从一个网格开始。 +2、单元格中的值通常就是要优化的值。 +3、每个单元格都是一个子问题,因此应考虑如何将问题分成子问题,这有助于找出网格的坐标轴。 + +最长公共子串: +两个字符串都包含的最长连续子串。 + +最长公共子序列: +两个字符串都包含的最长有序子串。 + +最长公共子串要求在原字符串中是连续的,而最长公共子序列只需要保持相对顺序一致,并不要求连续。 + +以下2例中待处理字符串: + +# str_a = 'cnbalodgs' +# str_b = 'cbaelodng' + +# str_a = 'abcdfg' +# str_b = 'abdfg' + +str_a = 'abcbdab' +str_b = 'bdcaba' + +# str_a = 'abcbdecb' +# str_b = 'ebdeaba' +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +示例:获取最长公共子串 +解法就是用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若匹配则为1,否则为0。然后求出对角线最长的1的序列,其对应的位置就是最长匹配子串的位置。 + +# 此例中有多个相同长度的公共子串,但只能获取第一个子串 +def find_lcsubstr(s1, s2): + # 下面4行不要直接写在循环中,减少计算 + s1_len = len(s1) + 1 #为方便后续计算,多了1行1列 + s2_len = len(s2) + 1 + s3_len = len(s1) + s4_len = len(s2) + m = [[0 for j in range(s2_len)] for i in range(s1_len)] #生成0矩阵 + maxNum = 0 #初始最长匹配长度 + p = 0 #匹配的起始位置 + for i in range(s3_len): + for j in range(s4_len): + if s1[i] == s2[j]: #相同则累加 + m[i + 1][j + 1] = m[i][j] + 1 #给相同字符赋值,值为左上角值加1 + if m[i + 1][j + 1] > maxNum: + maxNum = m[i + 1][j + 1] #获取最大匹配长度 + p = i + 1 #记录最大匹配长度的终止位置 + print(m) + return s1[p - maxNum : p], maxNum #返回最长子串及其长度 +print(find_lcsubstr(str_a, str_b)) +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +结果为: + +[[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 1], [0, 1, 0, 0, 0, 2, 0], [0, 0, 0, 1, 0, 0, 0], [0, 1, 0, 0, 0, 1, 0], [0, 0, 2, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 1], [0, 1, 0, 0, 0, 2, 0]] +('ab', 2) +1 +2 +示例:获取最长公共子序列,可辨别显示多组相同长度的不同最长公共子序列(此为转载) + +class LCS(): + # 读入待匹配的两个字符串 + def input(self, x, y): + if type(x) != str or type(y) != str: + print('input error') + return None + self.x = x + self.y = y + + # 生成最长公共子序列矩阵 + def Compute_LCS(self): + xlength = len(self.x) + ylength = len(self.y) + self.direction_list = [None] * xlength #这个二维列表存着回溯方向 + for i in range(xlength): + self.direction_list[i] = [None] * ylength + self.lcslength_list = [None] * (xlength + 1) #这个二维列表存着当前最长公共子序列长度 + for j in range(xlength + 1): + self.lcslength_list[j] = [None] * (ylength + 1) + for i in range(0, xlength + 1): #二维列表第一列设置为0 + self.lcslength_list[i][0] = 0 + for j in range(0, ylength + 1): #二维列表第一行设置为0 + self.lcslength_list[0][j] = 0 + #下面是进行回溯方向和长度表的赋值 + for i in range(1, xlength + 1): + for j in range(1, ylength + 1): + if self.x[i - 1] == self.y[j - 1]: + self.lcslength_list[i][j] = self.lcslength_list[i - 1][j - 1] + 1 + self.direction_list[i - 1][j - 1] = 0 #左上 + elif self.lcslength_list[i - 1][j] > self.lcslength_list[i][j - 1]: + self.lcslength_list[i][j] = self.lcslength_list[i - 1][j] + self.direction_list[i - 1][j - 1] = 1 #上 + elif self.lcslength_list[i - 1][j] < self.lcslength_list[i][j - 1]: + self.lcslength_list[i][j] = self.lcslength_list[i][j - 1] + self.direction_list[i - 1][j - 1] = -1 #左 + else: + self.lcslength_list[i][j] = self.lcslength_list[i - 1][j] + self.direction_list[i - 1][j - 1] = 2 #左或上 + self.lcslength = self.lcslength_list[-1][-1] + # print(self.direction_list) + # print(self.lcslength_list) + return self.direction_list, self.lcslength_list + + # 生成最长公共子序列 + def printLCS(self, curlen, i, j, s): + if i == 0 or j == 0: + return None + if self.direction_list[i - 1][j - 1] == 0: + if curlen == self.lcslength: + s += self.x[i - 1] + for i in range(len(s)-1, -1, -1): + print(s[i]) + print('\n') + elif curlen < self.lcslength: + s += self.x[i - 1] + self.printLCS(curlen + 1, i - 1, j - 1, s) + elif self.direction_list[i - 1][j - 1] == 1: + self.printLCS(curlen, i - 1, j, s) + elif self.direction_list[i - 1][j - 1] == -1: + self.printLCS(curlen, i, j - 1, s) + else: + self.printLCS(curlen, i - 1, j, s) + self.printLCS(curlen, i, j - 1, s) + + def returnLCS(self): #回溯的入口 + self.printLCS(1, len(self.x), len(self.y), '') + +if __name__ == '__main__': + p = LCS() #实例化类 + p.input(str_a, str_b) #读入待匹配的两个字符串 + p.Compute_LCS() #生成最长公共子序列矩阵 + p.returnLCS() #生成最长公共子序列 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +结果为: + +b +c +b +a + + +b +c +a +b + + +b +d +a +b +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 + + diff --git a/Week 08/id_018/NOTE.md b/Week 08/id_018/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_018/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_023/NOTE.md b/Week 08/id_023/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_023/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_023/leetCode_300_023.js b/Week 08/id_023/leetCode_300_023.js new file mode 100644 index 000000000..0e97af548 --- /dev/null +++ b/Week 08/id_023/leetCode_300_023.js @@ -0,0 +1,32 @@ +/** + * @param {number[]} nums + * @return {number} + */ +var lengthOfLIS = function (nums) { + let n = nums.length; + if (n <= 1) { + return n; + } + let tail = new Array(n); + tail[0] = nums[0]; + let end = 0; + for (let i = 1; i < n; i++) { + if (nums[i] > tail[end]) { + end++; + tail[end] = nums[i]; + } else { + let left = 0; + let right = end; + while (left < right) { + let mid = left + ((right - left) >> 1); + if (tail[mid] < nums[i]) { + left = mid + 1; + } else { + right = mid; + } + } + tail[left] = nums[i]; + } + } + return end + 1; +}; \ No newline at end of file diff --git a/Week 08/id_023/leetCode_387_023.js b/Week 08/id_023/leetCode_387_023.js new file mode 100644 index 000000000..1656d3139 --- /dev/null +++ b/Week 08/id_023/leetCode_387_023.js @@ -0,0 +1,22 @@ +/** + * @param {string} s + * @return {number} + */ +var firstUniqChar = function (s) { + if (s.length === 1) { + return 0; + } + let base = ['a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', + 'z' + ]; + let minIndex = Number.MAX_SAFE_INTEGER, firstIndex; + for (let i = 0; i < base.length; i++) { + firstIndex = s.indexOf(base[i]); + if (firstIndex >= 0 && firstIndex === s.lastIndexOf(base[i])) { + minIndex = Math.min(minIndex, firstIndex); + } + } + return (minIndex ^ Number.MAX_SAFE_INTEGER) == 0 ? -1 : minIndex; +}; \ No newline at end of file diff --git a/Week 08/id_028/NOTE.md b/Week 08/id_028/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_028/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_033/NOTE.md b/Week 08/id_033/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_033/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_038/NOTE.md b/Week 08/id_038/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_038/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_038/week-08-038/.gitignore b/Week 08/id_038/week-08-038/.gitignore new file mode 100644 index 000000000..a47486b2b --- /dev/null +++ b/Week 08/id_038/week-08-038/.gitignore @@ -0,0 +1,164 @@ +.vertx +*.ipr +# log files +*.log +*.log.2* +.log +out +output +dist +dist-3rd +*.txt +!agent-shell-banner.txt +!shell-banner.txt +!VERSION.txt + +# For Eclipse + +.metadata +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders +*.iml +.DS_Store + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + + +# For Java + +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# For Maven + +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# For JetBrains + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +#data module ignore +node_modules + +static +public + +BETHUNE_PRO_HOST_PID +BETHUNE_PRO_AGENT_PID +*PID +APP_PID +PID_* +alert-rules-*.yml +altmng-configs.yml +alertmanager.yml +app-info-*.properties +app-info.properties +inspectResult/ + +/file-uploads/ diff --git a/Week 08/id_038/week-08-038/pom.xml b/Week 08/id_038/week-08-038/pom.xml new file mode 100644 index 000000000..03601256b --- /dev/null +++ b/Week 08/id_038/week-08-038/pom.xml @@ -0,0 +1,96 @@ + + 4.0.0 + com.github.kylefeng + week-08-038 + jar + 1.0-SNAPSHOT + week-08-038 + http://maven.apache.org + + UTF-8 + 1.8 + 4.12 + 5.0.0 + ${junit.version}.0 + 1.0.0 + + + + + + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + + + + maven-surefire-plugin + 2.19.1 + + + **/Test*.java + **/*Test.java + **/*Tests.java + **/*TestCase.java + + + + slow + + + + + + org.junit.platform + junit-platform-surefire-provider + ${junit.platform.version} + + + + + + + + + com.google.guava + guava + 28.1-jre + + + + org.assertj + assertj-core + 3.13.2 + test + + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test + + + + org.junit.platform + junit-platform-launcher + ${junit.platform.version} + test + + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + + diff --git a/Week 08/id_038/week-08-038/src/main/java/com/github/kylefeng/LeetCode_300_038.java b/Week 08/id_038/week-08-038/src/main/java/com/github/kylefeng/LeetCode_300_038.java new file mode 100644 index 000000000..d7a083821 --- /dev/null +++ b/Week 08/id_038/week-08-038/src/main/java/com/github/kylefeng/LeetCode_300_038.java @@ -0,0 +1,25 @@ +package com.github.kylefeng; + +/** + * @author kylefeng + * @time 2019/12/8 16:03 + */ +public class LeetCode_300_038 { + public static int lengthOfLIS(int[] nums) { + int[] tails = new int[nums.length]; + int size = 0; + for (int x : nums) { + int i = 0, j = size; + while (i != j) { + int m = (i + j) / 2; + if (tails[m] < x) + i = m + 1; + else + j = m; + } + tails[i] = x; + if (i == size) ++size; + } + return size; + } +} diff --git a/Week 08/id_038/week-08-038/src/main/java/com/github/kylefeng/LeetCode_32_038.java b/Week 08/id_038/week-08-038/src/main/java/com/github/kylefeng/LeetCode_32_038.java new file mode 100644 index 000000000..9eb3fb52c --- /dev/null +++ b/Week 08/id_038/week-08-038/src/main/java/com/github/kylefeng/LeetCode_32_038.java @@ -0,0 +1,24 @@ +package com.github.kylefeng; + +/** + * 32. 最长有效括号 + */ +public class LeetCode_32_038 { + + public static int solution_dp(String s) { + int maxans = 0; + int dp[] = new int[s.length()]; + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) == ')') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + + } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + maxans = Math.max(maxans, dp[i]); + } + return maxans; + } + + +} diff --git a/Week 08/id_038/week-08-038/src/main/java/com/github/kylefeng/LeetCode_8_038.java b/Week 08/id_038/week-08-038/src/main/java/com/github/kylefeng/LeetCode_8_038.java new file mode 100644 index 000000000..0c4f1f09a --- /dev/null +++ b/Week 08/id_038/week-08-038/src/main/java/com/github/kylefeng/LeetCode_8_038.java @@ -0,0 +1,42 @@ +package com.github.kylefeng; + +/** + * @author kylefeng + * @time 2019/12/8 15:57 + */ +public class LeetCode_8_038 { + public static int myAtoi(String str) { + if (str == null) { + return 0; + } + str = str.trim(); + if (str.isEmpty()) { + return 0; + } + + int sign = 1; + int i = 0; + if (str.charAt(0) == '-' || str.charAt(0) == '+') { + sign = (str.charAt(0) == '-') ? -1 : 1; + if (str.length() < 2 || !Character.isDigit(str.charAt(1))) { + return 0; + } + i++; + } + int n = 0; + while (i < str.length()) { + if (Character.isDigit(str.charAt(i))) { + int d = str.charAt(i) - '0'; + if (n > (Integer.MAX_VALUE - d) / 10) { + n = (sign == -1) ? Integer.MIN_VALUE : Integer.MAX_VALUE; + return n; + } + n = n * 10 + d; + } else { + break; + } + i++; + } + return sign * n; + } +} diff --git a/Week 08/id_038/week-08-038/src/main/java/com/github/kylefeng/LeetCode_91_038.java b/Week 08/id_038/week-08-038/src/main/java/com/github/kylefeng/LeetCode_91_038.java new file mode 100644 index 000000000..a13a53cf2 --- /dev/null +++ b/Week 08/id_038/week-08-038/src/main/java/com/github/kylefeng/LeetCode_91_038.java @@ -0,0 +1,36 @@ +package com.github.kylefeng; + +/** + * 91. 解码方法 + */ +public class LeetCode_91_038 { + + + public static int solution(String s) { + if (s == null || s.isEmpty()) { + return 0; + } + + int strLen = s.length(); + char[] chars = s.toCharArray(); + int[] dp = new int[strLen]; + + + dp[0] = chars[0] == '0' ? 0 : 1; + + for (int i = 1; i < strLen; i++) { + int cur = chars[i] - '0'; + int pre = (chars[i - 1] - '0') * 10 + cur; + + if (cur != 0) { + dp[i] += dp[i - 1]; + } + + if (10 <= pre && pre <= 26) { + dp[i] += i >= 2 ? dp[i - 2] : 1; + } + } + + return dp[strLen - 1]; + } +} diff --git a/Week 08/id_043/NOTE.md b/Week 08/id_043/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_043/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_048/NOTE.md b/Week 08/id_048/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_048/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_053/NOTE.md b/Week 08/id_053/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_053/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_058/NOTE.md b/Week 08/id_058/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_058/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_063/Leetcode_115_063.java b/Week 08/id_063/Leetcode_115_063.java new file mode 100644 index 000000000..ec23d3d65 --- /dev/null +++ b/Week 08/id_063/Leetcode_115_063.java @@ -0,0 +1,44 @@ +/* +动态规划解决 +dp(i, j) 表示t[0:j] 在 s[0:i] 中出现的次数 + +若s[i] != t[j] + dp(i, j) = dp(i-1, j) +s[i] == t[j] + dp(i, j) = dp(i-1, j-1) + dp(i-1, j) + */ + + +public class Solution { + public int numDistinct(String s, String t) { + if (s.length() == 0) { + return t.length() == 0 ? 1 : 0; + } + + if (t.length() == 0) { + return 0; + } + + int[][] dp = new int[s.length()][t.length()]; + + dp[0][0] = s.charAt(0) == t.charAt(0) ? 1 : 0; + for (int i = 1; i < s.length(); i++) { + dp[i][0] = s.charAt(i) == t.charAt(0) ? dp[i-1][0] + 1 : dp[i-1][0]; + + for (int j = 1; j <= i && j < t.length(); j++) { + if (t.charAt(j) != s.charAt(i)) { + dp[i][j] = j <= i-1 ? dp[i-1][j] : 0; + } else { + dp[i][j] += (j-1 <= i-1 && j-1 >= 0) ? dp[i-1][j-1] : 0; + dp[i][j] += (j <= i-1) ? dp[i-1][j] : 0; + } + } + } + + return dp[s.length()-1][t.length()-1]; + } + + public static void main(String[] args) { + System.out.println(new Solution().numDistinct("babgbag", "bag")); + } +} \ No newline at end of file diff --git a/Week 08/id_063/Leetcode_125_063.java b/Week 08/id_063/Leetcode_125_063.java new file mode 100644 index 000000000..3707c66c7 --- /dev/null +++ b/Week 08/id_063/Leetcode_125_063.java @@ -0,0 +1,54 @@ +/* +思路: + 双指针夹逼判断 +*/ + + +class Solution { + + private boolean isCharOrNum(char ch) { + return ((ch >= 'a') && (ch <= 'z')) || + ((ch >= 'A') && (ch <= 'Z')) || + ((ch >= '0') && (ch <= '9')); + } + + private boolean isChar(char ch) { + return ((ch >= 'a') && (ch <= 'z')) || + ((ch >= 'A') && (ch <= 'Z')); + } + + public boolean isPalindrome(String s) { + if (s == null) { + return false; + } + + if ((s.length() == 0) || (s.length() == 1)) { + return true; + } + + int i = 0, j = s.length() - 1; + char ch1, ch2; + while (i < j) { + ch1 = s.charAt(i); ch2 = s.charAt(j); + if (!isCharOrNum(ch1)) { + i++; + continue; + } + + if (!isCharOrNum(ch2)) { + j--; + continue; + } + + if (ch1 != ch2) { + if ( ! (isChar(ch1) && isChar(ch2) && Math.abs(ch1-ch2) == Math.abs('A' - 'a')) ) { + return false; + } + } + + i++; j--; + } + + return true; + } +} diff --git a/Week 08/id_063/Leetcode_14_063.java b/Week 08/id_063/Leetcode_14_063.java new file mode 100644 index 000000000..8e11487d1 --- /dev/null +++ b/Week 08/id_063/Leetcode_14_063.java @@ -0,0 +1,52 @@ +/* +思路 + +简单字符串处理 + + */ + +class Solution { + public String longestCommonPrefix(String[] strs) { + if (strs.length == 0) { + return ""; + } + + int len = 0; + StringBuilder ret = new StringBuilder(); + char ch; + boolean endProc = false; + for (int j = 0; j <= Integer.MAX_VALUE; j++) { + if (j >= strs[0].length()) { + return ret.toString(); + } + + ch = strs[0].charAt(j); + + for (int i = 1; i < strs.length; i++) { + if (j >= strs[i].length()) { + endProc = true; + break; + } + + if (strs[i].charAt(j) != ch) { + endProc = true; + break; + } + } + + if (endProc) { + break; + } + + ret.append(ch); + } + + return ret.toString(); + } + + public static void main(String[] args) { + System.out.println(new Solution().longestCommonPrefix(new String[] { + "dog","racecar","car" + })); + } +} \ No newline at end of file diff --git a/Week 08/id_063/Leetcode_151_063.java b/Week 08/id_063/Leetcode_151_063.java new file mode 100644 index 000000000..05fadaad1 --- /dev/null +++ b/Week 08/id_063/Leetcode_151_063.java @@ -0,0 +1,22 @@ +class Solution { + public String reverseWords(String s) { + String[] strs = s.split(" +"); + StringBuilder ret = new StringBuilder(); + for (int i = strs.length - 1; i >= 0; i--) { + if (strs[i].equals("")) { + continue; + } + ret.append(strs[i].trim() + " "); + } + + if (ret.length() > 0) { + ret.deleteCharAt(ret.length() - 1); + } + + return ret.toString(); + } + + public static void main(String[] args) { + System.out.println(new Solution().reverseWords("")); + } +} \ No newline at end of file diff --git a/Week 08/id_063/Leetcode_205_063.java b/Week 08/id_063/Leetcode_205_063.java new file mode 100644 index 000000000..72c7af5de --- /dev/null +++ b/Week 08/id_063/Leetcode_205_063.java @@ -0,0 +1,32 @@ +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +class Solution { + public boolean isIsomorphic(String s, String t) { + Map relation = new HashMap<>(s.length()); + Set values = new HashSet<>(s.length()); + + if (s.length() != t.length()) { + return false; + } + + for (int i = 0; i < s.length(); i++) { + if (relation.containsKey(t.charAt(i))) { + if (relation.get(t.charAt(i)) != s.charAt(i)) { + return false; + } + } else { + if (values.contains(s.charAt(i))) { + return false; + } + + relation.put(t.charAt(i), s.charAt(i)); + values.add(s.charAt(i)); + } + } + + return true; + } +} \ No newline at end of file diff --git a/Week 08/id_063/Leetcode_242_063.java b/Week 08/id_063/Leetcode_242_063.java new file mode 100644 index 000000000..66fe0c2d4 --- /dev/null +++ b/Week 08/id_063/Leetcode_242_063.java @@ -0,0 +1,35 @@ +/* +思路: +用Hash保存每一个字符出现的频次,遍历s中所有字符统计字符频次,然后遍历t中所有字符 +将hash中对应字符的频次进行递减,t中出现任何不在hash中的字符或者有任何一个字符频次 +减少到0以下,都说明t和s不匹配,可以立即判断失败,不必在最后去统计是不是所有字符的频次 +都减到0 + +自己用数组实现Hash比起用库里面的hash表要快一些 + */ + +class Solution { + public boolean isAnagram(String s, String t) { + int[] cnt = new int[256]; + + if (s.length() != t.length()) { + return false; + } + + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + cnt[(int)ch]++; + } + + for (int i = 0; i < t.length(); i++) { + char ch = t.charAt(i); + cnt[(int)ch]--; + + if (cnt[(int)ch] < 0) { + return false; + } + } + + return true; + } +} diff --git a/Week 08/id_063/Leetcode_300_063.java b/Week 08/id_063/Leetcode_300_063.java new file mode 100644 index 000000000..593e6a897 --- /dev/null +++ b/Week 08/id_063/Leetcode_300_063.java @@ -0,0 +1,42 @@ +/* + 动态规划 + + dp[i]表示以i位置的字符结尾的上升子序列最大长度 + 迭代到i位置时候,找nums[i]前面所有比它小的元素的下标x, 找dp[x]最大的一个,dp[i] = dp[x] + 1 + 反之,i位置数字只能自己组成一个序列,dp[i] = 1 + + max(dp[0 : nums.length - 1]) 就是最后答案 + + +*/ + +class Solution { + public int lengthOfLIS(int[] nums) { + if ((nums == null) || (nums.length == 0)) { + return 0; + } + + int[] dp = new int[nums.length]; + dp[0] = 1; + + int result = 1; + for (int i = 1; i < nums.length; i++) { + int max = 1; + for (int j = i - 1; j >= 0; j--) { + if (nums[j] < nums[i]) { + max = Math.max(max, dp[j] + 1); + } + } + + dp[i] = max; + result = Math.max(result, dp[i]); + } + + return result; + } +/* + public static void main(String[] args) { + new Solution().lengthOfLIS(new int[] {10,9,2,5,3,7,101,18}); + } + */ +} \ No newline at end of file diff --git a/Week 08/id_063/Leetcode_32_063.java b/Week 08/id_063/Leetcode_32_063.java new file mode 100644 index 000000000..1e0005845 --- /dev/null +++ b/Week 08/id_063/Leetcode_32_063.java @@ -0,0 +1,67 @@ +/* + +思路 +动态规划求解 +合法子串有一个性质,就是多个合法子串连续穿起来也是合法子串,且每一个子串都以右括号结尾 + +dp[i]表示以i结尾的合法子串最长长度 +看i和i前面字符,如果是()形式,i结尾的最长子字符串就是i-1结尾的最长子字符串加上后面两个字符 +如果是))形式,如果i结尾的合法子串存在,i就必须找一个左括号来匹配,这个匹配位置肯定在一个i-1位置 +结尾的合法子串的前面,但是如果这个i-i结尾的子串不是最长的,那它前面肯定还有一个合法子串紧跟在它前面 +所以它前面一个字符一定是右括号,所以要找这个匹配位置,只可能在i-i结尾的最长子串的前面才可能找到 + +所以可以总结递推关系如下 +最后两个字符是()形式 + dp[i] = dp[i-2] + 2 +最后两个字符是))形式 + i-1 - dp[i-1] 位置是左括号 + dp[i] = dp[i-1] + 2 + dp[i-2 - dp[i-1]]; + + +其他 + dp[i] = 0 + + + */ + +class Solution { + public int longestValidParentheses(String s) { + int len = s.length(); + if (len == 0 || len == 1) { + return 0; + } + + int[] dp = new int[s.length()]; + + dp[0] = 0; dp[1] = (s.charAt(0) == '(' && s.charAt(1) == ')') ? 2 : 0; + int max = Math.max(dp[0], dp[1]); + for (int i = 2; i < s.length(); i++) { + if (s.charAt(i) != ')') { + dp[i] = 0; + continue; + } + + if (s.charAt(i-1) == '(') { + dp[i] = dp[i-2] + 2; + } else { + if (i-1-dp[i-1] >= 0 && s.charAt(i-1-dp[i-1]) == '(') { + dp[i] = dp[i-1] + 2; + if (i-2-dp[i-1] >= 0) { + // 还要加上前面的一串 + dp[i] += dp[i-2-dp[i-1]]; + } + } else { + dp[i] = 0; + } + } + + max = Math.max(dp[i], max); + } + + return max; + } + + public static void main(String[] args) { + System.out.println(new Solution().longestValidParentheses("()(())")); + } +} \ No newline at end of file diff --git a/Week 08/id_063/Leetcode_344_063.java b/Week 08/id_063/Leetcode_344_063.java new file mode 100644 index 000000000..7eb00ec9f --- /dev/null +++ b/Week 08/id_063/Leetcode_344_063.java @@ -0,0 +1,18 @@ + +/* + +思路 +简单字符串处理 + + */ + +class Solution { + public void reverseString(char[] s) { + int l = 0, r = s.length - 1; + + while (l < r) { + char t = s[l]; s[l] = s[r]; s[r] = t; + l++; r--; + } + } +} diff --git a/Week 08/id_063/Leetcode_387_063.java b/Week 08/id_063/Leetcode_387_063.java new file mode 100644 index 000000000..6d7384d20 --- /dev/null +++ b/Week 08/id_063/Leetcode_387_063.java @@ -0,0 +1,25 @@ +/* +两边扫描字符串 +第一遍统计每一个字符出现的频数 +第二遍从左到右找第一个频数是1的字符 + + */ + + +class Solution { + public int firstUniqChar(String s) { + int[] cnt = new int[26]; + + for (int i = 0; i < s.length(); i++) { + cnt[s.charAt(i) - 'a']++; + } + + for (int i = 0; i < s.length(); i++) { + if (cnt[s.charAt(i) - 'a'] == 1) { + return i; + } + } + + return -1; + } +} \ No newline at end of file diff --git a/Week 08/id_063/Leetcode_438_063.java b/Week 08/id_063/Leetcode_438_063.java new file mode 100644 index 000000000..266724f7e --- /dev/null +++ b/Week 08/id_063/Leetcode_438_063.java @@ -0,0 +1,54 @@ +/* +从左到右类似滑动窗口一样更新每一个字符的频率统计值即可 + */ + + +class Solution { + + private boolean isSame(int[] a, int[] b) { + for (int i = 0; i < 26; i++) { + if (a[i] != b[i]) { + return false; + } + } + + return true; + } + + public List findAnagrams(String s, String p) { + List ret = new ArrayList<>(); + + if (s.length() < p.length()) { + return ret; + } + + int[] target = new int[26]; + int[] cur = new int[26]; + for (char ch : p.toCharArray()) { + target[ch-'a']++; + } + + int l = 0, r = p.length() - 1; + for (int i = 0; i < p.length(); i++) { + cur[s.charAt(i)-'a']++; + } + + while (r < s.length()) { + if (isSame(cur, target)) { + ret.add(l); + } + + cur[s.charAt(l)-'a']--; + if (r+1 < s.length()) { + cur[s.charAt(r+1)-'a']++; + } + l++; r++; + } + + return ret; + } + + public static void main(String[] args) { + System.out.println(new Solution().findAnagrams("abab", "ab")); + } +} \ No newline at end of file diff --git a/Week 08/id_063/Leetcode_44_063.java b/Week 08/id_063/Leetcode_44_063.java new file mode 100644 index 000000000..3f5c6517b --- /dev/null +++ b/Week 08/id_063/Leetcode_44_063.java @@ -0,0 +1,83 @@ +/* + +动态规划 + +dp(i, j) 表示s[0:i] 和 p[0:j]能不能匹配 + +p[j]是*情况下,*有两种处理,一种是匹配空串,直接把*消耗掉,一种是*消耗掉s[i], 然后留给s[0:i-1]消耗, +留下去消耗不会带来更差结果,因为* 是可以匹配空串的,*不想要的时候可以随时舍弃 +此时 dp(i, j) = dp(i-1, j) | dp(i, j-1) + +p[j]是?情况下 dp(i, j) = dp(i-1, j-1) + +p[j]是其他字符情况下 dp(i, j) = s[i] == p[j] ? dp(i-1, j-1) : false + + + */ + + +class Solution { + public boolean isMatch(String s, String p) { + if (s.length() == 0) { + // s为空,只有可能p全部为*或者p为空串才可能 + for (int i = 0; i < p.length(); i++) { + if (p.charAt(i) != '*') + return false; + } + + return true; + } + + if (p.length() == 0) { + return false; + } + + boolean[][] dp = new boolean[s.length()][p.length()]; + + // i = 0 时候边界初始化 + boolean flag = true; + boolean allStar = true; // 目前是不是所有都是* + for (int j = 0; j < p.length(); j++) { + if (allStar) { + if (p.charAt(j) == '*') { + dp[0][j] = true; + } else if (p.charAt(j) == '?') { + dp[0][j] = true; allStar = false; + } else { + dp[0][j] = p.charAt(j) == s.charAt(0); allStar = false; + flag = p.charAt(j) == s.charAt(0); + } + } else { + if (!flag) { + dp[0][j] = false; + } else { + if (p.charAt(j) == '*') { + dp[0][j] = true; + } else { + dp[0][j] = false; flag = false; + } + } + } + } + + for (int i = 1; i < s.length(); i++) { + dp[i][0] = p.charAt(0) == '*'; + + for (int j = 1; j < p.length(); j++) { + if (p.charAt(j) == '*') { + dp[i][j] = dp[i][j-1] | dp[i-1][j]; + } else if (p.charAt(j) == '?') { + dp[i][j] = dp[i-1][j-1]; + } else { + dp[i][j] = s.charAt(i) == p.charAt(j) ? dp[i-1][j-1] : false; + } + } + } + + return dp[s.length()-1][p.length() - 1]; + } + + public static void main(String[] args) { + System.out.println(new Solution().isMatch("adceb", "a*b")); + } +} \ No newline at end of file diff --git a/Week 08/id_063/Leetcode_49_063.java b/Week 08/id_063/Leetcode_49_063.java new file mode 100644 index 000000000..07803a4c4 --- /dev/null +++ b/Week 08/id_063/Leetcode_49_063.java @@ -0,0 +1,68 @@ +/* +思路 +把每一个字符串映射成一个统计字符频率和所有字符个数的结构,用该结构作为Map的key,Map的值 +为容纳相同的key的字符串的List,迭代每一个字符串,更新Map结构,最后把Map中所有的Value全 +放到结果的List中去即可 + */ + + +import java.util.*; + + +class Solution { + private class KeyNode implements Comparable { + int[] cnt; + int totalCharNum; + + public KeyNode(int[] cnt, int totalCharNum) { + this.cnt = cnt; + this.totalCharNum = totalCharNum; + } + + @Override + public int compareTo(KeyNode o) { + if (totalCharNum < o.totalCharNum) { + return -1; + } else if (totalCharNum > o.totalCharNum) { + return 1; + } + + for (int i = 0; i < cnt.length; i++) { + if (cnt[i] != o.cnt[i]) { + return cnt[i] - o.cnt[i]; + } + } + + return 0; + } + } + + private KeyNode string2KeyNode(String str) { + int[] arr = new int[26]; + + for (int i = 0; i < str.length(); i++) { + arr[str.charAt(i) - 'a']++; + } + + return new KeyNode(arr, str.length()); + } + + + public List> groupAnagrams(String[] strs) { + Map> map = new TreeMap<>(); + + for (String str : strs) { + KeyNode key = string2KeyNode(str); + if (!map.containsKey(key)) { + List list = new LinkedList<>(); + list.add(str); + map.put(key, list); + } else { + List list = map.get(key); + list.add(str); + } + } + + return new LinkedList<>(map.values()); + } +} \ No newline at end of file diff --git a/Week 08/id_063/Leetcode_541_063.java b/Week 08/id_063/Leetcode_541_063.java new file mode 100644 index 000000000..df853c346 --- /dev/null +++ b/Week 08/id_063/Leetcode_541_063.java @@ -0,0 +1,32 @@ +/* + +思路 +简单字符串处理 + + */ + +class Solution { + public String reverseStr(String s, int k) { + StringBuilder ret = new StringBuilder(s); + + int startPos = 0; + int endPos = 0; + while (startPos < s.length()) { + endPos = startPos + k - 1 >= s.length() ? s.length()-1 : startPos + k - 1; + + int l = startPos, r = endPos; + while (l < r) { + char t = ret.charAt(l); ret.setCharAt(l, ret.charAt(r)); ret.setCharAt(r, t); + l++; r--; + } + + startPos += 2*k; + } + + return ret.toString(); + } + + public static void main(String[] args) { + System.out.println(new Solution().reverseStr("a", 2)); + } +} diff --git a/Week 08/id_063/Leetcode_557_063.java b/Week 08/id_063/Leetcode_557_063.java new file mode 100644 index 000000000..8555101da --- /dev/null +++ b/Week 08/id_063/Leetcode_557_063.java @@ -0,0 +1,29 @@ +/* + +简单字符串处理 + + */ + +class Solution { + public String reverseWords(String s) { + String[] strs = s.split(" +"); + StringBuilder ret = new StringBuilder(); + for (int i = 0; i < strs.length; i++) { + if (strs[i].equals("")) { + continue; + } + + ret.append(new StringBuilder(strs[i].trim()).reverse().toString() + " "); + } + + if (ret.length() > 0) { + ret.deleteCharAt(ret.length() - 1); + } + + return ret.toString(); + } + + public static void main(String[] args) { + System.out.println(new Solution().reverseWords("Let's take LeetCode contest")); + } +} \ No newline at end of file diff --git a/Week 08/id_063/Leetcode_58_063.java b/Week 08/id_063/Leetcode_58_063.java new file mode 100644 index 000000000..43b2f6af1 --- /dev/null +++ b/Week 08/id_063/Leetcode_58_063.java @@ -0,0 +1,43 @@ +public class Solution { + private boolean isChar(char ch) { + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); + } + + public int lengthOfLastWord(String s) { + if (s.length() == 0) { + return 0; + } + + char[] arr = s.toCharArray(); + int i = arr.length - 1, j = 0; + while (true) { + if (i < 0) { + break; + } + + if (!isChar(arr[i])) { + i--; + continue; + } + + for (j = i-1; j >= 0; j--) { + if (isChar(arr[j])) { + continue; + } + + if (arr[j] == ' ') { + return i - j; + } else { + i = j - 1; + break; + } + } + + if (j == -1 && isChar(arr[0])) { + return i + 1; + } + } + + return 0; + } +} diff --git a/Week 08/id_063/Leetcode_5_063.java b/Week 08/id_063/Leetcode_5_063.java new file mode 100644 index 000000000..effe8aea9 --- /dev/null +++ b/Week 08/id_063/Leetcode_5_063.java @@ -0,0 +1,104 @@ +/* + +经典Manacher 算法求最长回文串长度 + + */ + + + +public class Solution { + public String longestPalindrome(String s) { + if (s.length() == 0 || s.length() == 1) { + return s; + } + + // 字符串中间插入#特殊字符,防止奇偶回文串的判断 + StringBuffer buf = new StringBuffer(); + buf.append("#"); + for (int i = 0; i < s.length(); i++) { + buf.append(s.charAt(i) + "#"); + } + String ss = buf.toString(); + + int R; // 当所有产生过的回文右边界的最大值 + int C; // 第一次产生R的时候的回文中心位置 + int[] r = new int[ss.length()]; // r[i]表示以ss[i]为中心的最长回文的半径长度 + int L; // R C 对应的回文的左边界 + + R = 0; C = 0; r[0] = 1; // 第一个字符用来初始化状态 + for (int i = 1; i < ss.length(); i++) { + if (i > R) { + // 当前点不在L R区间里面,直接进行暴力搜索 + int j1 = i, j2 = i; + while (true) { + if ((j1 < 0) || (j2 > ss.length()-1) || ss.charAt(j1) != ss.charAt(j2)) { + j1++; j2--; + break; + } + + j1--; j2++; + } + + r[i] = j2 - i + 1; + if (j2 > R) { + R = j2; C = i; + } + + continue; + } + + L = C * 2 - R; + int ii = C *2 - i; // ii 是 i关于C的镜像点 + + if (ii - r[ii] + 1 < L) { + // 镜像点的最长回文左边界落在L左边,i的最长回文半径就是从ii到L的长度 + r[i] = ii - L + 1; + } else if (ii - r[ii] + 1 > L) { + // 镜像点的最长回文左边界落在L右边,i最长回味半径就是ii的最长回文半径 + r[i] = r[ii]; + } else { + // 镜像点最长回文左边界落在L上, i最长回文半径长度至少就是ii的最长回文半径长度,但是还需要往两边扩,才能知道最长是多长 + int j1 = i - r[ii], j2 = i + r[ii]; + while (true) { + if ((j1 < 0) || (j2 > ss.length()-1) || ss.charAt(j1) != ss.charAt(j2)) { + j1++; j2--; + break; + } + + j1--; j2++; + } + + r[i] = j2 - i + 1; + if (j2 > R) { + R = j2; C = i; + } + } + } + + int maxR = Integer.MIN_VALUE; + int maxIdx = -1; + for (int i = 0; i < r.length; i++) { + if (maxR < r[i]) { + maxR = r[i]; + maxIdx = i; + } + } + + StringBuilder result = new StringBuilder(); + for (int i = maxIdx - maxR + 1; i <= maxIdx + maxR -1; ) { + if (ss.charAt(i) != '#') { + result.append(ss.charAt(i)); + i += 2; + } else { + i++; + } + } + + return result.toString(); + } + + + public static void main(String[] args) { + System.out.println(new Solution().longestPalindrome("babad")); + } +} diff --git a/Week 08/id_063/Leetcode_680_063.java b/Week 08/id_063/Leetcode_680_063.java new file mode 100644 index 000000000..3da07487a --- /dev/null +++ b/Week 08/id_063/Leetcode_680_063.java @@ -0,0 +1,40 @@ +/* + +思路 +双指针前后夹逼,如果两个指针指向的字符不同,跳跃一次,如果至多跳跃一次能够 +让两个指针相遇或者错开,说明字符串合法 + + */ + +class Solution { + + private boolean isPalindrome(String s, int start, int end) { + int l = start, r = end; + while (l < r) { + if (s.charAt(l) != s.charAt(r)) { + return false; + } + + l++; r--; + } + + return true; + } + + public boolean validPalindrome(String s) { + if (s == null) { + return false; + } + + int l = 0, r = s.length() - 1; + while (l < r) { + if (s.charAt(l) != s.charAt(r)) { + return isPalindrome(s, l+1, r) || isPalindrome(s, l, r-1); + } else { + l++; r--; + } + } + + return true; + } +} \ No newline at end of file diff --git a/Week 08/id_063/Leetcode_709_063.java b/Week 08/id_063/Leetcode_709_063.java new file mode 100644 index 000000000..db7f800e7 --- /dev/null +++ b/Week 08/id_063/Leetcode_709_063.java @@ -0,0 +1,23 @@ + +/* + +简单字符串处理 + + */ + +class Solution { + public String toLowerCase(String str) { + StringBuilder builder = new StringBuilder(); + char ch; + for (int i = 0; i < str.length(); i++) { + ch = str.charAt(i); + builder.append((ch >= 'A' && ch <= 'Z') ? (char)(ch - 'A' + 'a') : ch); + } + + return builder.toString(); + } + + public static void main(String[] args) { + System.out.println(new Solution().toLowerCase("ABCabc")); + } +} \ No newline at end of file diff --git a/Week 08/id_063/Leetcode_771_063.java b/Week 08/id_063/Leetcode_771_063.java new file mode 100644 index 000000000..2332479c8 --- /dev/null +++ b/Week 08/id_063/Leetcode_771_063.java @@ -0,0 +1,30 @@ + +/* +简单字符串处理 + */ + +import java.util.HashSet; +import java.util.Set; + +class Solution { + public int numJewelsInStones(String J, String S) { + Set s = new HashSet<>(); + + for (char ch : J.toCharArray()) { + s.add(ch); + } + + int cnt = 0; + for (char ch : S.toCharArray()) { + if (s.contains(ch)) { + cnt++; + } + } + + return cnt; + } + + public static void main(String[] args) { + System.out.println(new Solution().numJewelsInStones("aA", "aAAbbbb")); + } +} \ No newline at end of file diff --git a/Week 08/id_063/Leetcode_818_063.java b/Week 08/id_063/Leetcode_818_063.java new file mode 100644 index 000000000..4460aa863 --- /dev/null +++ b/Week 08/id_063/Leetcode_818_063.java @@ -0,0 +1,85 @@ +/* + +Dp 递推 + +DP策略有点怪异 + +首先将位置一路A移动到离终点最近的位置,要么在终点左边,要么在终点右边 +在终点左边时候,可能需要倒退k步然后再前进 + +假设target + 1 大于 m = 2^(n-1) 但是小于M = 2^(n) + +dp(i)表示到i位置最少的操作个数 + +第一次一路A先走到左侧的情况下: + steps1 = min { (n-1) + 1 + k + 1 + dp(target - [(2^(n-1) - 1) - (2^k-1)] ) } + 前n-1步 反向R一步 反向A k步 反向1步 子问题步数 + + 其中需要保证 k < n-1, 否则位置又回到一开始地方,没有意义 + +第一次一路A走到右侧的情况: + 直接掉头走,处理一个子问题即可 + + steps2 = n + 1 + dp(2^n - 1 - target) + n个A 掉头一个R 子问题步数 + + */ + + +class Solution { + + private int[] dp; + + private boolean isPowSub1(int val) { + val += 1; + return ((val & (val - 1)) == 0); + } + + private int getPowNum(int val) { + val += 1; + int pow = 0; + while (val != 0) { + pow++; + val >>= 1; + } + + return pow - 1; + } + + private int solve(int target) { + //System.out.println(target); + + if (target == 0 || dp[target] != 0) { + return dp[target]; + } + + if (isPowSub1(target)) { + dp[target] = getPowNum(target); + return dp[target]; + } + + int rBound = 1; + while (true) { + if ((1< target) + break; + rBound++; + } + + dp[target] = rBound + 1 + solve((1 << rBound) - 1 - target); + for (int k = 0; k < rBound - 1; k++) { + dp[target] = Math.min(dp[target], (rBound-1) + 1 + k + 1 + solve(target - (((1<<(rBound-1)) - 1) - ((1< s = new Stack<>(); + int lBound = 0, idx = 0; + for (int j = 0; j < matrix[0].length; j++) { + if (s.isEmpty()) { + s.add(j); + } else { + while (!s.isEmpty() && h[s.peek()] > h[j]) { + idx = s.pop(); + lBound = s.isEmpty() ? -1 : s.peek(); + maxArea = Math.max(maxArea, (j - lBound - 1) * h[idx]); + } + s.add(j); + } + } + + while (!s.isEmpty()) { + idx = s.pop(); + lBound = s.isEmpty() ? -1 : s.peek(); + maxArea = Math.max(maxArea, (matrix[0].length - lBound - 1) * h[idx]); + } + } + + return maxArea; + } + + public static void main(String[] args) { + System.out.println(new Solution().maximalRectangle(new char[][] { + {'1','0','1','0','0'}, + {'1','0','1','1','1'}, + {'1','1','1','1','1'}, + {'1','0','0','1','0'} + })); + } +} \ No newline at end of file diff --git a/Week 08/id_063/Leetcode_8_063.java b/Week 08/id_063/Leetcode_8_063.java new file mode 100644 index 000000000..a5f9aa178 --- /dev/null +++ b/Week 08/id_063/Leetcode_8_063.java @@ -0,0 +1,60 @@ +/* + +思路 +简单字符串处理 + + */ + +class Solution { + private boolean isValid(char ch) { + return ch == '-' || ch == '+' || (ch >= '0' && ch <= '9'); + } + + public int myAtoi(String str) { + if (str.length() == 0) { + return 0; + } + + int i = 0; + while (i < str.length() && str.charAt(i) == ' ') { + i++; + } + + if (i == str.length() || !isValid(str.charAt(i))) { + return 0; + } + + boolean negative = str.charAt(i) == '-'; + long val = (str.charAt(i) >= '0' && str.charAt(i) <= '9') ? str.charAt(i)-'0' : 0; + if (negative) val *= -1; + i++; + + int[] buf = new int[str.length()]; + int cnt = 0; + while (i < str.length()) { + if (!(str.charAt(i) >= '0' && str.charAt(i) <= '9')) { + break; + } + + buf[cnt++] = str.charAt(i) - '0'; + i++; + } + + for (i = 0 ; i < cnt; i++) { + val = val *10 + (negative ? -1 * buf[i] : buf[i]); + if (negative && val < Integer.MIN_VALUE) { + return Integer.MIN_VALUE; + } + + if (!negative && val > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + } + + return (int)(val); + } + + public static void main(String[] args) { + System.out.println(new Solution().myAtoi(" 3.14")); + } +} \ No newline at end of file diff --git a/Week 08/id_063/Leetcode_917_063.java b/Week 08/id_063/Leetcode_917_063.java new file mode 100644 index 000000000..d6eefce02 --- /dev/null +++ b/Week 08/id_063/Leetcode_917_063.java @@ -0,0 +1,30 @@ + +class Solution { + private boolean isDigit(char ch) { + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); + } + + public String reverseOnlyLetters(String S) { + StringBuilder ret = new StringBuilder(S); + + int l = 0, r = ret.length()-1; + while (l < r) { + if (!isDigit(ret.charAt(l))) { + l++; continue; + } + + if (!isDigit(ret.charAt(r))) { + r--; continue; + } + + char ch = ret.charAt(l); ret.setCharAt(l, ret.charAt(r)); ret.setCharAt(r, ch); + l++; r--; + } + + return ret.toString(); + } + + public static void main(String[] args) { + System.out.println(new Solution().reverseOnlyLetters("Test1ng-Leet=code-Q!")); + } +} \ No newline at end of file diff --git a/Week 08/id_063/Leetcode_91_063.java b/Week 08/id_063/Leetcode_91_063.java new file mode 100644 index 000000000..fd1aea212 --- /dev/null +++ b/Week 08/id_063/Leetcode_91_063.java @@ -0,0 +1,45 @@ +/* + +DP 算法求解 +dp[i]表示i位置结尾的字符串的编码方式个数 +显然最后一个字符i要么最为一个两位数的最后一位,要么自己作为一个一位数 +num = (s[i-1]-'0')*10 + (i-'0') 在1到26之间,就可以组成两位数 + dp[i] = dp[i-2] + dp[i-1] +其他 + dp[i] = dp[i-1] + + + */ + +class Solution { + private int char2num(char ch1, char ch2) { + return (ch1 - '0') * 10 + (ch2 - '0'); + } + + public int numDecodings(String s) { + if (s.length() == 0) { + return 0; + } + + int[] dp = new int[s.length()]; + + dp[0] = (s.charAt(0) >='1' && s.charAt(0) <='9') ? 1 : 0; + for (int i = 1; i < s.length(); i++) { + dp[i] = (s.charAt(i) >= '1' && s.charAt(i) <= '9') ? dp[i-1] : 0; + + if (s.charAt(i-1) != '0') { + int num = char2num(s.charAt(i - 1), s.charAt(i)); + if ((num >= 1) && (num <= 26)) { + if (i >= 2) { + dp[i] += dp[i - 2]; + } else { + dp[i] += 1; + } + } + } + + } + + return dp[s.length()-1]; + } +} diff --git a/Week 08/id_063/NOTE.md b/Week 08/id_063/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_063/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_068/NOTE.md b/Week 08/id_068/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_068/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_073/NOTE.md b/Week 08/id_073/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_073/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_078/LeetCode_387_078 - Copy.java b/Week 08/id_078/LeetCode_387_078 - Copy.java new file mode 100644 index 000000000..963c1212e --- /dev/null +++ b/Week 08/id_078/LeetCode_387_078 - Copy.java @@ -0,0 +1,39 @@ +//给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。 +// +// 案例: +// +// +//s = "leetcode" +//返回 0. +// +//s = "loveleetcode", +//返回 2. +// +// +// +// +// 注意事项:您可以假定该字符串只包含小写字母。 +// Related Topics 哈希表 字符串 + + +import java.util.HashMap; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int firstUniqChar(String s) { + HashMap hashMap = new HashMap(); + int n = s.length(); + for (int i = 0; i < n; i++) { + char c = s.charAt(i); + hashMap.put(c, hashMap.getOrDefault(c, 0) + 1); + } + for (int i = 0; i < n; i++) { + if (hashMap.get(s.charAt(i)) == 1) { + return i; + } + } + return -1; + } + +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_078/LeetCode_917_078.java b/Week 08/id_078/LeetCode_917_078.java new file mode 100644 index 000000000..134868765 --- /dev/null +++ b/Week 08/id_078/LeetCode_917_078.java @@ -0,0 +1,70 @@ +//给定一个字符串 S,返回 “反转后的” 字符串,其中不是字母的字符都保留在原地,而所有字母的位置发生反转。 +// +// +// +// +// +// +// 示例 1: +// +// 输入:"ab-cd" +//输出:"dc-ba" +// +// +// 示例 2: +// +// 输入:"a-bC-dEf-ghIj" +//输出:"j-Ih-gfE-dCba" +// +// +// 示例 3: +// +// 输入:"Test1ng-Leet=code-Q!" +//输出:"Qedo1ct-eeLg=ntse-T!" +// +// +// +// +// 提示: +// +// +// S.length <= 100 +// 33 <= S[i].ASCIIcode <= 122 +// S 中不包含 \ or " +// +// Related Topics 字符串 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public String reverseOnlyLetters(String S) { + if(S.length() <= 1)return S; + //左右指针 + int pL = 0; int pR = S.length() - 1; + StringBuilder sb = new StringBuilder(); + //移动指针 + while(pL < S.length() && pR >= 0){ + //如果左端不是字符 + if(!isOkchar(S.charAt(pL))){ + sb.append(S.charAt(pL)); + pL++; + //右端不是字符 + }else if(!isOkchar(S.charAt(pR))){ + pR--; + }else{ + //都是字符 + sb.append(S.charAt(pR)); + pL++; + pR--; + } + } + //指针没到头的话 + sb.append(S.substring(pL,S.length())); + return sb.toString(); + } + public boolean isOkchar(char c){ + return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_078/NOTE.md b/Week 08/id_078/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_078/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_083/Leetcode_300_083.java b/Week 08/id_083/Leetcode_300_083.java new file mode 100644 index 000000000..c765fb39d --- /dev/null +++ b/Week 08/id_083/Leetcode_300_083.java @@ -0,0 +1,29 @@ +import java.util.Arrays; + +/* + * @lc app=leetcode id=300 lang=java + * + * [300] Longest Increasing Subsequence + */ + +// @lc code=start +class Solution { + public int lengthOfLIS(int[] nums) { + + //20191207 + //复杂度O(nlogn) + int []dp = new int[nums.length]; + int len = 0; + for(int x:nums){ + int i = Arrays.binarySearch(dp,0,len,x); + if(i<0) + i = -(i+1); + dp[i] = x; + if(i == len) + len++; + } + return len; + } +} +// @lc code=end + diff --git a/Week 08/id_083/Leetcode_32_083.java b/Week 08/id_083/Leetcode_32_083.java new file mode 100644 index 000000000..28c5d963e --- /dev/null +++ b/Week 08/id_083/Leetcode_32_083.java @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode id=32 lang=java + * + * [32] Longest Valid Parentheses + */ + +// @lc code=start +class Solution { + public int longestValidParentheses(String s) { + //使用动态规划的方法 + int maxans = 0; + int dp[] = new int[s.length()]; + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) == ')') { + if (s.charAt(i - 1) == '(') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + maxans = Math.max(maxans, dp[i]); + } + } + return maxans; + } +} +// @lc code=end + diff --git a/Week 08/id_083/Leetcode_62_083.java b/Week 08/id_083/Leetcode_62_083.java new file mode 100644 index 000000000..2a2f49510 --- /dev/null +++ b/Week 08/id_083/Leetcode_62_083.java @@ -0,0 +1,55 @@ +import java.util.Arrays; + +/* + * @lc app=leetcode id=62 lang=java + * + * [62] Unique Paths + */ + +// @lc code=start +class Solution { + public int uniquePaths(int m, int n) { + // //20191207 + // //复杂度O(m*n) + // int [][]dp = new int[m][n]; + // for(int i=0;i= 1 && first <= 9) { + dp[i] += dp[i-1]; + } + if(second >= 10 && second <= 26) { + dp[i] += dp[i-2]; + } + } + return dp[n]; + } +} +// @lc code=end \ No newline at end of file diff --git a/Week 08/id_083/NOTE.md b/Week 08/id_083/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_083/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_088/NOTE.md b/Week 08/id_088/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_088/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_093/NOTE.md b/Week 08/id_093/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_093/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_098/LeetCode_115_098.java b/Week 08/id_098/LeetCode_115_098.java new file mode 100644 index 000000000..e5a2dc828 --- /dev/null +++ b/Week 08/id_098/LeetCode_115_098.java @@ -0,0 +1,28 @@ +// 官方DP解法 +class Solution { + public int numDistinct(String s, String t) { + int[][] dp = new int[t.length() + 1][s.length() + 1]; + for (int j = 0; j < s.length() + 1; j++) dp[0][j] = 1; + for (int i = 1; i < t.length() + 1; i++) { + for (int j = 1; j < s.length() + 1; j++) { + if (t.charAt(i - 1) == s.charAt(j - 1)) dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1]; + else dp[i][j] = dp[i][j - 1]; + } + } + return dp[t.length()][s.length()]; + } +} + +//改进版 +class Solution { + public int numDistinct(String s, String t) { + int m = s.length(), n = t.length(), dp[] = new int[n + 1]; + dp[0] = 1; + for(int i = 1; i <= m; ++i) { + for(int j = n; j >= 1; --j) { + if(s.charAt(i - 1) == t.charAt(j - 1)) dp[j] += dp[j - 1]; + } + } + return dp[n]; + } +} \ No newline at end of file diff --git a/Week 08/id_098/LeetCode_44_098.java b/Week 08/id_098/LeetCode_44_098.java new file mode 100644 index 000000000..c53807b16 --- /dev/null +++ b/Week 08/id_098/LeetCode_44_098.java @@ -0,0 +1,26 @@ +class Solution { + public boolean isMatch(String s, String p) { + int m=s.length(),n=p.length(),i_start=-1,j_start=-1,i=0,j=0; + while(i-1){ + j=j_start; + i=i_start+1; + } + else { + return false; + } + } + + while(j int: + if not nums: + return 0 + + n = len(nums) + dp = [1] * n + + for i in range(n): + for j in range(i): + if nums[i] > nums[j]: + dp[i] = max(dp[i], 1+dp[j]) + return max(dp) \ No newline at end of file diff --git a/Week 08/id_118/LeetCode_387_118.py b/Week 08/id_118/LeetCode_387_118.py new file mode 100644 index 000000000..3a2745f0c --- /dev/null +++ b/Week 08/id_118/LeetCode_387_118.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Created by Isaac Zhou at 12/8/19 + +class Solution: + def firstUniqChar(self, s: str) -> int: + d = {} + for l in s: + if l not in d: + d[l] = 1 + else: + d[l] += 1 + + index = -1 + for i in range(len(s)): + if d[s[i]] == 1: + index = i + break + + return index diff --git a/Week 08/id_118/LeetCode_541_118.py b/Week 08/id_118/LeetCode_541_118.py new file mode 100644 index 000000000..904ca78c3 --- /dev/null +++ b/Week 08/id_118/LeetCode_541_118.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# Created by Isaac Zhou at 12/8/19 + +class Solution: + def reverseStr(self, s: str, k: int) -> str: + if len(s) < (k): return s[::-1] + if len(s) < (2 * k): return (s[:k][::-1] + s[k:]) + return s[:k][::-1] + s[k:2 * k] + self.reverseStr(s[2 * k:], k) diff --git a/Week 08/id_118/LeetCode_91_118.py b/Week 08/id_118/LeetCode_91_118.py new file mode 100644 index 000000000..3c5a3baf5 --- /dev/null +++ b/Week 08/id_118/LeetCode_91_118.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# Created by Isaac Zhou at 12/8/19 + +class Solution: + def numDecodings(self, s): + if not s: + return 0 + + dp = [0 for x in range(len(s) + 1)] + dp[0] = 1 + dp[1] = 1 if 0 < int(s[0]) <= 9 else 0 + + for i in range(2, len(s) + 1): + if 0 < int(s[i - 1:i]) <= 9: + dp[i] += dp[i - 1] + if s[i - 2:i][0] != '0' and int(s[i - 2:i]) <= 26: + dp[i] += dp[i - 2] + return dp[len(s)] \ No newline at end of file diff --git a/Week 08/id_118/NOTE.md b/Week 08/id_118/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_118/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_123/LeetCode_242_123.c b/Week 08/id_123/LeetCode_242_123.c new file mode 100644 index 000000000..a5b95a224 --- /dev/null +++ b/Week 08/id_123/LeetCode_242_123.c @@ -0,0 +1,44 @@ +//思路:可以用一个计数器表计算 s 字母的频率,用 t 减少计数器表中的每个字母的计数器,然后检查计数器是否回到零。 +// bool isAnagram(char * s, char * t){ +// int n = strlen(s); +// int m = strlen(t); +// if(n!=m) +// return false; +// int map[26]={0}; +// for(int i=0; i count = new Dictionary(); + int n = s.Length; + for (int i = 0; i < n; i++) + { + char c = s[i]; + if (!count.ContainsKey(c)) + count[c] = 0; + count[c]++; + } + + for (int i = 0; i < n; i++) + { + if (count[s[i]] == 1) + { + return i; + } + } + return -1; + } +} \ No newline at end of file diff --git a/Week 08/id_128/NOTE.md b/Week 08/id_128/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_128/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_133/NOTE.md b/Week 08/id_133/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_133/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_133/leetcode_300_133.java b/Week 08/id_133/leetcode_300_133.java new file mode 100644 index 000000000..c8a400740 --- /dev/null +++ b/Week 08/id_133/leetcode_300_133.java @@ -0,0 +1,26 @@ + +/** + * https://leetcode-cn.com/problems/longest-increasing-subsequence/ + * 题号:300 + * 题目:最长上升子序列 + */ + +// 动态规划+二分查找 + +class Solution { + public int lengthOfLIS(int[] nums) { + int[] tails = new int[nums.length]; + int res = 0; + for(int num : nums) { + int i = 0, j = res; + while(i < j) { + int m = (i + j) / 2; + if(tails[m] < num) i = m + 1; + else j = m; + } + tails[i] = num; + if(res == j) res++; + } + return res; + } +} \ No newline at end of file diff --git a/Week 08/id_133/leetcode_91_133.java b/Week 08/id_133/leetcode_91_133.java new file mode 100644 index 000000000..0d6502a7d --- /dev/null +++ b/Week 08/id_133/leetcode_91_133.java @@ -0,0 +1,36 @@ + +/** + * https://leetcode-cn.com/problems/decode-ways/ + * 题号:91 + * 题目:解码方法 + */ + + +class Solution { + public int numDecodings5(String s) { + int len = s.length(); + int end = 1; + int cur = 0; + if (s.charAt(len - 1) != '0') { + cur = 1; + } + for (int i = len - 2; i >= 0; i--) { + if (s.charAt(i) == '0') { + end = cur;//end 前移 + cur = 0; + continue; + } + int ans1 = cur; + int ans2 = 0; + int ten = (s.charAt(i) - '0') * 10; + int one = s.charAt(i + 1) - '0'; + if (ten + one <= 26) { + ans2 = end; + } + end = cur; //end 前移 + cur = ans1 + ans2; + + } + return cur; + } +} \ No newline at end of file diff --git a/Week 08/id_138/NOTE.md b/Week 08/id_138/NOTE.md new file mode 100755 index 000000000..4f601a0f4 --- /dev/null +++ b/Week 08/id_138/NOTE.md @@ -0,0 +1,35 @@ +# NOTE +算法训练营第八周 +1 高级动态规划 + 复习动态规划实现流程 + + 1)最小重复子问题 + 2)定义dp状态转换 + 3)推导定义dp方程 + 4)根据dp方程代码实现。 + + 最短路径dp状态转换方程: + + //row 行数,col 列数 + dp[row][col] = obstacleGrid[row-1][col] + obstacleGrid[row][col-1]; + + 股票买卖dp方程: + 定义dp状态:dp(n,k,s) + n是天数,k是允许交易的最大次数(1....k),s是当前的持有状态(0=没有持有股票,1=持有股票) + + for 状态1 in 状态1所有取值: + for 状态2 in 状态2所有取值: + for 状态3 in 状态3所有取值: + //dp[状态1][状态2][状态3] = 择优(选择1,选择2,.... 选择n) + + dp[i][kk][0] = Math.max(dp[i-1][kk][0], dp[i-1][kk][1]+prices[i]);//第i天未持有股票的最大获利 + dp[i][kk][1] = Math.max(dp[i-1][kk][1], dp[i-1][kk-1][0]-prices[i]);//第i天持有股票的最大获利 + +2 字符串算法 + java/python :字符串不可变性,线程安全的。字符串的修改操作会产生一个新的字符串对象。 + C/c++:字符串可变性。 + 字符串遍历 + 字符串子串问题 + 字符串匹配 + Rabin-Karp 缓存及特殊哈希算法 + KMP 通过计算prefix table方式加速向后移动 diff --git a/Week 08/id_138/study/week08/LeetCode_151_138.java b/Week 08/id_138/study/week08/LeetCode_151_138.java new file mode 100644 index 000000000..f49e7b5ae --- /dev/null +++ b/Week 08/id_138/study/week08/LeetCode_151_138.java @@ -0,0 +1,21 @@ +package study.week08; + +/** + * 翻转字符串里的单词 + * @author Lukas + * @since 2019/12/8 20:01 + **/ +public class LeetCode_151_138 { + + public String reverseWords(String s) { + String[] words = s.trim().split(" "); + StringBuilder sb = new StringBuilder(); + for (int i=words.length-1;i>=0;i--){ + if (!words[i].equals("")) { + sb.append(words[i]).append(" "); + } + } + String temp = sb.toString().trim(); + return temp; + } +} diff --git a/Week 08/id_138/study/week08/LeetCode_300_138.java b/Week 08/id_138/study/week08/LeetCode_300_138.java new file mode 100644 index 000000000..fc60f392e --- /dev/null +++ b/Week 08/id_138/study/week08/LeetCode_300_138.java @@ -0,0 +1,28 @@ +package study.week08; + +/** + * 最长上升子序列 + * @author Lukas + * @since 2019/12/8 18:31 + **/ +public class LeetCode_300_138 { + public int lengthOfLIS(int[] nums) { + if(nums == null || nums.length==0) + return 0; + //动态方程:dp[i]=max(dp[j])+1,0≤j nums[j]) {//判断是否升序 + temp = Math.max(temp,dp[j]); + } + } + dp[i] = temp +1; + max = Math.max(max,dp[i]); + } + return max; + } +} diff --git a/Week 08/id_138/study/week08/LeetCode_32_138.java b/Week 08/id_138/study/week08/LeetCode_32_138.java new file mode 100644 index 000000000..5476915cf --- /dev/null +++ b/Week 08/id_138/study/week08/LeetCode_32_138.java @@ -0,0 +1,27 @@ +package study.week08; + +/** + * 最长有效括号 + * @author Lukas + * @since 2019/12/8 18:45 + **/ +public class LeetCode_32_138 { + + public int longestValidParentheses(String s) { + int max = 0; + int len = s.length(); + int dp[] = new int[s.length()];//数组初始值全部为0, + for (int i = 1; i < len; i++) { + if (s.charAt(i) == ')') { + if (s.charAt(i - 1) == '(') { + dp[i] = (i >= 2?dp[i-2]:0)+2;//动态方程1 + } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i-1]+((i-dp[i-1])>=2?dp[i-dp[i-1]-2]:0) +2;//动态方程2 + } + max = Math.max(max,dp[i]); + } + } + return max; + } + +} diff --git a/Week 08/id_138/study/week08/LeetCode_387_138.java b/Week 08/id_138/study/week08/LeetCode_387_138.java new file mode 100644 index 000000000..63e416d54 --- /dev/null +++ b/Week 08/id_138/study/week08/LeetCode_387_138.java @@ -0,0 +1,26 @@ +package study.week08; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Lukas + * @since 2019/12/8 18:53 + **/ +public class LeetCode_387_138 { + public int firstUniqChar(String s) { + Map map = new HashMap<>(); + int n = s.length(); + for (int i = 0; i < n; i++) {//初始化map,计算每个字符出现的次数 + char c = s.charAt(i); + map.put(c,map.getOrDefault(c,0)+1); + } + for (int i = 0; i < n; i++) { + if (map.get(s.charAt(i)) == 1) { + return i; + } + } + return -1; + } + +} diff --git a/Week 08/id_138/study/week08/LeetCode_541_138.java b/Week 08/id_138/study/week08/LeetCode_541_138.java new file mode 100644 index 000000000..39269b7fb --- /dev/null +++ b/Week 08/id_138/study/week08/LeetCode_541_138.java @@ -0,0 +1,22 @@ +package study.week08; + +/** + * 反转字符串II + * @author Lukas + * @since 2019/12/8 18:58 + **/ +public class LeetCode_541_138 { + + public String reverseStr(String s, int k) { + char[] a = s.toCharArray(); + for (int start = 0; start < s.length(); start+=2*k) {//每一步遍历2*k个 + int i = start, j = Math.min(start + k - 1, a.length - 1); + while (i < j) {//处理2*k个元素 + char c = a[i];//辅助数组,用来翻转 s 的一半字符 + a[i++] = a[j]; + a[j--] = c; + } + } + return new String(a); + } +} diff --git a/Week 08/id_138/study/week08/LeetCode_8_138.java b/Week 08/id_138/study/week08/LeetCode_8_138.java new file mode 100644 index 000000000..e901bdaf1 --- /dev/null +++ b/Week 08/id_138/study/week08/LeetCode_8_138.java @@ -0,0 +1,37 @@ +package study.week08; + +/** + * 字符串转为数字 + * @author Lukas + * @since 2019/12/8 20:24 + **/ +public class LeetCode_8_138 { + public int myAtoi(String str) { + int index = 0; + int sign = 1; + int total = 0; + + if (str.length() == 0 || str.equals(" ") || str.trim().length()==0) + return 0; + while (str.charAt(index) == ' ' && index < str.length()) { + index++; + } + if (str.charAt(index) == '+' || str.charAt(index) == '-'){ + sign = str.charAt(index)=='+'?1:-1; + index++; + } + while (index < str.length()) { + int digit = str.charAt(index)-'0'; + if (digit<0 || digit>9) + break; + if (Integer.MAX_VALUE / 10 < total || Integer.MAX_VALUE / 10 == total + && Integer.MAX_VALUE % 10 < digit) { + return sign == 1?Integer.MAX_VALUE:Integer.MIN_VALUE; + } + total = 10*total+digit; + index++; + } + return total*sign; + } + +} diff --git a/Week 08/id_143/LeetCode_115_143.java b/Week 08/id_143/LeetCode_115_143.java new file mode 100644 index 000000000..76b1f7e7b --- /dev/null +++ b/Week 08/id_143/LeetCode_115_143.java @@ -0,0 +1,48 @@ +/* + * @lc app=leetcode.cn id=115 lang=java + * + * [115] 不同的子序列 + */ +/* +动态规划解决 +dp(i, j) 表示s[0:i] 中选出的子序列,包含t[0:j] 的个数 +若s[i] != t[j] + dp(i, j) = dp(i-1, j) +s[i] == t[j] + dp(i, j) = dp(i-1, j-1) + dp(i-1, j) + */ + +// @lc code=start +class Solution { + public int numDistinct(String s, String t) { + if (s.length() == 0) { + return t.length() == 0 ? 1 : 0; + } + + if (t.length() == 0) { + return 0; + } + int sN = s.length(); + int tN = t.length(); + int[][] dp = new int[sN][tN]; + + dp[0][0] = s.charAt(0) == t.charAt(0) ? 1 : 0; + for (int i = 1; i < sN; i++) { + dp[i][0] = s.charAt(i) == t.charAt(0) ? dp[i-1][0] + 1 : dp[i-1][0]; + + for (int j = 1; j <= i && j < tN; j++) { + if (t.charAt(j) != s.charAt(i)) { + dp[i][j] = j <= i-1 ? dp[i-1][j] : 0; + } else { + dp[i][j] += (j-1 <= i-1 && j-1 >= 0) ? dp[i-1][j-1] : 0; + dp[i][j] += (j <= i-1) ? dp[i-1][j] : 0; + } + } + } + + return dp[sN-1][tN-1]; + } + +} +// @lc code=end + diff --git a/Week 08/id_143/LeetCode_125_143.java b/Week 08/id_143/LeetCode_125_143.java new file mode 100644 index 000000000..37245d30b --- /dev/null +++ b/Week 08/id_143/LeetCode_125_143.java @@ -0,0 +1,56 @@ +/* + * @lc app=leetcode.cn id=125 lang=java + * 双指针法 + * [125] 验证回文串 + */ + +// @lc code=start +class Solution { + private boolean isCharOrNum(char ch) { + return ((ch >= 'a') && (ch <= 'z')) || + ((ch >= 'A') && (ch <= 'Z')) || + ((ch >= '0') && (ch <= '9')); + } + + private boolean isChar(char ch) { + return ((ch >= 'a') && (ch <= 'z')) || + ((ch >= 'A') && (ch <= 'Z')); + } + + public boolean isPalindrome(String s) { + if (s == null) { + return false; + } + + if ((s.length() == 0) || (s.length() == 1)) { + return true; + } + + int i = 0, j = s.length() - 1; + char ch1, ch2; + while (i < j) { + ch1 = s.charAt(i); ch2 = s.charAt(j); + if (!isCharOrNum(ch1)) { + i++; + continue; + } + + if (!isCharOrNum(ch2)) { + j--; + continue; + } + + if (ch1 != ch2) { + if ( ! (isChar(ch1) && isChar(ch2) && Math.abs(ch1-ch2) == Math.abs('A' - 'a')) ) { + return false; + } + } + + i++; j--; + } + + return true; + } +} +// @lc code=end + diff --git a/Week 08/id_143/LeetCode_151_143.java b/Week 08/id_143/LeetCode_151_143.java new file mode 100644 index 000000000..b4415b366 --- /dev/null +++ b/Week 08/id_143/LeetCode_151_143.java @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode.cn id=151 lang=java + * + * [151] 翻转字符串里的单词 + */ + +// @lc code=start +class Solution { + public String reverseWords(String s) { + + String[] strs = s.split(" +"); + StringBuffer result = new StringBuffer(); + for(int i=strs.length -1 ;i>=0;i--){ + if(!strs[i].equals("")){ + result.append(strs[i].trim()+" "); + } + + } + if(result.length() > 0){ + result.deleteCharAt(result.length()-1); + } + return result.toString(); + + } +} +// @lc code=end + diff --git a/Week 08/id_143/NOTE.md b/Week 08/id_143/NOTE.md new file mode 100755 index 000000000..41b5fc1c0 --- /dev/null +++ b/Week 08/id_143/NOTE.md @@ -0,0 +1,25 @@ +# 算法训练营学习 +## 第八周 +### 第十九课 高级动态规划 + +#### 复习递归、分治、动态规划的区别 + - 递归:函数自己调用自己(Terminator, process drill down reverse state) + - 分治(也会用递归):分而治之。 + - 动态规划(Dynamic Programming) + - “Simplifying a complicated problem by breaking it down into simpler sub-problems”(in a recursive manner) + - Divide & Conquer(分治) + Optimal substructure(最优子结构) + +#### 做好动态方程的推理后的优化 + - 缓存 + - 顺推 + +### 第二十课 字符串动态规划 + - 思路同19课。 + - Rabin-Karp 缓存及特殊哈希算法 + - KMP 通过计算prefix table方式加速向后移动 +## 个人感悟 + - **课程学习计划问题**: 从原来的7周多加了一周,本周感觉阔然开朗了一样。同样与工作冲突得比较严重。 + - **练习问题**: 见到很多同学在最后一节打卡上说,会好好学习做题。把过去的都捡起来。我同样希望如此。但我对此深表怀疑。因为大家在这样良好的氛围下,都抽不出太多时间。当工作任务一紧急,更是没有时间了。所以在这个点需要更加加强练习。在年底的这个记忆周期里内,完成固化计划。正好有了一定的基础,日练一两题。 + + + diff --git a/Week 08/id_148/NOTE.md b/Week 08/id_148/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_148/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_153/LeetCode_151_153.js b/Week 08/id_153/LeetCode_151_153.js new file mode 100644 index 000000000..f7463aea6 --- /dev/null +++ b/Week 08/id_153/LeetCode_151_153.js @@ -0,0 +1,7 @@ +/** + * @param {string} s + * @return {string} + */ +var reverseWords = function(s) { + return s.split(' ').filter(item => item !== '').reverse().join(' ') +}; \ No newline at end of file diff --git a/Week 08/id_153/LeetCode_387_153.js b/Week 08/id_153/LeetCode_387_153.js new file mode 100644 index 000000000..4d724764b --- /dev/null +++ b/Week 08/id_153/LeetCode_387_153.js @@ -0,0 +1,30 @@ +/** + * @param {string} s + * @return {number} + */ +var firstUniqChar = function(s) { + const map = new Map(); + for (let i = 0; i < s.length; i++) { + if (map.has(s[i])) { + map.set(s[i], map.get(s[i]) + 1) + } else { + map.set(s[i], 1) + } + } + + for (let i = 0; i < s.length; i++) { + if (map.get(s[i]) === 1) { + return i; + } + } + return -1; +}; + +var firstUniqChar1 = function(s) { + for (let i = 0; i < s.length; i++) { + if (s.indexOf(s[i]) === s.lastIndexOf(s[i])) { + return i; + } + } + return -1; +} \ No newline at end of file diff --git a/Week 08/id_153/LeetCode_557_153.js b/Week 08/id_153/LeetCode_557_153.js new file mode 100644 index 000000000..d97b2f460 --- /dev/null +++ b/Week 08/id_153/LeetCode_557_153.js @@ -0,0 +1,7 @@ +/** + * @param {string} s + * @return {string} + */ +var reverseWords = function(s) { + return s.split(' ').map(item => item.split('').reverse().join('')).join(' ') +}; \ No newline at end of file diff --git a/Week 08/id_153/NOTE.md b/Week 08/id_153/NOTE.md new file mode 100755 index 000000000..ca892f5f7 --- /dev/null +++ b/Week 08/id_153/NOTE.md @@ -0,0 +1,60 @@ +# 总结 + +## 高级动态规划 +1. 递归 - 函数自己调用自己 +2. 分治 + +* 人肉递归低效、很累 +* 找到最近最简方法,将其拆解成可重复解决的问题 +* 数学归纳法思维 +* 寻找重复性 --> 计算机指令集 + +### DP 顺推模板 +```python +function DP(): + dp = [][] // 二维情况 + + for i = 0 .. M { + for j = 0 .. N { + dp[i][j] = _Function(dp[i'][j']...) + } + } + return dp[M][N]; +``` + +关键点 +* 动态规划和递归或者分治没有根本上的区别(关键看有无最优的子结构) +* 拥有共性:找到重复子问题 +* 差异性:最优子结构、中途可以淘汰次优解 + +## 字符串匹配算法 +1. 暴力法 + ```java + public static int forcceSearch(String txt, String pat) { + int M = txt.length(); + int N = pat.length(); + + for (int i = 0; i <= M - N; i++) { + int j; + for (j = 0; j < N; j++) { + if (txt.charAt(i + j) != pat.charAt(j)) { + break; + } + } + if (j == N) { + return i; + } + // 更加聪明? + // 1. 预先判断 - hash(txt.substring(i, M)) == hash(pat) + // 2. KMP + } + return -1; + } + ``` +2. Rabin-Karp 算法 + * 假设子串的长度为 M(pat),目标字符串的长度为 N(txt) + * 计算子串的 hash 值 hash_pat + * 计算目标字符串 txt 中每个长度为 M 的子串的 hash 值 (共需要计算 N-M+1 次) + * 比较 hash 值:如果 hash 值不同,字符串必然不匹配;如果 hash 值相同,还需要使用朴素算法再次判断 +3. KMP 算法 + 当子串与目标字符串不匹配时,其实你已经知道了前面已经陪陪成功那一部分的字符(包括子串与目标字符串)。KMP 算法的想法是,设法利用这个一直心系,不要把“搜索位置”一会已经比较过的位置,继续把它向后移,这样就提高了效率。 \ No newline at end of file diff --git "a/Week 08/id_158/115.\344\270\215\345\220\214\347\232\204\345\255\220\345\272\217\345\210\227.cs" "b/Week 08/id_158/115.\344\270\215\345\220\214\347\232\204\345\255\220\345\272\217\345\210\227.cs" new file mode 100644 index 000000000..47926dd60 --- /dev/null +++ "b/Week 08/id_158/115.\344\270\215\345\220\214\347\232\204\345\255\220\345\272\217\345\210\227.cs" @@ -0,0 +1,26 @@ +/* + * @lc app=leetcode.cn id=115 lang=csharp + * + * [115] 不同的子序列 + */ + +// @lc code=start +public class Solution +{ + public int NumDistinct(string s, string t) + { + int[,] dp = new int[t.Length + 1, s.Length + 1]; + for (int j = 0; j < s.Length + 1; j++) dp[0, j] = 1; + for (int i = 1; i < t.Length + 1; i++) + { + for (int j = 1; j < s.Length + 1; j++) + { + if (t[i - 1] == s[j - 1]) dp[i, j] = dp[i - 1, j - 1] + dp[i, j - 1]; + else dp[i, j] = dp[i, j - 1]; + } + } + return dp[t.Length, s.Length]; + } +} +// @lc code=end + diff --git "a/Week 08/id_158/300.\346\234\200\351\225\277\344\270\212\345\215\207\345\255\220\345\272\217\345\210\227.cs" "b/Week 08/id_158/300.\346\234\200\351\225\277\344\270\212\345\215\207\345\255\220\345\272\217\345\210\227.cs" new file mode 100644 index 000000000..d042d5e81 --- /dev/null +++ "b/Week 08/id_158/300.\346\234\200\351\225\277\344\270\212\345\215\207\345\255\220\345\272\217\345\210\227.cs" @@ -0,0 +1,40 @@ +/* + * @lc app=leetcode.cn id=300 lang=csharp + * + * [300] 最长上升子序列 + */ + +// @lc code=start +public class Solution +{ + public class Solution + { + public int LengthOfLIS(int[] nums) + { + if (nums.Length == 0) + { + return 0; + } + int[] dp = new int[nums.Length]; + dp[0] = 1; + int maxans = 1; + for (int i = 1; i < dp.Length; i++) + { + int maxval = 0; + for (int j = 0; j < i; j++) + { + if (nums[i] > nums[j]) + { + maxval = Math.Max(maxval, dp[j]); + } + } + dp[i] = maxval + 1; + maxans = Math.Max(maxans, dp[i]); + } + return maxans; + } + + } +} +// @lc code=end + diff --git "a/Week 08/id_158/344.\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.cs" "b/Week 08/id_158/344.\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.cs" new file mode 100644 index 000000000..bed2af75d --- /dev/null +++ "b/Week 08/id_158/344.\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.cs" @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode.cn id=300 lang=csharp + * + * [300] 最长上升子序列 + */ + +// @lc code=start +public class Solution +{ + public class Solution + { + public void ReverseString(char[] s) + { + int n = s.Length; + char tmp; + for (int i = 0; i < n >> 1; i++) + { + tmp = s[i]; + s[i] = s[n - 1 - i]; + s[n - 1 - i] = tmp; + } + } + + } +} +// @lc code=end + diff --git "a/Week 08/id_158/49.\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204.cs" "b/Week 08/id_158/49.\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204.cs" new file mode 100644 index 000000000..6e179ed4e --- /dev/null +++ "b/Week 08/id_158/49.\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204.cs" @@ -0,0 +1,60 @@ +/* + * @lc app=leetcode.cn id=49 lang=csharp + * + * [49] 字母异位词分组 + */ + +// @lc code=start +public class Solution { + public IList> GroupAnagrams(string[] strs) + { + int len = strs.Length; + IList> result = new List>(); + Dictionary> map = new Dictionary>(); + foreach (var str in strs) + { + char[] arrStr = str.ToCharArray(); + Array.Sort(arrStr); + string strSort = new string(arrStr); + if (map.ContainsKey(strSort)) + { + map[strSort].Add(str); + } + else + { + map.Add(strSort, new List { str }); + } + } + foreach (var item in map) + { + result.Add(item.Value); + } + return result; + } + + public IList> GroupAnagrams(string[] strs) + { + int len = strs.Length; + IList> result = new List>(); + Dictionary> map = new Dictionary>(); + foreach (var str in strs) + { + char[] arrStr = str.ToCharArray(); + Array.Sort(arrStr); + string strSort = new string(arrStr); + if (!map.ContainsKey(strSort)) + { + map.Add(strSort, new List()); + + } + map[strSort].Add(str); + } + foreach (var item in map) + { + result.Add(item.Value); + } + return result; + } +} +// @lc code=end + diff --git "a/Week 08/id_158/541.\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262 II.cs" "b/Week 08/id_158/541.\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262 II.cs" new file mode 100644 index 000000000..eda93b555 --- /dev/null +++ "b/Week 08/id_158/541.\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262 II.cs" @@ -0,0 +1,23 @@ +/* + * @lc app=leetcode.cn id=541 lang=csharp + * + * [541] 反转字符串 II + */ + +// @lc code=start +using System; + +public class Solution +{ + public void ReverseArr(char[] arr, int from, int end) + { + for (int i = from, j = end; i < j; i++, j--) + { + char t = arr[i]; + arr[i] = arr[j]; + arr[j] = t; + } + } +} +// @lc code=end + diff --git a/Week 08/id_158/NOTE.md b/Week 08/id_158/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_158/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_163/NOTE.md b/Week 08/id_163/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_163/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_168/NOTE.md b/Week 08/id_168/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_168/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_173/LeetCode_300_173.cpp b/Week 08/id_173/LeetCode_300_173.cpp new file mode 100644 index 000000000..8fd8abee3 --- /dev/null +++ b/Week 08/id_173/LeetCode_300_173.cpp @@ -0,0 +1,35 @@ +/* + * 300. 最长上升子序列 + */ + +class Solution { +public: + int lengthOfLIS(vector& nums) { + if(nums.empty()) + return 0; + + int end = 0; + vector temp; + temp.push_back(nums[0]); + + for(int i=1; i temp[end]) { + temp.push_back(nums[i]); + end++; + } + else { + int left=0, right=end; + while(left < right) { + int mid = ((right-left)>>1) + left; + if(temp[mid] < nums[i]) + left = mid+1; + else + right = mid; + } + temp[left] = nums[i]; + } + } + + return end+1; + } +}; \ No newline at end of file diff --git a/Week 08/id_173/LeetCode_32_173.cpp b/Week 08/id_173/LeetCode_32_173.cpp new file mode 100644 index 000000000..b36ba0dd6 --- /dev/null +++ b/Week 08/id_173/LeetCode_32_173.cpp @@ -0,0 +1,27 @@ +/* + * 32. 最长有效括号 + */ + +class Solution { +public: + int longestValidParentheses(string s) { + if(s.empty() || s.size()<2) + return 0; + + int maxLength = 0; + vector dp(s.size(), 0); + + for(int i=1; i=0 ? dp[i-2] : 0) + 2; + else if(i-dp[i-1]-1>=0 && s[i-dp[i-1]-1]=='(') + dp[i] = dp[i-1] + (i-dp[i-1]-2>=0 ? dp[i-dp[i-1]-2] : 0) + 2; + + maxLength = max(maxLength, dp[i]); + } + } + + return maxLength; + } +}; \ No newline at end of file diff --git a/Week 08/id_173/LeetCode_387_173.cpp b/Week 08/id_173/LeetCode_387_173.cpp new file mode 100644 index 000000000..270e91735 --- /dev/null +++ b/Week 08/id_173/LeetCode_387_173.cpp @@ -0,0 +1,23 @@ +/* + * 387. 字符串中的第一个唯一字符 + */ + +class Solution { +public: + int firstUniqChar(string s) { + if(s.empty()) + return -1; + + vector hashTable(26, 0); + + for(int i=0; i>& matrix) { + int rows = matrix.size(); + if(rows == 0) return 0; + int cols = matrix[0].size(); + + int maxArea = 0; + + vector height(cols, 0); + vector left(cols, 0); + vector right(cols, cols); + + for(int i=0; i=0; --j) { + if(matrix[i][j] == '1') + right[j] = min(right[j], curRight); + else { + right[j] = cols; + curRight = j; + } + } + + for(int j=0; j9) break; + + if(res>INT_MAX/10 || res==INT_MAX/10 && digit>INT_MAX%10) + return (sign==1) ? INT_MAX : INT_MIN; + + res = res*10 + digit; + index++; + } + + return res*sign; + } +}; \ No newline at end of file diff --git a/Week 08/id_173/LeetCode_91_173.cpp b/Week 08/id_173/LeetCode_91_173.cpp new file mode 100644 index 000000000..69d9b0ce4 --- /dev/null +++ b/Week 08/id_173/LeetCode_91_173.cpp @@ -0,0 +1,30 @@ +/* + * 91. 解码方法 + */ + +class Solution { +public: + int numDecodings(string s) { + if(s[0] == '0') + return 0; + + int prev=1, cur=1; + + for(int i=1; i='1' && s[i]<='6')) + cur = cur + prev; + + prev = temp; + } + + return cur; + } +}; \ No newline at end of file diff --git a/Week 08/id_173/NOTE.md b/Week 08/id_173/NOTE.md new file mode 100755 index 000000000..e2144df3c --- /dev/null +++ b/Week 08/id_173/NOTE.md @@ -0,0 +1,20 @@ +## 一、动态规划 +#### 1. 概述 +  动态规划与递归、分治并没有根本上的区别,都是寻找重复子问题,但差异在于它存在最优子结构,一般用来解决最大、最小、最多或最少的问题,在解题的过程中需要不断地去除次优解,只保留最优解即可。
+#### 2. 解题三部曲 +> (1)寻找最优子结构
+> (2)定义状态数组
+> (3)写出状态转移方程
+#### 3. 高阶的 DP 问题 +> (1)状态拥有更多维度(二维、三维、或者更多、甚至需要压缩)
+> (2)状态方程更加复杂
+ +## 二、字符串匹配算法 +#### 1. 暴力法(brute force) +#### 2. Rabin-Karp 算法 +> 算法的思想:
+> (1)假设子串的长度为 M (pat),目标字符串的长度为 N (txt)
+> (2)计算子串的 hash 值 hash_pat
+> (3)计算目标字符串 txt 中每个长度为 M 的子串的 hash 值(共需要计算 N-M+1次)
+> (4)比较 hash 值:如果 hash 值不同,字符串必然不匹配; 如果 hash 值相同,还需要使用朴素算法再次判断
+#### 3. [KMP 算法](http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html) diff --git a/Week 08/id_178/NOTE.md b/Week 08/id_178/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_178/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_183/Leetcode_05_183.cpp b/Week 08/id_183/Leetcode_05_183.cpp new file mode 100644 index 000000000..2471a30aa --- /dev/null +++ b/Week 08/id_183/Leetcode_05_183.cpp @@ -0,0 +1,43 @@ +/* + * @lc app=leetcode id=5 lang=cpp + * + * [5] Longest Palindromic Substring + */ + +// @lc code=start +class Solution +{ +public: + string longestPalindrome(string s) + { + //判断空字符串的情况 + if (s == "") + { + return ""; + } + string result(""); + int sSize = int(s.size()); + vector store(sSize, false); + int start = 0, end = 0, maxLen = 0; + //动态规划 store[i] 从上层向下层循环 + for (int j = 0; j < sSize; j++) + { + for (int i = 0; i <= j; i++) + { + //长度为1,2的情况单独考虑 + store[i] = (s[i] == s[j] && (j - i < 3 || store[i + 1])); + if (store[i] && (j - i + 1) > maxLen) + { + maxLen = j - i + 1; + start = i; + end = j; + } + } + } + + result = s.substr(start, end - start + 1); + return result; + } +}; +// @lc code=end + diff --git a/Week 08/id_183/Leetcode_300_183.cpp b/Week 08/id_183/Leetcode_300_183.cpp new file mode 100644 index 000000000..ef9ecaf47 --- /dev/null +++ b/Week 08/id_183/Leetcode_300_183.cpp @@ -0,0 +1,46 @@ +/* + * @lc app=leetcode id=300 lang=cpp + * + * [300] Longest Increasing Subsequence + */ + +// @lc code=start +class Solution { +public: + int lengthOfLIS(vector &nums) { + int len = nums.size(); + if (len < 2) { + return len; + } + + vector tail; + tail.push_back(nums[0]); + // tail 结尾的那个索引 + int end = 0; + + for (int i = 1; i < len; ++i) { + if (nums[i] > tail[end]) { + tail.push_back(nums[i]); + end++; + } else { + int left = 0; + int right = end; + while (left < right) { + int mid = (left + right) >> 1; + if (tail[mid] < nums[i]) { + left = mid + 1; + } else { + right = mid; + } + } + tail[left] = nums[i]; + } + } + return end + 1; + } +}; + + + +// @lc code=end + diff --git a/Week 08/id_183/NOTE.md b/Week 08/id_183/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_183/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_188/NOTE.md b/Week 08/id_188/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_188/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_188/leetcod_771_188.go b/Week 08/id_188/leetcod_771_188.go new file mode 100644 index 000000000..b53bb8054 --- /dev/null +++ b/Week 08/id_188/leetcod_771_188.go @@ -0,0 +1,43 @@ +//给定字符串J 代表石头中宝石的类型,和字符串 S代表你拥有的石头。 S 中每个字符代表了一种你拥有的石头的类型,你想知道你拥有的石头中有多少是宝石。 +// +// J 中的字母不重复,J 和 S中的所有字符都是字母。字母区分大小写,因此"a"和"A"是不同类型的石头。 +// +// 示例 1: +// +// 输入: J = "aA", S = "aAAbbbb" +//输出: 3 +// +// +// 示例 2: +// +// 输入: J = "z", S = "ZZ" +//输出: 0 +// +// +// 注意: +// +// +// S 和 J 最多含有50个字母。 +// J 中的字符不重复。 +// +// Related Topics 哈希表 + +//leetcode submit region begin(Prohibit modification and deletion) +package main + +func numJewelsInStones(J string, S string) int { + counter := 0 + cache := make(map[rune]bool, len(J)) + for _, value := range J { + cache[value] = true + } + for _, value := range S { + if !cache[value] { + continue + } + counter++ + } + return counter +} + +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_188/leetcode_387_188.go b/Week 08/id_188/leetcode_387_188.go new file mode 100644 index 000000000..f94184fc4 --- /dev/null +++ b/Week 08/id_188/leetcode_387_188.go @@ -0,0 +1,34 @@ +//给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。 +// +// 案例: +// +// +//s = "leetcode" +//返回 0. +// +//s = "loveleetcode", +//返回 2. +// +// +// +// +// 注意事项:您可以假定该字符串只包含小写字母。 +// Related Topics 哈希表 字符串 + +//leetcode submit region begin(Prohibit modification and deletion) +package main + +func firstUniqChar(s string) int { + cache := [256]int{} + for _, value := range s { + cache[value]++ + } + for index, value := range s { + if cache[value] == 1 { + return index + } + } + return -1 +} + +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_188/leetcode_58_188.go b/Week 08/id_188/leetcode_58_188.go new file mode 100644 index 000000000..03620c958 --- /dev/null +++ b/Week 08/id_188/leetcode_58_188.go @@ -0,0 +1,31 @@ +//给定一个仅包含大小写字母和空格 ' ' 的字符串,返回其最后一个单词的长度。 +// +// 如果不存在最后一个单词,请返回 0 。 +// +// 说明:一个单词是指由字母组成,但不包含任何空格的字符串。 +// +// 示例: +// +// 输入: "Hello World" +//输出: 5 +// +// Related Topics 字符串 + +//leetcode submit region begin(Prohibit modification and deletion) +package main + +func lengthOfLastWord(s string) int { + counter := 0 + for i := len(s) - 1; i >= 0; i-- { + str := string(s[i]) + if str != " " { + counter++ + } + if counter > 0 && str == " " { + break + } + } + return counter +} + +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_188/leetcode_709_188.go b/Week 08/id_188/leetcode_709_188.go new file mode 100644 index 000000000..462d19a26 --- /dev/null +++ b/Week 08/id_188/leetcode_709_188.go @@ -0,0 +1,39 @@ +//实现函数 ToLowerCase(),该函数接收一个字符串参数 str,并将该字符串中的大写字母转换成小写字母,之后返回新的字符串。 +// +// +// +// 示例 1: +// +// +//输入: "Hello" +//输出: "hello" +// +// 示例 2: +// +// +//输入: "here" +//输出: "here" +// +// 示例 3: +// +// +//输入: "LOVELY" +//输出: "lovely" +// +// Related Topics 字符串 + +//leetcode submit region begin(Prohibit modification and deletion) +package main + +func toLowerCase(str string) string { + var ret string + for _, value := range str { + if value >= 'A' && value <= 'Z' { + value += 32 + } + ret += string(value) + } + return ret +} + +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_188/leetcode_72_188.go b/Week 08/id_188/leetcode_72_188.go new file mode 100644 index 000000000..2878a0939 --- /dev/null +++ b/Week 08/id_188/leetcode_72_188.go @@ -0,0 +1,81 @@ +//给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。 +// +// 你可以对一个单词进行如下三种操作: +// +// +// 插入一个字符 +// 删除一个字符 +// 替换一个字符 +// +// +// 示例 1: +// +// 输入: word1 = "horse", word2 = "ros" +//输出: 3 +//解释: +//horse -> rorse (将 'h' 替换为 'r') +//rorse -> rose (删除 'r') +//rose -> ros (删除 'e') +// +// +// 示例 2: +// +// 输入: word1 = "intention", word2 = "execution" +//输出: 5 +//解释: +//intention -> inention (删除 't') +//inention -> enention (将 'i' 替换为 'e') +//enention -> exention (将 'n' 替换为 'x') +//exention -> exection (将 'n' 替换为 'c') +//exection -> execution (插入 'u') +// +// Related Topics 字符串 动态规划 + +//leetcode submit region begin(Prohibit modification and deletion) +package main + +func minDistance(word1 string, word2 string) int { + if len(word1) == 0 { + return len(word2) + } + + word1Len, word2Len := len(word1), len(word2) + dp := make([][]int, word1Len+1) + + for index, _ := range dp { + dp[index] = make([]int, word2Len+1) + dp[index][0] = index + } + + for index, _ := range dp[0] { + dp[0][index] = index + } + + for i := 1; i <= word1Len; i++ { + for j := 1; j <= word2Len; j++ { + if word1[i-1] == word2[j-1] { + dp[i][j] = dp[i-1][j-1] + } else { + dp[i][j] = min( + dp[i-1][j], + dp[i][j-1], + dp[i-1][j-1]) + 1 + } + } + } + + return dp[word1Len][word2Len] +} + +func min(value1, value2, value3 int) int { + ret := value1 + if ret > value2 { + ret = value2 + } + if ret > value3 { + ret = value3 + } + return ret +} + +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_193/NOTE.md b/Week 08/id_193/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_193/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_198/LeetCode_58_198.go b/Week 08/id_198/LeetCode_58_198.go new file mode 100644 index 000000000..64e1f5b89 --- /dev/null +++ b/Week 08/id_198/LeetCode_58_198.go @@ -0,0 +1,17 @@ +package leetcode + +func lengthOfLastWord(s string) int { + + x := 0 + chars := []rune(s) + for i := 0; i < len(chars); i++ { + + if chars[i] == ' ' { + + x = 0 + continue + } + x++ + } + return x +} diff --git a/Week 08/id_198/LeetCode_709_198.go b/Week 08/id_198/LeetCode_709_198.go new file mode 100644 index 000000000..0a230d572 --- /dev/null +++ b/Week 08/id_198/LeetCode_709_198.go @@ -0,0 +1,15 @@ +package leetcode + +//大写变小写、小写变大写 : ASCII码 ^= 32 +//大写变小写、小写变小写 : ASCII码 |= 32 +//小写变大写、大写变大写 : ASCII码 &= -33 + +func toLowerCase(str string) string { + + chars := []rune(str) + for i := 0; i < len(chars); i++ { + + chars[i] |= 32 + } + return string(chars) +} diff --git a/Week 08/id_198/LeetCode_72_198.go b/Week 08/id_198/LeetCode_72_198.go new file mode 100644 index 000000000..7ee820332 --- /dev/null +++ b/Week 08/id_198/LeetCode_72_198.go @@ -0,0 +1,15 @@ +package leetcode + +func minDistance(word1 string, word2 string) int { + + if word1 == "" || word2 == "" { + + rt := len(word1) - len(word2) + if rt < 0 { + return -1 * rt + } + + return rt + } + +} diff --git a/Week 08/id_198/LeetCode_91_198.go b/Week 08/id_198/LeetCode_91_198.go new file mode 100644 index 000000000..3aba6a6a8 --- /dev/null +++ b/Week 08/id_198/LeetCode_91_198.go @@ -0,0 +1,5 @@ +package leetcode + +func numDecodings(s string) int { + +} diff --git a/Week 08/id_198/NOTE.md b/Week 08/id_198/NOTE.md new file mode 100755 index 000000000..4375e3503 --- /dev/null +++ b/Week 08/id_198/NOTE.md @@ -0,0 +1,12 @@ +# NOTE + +## 不同路径II动态方程 + +## 字符串相关算法 + +[KMP算法]KMP 算法(Knuth-Morris-Pratt 算法)是一个著名的字符串匹配算法,效率很高 + + +## 学习文章 + +- [Are your strings immutable?])(https://lemire.me/blog/2017/07/07/are-your-strings-immutable/) diff --git a/Week 08/id_198/rabinkarp_search.go b/Week 08/id_198/rabinkarp_search.go new file mode 100644 index 000000000..9754a0877 --- /dev/null +++ b/Week 08/id_198/rabinkarp_search.go @@ -0,0 +1,25 @@ +package leetcode + +func rabinkarpSearch(a, b string) int { + xa := []rune(a) + xb := []rune(b) + m := len(xa) + n := len(xb) + for i := 0; i < m-n; i++ { + + j := 0 + for ; j < n; j++ { + + if xa[i+j] != xb[j] { + + break + } + } + + if j == n { + return i + } + } + + return -1 +} diff --git a/Week 08/id_198/str_search.go b/Week 08/id_198/str_search.go new file mode 100644 index 000000000..f867be54a --- /dev/null +++ b/Week 08/id_198/str_search.go @@ -0,0 +1,26 @@ +package leetcode + +func forceSearch(a, b string) int { + + xa := []rune(a) + xb := []rune(b) + m := len(xa) + n := len(xb) + for i := 0; i < m-n; i++ { + + j := 0 + for ; j < n; j++ { + + if xa[i+j] != xb[j] { + + break + } + } + + if j == n { + return i + } + } + + return -1 +} diff --git a/Week 08/id_198/str_search_test.go b/Week 08/id_198/str_search_test.go new file mode 100644 index 000000000..92bc1f5f2 --- /dev/null +++ b/Week 08/id_198/str_search_test.go @@ -0,0 +1,24 @@ +package leetcode + +import "testing" + +func Test_forceSearch(t *testing.T) { + type args struct { + a string + b string + } + tests := []struct { + name string + args args + want int + }{ + {name: "a", args: args{a: "aebcdeabcfg", b: "abc"}, want: 6}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := forceSearch(tt.args.a, tt.args.b); got != tt.want { + t.Errorf("forceSearch() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/Week 08/id_203/LeetCode_541_203.go b/Week 08/id_203/LeetCode_541_203.go new file mode 100644 index 000000000..a013c185a --- /dev/null +++ b/Week 08/id_203/LeetCode_541_203.go @@ -0,0 +1,40 @@ +package week08 + +/** +给定一个字符串和一个整数 k,你需要对从字符串开头算起的每个 2k 个字符的前k个字符进行反转。如果剩余少于 k 个字符,则将剩余的所有全部反转。如果有小于 2k 但大于或等于 k 个字符,则反转前 k 个字符,并将剩余的字符保持原样。 + +示例: + +输入: s = "abcdefg", k = 2 +输出: "bacdfeg" +要求: + +该字符串只包含小写的英文字母。 +给定字符串的长度和 k 在[1, 10000]范围内。 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/reverse-string-ii +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +func reverseStr(s string, k int) string { + + tmp := []byte(s) + end := len(tmp)-1 + + for i := 0; i <= end; i += 2*k { + x := i + y := i + k - 1 + if y > end { + y = end + } + for x < y { + tmp[x], tmp[y] = tmp[y], tmp[x] + x++ + y-- + } + } + + return string(tmp) + +} diff --git a/Week 08/id_203/LeetCode_91_203.go b/Week 08/id_203/LeetCode_91_203.go new file mode 100644 index 000000000..39d19c207 --- /dev/null +++ b/Week 08/id_203/LeetCode_91_203.go @@ -0,0 +1,47 @@ +package week08 + +/** +一条包含字母 A-Z 的消息通过以下方式进行了编码: + +'A' -> 1 +'B' -> 2 +... +'Z' -> 26 +给定一个只包含数字的非空字符串,请计算解码方法的总数。 + +示例 1: + +输入: "12" +输出: 2 +解释: 它可以解码为 "AB"(1 2)或者 "L"(12)。 +示例 2: + +输入: "226" +输出: 3 +解释: 它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/decode-ways +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + +import "strconv" + +func numDecodings(s string) int { + if len(s) == 0 || s[0] == '0' { + return 0 + } + magic := make([]int, len(s)+1) + magic[0], magic[1] = 1, 1 + for i := 2; i < len(s)+1; i++ { + if s[i-1] != '0' { + magic[i] += magic[i-1] + } + if s[i-2] != '0' { + if v, _ := strconv.Atoi(s[i-2:i]); v <= 26 { + magic[i] += magic[i-2] + } + } + } + return magic[len(s)] +} \ No newline at end of file diff --git a/Week 08/id_203/NOTE.md b/Week 08/id_203/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_203/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_208/NOTE.md b/Week 08/id_208/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_208/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_213/NOTE.md b/Week 08/id_213/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_213/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_218/LeetCode_344_218.java b/Week 08/id_218/LeetCode_344_218.java new file mode 100644 index 000000000..48152327b --- /dev/null +++ b/Week 08/id_218/LeetCode_344_218.java @@ -0,0 +1,20 @@ +package leetcode.week8; + +/** + * https://leetcode-cn.com/problems/reverse-string + * + * @author eason.feng at 2019/12/7/0007 15:52 + **/ +public class LeetCode_344_218 { + + public void reverseString(char[] s) { + if (s == null || s.length == 0) { + return; + } + for (int i = 0, j = s.length - 1; i < j; ++i, --j) { + char t = s[i]; + s[i] = s[j]; + s[j] = t; + } + } +} diff --git a/Week 08/id_218/LeetCode_387_218.java b/Week 08/id_218/LeetCode_387_218.java new file mode 100644 index 000000000..e4a119f74 --- /dev/null +++ b/Week 08/id_218/LeetCode_387_218.java @@ -0,0 +1,34 @@ +package leetcode.week8; + +import java.util.HashMap; +import java.util.Map; + +/** + * 字符串中的第一个唯一字符 + * https://leetcode-cn.com/problems/first-unique-character-in-a-string/ + * + * @author eason.feng at 2019/12/4/0004 20:36 + **/ +public class LeetCode_387_218 { + + public int firstUniqChar(String s) { + int res = -1; + if (s == null || "".equals(s)) { + return res; + } + int[] arr = new int[32]; + int len = s.length(); + for (int i = 0; i < len; i++) { + char t = s.charAt(i); + int index = t - 'a'; + arr[index]++; + } + for (int i = 0; i < len; i++) { + int k = s.charAt(i) - 'a'; + if (arr[k] == 1) { + return i; + } + } + return res; + } +} diff --git a/Week 08/id_218/LeetCode_541_218.java b/Week 08/id_218/LeetCode_541_218.java new file mode 100644 index 000000000..3e570448c --- /dev/null +++ b/Week 08/id_218/LeetCode_541_218.java @@ -0,0 +1,52 @@ +package leetcode.week8; + +/** + * https://leetcode-cn.com/problems/reverse-string-ii/ + * + * @author eason.feng at 2019/12/7/0007 15:56 + **/ +public class LeetCode_541_218 { + + public static void main(String[] args) { + LeetCode_541_218 leetCode_541_218 = new LeetCode_541_218(); + System.out.println(leetCode_541_218.reverseStr("krmyfshbspcgtesxnnljhfursyissjnsocgdhgfxubewllxzqhpasguvlrxtkgatzfybprfmmfithphckksnvjkcvnsqgsgosfxc", 20)); + } + + public String reverseStr(String s, int k) { + if (s == null || "".equals(s) || k <= 0) { + return s; + } + StringBuilder res = new StringBuilder(""); + int length = s.length(); + int index = 0; + while (index + 2 * k < length) { + int mid = index + k; + int end = index + 2 * k; + String reverseStr = reverse(s.substring(index, mid)); + res.append(reverseStr).append(s.substring(mid, end)); + index += 2 * k; + } + String sub = s.substring(index, length); + int subLength = sub.length(); + if (subLength < k) { + res.append(reverse(sub)); + } else { + res.append(reverse(sub.substring(0, k))).append(sub.substring(k)); + } + return res.toString(); + } + + private String reverse(String s) { + if (s == null || s.length() <= 0) { + return ""; + } + char[] chars = s.toCharArray(); + int len = chars.length; + for (int i = 0, j = len - 1; i < j; i++, j--) { + char t = chars[i]; + chars[i] = chars[j]; + chars[j] = t; + } + return new String(chars); + } +} diff --git a/Week 08/id_218/LeetCode_58_218.java b/Week 08/id_218/LeetCode_58_218.java new file mode 100644 index 000000000..1da174453 --- /dev/null +++ b/Week 08/id_218/LeetCode_58_218.java @@ -0,0 +1,28 @@ +package leetcode.week8; + +/** + * https://leetcode-cn.com/problems/length-of-last-word/ + * + * @author eason.feng at 2019/12/4/0004 20:25 + **/ +public class LeetCode_58_218 { + + public int lengthOfLastWord(String s) { + if (s == null || "".equals(s)) { + return 0; + } + int end = s.length() - 1; + while (end >= 0 && s.charAt(end) == ' ') { + end--; + } + if (end < 0) { + return 0; + } + int start = end; + while (start >= 0 && s.charAt(start) != ' ') { + start--; + } + return end - start; + } + +} diff --git a/Week 08/id_218/LeetCode_709_218.java b/Week 08/id_218/LeetCode_709_218.java new file mode 100644 index 000000000..b561c8008 --- /dev/null +++ b/Week 08/id_218/LeetCode_709_218.java @@ -0,0 +1,30 @@ +package leetcode.week8; + +/** + * https://leetcode-cn.com/problems/to-lower-case/submissions/ + * @author eason.feng at 2019/12/4/0004 20:12 + **/ +public class LeetCode_709_218 { + + public String toLowerCaseV2(String str) { + if (str == null || "".equals(str)) { + return str; + } + return str.toLowerCase(); + } + public String toLowerCase(String str) { + if (str == null || "".equals(str)) { + return str; + } + int len = str.length(); + char[] chs = new char[len]; + for (int i = 0; i < len; i++) { + char t = str.charAt(i); + if (t <= 'Z' && t >= 'A') { + t += (char) 32; + } + chs[i] = t; + } + return new String(chs); + } +} diff --git a/Week 08/id_218/LeetCode_771_218.java b/Week 08/id_218/LeetCode_771_218.java new file mode 100644 index 000000000..d407e5992 --- /dev/null +++ b/Week 08/id_218/LeetCode_771_218.java @@ -0,0 +1,30 @@ +package leetcode.week8; + +/** + * http://leetcode-cn.com/problems/jewels-and-stones/ + * + * @author eason.feng at 2019/12/7/0007 13:08 + **/ +public class LeetCode_771_218 { + + public int numJewelsInStones(String J, String S) { + int res = 0; + if (J == null || "".equals(J) || S == null || "".equals(S)) { + return res; + } + char[] jchs = J.toCharArray(); + char[] schs = S.toCharArray(); + int sLen = schs.length; + int jLen = jchs.length; + for (int i = 0; i < sLen; i++) { + for (int k = 0; k < jLen; k++) { + char t = jchs[k]; + if (t == schs[i]) { + res++; + } + } + } + return res; + } + +} diff --git a/Week 08/id_218/LeetCode_8_218.java b/Week 08/id_218/LeetCode_8_218.java new file mode 100644 index 000000000..bedc916e1 --- /dev/null +++ b/Week 08/id_218/LeetCode_8_218.java @@ -0,0 +1,42 @@ +package leetcode.week8; + +/** + * @author eason.feng at 2019/12/7/0007 13:33 + **/ +public class LeetCode_8_218 { + + public static void main(String[] args) { + LeetCode_8_218 leetCode_8_218 = new LeetCode_8_218(); + System.out.println(leetCode_8_218.myAtoi("42")); + } + + public int myAtoi(String str) { + int res = 0; + if (str == null || "".equals(str.trim())) { + return res; + } + str = str.trim(); + int sign = 1; + int index = 0; + if ('+' == str.charAt(index)) { + sign = 1; + index++; + } else if ('-' == str.charAt(index)) { + sign = -1; + index++; + } + while (index < str.length()) { + int digest = str.charAt(index) - '0'; + if (digest > 9 || digest < 0) { + break; + } + if (Integer.MAX_VALUE / 10 < res || (Integer.MAX_VALUE / 10 == res && Integer.MAX_VALUE % 10 < digest)) { + return (sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE); + } + res = 10 * res + digest; + index++; + } + return res * sign; + } + +} diff --git a/Week 08/id_218/NOTE.md b/Week 08/id_218/NOTE.md new file mode 100755 index 000000000..8a2d8ee5e --- /dev/null +++ b/Week 08/id_218/NOTE.md @@ -0,0 +1,115 @@ +# NOTE + +#### 动态规划、状态转移方程 + +* 动态规划复习; + * 递归:函数自己调用自己(Terminator, process drill down reverse state) + * 分治(也会用递归):分而治之。 + * 动态规划(Dynamic Programming,面试很多时候都是面的二维数组的模式的DP): + * 1:“Simplifying a complicated problem by breaking it down into simpler sub-problems”(in a recursive manner) + * 2: Divide & Conquer(分治) + Optimal substructure(最优子结构) + * 3:顺推形式:动态递推 +* 多种情况的动态规划的状态转移方程串讲; +* 进阶版动态规划的习题; + +* 感触 + * 1:人肉递归低效,且很累 + * 2:找到最近最简单的方法(最大公约数),将其拆解成可重复解决的问题 + * 3:数学归纳法思维 +* 本质:寻找重复性 --> 计算机指令集 + +#### 在第 8 周学习总结中,写出不同路径 2 这道题目的状态转移方程。 + +* 复杂: + * DP状态的定义,把现实的问题定义成一个数组,保存状态 + * 写出状态转移方程(斐波那契:dp[i] = dp[i - 1] + dp[i - 2]) + * +* 关键点 + * 动态规划和递归或者分治没有根本上的区别(关键是看有无最优的子结构) + * 拥有共性:找到重复子问题。 + * 差异性:最优子结构、中途可以淘汰次优解。 + +DP顺推模板 +``` +function DP(): + dp = [][]#二维情况 + for i = 0 .. M { + for j = 0 .. N { + dp[i][j] = _Function(dp[i'][j']...) + } + } + + return dp[M][N] +``` +#### DP创建的问题 + +* 1:爬楼梯问题,与硬币置换问题有异曲同工之处 + * 动态规划状态转移方程为:f(n)=f(n-1)+f(n-2), f(1)=1, f(0)=0 + * ![dbbc67876c52748717c53e44e829946c.png](en-resource://database/2544:1) +* 2:不同路径问题 + * 状态转移方程为:f(x,y) = f(x-1, y) + f(x, y-1) + * ![c126b26f3dcbda7219e88b2015f6eac2.png](en-resource://database/2546:1) + +* 3:打家劫舍问题: + * 状态转移方程:见下图 + * ![138ff5415244617a9802b447150f2cfa.png](en-resource://database/2548:1) + +* 4:最小路径和: + * 状态转移方程:见下图 + * ![d6fc34ffccdc5455f8830212013f4c5a.png](en-resource://database/2550:1) + +* 5:买卖股票问题: + * 状态转移方程的维度:1、天i;2、当前是否拥有股票(0或1);3、最多只能交易次数k,当前交易次数。冷冻期和偷房子问题,只能相隔来偷。 + * ![ae523556877b77ca09022ffec57d1124.png](en-resource://database/2552:1) + * 状态定义:dp[i][k][0 or 1](0 <= i <= n -1, 1 <= k <= K;总状态为:n * K * 2 + * 状态转移方程:见下图 + * ![aa86d4ae5d5cc5121391f9c4829cc6e4.png](en-resource://database/2554:1) + * ![208c7aad4b037c785b6e9f8930a72bc1.png](en-resource://database/2556:1) + + * 模板代码: + +``` +for 0 <= i < n: + for 1 <= k <= K: + for s in {0, 1}: + dp[i][k][s] = max(buy, sell, rest) #状态转移方程 +``` + + +#### 复杂度来源 +* 1:状态拥有更多纬度(二维、三维、或者更多、甚至需要压缩) +* 2:状态方程更加复杂 + +本质:内功、逻辑思维、数学 + +#### 字符串算法 + +##### 基础知识 +* Python 和 java的String是不可变的。新增或者减少时,是新生成一个String,c++则不一样,存在并发问题 +* 遍历字符串,方法:调用库api + +##### 字符串操作问题 + +##### 高级字符串算法 +* 动态规划 和 字符串问题相结合 + * 最长子串、子序列问题 + * https://leetcode-cn.com/problems/longest-common-subsequence/ + * https://leetcode-cn.com/problems/edit-distance/ + +##### 字符串匹配算法 + +* 1:暴力法 +* 2: Rabin-Karp算法 +* 3:KMP算法 + * 思想:当子串与目标字符串不匹配时,其实你已经知道了前面已经成功那部分的字符(包括子串与目标字符串)。 + * 步骤: + * 1:枚举出待匹配的字符串的所有前缀 + * 2:计算出最长前后缀公共列表 + * ![4e01e3181799100f37267c6f938c390d.png](en-resource://database/2579:1) + * ![e174c5739f57cbfe7978fd3b5829d42f.png](en-resource://database/2581:1) + + * + + + + diff --git a/Week 08/id_223/LeetCode_300_223.py b/Week 08/id_223/LeetCode_300_223.py new file mode 100644 index 000000000..61e6e20e1 --- /dev/null +++ b/Week 08/id_223/LeetCode_300_223.py @@ -0,0 +1,9 @@ +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + if not nums: return 0 + dp = [1] * len(nums); + for i in range(len(nums)): + for j in range(i): + if nums[j] map = new HashMap<>(); + for(char c : s.toCharArray()) { + map.put(c, map.getOrDefault(c, 0) + 1); + } + for(int i = 0; i < s.length(); i++) { + if (map.get(s.charAt(i)) == 1) return i; + } + return -1; + } +} \ No newline at end of file diff --git a/Week 08/id_223/NOTE.md b/Week 08/id_223/NOTE.md new file mode 100755 index 000000000..c15263708 --- /dev/null +++ b/Week 08/id_223/NOTE.md @@ -0,0 +1,36 @@ +# NOTE +### Week 08 +1. 人肉递归低效、很累 +2. 找到最近最简方法,将其拆解成可重复解决的问题 3. 数学归纳法思维 + +##### 关键点 +- 动态规划 和 递归或者分治 没有根本上的区别(关键看有无最优的子结构) +- 拥有共性:找到重复子问题 +- 差异性:最优子结构、中途可以淘汰次优解 +``` +分治代码模板 +def divide_conquer(problem, param1, param2, ...): # recursion terminator + if problem is None: + print_result + return + # prepare data + data = prepare_data(problem) + subproblems = split_problem(problem, data) + # conquer subproblems + subresult1 = self.divide_conquer(subproblems[0], p1, ...) subresult2 = self.divide_conquer(subproblems[1], p1, ...) subresult3 = self.divide_conquer(subproblems[2], p1, ...) ... + # process and generate the final result + result = process_result(subresult1, subresult2, subresult3, ...) # revert the current level states +``` +常见dp问题: +- 爬楼梯 +- 不同路径 +- 打家劫舍 +- 最小路径和 +- 股票买卖 +> Java: +String x = “abb”; String y = “abb”; +x == y —-> false +x.equals(y) —-> true +x.equalIgnoreCase --> true + + diff --git a/Week 08/id_228/NOTE.md b/Week 08/id_228/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_228/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_233/NOTE.md b/Week 08/id_233/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_233/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_238/NOTE.md b/Week 08/id_238/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_238/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_243/LeetCode_14_243.java b/Week 08/id_243/LeetCode_14_243.java new file mode 100644 index 000000000..9c5d0b259 --- /dev/null +++ b/Week 08/id_243/LeetCode_14_243.java @@ -0,0 +1,22 @@ +/** + * @author eazonshaw + * @date 2019/12/5 10:18 + */ +public class LeetCode_14_243 { + + //垂直扫描 + public String longestCommonPrefix(String[] strs) { + if(strs == null || strs.length == 0){ + return ""; + } + for(int i = 0;i < strs[0].length();i++){ + char c = strs[0].charAt(i); + for(int j = 1;j < strs.length;j++){ + if(i == strs[j].length() || c!=strs[j].charAt(i)){ + return strs[0].substring(0,i); + } + } + } + return strs[0]; + } +} diff --git a/Week 08/id_243/LeetCode_32_243.java b/Week 08/id_243/LeetCode_32_243.java new file mode 100644 index 000000000..86ac3aa63 --- /dev/null +++ b/Week 08/id_243/LeetCode_32_243.java @@ -0,0 +1,35 @@ +/** + * @author eazonshaw + * @date 2019/12/8 22:22 + */ +public class LeetCode_32_243 { + + + //dp + //状态数组 dp[i] + //状态方程: + //1.当i为右括号且i-1为左括号时,dp[i] = dp[i-2] + 2 + //2.当i为右括号,i-1也为右括号,且i-dp[i-1]-1为左括号时,dp[i] = dp[i-1] + dp[i-dp[i-1]-2] + 2 + public int longestValidParentheses(String s) { + int max = 0; + //状态数组 + int[] dp = new int[s.length()]; + //状态方程 + for(int i = 1;i < s.length();i++){ + //当i为右括号 + if(s.charAt(i) == ')'){ + //i-1为左括号 + if(s.charAt(i-1) == '('){ + dp[i] = (i>=2 ? dp[i-2] : 0) + 2; + }else if(i - dp[i - 1] > 0 && s.charAt(i-dp[i-1]-1) == '('){ + dp[i] = dp[i-1] + (i-dp[i-1]>=2 ? dp[i-dp[i-1]-2] : 0) + 2; + } + max = Math.max(max,dp[i]); + } + } + return max; + } + + + +} diff --git a/Week 08/id_243/LeetCode_63_243.java b/Week 08/id_243/LeetCode_63_243.java new file mode 100644 index 000000000..e18d54fb0 --- /dev/null +++ b/Week 08/id_243/LeetCode_63_243.java @@ -0,0 +1,30 @@ +/** + * @author eazonshaw + * @date 2019/12/8 22:59 + */ +public class LeetCode_63_243 { + + public int uniquePathsWithObstacles(int[][] obstacleGrid) { + int m = obstacleGrid.length; + int n = obstacleGrid[0].length; + //起点为障碍 + if(obstacleGrid[0][0] == 1){ + return 0; + } + //边界条件 + obstacleGrid[0][0] = 1; + for(int i = 1;i < m;i++){ + obstacleGrid[i][0] = (obstacleGrid[i][0] == 0 && obstacleGrid[i-1][0] == 1)? 1:0; + } + for(int j = 1;j < n;j++){ + obstacleGrid[0][j] = (obstacleGrid[0][j] == 0 && obstacleGrid[0][j-1] == 1)? 1:0; + } + for(int i = 1;i < m;i++){ + for(int j = 1;j < n;j++){ + obstacleGrid[i][j] = obstacleGrid[i][j] == 0? (obstacleGrid[i-1][j]+obstacleGrid[i][j-1]) : 0; + } + } + return obstacleGrid[m-1][n-1]; + } + +} diff --git a/Week 08/id_243/NOTE.md b/Week 08/id_243/NOTE.md new file mode 100755 index 000000000..ffc48db4b --- /dev/null +++ b/Week 08/id_243/NOTE.md @@ -0,0 +1,62 @@ +# 学习总结 +## 路径I +1. 题目: +[不同路径](https://leetcode-cn.com/problems/unique-paths/submissions/) +2. 状态数组:`dp[i][j]` +3. 状态转移方程: +> dp[i][j] = dp[i-1][j] + dp[i][j-1] +4. 代码实现: +``` +public int uniquePaths(int m, int n) { + int[][] dp = new int[m][n]; + //边界条件 + for(int i = 0;i < m;i++){ + dp[i][0] = 1; + } + for(int j = 0;j < n;j++){ + dp[0][j] = 1; + } + for(int i = 1;i < m;i++){ + for(int j = 1;j < n;j++){ + dp[i][j] = dp[i-1][j] + dp[i][j-1]; + } + } + return dp[m-1][n-1]; +} +``` +## 作业:路径II状态转移方程 +1. 题目: +[不同路径 II](https://leetcode-cn.com/problems/unique-paths-ii/) +2. 状态数组:`dp[i][j]` +3. 状态转移方程: +* 当i不为障碍物时, +> dp[i][j] = dp[i][j-1] + dp[i-1][j]; +* 当i为障碍物时, +> dp[i][j] = 0; +4. 代码实现: +``` +public int uniquePathsWithObstacles(int[][] obstacleGrid) { + int m = obstacleGrid.length; + int n = obstacleGrid[0].length; + //起点为障碍 + if(obstacleGrid[0][0] == 1){ + return 0; + } + //边界条件 + obstacleGrid[0][0] = 1; + for(int i = 1;i < m;i++){ + obstacleGrid[i][0] = (obstacleGrid[i][0] == 0 && obstacleGrid[i-1][0] == 1)? 1:0; + } + for(int j = 1;j < n;j++){ + obstacleGrid[0][j] = (obstacleGrid[0][j] == 0 && obstacleGrid[0][j-1] == 1)? 1:0; + } + for(int i = 1;i < m;i++){ + for(int j = 1;j < n;j++){ + obstacleGrid[i][j] = obstacleGrid[i][j] == 0? (obstacleGrid[i-1][j]+obstacleGrid[i][j-1]) : 0; + } + } + return obstacleGrid[m-1][n-1]; +} +``` + + diff --git a/Week 08/id_248/NOTE.md b/Week 08/id_248/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_248/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_253/NOTE.md b/Week 08/id_253/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_253/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_258/LeetCode_300_258.js b/Week 08/id_258/LeetCode_300_258.js new file mode 100644 index 000000000..dd3a34d22 --- /dev/null +++ b/Week 08/id_258/LeetCode_300_258.js @@ -0,0 +1,24 @@ +/* + * @lc app=leetcode.cn id=300 lang=javascript + * + * [300] 最长上升子序列 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var lengthOfLIS = function (nums) { + let lis = []; + for (let i = 0; i < nums.length; i++) { + lis.push(1); + for (let j = 0; j < i; j++) { + if (nums[j] < nums[i]) lis[i] = Math.max(lis[i], lis[j] + 1); + } + } + + return nums.length ? Math.max.apply(null, lis) : 0; +}; +// @lc code=end + diff --git a/Week 08/id_258/LeetCode_387_258.js b/Week 08/id_258/LeetCode_387_258.js new file mode 100644 index 000000000..505c01795 --- /dev/null +++ b/Week 08/id_258/LeetCode_387_258.js @@ -0,0 +1,20 @@ +/* + * @lc app=leetcode.cn id=387 lang=javascript + * + * [387] 字符串中的第一个唯一字符 + */ + +// @lc code=start +/** + * @param {string} s + * @return {number} + */ +var firstUniqChar = function (s) { + for (let i = 0; i < s.length; i++) { + if (s.indexOf(s[i]) === s.lastIndexOf(s[i])) return i; + } + + return -1; +}; +// @lc code=end + diff --git a/Week 08/id_258/NOTE.md b/Week 08/id_258/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_258/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_263/NOTE.md b/Week 08/id_263/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_263/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_268/NOTE.md b/Week 08/id_268/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_268/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_273/LeetCode_10_273.java b/Week 08/id_273/LeetCode_10_273.java new file mode 100644 index 000000000..b04b5588f --- /dev/null +++ b/Week 08/id_273/LeetCode_10_273.java @@ -0,0 +1,42 @@ +//10. 正则表达式匹配 + +//解法1:DP 执行用时:3ms +//思路:通过创建一个二维数组来存储S[0~i]和P[0~j]是否满足表达式匹配 +// 首先可以明确一种最简单的情况: +// 1. 当S[:i]和P[:j]的最后一个字符如果相同, 那么只需要判断S[:i - 1]和P[:j - 1]的部分即可 +// 2. 当P[:j]的最后一个字符为'.', 此时可以匹配任意单个字符, 那么判断方式与第一种相同 +// 以上两种情况的动态方程可以确定为:DP[i][j] = DP[i - 1][j - 1] +// 3. 当P[:j]的最后一个字符为'*', 可以选择匹配0个或n个前一个字符, 那么情况如下: +// 3.1 当S[:i]和P[:j - 1]的最后一个字符不相同, 且P[j] = '*'时, 可以考虑匹配0个前字符看是否能够匹配 +// 此时DP[i][j] = DP[i][j - 2] +// 3.2 当S[:i]和P[:j - 1]的最后一个字符相同, 可以选择匹配n个前字符, 那么'*'选择匹配了n个字符后需要观察选择匹配n - 1个字符是否能匹配 +// 例如:s:aaa p:a* +// '*'匹配1个a:s:aaa p:a ---> s:aa p:"" false +// '*'匹配2个a:s:aaa p:aa ---> s:aa p: "" false +// '*'匹配3个a:s:aaa p:aaa---> s: aaa p: "" true +// S[a, a, a] P[a, *] 需要观察S[a, a] p[a, *]是否成功匹配了, 如果成功了, 那么*可以再多匹配一个a +// 此时DP[i][j] = DP[i - 1][j] +//时间复杂度:O(m*n) +//空间复杂度:O(m*n) +public boolean isMatch(String s, String p) { + int sLen = s.length(); + int pLen = p.length(); + boolean[][] dp = new boolean[sLen + 1][pLen + 1]; + dp[0][0] = true; + for (int j = 2; j <= pLen; j++) { + if (p.charAt(j - 1) == '*') { + dp[0][j] = dp[0][j - 2]; + } + } + for (int i = 1; i <= sLen; i++) { + for (int j = 1; j <= pLen; j++) { + int m = i - 1, n = j - 1; + if (s.charAt(m) == p.charAt(n) || p.charAt(n) == '.') { + dp[i][j] = dp[i - 1][j - 1]; + } else if (p.charAt(n) == '*') { + dp[i][j] = (dp[i][j - 2] || (dp[i - 1][j] && (s.charAt(m) == p.charAt(n - 1) || p.charAt(n - 1) == '.'))); + } + } + } + return dp[sLen][pLen]; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_115_273.java b/Week 08/id_273/LeetCode_115_273.java new file mode 100644 index 000000000..20bff016b --- /dev/null +++ b/Week 08/id_273/LeetCode_115_273.java @@ -0,0 +1,21 @@ +//115. 不同子序列 + +//解法1:DP 执行用时:10ms +//思路: +public int numDistinct(String s, String t) { + int[][] dp = new int[t.length() + 1][s.length() + 1]; + for (int j = 0; j < dp[0].length; j++) { + dp[0][j] = 1; + } + for (int i = 1; i < dp.length; i++) { + for (int j = 1; j < dp[0].length; j++) { + int m = i - 1, n = j - 1; + if (t.charAt(m) == s.charAt(n)) { + dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1]; + } else { + dp[i][j] = dp[i][j - 1]; + } + } + } + return dp[dp.length - 1][dp[0].length - 1]; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_121_273.java b/Week 08/id_273/LeetCode_121_273.java new file mode 100644 index 000000000..83fe84fb4 --- /dev/null +++ b/Week 08/id_273/LeetCode_121_273.java @@ -0,0 +1,126 @@ +//121. 买卖股票的最佳时机 + + + +//解法1:暴力枚举 执行用时:280ms +//思路:获取所有买入卖出的组合, 计算其中的利润最大值返回 +//时间复杂度:O(n^2) +//空间复杂度:O(1) +public int maxProfit(int[] prices) { + int sum = 0; + for (int i = 0; i < prices.length - 1; i++) { + for (int j = i + 1; j < prices.length; j++) { + sum = Math.max(sum, prices[j] - prices[i]); + } + } + return sum; +} + +//解法1.1:暴力枚举降维 执行用时:2ms +//思路:我们在枚举所有买入卖出的组合时, 只需要通过枚举 "当天价格 - 之前某天的最小价格" 的所有结果, 然后返回其中的最大值即是最大利润 +//时间复杂度:O(n) +//空间复杂度:O(1) +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int sum = 0; + int minVal = prices[0]; + for (int i = 1; i < prices.length; i++) { + sum = Math.max(sum, prices[i] - minVal); + minVal = Math.min(minVal, prices[i]); + } + return sum; +} + +//股票问题动态规划统一分析: +//1. 穷举所有的"状态" +// 每天都可以有三种选择分别是:买入buy, 卖出sell, 无操作rest +// 买入必须在卖出之后, 因为题目限制只能完成一次交易后才能开始第二次交易 +// 卖出必须在买入之后, 因为卖出的前提是要持有股票 +// 无操作可以在买入后继续保持持有股票, 也可以在卖出后继续保持不持有股票 + +// 那么可以通过一个三维数组存放这几种状态的全部组合:DP[i][k][0 or 1] +// 语义为:当前为第i天,进行第k次交易,当前未持有/持有股票 +// 最后要求的结果则是:DP[n - 1][k - 1][0] +// 即:最后一天, 不能再进行交易, 且手上没持有股票 + +//2. 状态转移方程: +// 那么统一的状态转移方程就可以确定如下: +// 1. DP[i][k][0] = max(DP[i - 1][k][0], DP[i - 1][k][1] + prices[i]) +// 即:第i天未持有股票, 那么可以从中择优:i - 1天也未持有股票, 第i天继续保持未持有; i - 1天持有股票, 第i天抛售股票完成一次交易 +// 2. DP[i][k][1] = max(DP[i - 1][k][1], DP[i - 1][k - 1][0] - prices[i]) +// 即:第i天持有股票, 那么可以从中择优:i - 1天持有股票, 第i天继续保持持有; i - 1天未持有股票, 在第k - 1次交易的利润额基础上再买入一支股票 +// BaseCase: +// DP[i][0][0] = 0 :k从1开始进行交易, k = 0, 不允许交易 + +//3. 当前问题分析 +// 对于当前题目的要求, K = 1, 也就是这几天内只能完成一笔交易, 则状态方程如下: +// 1. DP[i][1][0] = max(DP[i - 1][1][0], DP[i - 1][1][1] + prices[i]) +// 2. DP[i][1][1] = max(DP[i - 1][1][1], (DP[i - 1][0][0] == 0) - prices[i]) +// 可以发现第2个转移方程中出现的"DP[i - 1][0][0]", 对应BaseCase可以省略不写 +// 而其他情况中k都为1, 即k对状态转移不产生影响, 因此状态K也可以省略不写 +// 到最后, 这个问题的状态转移方程如下: +// 1. DP[i][0] = max(DP[i - 1][0], DP[i - 1][1] + prices[i]) +// 2. DP[i][1] = max(DP[i - 1][1], -prices[i]) + +//解法2:动态规划(三维数组) 执行用时:7ms +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int[][][] dp = new int[prices.length][2][2]; + for (int i = 0; i < prices.length; i++) { + for (int k = 1; k >= 1; k--) { + if (i - 1 == -1) { + dp[i][k][0] = 0; + dp[i][k][1] = -prices[i]; + continue; + } + //当天不持股 = max(前一天也不持股, 前一天持股卖出) + dp[i][k][0] = Math.max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]); + //当天持股 = max(持有前一天的股, 前一天不持股买入) + dp[i][k][1] = Math.max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i]); + } + } + return dp[prices.length - 1][1][0]; +} + +//解法2.1:动态规划(二维数组版) 执行用时:3ms +//思路:可以将当天的状态分为股票未持有和持有, 当天未持有或持有能获取的最大利润额度分别用dp[i][0], dp[i][1]表示 +// 首先我们需要明确问题, 整个流程只能进行一次买卖操作!! +// 我们将买入股票设为-prices[i], 卖出股票设置为+prices[i] +// 若当天选择不持有股票, 那么有两种情况: +// 1. 前一天未持有股票:那么当天选择不持有股票的利润额就跟前一天不持有股票的利润额相同 dp[i][0] = dp[i - 1][0] +// 2. 前一天持有股票:那么当天选择不持有股票(抛售股票)的利润额为前一天持有的利润额加上当天卖出的价钱 dp[i][0] = dp[i - 1][1] + prices[i] +// 因此当天不持有股票的最大利润额 dp[i][0] = max([i - 1][0], dp[i - 1][1] + prices[i]) +// 若当天选择持有股票, 也分为两种情况: +// 1. 前一天若未持有股票, 那么当天可以选择买入股票 dp[i][1] = -prices[i]; +// 2. 前一天若持有股票, 那么当天可以选择继续持有原有的股票dp[i][1] = dp[i - 1][1] +// 因此当天持有股票情况的最大利润额 dp[i][1] = max(-prices[i], dp[i - 1][1]) +//时间复杂度:O(n) +//空间复杂度:O(n) +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int[][] dp = new int[prices.length][2]; + dp[0][0] = 0; + dp[0][1] = -prices[0]; + for (int i = 1; i < prices.length; i++) { + dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]); + dp[i][1] = Math.max(-prices[i], dp[i - 1][1]); + } + return dp[prices.length - 1][0]; +} + +//解法2.1:动态规划(状态压缩) 执行用时:2ms +//思路:我们发现解法2在计算过程中, 计算当天利润额只用到了前一天持有股票和未持有股票的利润额 +// 那么可以压缩数组长度为2, 仅仅用于保存当天和前一天的利润额 +//时间复杂度:O(n) +//空间复杂度:O(1) +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int[] dp = new int[2]; + dp[0] = 0; + dp[1] = -prices[0]; + for (int i = 1; i < prices.length; i++) { + dp[0] = Math.max(dp[0], dp[1] + prices[i]); + dp[1] = Math.max(dp[1], -prices[i]); + } + return dp[0]; +} diff --git a/Week 08/id_273/LeetCode_122_273.java b/Week 08/id_273/LeetCode_122_273.java new file mode 100644 index 000000000..0999d93a6 --- /dev/null +++ b/Week 08/id_273/LeetCode_122_273.java @@ -0,0 +1,59 @@ +//122. 买卖股票的最佳时机II + +//解法1:贪心思想 执行用时:1ms +//思路:若当天价格大于前一天的价格, 就进行买卖操作, 也就是获取差值总额 +//时间复杂度O(n) +//空间复杂度O(1) +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int sum = 0; + for (int i = 0; i < prices.length - 1; i++) { + if (prices[i] < prices[i + 1]) sum += prices[i + 1] - prices[i]; + } + return sum; +} + +//当前问题分析 +// 对于当前题目的要求, K = +infinite, 也就是这几天内可以进行多笔交易, 则状态方程如下: +// 1. DP[i][k][0] = max(DP[i - 1][k][0], DP[i - 1][k][1] + prices[i]) +// 2. DP[i][k][1] = max(DP[i - 1][k][1], DP[i - 1][k - 1][0] - prices[i]) +// 可以发现第2个转移方程中出现的"DP[i - 1][k - 1][0]", 由于k为正无穷, 因此k - 1 = k +// 而所有k对状态转移不产生影响, 因此状态K可以省略不写 +// 到最后, 这个问题的状态转移方程如下: +// 1. DP[i][0] = max(DP[i - 1][0], DP[i - 1][1] + prices[i]) +// 2. DP[i][1] = max(DP[i - 1][1], DP[i - 1][0] - prices[i]) + +//解法2:动态规划 执行用时:3ms +//思路:和121题基本一致, 通过一个二维数组存储每一天持有股票和未持有股票的利润最大值 +// 不同点在于:现在我们可以进行多笔交易。因此, 若当天选择持有股票, 情况就稍有变化: +// 当天可以选择继续持有前一天的股票以及买入股票。若选择买入股票, 那么就需要加上当天抛售股票获得的利润额 +// dp[i][1] = max(dp[i - 1][1], dp[i][0] - prices[i]) +//时间复杂度:O(n) +//空间复杂度:O(n) +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int[][] dp = new int[prices.length][2]; + dp[0][1] = -prices[0]; + dp[0][0] = 0; + for (int i = 1; i < prices.length; 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][0] - prices[i]); + } + return dp[dp.length - 1][0]; +} + +//解法2.1:动态规划-空间压缩 执行用时:2ms +//思路:参考121动态规划的空间压缩 +//时间复杂度:O(n) +//空间复杂度:O(1) +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int[] dp = new int[2]; + dp[0] = 0; + dp[1] = -prices[0]; + for (int i = 1; i < prices.length; i++) { + dp[0] = Math.max(dp[0], dp[1] + prices[i]); + dp[1] = Math.max(dp[1], dp[0] - prices[i]); + } + return dp[0]; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_123_273.java b/Week 08/id_273/LeetCode_123_273.java new file mode 100644 index 000000000..6a05185f7 --- /dev/null +++ b/Week 08/id_273/LeetCode_123_273.java @@ -0,0 +1,57 @@ +//123. 买卖股票的最佳时机III + +//当前问题分析 +// 对于当前题目的要求, K = 2, 也就是这几天内只能进行2笔交易, 而由于K = 2, 此时无法消除k的影响, 所以必须对交易次数K也进行穷举: +// for (int k = 2; k >= 1; k--) { +// DP[i][k][0] = max(DP[i - 1][k][0], DP[i - 1][k][1] + prices[i]) +// DP[i][k][1] = max(DP[i - 1][k][1], DP[i - 1][k - 1][0] - prices[i]) +// } + +//解法1:三维DP 执行用时:6ms +public int maxProfit(int[] prices) { + int[][][] dp = new int[prices.length][3][2]; + for (int i = 0; i < prices.length; i++) { + for (int k = 2; k >= 1; k--) { + if (i - 1 == -1) { + dp[i][k][0] = 0; + dp[i][k][1] = -prices[i]; + continue; + } + dp[i][k][0] = Math.max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]); + dp[i][k][1] = Math.max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i]); + } + } + return dp[prices.length - 1][2][0]; +} + +//解法2:状态压缩 执行用时:5ms +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int[][] dp = new int[3][2]; + for (int i = 0; i < prices.length; i++) { + for (int k = 2; k >= 1; k--) { + if (i == 0) { + dp[k][0] = 0; + dp[k][1] = -prices[i]; + continue; + } + dp[k][0] = Math.max(dp[k][0], dp[k][1] + prices[i]); + dp[k][1] = Math.max(dp[k][1], dp[k - 1][0] - prices[i]); + } + } + return dp[2][0]; +} + +//解法3:一维 执行用时:3ms +public int maxProfit(int[] prices) { + if (prices.length == 0 || prices == null) return 0; + int dp_10 = 0, dp_11 = -prices[0]; + int dp_20 = 0, dp_21 = -prices[0]; + for (int i = 1; i < prices.length; i++) { + dp_20 = Math.max(dp_20, dp_21 + prices[i]); + dp_21 = Math.max(dp_21, dp_10 - prices[i]); + dp_10 = Math.max(dp_10, dp_11 + prices[i]); + dp_11 = Math.max(dp_11, -prices[i]); + } + return dp_20; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_125_273.java b/Week 08/id_273/LeetCode_125_273.java new file mode 100644 index 000000000..362740d2d --- /dev/null +++ b/Week 08/id_273/LeetCode_125_273.java @@ -0,0 +1,22 @@ +//125. 验证回文串 + +//解法1:暴力 执行用时:6ms +//思路: +public boolean isPalindrome(String s) { + s = s.toLowerCase(); + char[] charArr = s.toCharArray(); + int i = 0; + int j = charArr.length - 1; + while (i < j) { + while (i < j && !((charArr[i] >= '0' && charArr[i] <= '9') || (charArr[i] >= 'a' && charArr[i] <= 'z'))){ + i++; + } + while (i < j && !((charArr[j] >= '0' && charArr[j] <= '9') || (charArr[j] >= 'a' && charArr[j] <= 'z'))) { + j--; + } + if (charArr[i++] != charArr[j--]) { + return false; + } + } + return true; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_14_273.java b/Week 08/id_273/LeetCode_14_273.java new file mode 100644 index 000000000..7e20c25d3 --- /dev/null +++ b/Week 08/id_273/LeetCode_14_273.java @@ -0,0 +1,18 @@ +//14. 最常公共前缀 + +//解法1: 执行用时:0ms +//思路:因为最长公共前缀最长也就是字符串数组中最短的那个字符串 +// 因此取字符串数组的首位为"标杆", 然后遍历整个字符串数组, 如果存在以"标杆"打头的字符串, 那么"标杆"就是最长公共前缀 +// 如果遍历完整个数组都不存在, 那么"标杆"取0~length - 1位继续比较 +//时间复杂度:O(strs.length * Max((String s : strs).length) +//空间复杂度:O(Max((String s : strs).length) //如果按字符串为数组为前提进行计算的话是这个复杂度 +public String longestCommonPrefix(String[] strs) { + if (strs.length == 0 || strs == null) return ""; + String pre = strs[0]; + for (int i = 1; i < strs.length; i++) { + while (!strs[i].startsWith(pre)) { + pre = pre.substring(0, pre.length() - 1); + } + } + return pre; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_151_273.java b/Week 08/id_273/LeetCode_151_273.java new file mode 100644 index 000000000..86c501b83 --- /dev/null +++ b/Week 08/id_273/LeetCode_151_273.java @@ -0,0 +1,51 @@ +//151. 翻转字符串里的单词 + +//解法1: 执行用时:3ms +//思路: +public String reverseWords(String s) { + char[] charArr = s.toCharArray(); + //reverse whole string + reverse(charArr, 0, charArr.length - 1); + int startIdx= 0; + int endIdx = 0; + while (startIdx < charArr.length) { + startIdx = endIdx; + while (startIdx < charArr.length && charArr[startIdx] == ' ') { + startIdx++; + } + endIdx = startIdx; + while (endIdx < charArr.length && charArr[endIdx] != ' ') { + endIdx++; + } + reverse(charArr, startIdx, endIdx - 1); + } + return cleanBank(charArr); +} + +private char[] reverse(char[] charArr, int start, int end) { + for (int i = start, j = end; i < j; i++, j--) { + char temp = charArr[i]; + charArr[i] = charArr[j]; + charArr[j] = temp; + } + return charArr; +} + +private String cleanBank(char[] charArr) { + int i = 0, j = 0; + while (j < charArr.length) { + while (j < charArr.length && charArr[j] == ' ') { + j++; + } + while (j < charArr.length && charArr[j] != ' ') { + charArr[i++] = charArr[j++]; + } + while (j < charArr.length && charArr[j] == ' ') { + j++; + } + if (j < charArr.length) { + charArr[i++] = ' '; + } + } + return new String(charArr).substring(0, i); +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_188_273.java b/Week 08/id_273/LeetCode_188_273.java new file mode 100644 index 000000000..d9563299c --- /dev/null +++ b/Week 08/id_273/LeetCode_188_273.java @@ -0,0 +1,65 @@ +//188. 买卖股票的最佳时机IV + +//当前问题分析 +// 一次完整的交易由一次买入和卖出构成, 至少需要两天, 那么限制k次交易也就意味着只有当price.length = 2*k才是有效的 +// 对于k > price.length/2的情况, 我们可以认为k = +无穷, 也就可以转换为股票问题II +// + + +//解法1:三维DP + 贪心 执行用时:9ms +//时间复杂度:O(n*k) +//空间复杂度:O(n*k*2) +public int maxProfit(int k, int[] prices) { + if (prices.length == 0 || prices == null) return 0; + if (k > prices.length >> 1) { + return maxProfitWithoutLimit(prices); + } + int[][][] dp = new int[prices.length][k + 1][2]; + for (int i = 0; i < prices.length; i++) { + for (int j = k; j >= 1; j--) { + if (i - 1 == -1) { + dp[i][j][0] = 0; + dp[i][j][1] = -prices[i]; + continue; + } + dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]); + dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]); + } + } + return dp[prices.length - 1][k][0]; +} + +private int maxProfitWithoutLimit(int[] prices) { + int sum = 0; + for (int i = 0; i < prices.length - 1; i++) { + if (prices[i] < prices[i + 1]) { + sum+= prices[i + 1] - prices[i]; + } + } + return sum; +} + +//解法2:降维 + 贪心 执行用时:7ms +//时间复杂度:O(n*k) +//空间复杂度:O(2 * K) +public int maxProfit(int k, int[] prices) { + if (prices.length == 0 || prices == null) return 0; + if (k > prices.length >> 1) { + return maxProfitWithoutLimit(prices); + } + int[][] dp = new int[k + 1][2]; + for (int i = 0; i < prices.length; i++) { + for (int j = k; j >= 1; j--) { + if (i == 0) { + dp[j][0] = 0; + dp[j][1] = -prices[i]; + continue; + } + dp[j][0] = Math.max(dp[j][0], dp[j][1] + prices[i]); + dp[j][1] = Math.max(dp[j][1], dp[j - 1][0] - prices[i]); + } + } + return dp[k][0]; +} + +//贪心.... diff --git a/Week 08/id_273/LeetCode_205_273.java b/Week 08/id_273/LeetCode_205_273.java new file mode 100644 index 000000000..0efa47738 --- /dev/null +++ b/Week 08/id_273/LeetCode_205_273.java @@ -0,0 +1,14 @@ +//205. 重构字符串 + +//解法1: 执行用时:8ms +//思路: +public boolean isIsomorphic(String s, String t) { + int[] counter1 = new int[256]; + int[] counter2 = new int[256]; + for (int i = 0; i < s.length(); i++) { + if (counter1[s.charAt(i)] != counter2[t.charAt(i)]) return false; + counter1[s.charAt(i)] = i + 1; + counter2[t.charAt(i)] = i + 1; + } + return true; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_300_273.java b/Week 08/id_273/LeetCode_300_273.java new file mode 100644 index 000000000..2b6c9e939 --- /dev/null +++ b/Week 08/id_273/LeetCode_300_273.java @@ -0,0 +1,19 @@ +//300. 最长上升子序列 + +//解法1:DP 执行用时:17ms +//思路: +public int lengthOfLIS(int[] nums) { + if (nums.length == 1) return 1; + int[] dp = new int[nums.length]; + Arrays.fill(dp, 1); + int res = 0; + for (int i = 1; i < dp.length; i++) { + for (int j = 0; j < i; j++) { + if (nums[i] > nums[j]) { + dp[i] = Math.max(dp[i], dp[j] + 1); + } + } + res = Math.max(res, dp[i]); + } + return res; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_309_273.java b/Week 08/id_273/LeetCode_309_273.java new file mode 100644 index 000000000..31574f4b2 --- /dev/null +++ b/Week 08/id_273/LeetCode_309_273.java @@ -0,0 +1,39 @@ +//309. 最佳买卖股票时机含冷冻期 + +//当前问题分析: +// 每当完成一次完整的交易后, 需要间隔一天再继续下一次交易 +// 因此, 状态方程为: +// 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 - 2][0] - prices[i]); + +//解法1:二维DP 执行用时:2ms +//时间复杂度:O(n) +//空间复杂度:O(2 * n) +public int maxProfit(int[] prices) { + if (prices.length <= 1 || prices == null) return 0; + int[][] dp = new int[prices.length][2]; + dp[0][0] = 0; + dp[0][1] = -prices[0]; + dp[1][0] = Math.max(dp[0][0], dp[0][1] + prices[1]); + dp[1][1] = Math.max(dp[0][1], -prices[1]); + for (int i = 2; i < prices.length; 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 - 2][0] - prices[i]); + } + return dp[prices.length - 1][0]; +} + +//解法2:DP降维 执行用时:1ms +//时间复杂度:O(n) +//空间复杂度:O(1) +public int maxProfit(int[] prices) { + if (prices.length <= 1 || prices == null) return 0; + int dp_0 = 0, dp_1 = -prices[0], dp_pre_0 = 0; + for (int i = 1; i < prices.length; i++) { + int temp = dp_0; + dp_0 = Math.max(dp_0, dp_1 + prices[i]); + dp_1 = Math.max(dp_1, dp_pre_0 - prices[i]); + dp_pre_0 = temp; + } + return dp_0; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_344_273.java b/Week 08/id_273/LeetCode_344_273.java new file mode 100644 index 000000000..7d8101223 --- /dev/null +++ b/Week 08/id_273/LeetCode_344_273.java @@ -0,0 +1,11 @@ +//344. 反转字符串 + +//解法1: 执行用时:1ms +//思路: +public void reverseString(char[] s) { + for (int i = 0, j = s.length - 1; i < j; i++, j--) { + char temp = s[i]; + s[i] = s[j]; + s[j] = temp; + } +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_387_273.java b/Week 08/id_273/LeetCode_387_273.java new file mode 100644 index 000000000..cfbec341a --- /dev/null +++ b/Week 08/id_273/LeetCode_387_273.java @@ -0,0 +1,32 @@ +//387. 字符串中的第一个唯一字符 + +//解法1:HashMap(数组) 执行用时:5ms +//思路:创建一个count数组用于记录字符串中每个字符出现的次数, 遍历字符串, 观察每一个字符对应count数组中的值是否为1 +//时间复杂度:O(N) +//空间复杂度:O(N) +//总结:之所以转换字符数组是因为这样比起直接操作String更加高效 +public int firstUniqChar(String s) { + if (s == null || s.length() == 0) return -1; + char[] temp = s.toCharArray(); + int[] count = new int[26]; + for (char c : temp) { + count[c - 'a']++; + } + for (int i = 0; i < temp.length; i++) { + if (count[temp[i] - 'a'] == 1) return i; + } + return -1; +} + + +//解法2:API 执行用时:25ms +//思路:遍历字符, 若当前字符‘c’与从后往前寻找到的字符‘c’index相同, 说明它是唯一字符 +//时间复杂度:O(n^2) +//空间复杂度:O(n) +public int firstUniqChar(String s) { + char[] arr = s.toCharArray(); + for (int i = 0; i < s.length(); i++) { + if (s.indexOf(arr[i]) == s.lastIndexOf(arr[i])) return i; + } + return -1; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_438_273.java b/Week 08/id_273/LeetCode_438_273.java new file mode 100644 index 000000000..1c532e516 --- /dev/null +++ b/Week 08/id_273/LeetCode_438_273.java @@ -0,0 +1,28 @@ +//438. 找到字符串中所有字母异位词 + +//解法1:暴力解法 执行用时:569ms +//思路: +public List findAnagrams(String s, String p) { + List res = new ArrayList<>(); + if (s.length() < p.length()) return res; + char[] pArr = p.toCharArray(); + for (int i = 0; i <= s.length() - p.length(); i++) { + char[] temp = s.substring(i, i + p.length()).toCharArray(); + if (isAnagrams(temp, pArr)) { + res.add(i); + } + } + return res; +} + +private boolean isAnagrams(char[] temp1, char[] temp2) { + int[] count = new int[26]; + for (int i = 0; i < temp1.length; i++) { + count[temp1[i] - 'a']++; + count[temp2[i] - 'a']--; + } + for (int i = 0; i < count.length; i++) { + if (count[i] != 0) return false; + } + return true; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_44_273.java b/Week 08/id_273/LeetCode_44_273.java new file mode 100644 index 000000000..81d7bc8d8 --- /dev/null +++ b/Week 08/id_273/LeetCode_44_273.java @@ -0,0 +1,28 @@ +//44. 通配符匹配 + +//解法1:DP 执行用时:15ms +//思路:解法参考10. 正则表达式匹配 +//时间复杂度:O(m*n) +//空间复杂度:O(m*n) +public boolean isMatch(String s, String p) { + boolean[][] dp = new boolean[s.length() + 1][p.length() + 1]; + dp[0][0] = true; + for (int j = 1; j < dp[0].length; j++) { + if (p.charAt(j - 1) == '*') { + dp[0][j] = true; + } else { + break; + } + } + for (int i = 1; i < dp.length; i++) { + for (int j = 1; j < dp[0].length; j++) { + int m = i - 1, n = j - 1; + if (p.charAt(n) == '*') { + dp[i][j] = dp[i - 1][j] || dp[i][j - 1]; + } else if (s.charAt(m) == p.charAt(n) || p.charAt(n) == '?') { + dp[i][j] = dp[i - 1][j - 1]; + } + } + } + return dp[dp.length - 1][dp[0].length - 1]; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_541_273.java b/Week 08/id_273/LeetCode_541_273.java new file mode 100644 index 000000000..555685074 --- /dev/null +++ b/Week 08/id_273/LeetCode_541_273.java @@ -0,0 +1,15 @@ +//541. 反转字符串II + +//解法1:暴力 执行用时:1ms +//思路: +public String reverseStr(String s, int k) { + char[] charArr = s.toCharArray(); + for (int i = 0; i < charArr.length; i += k * 2) { + for (int m = i, n = Math.min(m + k - 1, charArr.length - 1); m < n; m++, n--) { + char temp = charArr[m]; + charArr[m] = charArr[n]; + charArr[n] = temp; + } + } + return String.valueOf(charArr); +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_557_273.java b/Week 08/id_273/LeetCode_557_273.java new file mode 100644 index 000000000..ff42d8c49 --- /dev/null +++ b/Week 08/id_273/LeetCode_557_273.java @@ -0,0 +1,26 @@ +//557. 反转字符串中的单词III + +//解法1: 执行用时:5ms +//思路: +public String reverseWords(String s) { + char[] arr = s.toCharArray(); + int startIdx = 0; + int endIdx = 0; + while (endIdx < s.length()) { + endIdx = startIdx; + while (endIdx < s.length() && arr[endIdx] != ' ') { + endIdx++; + } + reverse(arr, startIdx, endIdx - 1); + startIdx = endIdx + 1; + } + return String.valueOf(arr); +} + +private void reverse(char[] arr, int startIdx, int endIdx) { + for (int i = startIdx, j = endIdx; i < j; i++, j--) { + char c = arr[i]; + arr[i] = arr[j]; + arr[j] = c; + } +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_58_273.java b/Week 08/id_273/LeetCode_58_273.java new file mode 100644 index 000000000..39d27b858 --- /dev/null +++ b/Week 08/id_273/LeetCode_58_273.java @@ -0,0 +1,16 @@ +//58. 最后一个单词的长度 + +//解法1:暴力解法 执行用时:2ms +//思路:遍历字符串, 每一次遇到新的单词的时候都重新统计单词的长度 +// 新的单词意味着前一位字符是空格 +public int lengthOfLastWord(String s) { + if (s.length() == 0 || s == null) return 0; + char[] temp = s.toCharArray(); + int count = 0; + for (int i = 0; i < temp.length; i++) { + if (temp[i] == ' ') continue; + if (i > 0 && temp[i] != ' ' && temp[i - 1] == ' ') count = 0; + count++; + } + return count; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_5_273.java b/Week 08/id_273/LeetCode_5_273.java new file mode 100644 index 000000000..68a18e946 --- /dev/null +++ b/Week 08/id_273/LeetCode_5_273.java @@ -0,0 +1,58 @@ +//5. 最长回文子串 + +//解法1:暴力法 执行超时 +//思路:遍历获取所有子串, 并且判断该子串是否为回文串且尝试更新最大回文串, 由于判断回文串的方法遍历了整个回文串, 因此复杂度较高 +//时间复杂度:O(n^3) +//空间复杂度:O(1) +public String longestPalindrome1(String s) { + int len = s.length(); + if (len < 2) return s; + int maxLen = 1; + String res = s.substring(0, 1); + for (int i = 1; i < len; i++) { + for (int j = 0; j < i; j++) { + String temp = s.substring(j, i + 1); + if (isValid(temp) && temp.length() > maxLen) { + maxLen = Math.max(maxLen, temp.length()); + res = temp; + } + } + } + return res; +} + +private boolean isValid(String s) { + for (int i = 0, j = s.length() - 1; i < j; i++, j--) { + if (s.charAt(i) != s.charAt(j)) return false; + } + return true; +}} + +//解法2:DP 执行用时:54ms +//思路:创建一个二维DP用于存储子串S[left, right]是否为回文子串, +// 每当判断了dp[i][j]=true, 确认当前子串为回文子串后, 尝试更新最大回文子串 +// 这里判断是否为回文子串的细节是: +// 若当前子串的首尾字符相同, 且去掉首尾后子串长度小于等于1时, 可以确认该子串为回文子串, 例如:aba -> b +// 若当前子串的首尾字符相同, 且去掉收尾后子串长度大于1, 那么需要再判断去掉首尾的子串是否为回文子串 +// 也因为此处判断回文串的方法时间复杂度为O(1), 使得DP比暴力解法复杂度少一维 +//时间复杂度:O(m^2) +//空间复杂度:O(m^2) +public String longestPalindrome(String s) { + int len = s.length(); + if (len < 2) return s; + boolean[][] dp = new boolean[len][len]; + String res = s.substring(0,1); + int maxLen = res.length(); + for (int right = 1; right < dp.length; right++) { + for (int left = 0; left < right; left++) { + if (s.charAt(left) == s.charAt(right) && (right - left <= 2 || dp[left + 1][right - 1])) { + dp[left][right] = true; + if (right - left + 1 > maxLen) { + maxLen = right - left + 1; + res = s.substring(left, right + 1); + } + } + } + } + return res; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_709_273.java b/Week 08/id_273/LeetCode_709_273.java new file mode 100644 index 000000000..bfb27e5cc --- /dev/null +++ b/Week 08/id_273/LeetCode_709_273.java @@ -0,0 +1,21 @@ +//709. 转换成小写字母 + +//解法1:ASCII码计算 执行用时:0ms +//思路:遍历所有字符, 如果它属于大写字符, 那么就加上小写字符与大写字符的ASCII码的差值 +//时间复杂度:O(n) +//空间复杂度:O(n) +//总结:需要记住的是小写字符的ASCII码大于大写字符的ASCII码 +public String toLowerCase(String str) { + char[] temp = str.toCharArray(); + for (int i = 0; i < temp.length; i++) { + if (temp[i] >= 'A' && temp[i] <= 'Z') { + temp[i] += ('a' - 'A'); + } + } + return String.valueOf(temp); +} + +//解法2:调用API 执行用时:0ms +public String toLowerCase(String str) { + return str.toLowerCase(); +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_714_273.java b/Week 08/id_273/LeetCode_714_273.java new file mode 100644 index 000000000..6ae687fda --- /dev/null +++ b/Week 08/id_273/LeetCode_714_273.java @@ -0,0 +1,36 @@ +//714. 买卖股票的最佳时机含手续费 + +//当前问题分析: +// 只需要在原DP方程的基础上, 每次完成交易的时候减去手续费即可 +// + +//解法1:DP 执行用时:22ms +//时间复杂度:O(N) +//空间复杂度:O(N*2) +public int maxProfit(int[] prices, int fee) { + int[][] dp = new int[prices.length][2]; + for (int i = 0; i < prices.length; i++) { + if (i - 1 == -1) { + dp[i][0] = 0; + dp[i][1] = -prices[i]; + continue; + } + dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee); + dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]); + } + return Math.max(dp[prices.length - 1][0], dp[prices.length - 1][1]); +} + +//解法2:DP降维 执行用时:8ms +//时间复杂度:O(N) +//空间复杂度:O(1) +public int maxProfit(int[] prices, int fee) { + int[] dp = new int[2]; + dp[0] = 0; + dp[1] = -prices[0]; + for (int i = 1; i < prices.length; i++) { + dp[0] = Math.max(dp[0], dp[1] + prices[i] - fee); + dp[1] = Math.max(dp[1], dp[0] - prices[i]); + } + return dp[0]; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_72_273.java b/Week 08/id_273/LeetCode_72_273.java new file mode 100644 index 000000000..f13c9e81d --- /dev/null +++ b/Week 08/id_273/LeetCode_72_273.java @@ -0,0 +1,23 @@ +//72. 编辑距离 + +//解法1:二维DP 执行用时:12ms +//思路: +public int minDistance(String word1, String word2) { + int[][] dp = new int[word1.length() + 1][word2.length() + 1]; + for (int i = 0; i < dp.length; i++) { + dp[i][0] = i; + } + for (int j = 0; j < dp[0].length; j++) { + dp[0][j] = j; + } + for (int i = 1; i < dp.length; i++) { + for (int j = 1; j < dp[0].length; j++) { + if (word1.charAt(i - 1) == word2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1]; + continue; + } + dp[i][j] = 1 + Math.min(dp[i - 1][j - 1], Math.min(dp[i][j - 1], dp[i - 1][j])); + } + } + return dp[dp.length - 1][dp[0].length - 1]; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_746_273.java b/Week 08/id_273/LeetCode_746_273.java new file mode 100644 index 000000000..35adbe3e2 --- /dev/null +++ b/Week 08/id_273/LeetCode_746_273.java @@ -0,0 +1,16 @@ +//746. 使用最小花费爬楼梯 + +public int minCostClimbingStairs(int[] cost) { + int[] dp = new int [cost.length + 1]; + dp[0] = cost[0]; + dp[1] = Math.min(dp[0] + cost[1], cost[1]); + for (int i = 2; i < dp.length; i++) { + if (i == cost.length) { + dp[i] = Math.min(dp[i - 1], dp[i - 2]); + break; + } + dp[i] = Math.min(dp[i - 1] + cost[i], dp[i - 2] + cost[i]); + + } + return dp[cost.length]; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_771_273.java b/Week 08/id_273/LeetCode_771_273.java new file mode 100644 index 000000000..62e500e28 --- /dev/null +++ b/Week 08/id_273/LeetCode_771_273.java @@ -0,0 +1,16 @@ +//771. 宝石与石头 + +//解法1:暴力 执行用时:1ms +//思路:遍历宝石的所有字符, 观察都在石头中出现了几次 +// 因为题目给定宝石不会重复, 所以该解法是可行的, 否则不通过 +//时间复杂度:O(J.Length * S.Length) +//空间复杂度:O(1) +public int numJewelsInStones(String J, String S) { + int count = 0; + for (char c1 : J.toCharArray()) { + for (char c2 : S.toCharArray()) { + if (c1 == c2) count++; + } + } + return count; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_818_273.java b/Week 08/id_273/LeetCode_818_273.java new file mode 100644 index 000000000..278bf7e05 --- /dev/null +++ b/Week 08/id_273/LeetCode_818_273.java @@ -0,0 +1,24 @@ +//818. 赛车 + +//解法1:DP 执行用时:7ms +//思路: +public int racecar(int target) { + int[] dp = new int[target + 1]; + for (int i = 1; i <= target; i++) { + dp[i] = Integer.MAX_VALUE; + for (int forward = 1; (1 << forward) - 1 < i * 2; forward++) { + int j = (1 << forward) - 1; + if (i == j) { + dp[i] = forward; + } else if (j > i) { + dp[i] = Math.min(dp[i], dp[j - i] + forward + 1); + } else { + for (int back = 0; back < forward; back++) { + int k = (1 << back) - 1; + dp[i] = Math.min(dp[i], dp[i - j + k] + forward + 2 + back); + } + } + } + } + return dp[target]; +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_8_273.java b/Week 08/id_273/LeetCode_8_273.java new file mode 100644 index 000000000..7758273af --- /dev/null +++ b/Week 08/id_273/LeetCode_8_273.java @@ -0,0 +1,43 @@ +//8. 字符串转换整数 + +//解法1:暴力解法 执行用时:2ms +//思路: +// 1. startIdx指向第一个不为空格的字符, 起到trim()的作用 +// 2. sign标识正负号, 且只能设置一次sign, 且进行计数后不能再出现正负号, 因此通过一个boolean参数进行记录 +// 3. 如果第一个字符不是符号也不是数字, 那么就无法执行有效的转换, 直接break +// 4. 若res在累加途中, 大于Integer的最大/最小值, 那么就直接返回结果 +public int myAtoi(String str) { + if (str.length() == 0) return 0; + char[] temp = str.toCharArray(); + int sign = 1; + long res = 0; + boolean flag = false; + int startIdx = 0; + //startIdx指向第一个不为空格的字符 + while (startIdx < str.length() && temp[startIdx] == ' ') { + startIdx++; + } + //从startIdx开始遍历字符串 + for (int i = startIdx; i < temp.length; i++) { + //如果遍历到的字符不是数字, 那么可能是符号位, 也可能是其他字符串 + if (temp[i] < '0' || temp[i] > '9') { + if (temp[i] == '+' && !flag) { //设置符号位 + flag = true; + continue; + } else if (temp[i] == '-' && !flag) { + sign = -1; + flag = true; + continue; + } else {//如果是其他字符串, 那么直接break, res累加结束 + break; + } + } + res = res * 10 + (temp[i] - '0');//累加res + flag = true;//如果未设置符号位, 那么默认是为正数的, 此时如果累加途中遇到了符号位, 我们不能让它影响到此时累加的结果, 因此此处设置了flag=true + if (res > Integer.MAX_VALUE) { + if (sign == 1) return Integer.MAX_VALUE; + else return Integer.MIN_VALUE; + } + } + return (int)(res * sign); +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_917_273.java b/Week 08/id_273/LeetCode_917_273.java new file mode 100644 index 000000000..aceac1e8b --- /dev/null +++ b/Week 08/id_273/LeetCode_917_273.java @@ -0,0 +1,18 @@ +//917. 仅仅反转字母 + +//解法1:暴力解法 执行用时:1ms +//思路: +public String reverseOnlyLetters(String S) { + char[] arr = S.toCharArray(); + int i = 0, j = S.length() - 1; + while (i < j) { + while (i < j && !((arr[i] >= 'a' && arr[i] <= 'z') || (arr[i] >= 'A' && arr[i] <= 'Z'))) i++; + while (i < j && !((arr[j] >= 'a' && arr[j] <= 'z') || (arr[j] >= 'A' && arr[j] <= 'Z'))) j--; + char temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + i++; + j--; + } + return String.valueOf(arr); +} \ No newline at end of file diff --git a/Week 08/id_273/LeetCode_91_273.java b/Week 08/id_273/LeetCode_91_273.java new file mode 100644 index 000000000..073040cfd --- /dev/null +++ b/Week 08/id_273/LeetCode_91_273.java @@ -0,0 +1,19 @@ +//91. 解码方法 + +//解法1:DP 执行用时:1ms +public int numDecodings(String s) { + int len = s.length(); + int[] dp = new int[len + 1]; + dp[len] = 1; + dp[len - 1] = s.charAt(len - 1) - '0' == 0 ? 0 : 1; + for (int i = len - 2; i >= 0; i--) { + if (s.charAt(i) - '0' == 0) { + dp[i] = 0; + } else if ((s.charAt(i) - '0') * 10 + (s.charAt(i + 1) - '0') <= 26) { + dp[i] = dp[i + 1] + dp[i + 2]; + } else { + dp[i] = dp[i + 1]; + } + } + return dp[0]; +} \ No newline at end of file diff --git a/Week 08/id_273/NOTE.md b/Week 08/id_273/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_273/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_278/Leetcode_300_278.js b/Week 08/id_278/Leetcode_300_278.js new file mode 100644 index 000000000..7e4414066 --- /dev/null +++ b/Week 08/id_278/Leetcode_300_278.js @@ -0,0 +1,21 @@ +/** + * @param {number[]} nums + * @return {number} + */ +var lengthOfLIS = function(nums) { + var f = new Array(nums.length); + var max = 0; + var i, j; + for (i = 0; i < nums.length; i++) { + f[i] = 1; + for (j = 0; j < i; j++) { + if (nums[j] < nums[i]) { + f[i] = f[i] > f[j] + 1 ? f[i] : f[j] + 1; + } + } + if (f[i] > max) { + max = f[i]; + } + } + return max; +}; diff --git a/Week 08/id_278/Leetcode_5_278.js b/Week 08/id_278/Leetcode_5_278.js new file mode 100644 index 000000000..6e8e4ba8d --- /dev/null +++ b/Week 08/id_278/Leetcode_5_278.js @@ -0,0 +1,38 @@ +/** + * @param {string} s + * @return {string} + */ +var longestPalindrome = function(s) { + let longest = 0; + let current = 0; + let start = 0; + for (let i = 0; i < s.length; i++) { + //find length for "aba" + current = findLength(s, i, i); + if (current > longest) { + longest = current; + start = i - Math.floor(current / 2); + } + + //find length for "abba" + current = findLength(s, i, i + 1); + if (current > longest) { + longest = current; + start = i - Math.floor(current / 2) + 1; + } + } + return s.substring(start, start + longest); +}; + +function findLength(s, left, right) { + let length = 0; + while (left >= 0 && right < s.length) { + if (s[left] !== s[right]) { + break; + } + length += left === right ? 1 : 2; + left--; + right++; + } + return length; +} diff --git a/Week 08/id_278/NOTE.md b/Week 08/id_278/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_278/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_283/LeetCode_32_283.java b/Week 08/id_283/LeetCode_32_283.java new file mode 100644 index 000000000..4bb8bddc3 --- /dev/null +++ b/Week 08/id_283/LeetCode_32_283.java @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode id=32 lang=java + * + * [32] Longest Valid Parentheses + */ + +// @ly code=start +class Solution { + public int longestValidParentheses(String s) { + int max = 0; + int dp[] = new int[s.length()]; + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) == ')') { + if (s.charAt(i - 1) == '(') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + max = Math.max(max, dp[i]); + } + } + return max; + } + +} +// @lc code=end + diff --git a/Week 08/id_283/LeetCode_5_283.java b/Week 08/id_283/LeetCode_5_283.java new file mode 100644 index 000000000..3094d1f91 --- /dev/null +++ b/Week 08/id_283/LeetCode_5_283.java @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode id=5 lang=java + * + * [5] Longest Palindromic Substring + */ + +// @lc code=start +class Solution { + public String longestPalindrome(String s) { + if (s == null || s.length() < 1) + { + return ""; + } + int start = 0, end = 0; + for (int i = 0; i < s.length(); i++) { + int len = Math.max(expand(s, i, i), expand(s, i, i + 1)); + if (len > end - start) { + start = i - (len - 1) / 2; + end = i + len / 2; + } + } + return s.substring(start, end + 1); + } + + + private int expand(String s, int left, int right) { + while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) { + left--; + right++; + } + return right - left - 1; + } +} +// @lc code=end + diff --git a/Week 08/id_283/NOTE.md b/Week 08/id_283/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_283/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_288/NOTE.md b/Week 08/id_288/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_288/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_293/NOTE.md b/Week 08/id_293/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_293/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_298/NOTE.md b/Week 08/id_298/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_298/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_298/first-unique-character-in-a-string.py b/Week 08/id_298/first-unique-character-in-a-string.py new file mode 100644 index 000000000..978a0e525 --- /dev/null +++ b/Week 08/id_298/first-unique-character-in-a-string.py @@ -0,0 +1,17 @@ +class Solution: + def firstUniqChar(self, s: str) -> int: + temp_dict = {} + chs = [] + result = None + if s == "": return -1 + for idx, ch in enumerate(s): + chs.append(ch) + if ch not in temp_dict: + temp_dict[ch] = 1 + else: + temp_dict[ch] += 1 + for idx, ch in enumerate(chs): + if temp_dict[ch] == 1: + result = idx + break + return -1 if result is None else result diff --git a/Week 08/id_298/longest-increasing-subsequence.py b/Week 08/id_298/longest-increasing-subsequence.py new file mode 100644 index 000000000..997fe1bca --- /dev/null +++ b/Week 08/id_298/longest-increasing-subsequence.py @@ -0,0 +1,11 @@ +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + if not nums: + return 0 + dp = [1] * len(nums) + for i in range(len(nums)): + for j in range(i): + if nums[j] < nums[i]: + dp[i] = max(dp[i], dp[j] + 1) + return max(dp) + diff --git a/Week 08/id_298/string-to-integer-atoi.py b/Week 08/id_298/string-to-integer-atoi.py new file mode 100644 index 000000000..e4655bcfe --- /dev/null +++ b/Week 08/id_298/string-to-integer-atoi.py @@ -0,0 +1,24 @@ +class Solution: + def myAtoi(self, str: str) -> int: + new_str = str.strip() + if len(new_str) == 0: + return 0 + tmp = "0" + result = 0 + i = 0 + if new_str[0] in "-+": + tmp = new_str[0] + i = 1 + for i in range(i, len(new_str)): + if new_str[i].isdigit(): + tmp += new_str[i] + else: + break + if len(tmp) > 1: + result = int(tmp) + result = max(result, -(2 ** 31)) + result = min(result, 2 ** 31 - 1) + return result + + +# 使用i控制第一位 diff --git a/Week 08/id_303/NOTE.md b/Week 08/id_303/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_303/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_308/LeedCode_14.js b/Week 08/id_308/LeedCode_14.js new file mode 100644 index 000000000..16ecef7f8 --- /dev/null +++ b/Week 08/id_308/LeedCode_14.js @@ -0,0 +1,22 @@ + +/** + * 题目: 最长公共前缀 + * 语言: JavaScript + * 执行结果: 打败了78.75%的用户 + * */ + + +var longestCommonPrefix = function(strs) { + let res = strs[0] || ''; + if(res.length == 0) return ''; + for(let i=1;iJ.indexOf(v) > -1); + return arr.length; +}; diff --git a/Week 08/id_308/LeedCode_8.js b/Week 08/id_308/LeedCode_8.js new file mode 100644 index 000000000..17cde9ebb --- /dev/null +++ b/Week 08/id_308/LeedCode_8.js @@ -0,0 +1,21 @@ + +/** + * 题目: 字符串转换整数 (atoi) + * 语言: JavaScript + * 执行结果: 打败了63.59%的用户 + * */ + + +/** + * @param {string} str + * @return {number} + */ +var myAtoi = function(str) { + const count = parseInt(str); + const Max = Math.pow(2,31)-1; + const Min = Math.pow(-2,31); + if(count > Max) return Max; + if(count < Min) return Min; + + return count || 0; +}; diff --git a/Week 08/id_308/NOTE.md b/Week 08/id_308/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_308/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_313/LeetCode_300_313.go b/Week 08/id_313/LeetCode_300_313.go new file mode 100644 index 000000000..004a225d4 --- /dev/null +++ b/Week 08/id_313/LeetCode_300_313.go @@ -0,0 +1,29 @@ +package id_313 + +func lengthOfLIS(nums []int) int { + tails := make([]int, 0) + length := len(nums) + for i := 0; i < length; i++ { + tlen := len(tails) + if tlen == 0 { + tails = []int{nums[i]} + continue + } + if nums[i] > tails[tlen-1] { + tails = append(tails, nums[i]) + continue + } + + start, end, mid := 0, tlen-1, 0 + for start != end { + mid = start + (end-start)/2 + if nums[i] > tails[mid] { + start = mid + 1 + } else { + end = mid + } + } + tails[end] = nums[i] + } + return len(tails) +} diff --git a/Week 08/id_313/LeetCode_387_313.go b/Week 08/id_313/LeetCode_387_313.go new file mode 100644 index 000000000..8705d8341 --- /dev/null +++ b/Week 08/id_313/LeetCode_387_313.go @@ -0,0 +1,16 @@ +package id_387 + +func firstUniqChar(s string) int { + rec := make([]int, 26) + for _, bs := range s { + rec[bs-'a']++ + } + + for i, bs := range s { + if rec[bs-'a'] == 1 { + return i + } + } + + return -1 +} diff --git a/Week 08/id_313/LeetCode_8_313.go b/Week 08/id_313/LeetCode_8_313.go new file mode 100644 index 000000000..aadf39bd2 --- /dev/null +++ b/Week 08/id_313/LeetCode_8_313.go @@ -0,0 +1,46 @@ +package id_313 + +import "math" + +func myAtoi(str string) int { + b := []byte(str) + s := 1 + n := 0 + i := 0 + + for i < len(b) && b[i] == ' ' { + i++ + } + + if i < len(b) && (b[i] == '+' || b[i] == '-') { + if b[i] == '+' { + s = 1 + } else { + s = -1 + } + i++ + } + + if i < len(b) && (b[i] == '0') { + i++ + } + + for i < len(b) { + if b[i] >= '0' && b[i] <= '9' { + n *= 10 + n += int(b[i] - '0') + if s == 1 && n >= math.MaxInt32 { + return math.MaxInt32 + } else if s == -1 && n >= math.MaxInt32+1 { + return math.MinInt32 + } + } else { + break + } + i++ + } + + n *= s + + return n +} diff --git a/Week 08/id_313/LeetCode_91_313.go b/Week 08/id_313/LeetCode_91_313.go new file mode 100644 index 000000000..ebae9017c --- /dev/null +++ b/Week 08/id_313/LeetCode_91_313.go @@ -0,0 +1,34 @@ +package id_313 + +func numDecodings(s string) int { + size := len(s) + if size == 0 { + return 0 + } + m := make([]int, size+1) + m[size] = 1 + if s[size-1] != '0' { + m[size-1] = 1 + } + + for i := size - 2; i >= 0; i-- { + switch cur := s[i]; cur { + case '0': + if s[i+1] == '0' || i == 0 { + return 0 + } + m[i] = 0 + case '1': + m[i] = m[i+1] + m[i+2] + case '2': + if int(s[i+1]-'0') < 7 { + m[i] = m[i+1] + m[i+2] + } else { + m[i] = m[i+1] + } + default: + m[i] = m[i+1] + } + } + return m[0] +} diff --git a/Week 08/id_313/NOTE.md b/Week 08/id_313/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_313/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_318/LeetCode_300_318.py b/Week 08/id_318/LeetCode_300_318.py new file mode 100644 index 000000000..da225367a --- /dev/null +++ b/Week 08/id_318/LeetCode_300_318.py @@ -0,0 +1,23 @@ +# +# @lc app=leetcode id=300 lang=python3 +# +# [300] Longest Increasing Subsequence +# + +# @lc code=start +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + tails, res = [0] * len(nums), 0 + for num in nums: + i, j = 0, res + while i < j: + m = (i + j) // 2 + if tails[m] <= num: i = m + 1 + else: j = m + tails[i] = num + if j == res: + res += 1 + return res + +# @lc code=end + diff --git a/Week 08/id_318/LeetCode_91_318.java b/Week 08/id_318/LeetCode_91_318.java new file mode 100644 index 000000000..37ac90d7e --- /dev/null +++ b/Week 08/id_318/LeetCode_91_318.java @@ -0,0 +1,37 @@ +/* + * @lc app=leetcode id=91 lang=java + * + * [91] Decode Ways + */ + +// @lc code=start +class Solution { + public int numDecodings(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int len = s.length(); + + int help = 1; + int res = 0; + if (s.charAt(len - 1) != '0') { + res = 1; + } + for (int i = len - 2; i >= 0; i--) { + if (s.charAt(i) == '0') { + help = res; + res = 0; + continue; + } + if ((s.charAt(i) - '0') * 10 + (s.charAt(i + 1) - '0') <= 26) { + res += help; + help = res-help; + } else { + help = res; + } + } + return res; + } +} +// @lc code=end + diff --git a/Week 08/id_318/NOTE.md b/Week 08/id_318/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_318/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_323/NOTE.md b/Week 08/id_323/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_323/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_328/FirstUniqChar.java b/Week 08/id_328/FirstUniqChar.java new file mode 100644 index 000000000..617fb2feb --- /dev/null +++ b/Week 08/id_328/FirstUniqChar.java @@ -0,0 +1,16 @@ +class Solution { + public int firstUniqChar(String s) { + HashMap count = new HashMap(); + int n = s.length(); + for (int i = 0; i < n; i++) { + char c = s.charAt(i); + count.put(c, count.getOrDefault(c, 0) + 1); + } + + for (int i = 0; i < n; i++) { + if (count.get(s.charAt(i)) == 1) + return i; + } + return -1; + } +} \ No newline at end of file diff --git a/Week 08/id_328/LengthOfLIS.java b/Week 08/id_328/LengthOfLIS.java new file mode 100644 index 000000000..0ca4b8f7b --- /dev/null +++ b/Week 08/id_328/LengthOfLIS.java @@ -0,0 +1,21 @@ +class Solution { + public int lengthOfLIS(int[] nums) { + if (nums.length == 0) { + return 0; + } + int[] dp = new int[nums.length]; + dp[0] = 1; + int maxans = 1; + for (int i = 1; i < dp.length; i++) { + int maxval = 0; + for (int j = 0; j < i; j++) { + if (nums[i] > nums[j]) { + maxval = Math.max(maxval, dp[j]); + } + } + dp[i] = maxval + 1; + maxans = Math.max(maxans, dp[i]); + } + return maxans; + } +} \ No newline at end of file diff --git a/Week 08/id_328/NOTE.md b/Week 08/id_328/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_328/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_333/NOTE.md b/Week 08/id_333/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_333/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_338/LeetCode_14_338.java b/Week 08/id_338/LeetCode_14_338.java new file mode 100644 index 000000000..595490762 --- /dev/null +++ b/Week 08/id_338/LeetCode_14_338.java @@ -0,0 +1,21 @@ +package id_338; + +/** + * @author Leesen + * @date 2019/12/8 23:10 + */ +public class LeetCode_14_338 { + public String longestCommonPrefix(String[] strs) { + if (strs.length == 0) { + return ""; + } + for (int i=0; i map = new LinkedHashMap<>(); + for (int i = 0; i < s.length(); i++) { + map.put(s.charAt(i), map.getOrDefault(s.charAt(i), 0) + 1); + } + for (int i = 0; i < s.length(); i++) { + if (map.get(s.charAt(i)) == 1) { + return i; + } + } + return -1; + } +} diff --git a/Week 08/id_338/LeetCode_72_338.java b/Week 08/id_338/LeetCode_72_338.java new file mode 100644 index 000000000..199174f06 --- /dev/null +++ b/Week 08/id_338/LeetCode_72_338.java @@ -0,0 +1,35 @@ +package id_338; + +/** + * @author Leesen + * @date 2019/12/8 23:08 + */ +public class LeetCode_72_338 { + public static int minDistance(String word1, String word2) { + int[][] dp = new int[word1.length()+1][word2.length()+1]; + //****自底向上, 初始化条件要联想那个表格, 代表空字符与字符串的编辑距离。 各种等号容易遗漏 + for (int i=1; i=0; i--) { + num += (str.charAt(i) - '0') * Math.pow(10, len-i-1); //ascii码相减后0就是0 + str = str.substring(0, i); + } + return num; + } +} diff --git a/Week 08/id_338/NOTE.md b/Week 08/id_338/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_338/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_343/LeetCode_8.go b/Week 08/id_343/LeetCode_8.go new file mode 100644 index 000000000..bb8592d48 --- /dev/null +++ b/Week 08/id_343/LeetCode_8.go @@ -0,0 +1,40 @@ +func myAtoi(str string) int { + start, sign, total := 0, 1, 0 + for start < len(str) { + if str[start] != ' ' { + break + } + start++ + } + if len(str) == start { + return 0 + } + first := str[start] + + if first == '-' { + sign = -1 + start++ + } else if first == '+' { + start++ + } else if first < '0' || first > '9' { + return 0 + } + + for ; start < len(str); start++ { + dig := int(str[start] - '0') + if dig > 9 || dig < 0 { + break + } + if math.MaxInt32/10 < total || (math.MaxInt32/10 == total && math.MaxInt32%10 < dig){ + if sign > 0 { + return math.MaxInt32 + } else { + return math.MinInt32 + } + } + + total = 10 * total + dig + } + + return sign * total +} diff --git a/Week 08/id_343/LeetCode_917.go b/Week 08/id_343/LeetCode_917.go new file mode 100644 index 000000000..317354dde --- /dev/null +++ b/Week 08/id_343/LeetCode_917.go @@ -0,0 +1,30 @@ +func reverseOnlyLetters(S string) string { + i, j := 0, len(S) - 1 + + var res = []byte(S) + + for i < j { + if !isWord(res[i]) { + i++ + continue + } + if !isWord(res[j]) { + j-- + continue + } + + res[i], res[j] = res[j], res[i] + i++ + j-- + } + + return string(res) +} + +func isWord(s byte) bool { + if (s >= 'a' && s <= 'z') || (s >= 'A' && s <= 'Z') { + return true + } + + return false +} diff --git a/Week 08/id_343/NOTE.md b/Week 08/id_343/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_343/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_348/NOTE.md b/Week 08/id_348/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_348/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_353/Leetcode_300_353.cpp b/Week 08/id_353/Leetcode_300_353.cpp new file mode 100644 index 000000000..9ce6f9de4 --- /dev/null +++ b/Week 08/id_353/Leetcode_300_353.cpp @@ -0,0 +1,29 @@ +/* + * @lc app=leetcode.cn id=300 lang=cpp + * + * [300] 最长上升子序列 + */ + +// @lc code=start +class Solution { +public: + int lengthOfLIS(vector& nums) { + if (nums.size() == 0) return 0; + int dp[nums.size()]; + int res = 0; + + for (int n = 0; n < nums.size(); n++) { + dp[n] = 1; + } + for (int i = 0; i < nums.size(); i++) { + for (int j = 0; j < i; j++) { + if (nums[j] < nums[i]) + dp[i] = max(dp[i], dp[j] + 1); + } + res = max(res, dp[i]); + } + return res; + } +}; +// @lc code=end + diff --git a/Week 08/id_353/Leetcode_387_353.cpp b/Week 08/id_353/Leetcode_387_353.cpp new file mode 100644 index 000000000..ec79de137 --- /dev/null +++ b/Week 08/id_353/Leetcode_387_353.cpp @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode.cn id=387 lang=cpp + * + * [387] 字符串中的第一个唯一字符 + */ + +// @lc code=start +class Solution { +public: + int firstUniqChar(string s) { + std::map char_count; + int n = s.length(); + + for (int i = 0; i < n; i++) { + char_count[s[i]]++; + } + + // find the index + for (int i = 0; i < n; i++) { + if (char_count[s[i]] == 1) + return i; + } + return -1; + } +}; +// @lc code=end + diff --git a/Week 08/id_353/NOTE.md b/Week 08/id_353/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_353/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_358/NOTE.md b/Week 08/id_358/NOTE.md new file mode 100755 index 000000000..8d32c3526 --- /dev/null +++ b/Week 08/id_358/NOTE.md @@ -0,0 +1,110 @@ +# 第8周学习总结 + +## 第19课 高级动态规划 + +### 回顾解决DP问题的步骤 + +1. 分解成子问题 +2. 分治 + 最优子结构 +3. 递推公式 + +### DP解法的代码模板 + +``` +function dp() { + dp = [][] // 定义状态数组,可能为一维,二维。。。 + // 初始化 + dp[0] or dp[0][x] + for i in 0...m: + for j in 0...n: + // dp状态转移方程。 + dp[i][j] = _function(dp[i'][j']) + return dp[m][n] // 返回结果 +} +``` + +### 高级DP问题复杂度来源 + +1. 状态拥有更多维度 +2. 状态方程更加复杂 + +### 实战题 + +#### 1.爬楼梯问题改进 + +定义更多状态数组 + +#### 2.编辑距离 + +1. BFS(双端BFS) +2. DP + +dp[i][j]: word1的0...i 字符串与word2的0...j字符串的编辑距离 + +当 word1[i] === word2[j], dp[i][j] = dp[i-1][j-1] + +否则, dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1 + +## 第20课 字符串问题 + +### 高级字符串算法 + +### 1. 编辑距离 + +#### 2. 最长公共子序列 + +if t[i] = s[j], dp[i][j] = dp[i-1][j-1] + 1 + +else dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + + return dp[m][n] + +#### 3. 最长公共子串(连续且顺序相同) + +if text1[j-1] = text2[i-1] dp[i][j] = dp[i-1][j-1] +1 + +else dp[i][j] = 0 + +#### 4. 最长回文子串 + +1. 暴力法 + +2. 枚举回文子串的中心 + +每次循环选择一个中心,左右扩展,判断左右是否相等。由于存在奇数偶数子串,需要分布从 i(中间元素), i+1(元素中间空隙)扩展 + +3. 动态规划 + +dp[i][j] = true( s(i,j))是回文串, false(s(i,j))不是回文串 + +i是起点,j是终点 + +dp[i][j] = dp[i+1][j+1] && s[i] = s[j] (dp[i+1][j+1]) = true + +#### 5 实现正则 + +dp + + +#### 6 不同的子序列 + +## 字符串匹配算法 + +1. 暴力法 + +2. 暴力法上改进 + + 1)预先判断: hash(txt.substring(i,m)) = hash(pat),hash值一致则继续比较。也叫Rabin-karp 算法 + 2) KMP 算法 + +### Rabin-Karp 算法思想 + +1. 设子串长度为M(pat),目标字符串长度为N(txt) +2. 计算子串的hash值 hash(pat) +3. 计算目标字符串中每个长度为M的子串的hash值,需要计算N-M+1次 +4. 比较hash值,不同则不匹配,相同则还需要继续用朴素算法再次比较每个字符。 + +### KMP + +1. 计算 前缀表 +2. 循环字符串,根据前缀表,不断比较,横向移动 \ No newline at end of file diff --git a/Week 08/id_358/decode-ways.js b/Week 08/id_358/decode-ways.js new file mode 100644 index 000000000..1f388c059 --- /dev/null +++ b/Week 08/id_358/decode-ways.js @@ -0,0 +1,31 @@ +/** + * @param {string} s + * @return {number} + * dp[i] : 从第i个字符开始的字符串的解码结果个数 + * if(s[i] === 0) return 0 + * if s[i] + s[i+1] <= 26, dp[i] = dp[i+1] + dp[i+2] + * else dp[i] = dp[i+1] + */ +var numDecodings = function(s) { + if(!s || s.length === 0) return 0; + const n = s.length; + const dp = new Array(n+1) + dp[n] = 1; + if(s[n-1] === '0') { + dp[n-1] = 0 + } else { + dp[n-1] = 1 + } + for(let i = n-2; i >= 0; i--) { + if (s[i] === '0') { + dp[i] = 0; + continue; + } + if(parseInt(s[i])*10 + parseInt(s[i+1]) <=26) { + dp[i] = dp[i+1] + dp[i+2] + } else { + dp[i] = dp[i+1] + } + } + return dp[0] +}; \ No newline at end of file diff --git a/Week 08/id_358/distinct_subsequences.js b/Week 08/id_358/distinct_subsequences.js new file mode 100644 index 000000000..18879d70e --- /dev/null +++ b/Week 08/id_358/distinct_subsequences.js @@ -0,0 +1,35 @@ +/** 不同的子序列 + * @param {string} s + * @param {string} t + * @return {number} + * 动态规划 + +dp[i][j] 代表 T 前 i 字符串可以由 S j 字符串组成最多个数. +所以动态方程: +当 S[j] == T[i] , dp[i][j] = dp[i-1][j-1] + dp[i][j-1]; +当 S[j] != T[i] , dp[i][j] = dp[i][j-1] +二维数组中,S作为行,T作为列 +初始化时,第一行都是1,表示空字符串的匹配个数都是1,第一列都是0,表示没有匹配的字符, + */ +var numDistinct = function(s, t) { + const m = s.length; + const n = t.length; + const dp = [] + for (let i = 0; i <= n; i++) { + dp[i] = new Array(m+1).fill(0) + } + // 初始化第一行 + for (let j = 0; j <= m; j++) { + dp[0][j] = 1 + } + for(let i = 1; i <= n; i++) { + for (let j = 1; j <= m; j++) { + if(t[i-1] === s[j-1]) { + dp[i][j] = dp[i-1][j-1] + dp[i][j-1] // + } else { + dp[i][j] = dp[i][j-1] + } + } + } + return dp[n][m] +}; \ No newline at end of file diff --git a/Week 08/id_358/first_unique_char.js b/Week 08/id_358/first_unique_char.js new file mode 100644 index 000000000..ca24f60dd --- /dev/null +++ b/Week 08/id_358/first_unique_char.js @@ -0,0 +1,20 @@ +/** + * @param {string} s + * @return {number} + */ +var firstUniqChar = function(s) { + const set = {} + s.split('').forEach(i => { + if(set[i]) { + set[i] ++ + } else { + set[i] = 1 + } + }) + for(let i = 0; i < s.length; i++) { + if(set[s[i]] === 1){ + return i; + } + } + return -1 +}; \ No newline at end of file diff --git a/Week 08/id_358/longest-increasing-subsequence.js b/Week 08/id_358/longest-increasing-subsequence.js new file mode 100644 index 000000000..4c19b1095 --- /dev/null +++ b/Week 08/id_358/longest-increasing-subsequence.js @@ -0,0 +1,18 @@ +/** + * @param {number[]} nums + * @return {number} + * dp[i]: nums中前i 个最长上升子序列长度 + * for j in range(0,i) dp[i] = max(dp[j]+1, dp[i]) if nums[i] < nums[j] + */ +var lengthOfLIS = function(nums) { + if(nums.length === 0) return 0; + let dp = new Array(nums.length).fill(1) + for(let i = 0; i < nums.length; i++) { + for (let j = 0; j < i; j++) { + if(nums[j] < nums[i]) + dp[i] = Math.max(dp[i], dp[j] +1) + } + } + const res = dp.sort((a,b) => a - b) + return res[nums.length - 1]; +}; \ No newline at end of file diff --git a/Week 08/id_363/LeetCode_115_363.java b/Week 08/id_363/LeetCode_115_363.java new file mode 100644 index 000000000..790c28606 --- /dev/null +++ b/Week 08/id_363/LeetCode_115_363.java @@ -0,0 +1,81 @@ +package com.test.leetcode.week08; + +import org.junit.Test; + + +public class SolutionDistinctSubsequence115 { + + + /** + * 给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。 + * + * 一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。 + * (例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是 + * + * 输入: S = "rabbbit", T = "rabbit" + * 输出: 3 + * 解释: + * + * 如下图所示, 有 3 种可以从 S 中得到 "rabbit" 的方案。 + * (上箭头符号 ^ 表示选取的字母) + * + * rabbbit + * ^^^^ ^^ + * rabbbit + * ^^ ^^^^ + * rabbbit + * ^^^ ^^^ + * + */ + @Test + public void test1() { + System.out.println(numDistinct("rabbbit", "rabbit")); + System.out.println(numDistinct("babgbag", "bag")); + } + + /** + * DP + * 两个字符串之间的变换问题经常使用一个二维数组来解决 + * 1.子问题 + * 2.状态数组 存储当前s,t 中 s的子序列中t出现的个数 + * 3.DP方程 + * + * @param s + * @param t + * @return + */ + public int numDistinct(String s, String t) { + if (s == null || s.length() == 0) { + return 0; + } + if (t == null || t.length() == 0) { + return 1; + } + char[] cols = ("#" + s).toCharArray(); + char[] rows = ("#" + t).toCharArray(); + int rowCnt = rows.length, colCnt = cols.length; + int[][] dp = new int[rowCnt][colCnt]; + dp[0][0] = 1; + for (int c = 1; c < colCnt; c ++) { + dp[0][c] = 1; + } + for (int r = 1; r < rowCnt; r ++) { + dp[r][0] = 0; + } + for (int r = 1; r < rowCnt; r ++) { + for (int c = 1; c < colCnt; c++) { + if (rows[r] == cols[c]) { + dp[r][c] = dp[r - 1][c - 1] + dp[r][c - 1]; + } else { + dp[r][c] = dp[r][c - 1]; + } + } + } + return dp[rowCnt - 1][colCnt - 1]; + } + + + + + +} diff --git a/Week 08/id_363/LeetCode_300_363.java b/Week 08/id_363/LeetCode_300_363.java new file mode 100644 index 000000000..f286f9b13 --- /dev/null +++ b/Week 08/id_363/LeetCode_300_363.java @@ -0,0 +1,100 @@ +package com.test.leetcode.week08; + +import org.junit.Test; + +import java.util.Arrays; + + +public class SolutionLongestIncreasingSub300 { + + + /** + * 题目:给定一个无序的整数数组,找到其中最长上升子序列的长度。 + * 输入: [10,9,2,5,3,7,101,18] + * 输出: 4 + * 解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4 + * @return + */ + @Test + public void test1() { + System.out.println(lengthOfLIS(new int[]{2,2})); + System.out.println(lengthOfLIS(new int[]{10,9,2,5,3,7,101,18})); + System.out.println(lengthOfLIS(new int[]{3,2,1})); + + System.out.println(lengthOfLIS_2(new int[]{10,9,2,5,3,7,101,18})); + System.out.println(lengthOfLIS_2(new int[]{2,2})); + System.out.println(lengthOfLIS_2(new int[]{3,2,1})); + } + + + /** + * 时间复杂度O(n ^ 2) + * + * @param nums + * @return + */ + public int lengthOfLIS(int[] nums) { + if (nums.length == 1) { + return 1; + } + // 创建数组 求第一个比当前值大的元素的下标 + int len = nums.length; + // 从后想前循环nums + int[] dp = new int[len]; + Arrays.fill(dp, 1); + int max = 0; + for (int i = len - 2; i >= 0; i --) { + // 比他大的元素 + 1 + int cur = nums[i]; + for (int j = i + 1; j < len ; j ++) { + if (nums[j] > cur) { + dp[i] = Math.max(dp[i], dp[j] + 1); + } + max = Math.max(max, dp[i]); + } + } + return max; + } + + + /** + * 贪心算法 + 二分查找 + * + * @param nums + * @return + */ + public int lengthOfLIS_2(int[] nums) { + if (nums.length == 1) { + return 1; + } + int len = nums.length; + int[] tail = new int[len]; + tail[0] = nums[0]; + int end = 0; + // 如果比end值大, 那么直接加入到end中 + // 如果比end值小,那么替换tail中第一个大于等于num[i]的元素 + // 贪心:尽量让最终的有序数组都是最小值 + for (int i = 1; i < len; i ++) { + if (nums[i] > tail[end]) { + end ++; + tail[end] = nums[i]; + } else { + // 找到第一个>= nums[i] 的值 替换成 nums[i] + int left = 0, right = end; + while (left < right) { + int mid = left + ((right - left) >> 1); + if (tail[mid] < nums[i]) { + left = mid + 1; + } else { + right = mid; + } + } + tail[left] = nums[i]; + } + } + end ++; + return end; + } + + +} diff --git a/Week 08/id_363/LeetCode_32_363.java b/Week 08/id_363/LeetCode_32_363.java new file mode 100644 index 000000000..a61c96560 --- /dev/null +++ b/Week 08/id_363/LeetCode_32_363.java @@ -0,0 +1,50 @@ +package com.test.leetcode.week05; + +import org.junit.Test; + +import java.util.Stack; + + +public class SolutionLongestValidParenthese32 { + + + + @Test + public void test1() { + System.out.println(longestValidParentheses_20191208_1(")()())")); + System.out.println(longestValidParentheses_20191208_1("())")); + System.out.println(longestValidParentheses_20191208_1("(()")); + } + + + /** + * DP + * 1.子问题 + * 2.状态数组:记录以当前元素结尾的所有字序列中最大有效括号的对数 + * 3.dp方程 + * + * @param s + * @return + */ + public int longestValidParentheses_20191208_1(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int[] dp = new int[s.length()]; + char[] schars = s.toCharArray(); + int max = 0; + for (int i = 1; i < schars.length; i ++) { + if (schars[i] == ')') { + if (schars[i - 1] == '(') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + } else { + if (i > dp[i - 1] && schars[i - dp[i - 1] - 1] == '(') { + dp[i] = dp[i - 1] + 2 + (i >= dp[i - 1] + 2 ? dp[i - dp[i - 1] - 2] : 0); + } + } + max = Math.max(max, dp[i]); + } + } + return max; + } +} diff --git a/Week 08/id_363/LeetCode_85_363.java b/Week 08/id_363/LeetCode_85_363.java new file mode 100644 index 000000000..87211fc3e --- /dev/null +++ b/Week 08/id_363/LeetCode_85_363.java @@ -0,0 +1,55 @@ +package com.test.leetcode.week08; + +import org.junit.Test; + + +public class SolutionMaxmimalRectangle85 { + + + @Test + public void test1() { + char[][] matrix = { + {'1','0','1','0','0'}, + {'1','0','1','1','1'}, + {'1','1','1','1','1'}, + {'1','0','0','1','0'} + }; + System.out.println(maximalRectangle(matrix)); + } + + /** + * 给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。 + * 输入: + * [ + * {'1','0','1','0','0'}, + * {'1','0','1','1','1'}, + * {'1','1','1','1','1'}, + * {'1','0','0','1','0'} + * ] + * 输出: 6 + * + * @param matrix + * @return + */ + public int maximalRectangle(char[][] matrix) { + if (matrix == null || matrix.length == 0) { + return 0; + } + int maxArea = 0, row = matrix.length, col = matrix[0].length; + int[][] dp = new int[row][col]; + for (int r = 0; r < row; r ++) { + for (int c = 0; c < col; c ++) { + if (matrix[r][c] == '1') { + dp[r][c] = c == 0 ? 1 : dp[r][c - 1] + 1; + } + int width = dp[r][c]; + for (int k = r; k >= 0; k --) { + width = Math.min(width, dp[k][c]); + maxArea = Math.max(maxArea, width * (r - k + 1)); + } + } + } + return maxArea; + } + +} diff --git a/Week 08/id_363/LeetCode_91_363.java b/Week 08/id_363/LeetCode_91_363.java new file mode 100644 index 000000000..82687908d --- /dev/null +++ b/Week 08/id_363/LeetCode_91_363.java @@ -0,0 +1,55 @@ +package com.test.leetcode.week05; + +import org.junit.Test; + + +public class SolutionDecodeWays91 { + + + @Test + public void test1() { + System.out.println(numDecodings_20191208("220")); + System.out.println(numDecodings_20191208("12")); + System.out.println(numDecodings_20191208("0")); + System.out.println(numDecodings_20191208("226")); + + } + + /** + * 题目: 给定一个只包含数字的非空字符串,请计算解码方法的总数。 + * dp: + * 1.子问题 + * 2.状态数组 + * 3.DP方程 + * + * 循环数组 + * 如果当前值是0 前面的值 大于2 那么 返回 0 否则 dp[i] = dp[i - 1] + * 如果前一个值是1 或者 前一个值是 2 当前值 <= 6 那么 dp[i] = dp[i-1] + dp[i - 2] + * 其他情况 : dp[i] = dp[i - 1]; + * + * @param s + * @return + */ + public int numDecodings_20191208(String s) { + if (s.charAt(0) == '0') { + return 0; + } + int[] dp = new int[s.length() + 1]; + dp[0] = 1; dp[1] = 1; + char[] schars = ("#" + s ).toCharArray(); + for (int i = 1; i < schars.length; i ++) { + if (schars[i] == '0') { + if (schars[i - 1] == '1' || schars[i - 1] == '2') { + dp[i] = dp[i - 2]; + } else { + return 0; + } + } else if (schars[i - 1] == '1' || (schars[i - 1] == '2' && schars[i] <= '6')) { + dp[i] = dp[i - 1] + dp[i - 2]; + } else { + dp[i] = dp[i - 1]; + } + } + return dp[s.length()]; + } +} diff --git a/Week 08/id_363/NOTE.md b/Week 08/id_363/NOTE.md new file mode 100755 index 000000000..53f230e7f --- /dev/null +++ b/Week 08/id_363/NOTE.md @@ -0,0 +1,9 @@ +#### 高级DP +复杂度来源: 1.状态由更多维度(二维、三维、甚至更多、或者需要压缩) 2.状态方程更加复杂 +#### 字符串匹配算法 +- 1.BF: 暴力匹配算法,假如模式串的长度是M,主串的长度是N。在主串的(N - M + 1)个子串中分别尝试和模式串M进行匹配。时间复杂度是O(NM); +- 2.RK: 使用hash算法来减少时间复杂度 + 算法步骤: 1.计算模式串的hash值。在主串的(N - M + 1)个子串中分别求hash值,如果hash(主串的子串) = hash(模式串),那么在挨个比较两者的字符是否相等。如果hash值不相等,那么直接跳过。时间复杂度是O(N) +- 3.KMP: 好前缀匹配, 主要思路是跳过一些绝对不会匹配的情况。 +- 4.BM:好后缀 和 坏字符 + \ No newline at end of file diff --git a/Week 08/id_368/NOTE.md b/Week 08/id_368/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_368/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_373/300.cpp b/Week 08/id_373/300.cpp new file mode 100644 index 000000000..917b875e2 --- /dev/null +++ b/Week 08/id_373/300.cpp @@ -0,0 +1,24 @@ +class Solution { +public: + int lengthOfLIS(vector &nums) { + int len = nums.size(); + if (len < 2) { + return len; + } + + vector dp(len, 1); + for (int i = 1; i < len; ++i) { + for (int j = 0; j < i; ++j) { + if (nums[j] < nums[i]) { + dp[i] = max(dp[i], dp[j] + 1); + } + } + } + + int res = dp[0]; + for (int k = 1; k < len; ++k) { + res = max(res, dp[k]); + } + return res; + } +}; diff --git a/Week 08/id_373/91.cpp b/Week 08/id_373/91.cpp new file mode 100644 index 000000000..3cfe7d4f0 --- /dev/null +++ b/Week 08/id_373/91.cpp @@ -0,0 +1,27 @@ +class Solution { +public: + int numDecodings(string s) { + const int size = s.size(); + if (s.size() == 0 || s[0] == '0') // 起始位0无解 + return 0; + int dp_last = 1; + int dp_last_last = 1; + for (int i = 1; i < size; i++) { + int cv = (s[i - 1] - '0') * 10 + (s[i] - '0'); + if (cv == 0 || cv > 26 && s[i] == '0') // 00 || x0 && x0 > 26 无解 + return 0; + // 1. 当前无法与上一个字符组合 + // 2. 当前为0只能和上一个结合 + // 3. 前一个为0只能和前一个结合 + // 4. 下一个为0 只能跟下一个结合 + if ((cv > 26 || s[i] == '0' || cv < 10) || (i < size - 1 && s[i + 1] == '0' )) { + dp_last_last = dp_last; + } else { + auto dbb = dp_last_last; + dp_last_last = dp_last; + dp_last += dbb; + } + } + return dp_last; + } +}; diff --git a/Week 08/id_373/NOTE.md b/Week 08/id_373/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_373/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_378/NOTE.md b/Week 08/id_378/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_378/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_383/NOTE.md b/Week 08/id_383/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_383/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_388/LeeCode_388_10.java b/Week 08/id_388/LeeCode_388_10.java new file mode 100644 index 000000000..7d48df696 --- /dev/null +++ b/Week 08/id_388/LeeCode_388_10.java @@ -0,0 +1,100 @@ +package com.company.leetcode.editor.cn;//给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。 +// +// '.' 匹配任意单个字符 +//'*' 匹配零个或多个前面的那一个元素 +// +// +// 所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。 +// +// 说明: +// +// +// s 可能为空,且只包含从 a-z 的小写字母。 +// p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。 +// +// +// 示例 1: +// +// 输入: +//s = "aa" +//p = "a" +//输出: false +//解释: "a" 无法匹配 "aa" 整个字符串。 +// +// +// 示例 2: +// +// 输入: +//s = "aa" +//p = "a*" +//输出: true +//解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。 +// +// +// 示例 3: +// +// 输入: +//s = "ab" +//p = ".*" +//输出: true +//解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。 +// +// +// 示例 4: +// +// 输入: +//s = "aab" +//p = "c*a*b" +//输出: true +//解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。 +// +// +// 示例 5: +// +// 输入: +//s = "mississippi" +//p = "mis*is*p*." +//输出: false +// Related Topics 字符串 动态规划 回溯算法 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_10 { + public boolean isMatch(String s, String p) { + if (s == null || p == null) { + return false; + } + boolean[][] dp = new boolean[s.length() + 1][p.length() + 1]; + dp[0][0] = true; + for (int i = 0; i < p.length(); i++) { + if (p.charAt(i) == '*' && dp[0][i - 1]) { + dp[0][i + 1] = true; + } + } + for (int i = 0; i < s.length(); i++) { + for (int j = 0; j < p.length(); j++) { + if (p.charAt(j) == '.') { + dp[i + 1][j + 1] = dp[i][j]; + } + if (p.charAt(j) == s.charAt(i)) { + dp[i + 1][j + 1] = dp[i][j]; + } + if (p.charAt(j) == '*') { + if (p.charAt(j-1) != s.charAt(i) && p.charAt(j - 1) != '.') { + dp[i + 1][j + 1] = dp[i + 1][j - 1]; + } else { + dp[i + 1][j + 1] = (dp[i + 1][j] || dp[i][j + 1] || dp[i + 1][j - 1]); + } + } + } + } + return dp[s.length()][p.length()]; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// s.isMatch("aa","a"); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_388/LeeCode_388_1143.java b/Week 08/id_388/LeeCode_388_1143.java new file mode 100644 index 000000000..1acc39253 --- /dev/null +++ b/Week 08/id_388/LeeCode_388_1143.java @@ -0,0 +1,103 @@ +package com.company.leetcode.editor.cn; + +//给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列。 +//count = 2 +// 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 +//例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。 +// +// 若这两个字符串没有公共子序列,则返回 0。 +// +// +// +// 示例 1: +// +// 输入:text1 = "abcde", text2 = "ace" +//输出:3 +//解释:最长公共子序列是 "ace",它的长度为 3。 +// +// +// 示例 2: +// +// 输入:text1 = "abc", text2 = "abc" +//输出:3 +//解释:最长公共子序列是 "abc",它的长度为 3。 +// +// +// 示例 3: +// +// 输入:text1 = "abc", text2 = "def" +//输出:0 +//解释:两个字符串没有公共子序列,返回 0。 +// +// +// +// +// 提示: +// +// +// 1 <= text1.length <= 1000 +// 1 <= text2.length <= 1000 +// 输入的字符串只含有小写英文字符。 +// +// Related Topics 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_1143 { + + public int longestCommonSubsequence(String text1, String text2) { + + char[] c1 = text1.toCharArray(); + char[] c2 = text2.toCharArray(); + + if (c1.length < 1 || c2.length < 1) { + return 0; + } + + int[][] dp = new int[c1.length][c2.length]; + dp[0][0] = c1[0] == c2[0] ? 1 : 0; + //row0 + for (int i = 1; i < c2.length; i++) { + if (c1[0] == c2[i]) { + dp[0][i] = 1; + } else { + dp[0][i] = dp[0][i - 1]; + } + } + //col0 + for (int j = 1; j < c1.length; j++) { + if (c1[j] == c2[0]) { + dp[j][0] = 1; + } else { + dp[j][0] = dp[j - 1][0]; + } + } + + for (int i = 1; i < c1.length; i++) { + for (int j = 1; j < c2.length; j++) { + if (c1[i] == c2[j]) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = Math.max(dp[i - 1][j],dp[i][j - 1]); + } + } + } + + return dp[c1.length - 1][c2.length - 1]; + } + + +// public static void main(String[] args) { +// Solution s = new Solution(); +// String text1 = "dknkdizqxkdczafixidorgfcnkrirmhmzqbcfuvojsxwraxe"; +// String text2 = "dulixqfgvipenkfubgtyxujixspoxmhgvahqdmzmlyhajerqz"; +// //Wrong Answer: input:"bsbininm" "jmjkbkjkv" Output:2 Expected:1 +// //Wrong Answer: input:"dknkdizqxkdczafixidorgfcnkrirmhmzqbcfuvojsxwraxe" +// // "dulixqfgvipenkfubgtyxujixspoxmhgvahqdmzmlyhajerqz" +// // Output:15 Expected:14 +// int len = s.longestCommonSubsequence(text1,text2); +// System.out.println(len); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_388/LeeCode_388_115.java b/Week 08/id_388/LeeCode_388_115.java new file mode 100644 index 000000000..8407d7ec6 --- /dev/null +++ b/Week 08/id_388/LeeCode_388_115.java @@ -0,0 +1,76 @@ +package com.company.leetcode.editor.cn; + +//给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。 +// +// 一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是) +// count = 2 +// 示例 1: +// +// 输入: S = "rabbbit", T = "rabbit" +//输出: 3 +//解释: +// +//如下图所示, 有 3 种可以从 S 中得到 "rabbit" 的方案。 +//(上箭头符号 ^ 表示选取的字母) +// +//rabbbit +//^^^^ ^^ +//rabbbit +//^^ ^^^^ +//rabbbit +//^^^ ^^^ +// +// +// 示例 2: +// +// 输入: S = "babgbag", T = "bag" +//输出: 5 +//解释: +// +//如下图所示, 有 5 种可以从 S 中得到 "bag" 的方案。 +//(上箭头符号 ^ 表示选取的字母) +// +//babgbag +//^^ ^ +//babgbag +//^^ ^ +//babgbag +//^ ^^ +//babgbag +// ^ ^^ +//babgbag +// ^^^ +// Related Topics 字符串 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_115 { + public int numDistinct(String s, String t) { + int rows = s.length(); + int cols = t.length(); + int[][] dp = new int[rows + 1][cols + 1]; + dp[0][0] = 1; + for (int i = 1; i <= cols; i++) { + dp[0][i] = 1; + } + for (int i = 1; i <= rows; i++) { + for (int j = 1; j <= cols; j++) { + if (s.charAt(i - 1) == t.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1]; + } else { + dp[i][j] = dp[i][j - 1]; + } + } + } + return dp[rows][cols]; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// String S = "babgbag"; +// String T = "bag"; +// System.out.println(s.numDistinct(S,T)); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_388/LeeCode_388_125.java b/Week 08/id_388/LeeCode_388_125.java new file mode 100644 index 000000000..4c84b16f1 --- /dev/null +++ b/Week 08/id_388/LeeCode_388_125.java @@ -0,0 +1,76 @@ +package com.company.leetcode.editor.cn; +//给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。 +// +// 说明:本题中,我们将空字符串定义为有效的回文串。 +// +// 示例 1: +// +// 输入: "A man, a plan, a canal: Panama" +//输出: true +// +// +// 示例 2: +// +// 输入: "race a car" +//输出: false +// +// Related Topics 双指针 字符串 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_125 { + public boolean isPalindrome(String s) { + if (s == null) { + return false; + } + if (s.length() == 1) { + return true; + } + + int left = 0; + int right = s.length() - 1; + while (left < right) { + char leftChar = s.charAt(left); + while (left < right && !isValidChar(leftChar)) { + left++; + leftChar = s.charAt(left); + } + if (left == right) { + return true; + } + char rightChar = s.charAt(right); + while (left < right && !isValidChar(rightChar)){ + right--; + rightChar = s.charAt(right); + } + if (left == right) { + return true; + } + String leftStr = String.valueOf(leftChar); + String rightStr = String.valueOf(rightChar); + if (!leftStr.equalsIgnoreCase(rightStr)){ + return false; + } + left++; + right--; + } + + return true; + + } + + //字母和数字字符 + private boolean isValidChar(char c){ + boolean b = (c >= '0' && c <= '9') + || (c >= 'A' && c <= 'Z') + || (c >= 'a' && c <= 'z'); + return b; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// System.out.println(s.isPalindrome("race a car")); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_388/LeeCode_388_14.java b/Week 08/id_388/LeeCode_388_14.java new file mode 100644 index 000000000..a9453a561 --- /dev/null +++ b/Week 08/id_388/LeeCode_388_14.java @@ -0,0 +1,61 @@ +package com.company.leetcode.editor.cn; +//编写一个函数来查找字符串数组中的最长公共前缀。 +// +// 如果不存在公共前缀,返回空字符串 ""。 +// +// 示例 1: +// +// 输入: ["flower","flow","flight"] +//输出: "fl" +// +// +// 示例 2: +// +// 输入: ["dog","racecar","car"] +//输出: "" +//解释: 输入不存在公共前缀。 +// +// +// 说明: +// +// 所有输入只包含小写字母 a-z 。 +// Related Topics 字符串 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_14 { + public String longestCommonPrefix(String[] strs) { + if (strs == null || strs.length == 0) { + return ""; + } + if (strs.length == 1) { + return strs[0]; + } + char[] com = strs[0].toCharArray(); + int resLen = com.length; + for (int i = 1; i < strs.length; i++) { + String tmp = strs[i]; + resLen = Math.min(tmp.length(),resLen); + for (int j = 0; j < resLen; j++) { + if (tmp.charAt(j) != com[j]) { + resLen = j; + break; + } + } + + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < resLen; i++) { + sb.append(com[i]); + } + return sb.toString(); + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// String[] strs = new String[]{"aa","a"}; +// System.out.println(s.longestCommonPrefix(strs)); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_388/LeeCode_388_151.java b/Week 08/id_388/LeeCode_388_151.java new file mode 100644 index 000000000..afbadc8f1 --- /dev/null +++ b/Week 08/id_388/LeeCode_388_151.java @@ -0,0 +1,84 @@ +package com.company.leetcode.editor.cn; + +//给定一个字符串,逐个翻转字符串中的每个单词。 +// +// +// +// 示例 1: +// +// 输入: "the sky is blue" +//输出: "blue is sky the" +// +// +// 示例 2: +// +// 输入: "  hello world!  " +//输出: "world! hello" +//解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 +// +// +// 示例 3: +// +// 输入: "a good   example" +//输出: "example good a" +//解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 +// +// +// +// +// 说明: +// +// +// 无空格字符构成一个单词。 +// 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 +// 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 +// +// +// +// +// 进阶: +// +// 请选用 C 语言的用户尝试使用 O(1) 额外空间复杂度的原地解法。 +// Related Topics 字符串 + + +import java.util.ArrayList; +import java.util.List; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_151 { + public String reverseWords(String s) { + List words = new ArrayList<>(); + + int i = 0; + while (i < s.length()) { + if (s.charAt(i) ==' ') { + i++; + continue; + } + StringBuilder tmp = new StringBuilder(); + while (i < s.length() && s.charAt(i) != ' ') { + tmp.append(s.charAt(i)); + i++; + } + + if (!tmp.toString().equals(" ")) { + words.add(tmp); + } + } + StringBuilder res = new StringBuilder(); + for (int j = words.size() - 1; j >= 0; j--) { + res.append(words.get(j).toString()); + if (j != 0) { + res.append(" "); + } + } + return res.toString(); + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// System.out.println(s.reverseWords("  hello world! ")); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_388/LeeCode_388_300.java b/Week 08/id_388/LeeCode_388_300.java new file mode 100644 index 000000000..8329d2f7e --- /dev/null +++ b/Week 08/id_388/LeeCode_388_300.java @@ -0,0 +1,50 @@ +package com.company.leetcode.editor.cn;//给定一个无序的整数数组,找到其中最长上升子序列的长度。 +// +// 示例: +// +// 输入: [10,9,2,5,3,7,101,18] +//输出: 4 +//解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。 +// +// 说明: +// +// +// 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。 +// 你算法的时间复杂度应该为 O(n2) 。 +// +// +// 进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗? +// Related Topics 二分查找 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_300 { + public int lengthOfLIS(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + + int[] dp = new int[nums.length]; + int maxans = 1; + dp[0] = 1; + for (int i = 1; i < nums.length; i++) { + int maxVal = 0; + for (int j = 0; j < i; j++) { + if (nums[i] > nums[j]) { + maxVal = Math.max(maxVal,dp[j]); + } + } + dp[i] = maxVal + 1; + maxans = Math.max(dp[i],maxans); + } + return maxans; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// int[] nums = new int[]{10,9,2,5,3,7,101,18}; +// System.out.println(s.lengthOfLIS(nums)); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_388/LeeCode_388_32.java b/Week 08/id_388/LeeCode_388_32.java new file mode 100644 index 000000000..4efd4d47d --- /dev/null +++ b/Week 08/id_388/LeeCode_388_32.java @@ -0,0 +1,51 @@ +package com.company.leetcode.editor.cn; + +//给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。 +// +// 示例 1: +// +// 输入: "(()" +//输出: 2 +//解释: 最长有效括号子串为 "()" +// +// +// 示例 2: +// +// 输入: ")()())" +//输出: 4 +//解释: 最长有效括号子串为 "()()" +// +// Related Topics 字符串 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_32 { + public int longestValidParentheses(String s) { + if (s == null || s.length() <= 1) { + return 0; + } + int len = s.length(); + int[] dp = new int[len]; + dp[0] = 0; + int maxans = 0; + for (int i = 1; i < len; i++) { + char c = s.charAt(i); + if (c == ')') { + if (s.charAt(i - 1) == '(') { + dp[i] = 2 + (i >= 2 ? dp[i - 2] : 0); + } else if(i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + 2+ ((i - dp[i - 1] - 2) >= 0 ? dp[i - dp[i - 1] - 2] : 0); + } + maxans = Math.max(maxans,dp[i]); + } + } + return maxans; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// System.out.println(s.longestValidParentheses("()(()")); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_388/LeeCode_388_44.java b/Week 08/id_388/LeeCode_388_44.java new file mode 100644 index 000000000..213145059 --- /dev/null +++ b/Week 08/id_388/LeeCode_388_44.java @@ -0,0 +1,88 @@ +package com.company.leetcode.editor.cn;//给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 '?' 和 '*' 的通配符匹配。 +// +// '?' 可以匹配任何单个字符。 +//'*' 可以匹配任意字符串(包括空字符串)。 +// +// +// 两个字符串完全匹配才算匹配成功。 +// +// 说明: +// +// +// s 可能为空,且只包含从 a-z 的小写字母。 +// p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *。 +// +// +// 示例 1: +// +// 输入: +//s = "aa" +//p = "a" +//输出: false +//解释: "a" 无法匹配 "aa" 整个字符串。 +// +// 示例 2: +// +// 输入: +//s = "aa" +//p = "*" +//输出: true +//解释: '*' 可以匹配任意字符串。 +// +// +// 示例 3: +// +// 输入: +//s = "cb" +//p = "?a" +//输出: false +//解释: '?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。 +// +// +// 示例 4: +// +// 输入: +//s = "adceb" +//p = "*a*b" +//输出: true +//解释: 第一个 '*' 可以匹配空字符串, 第二个 '*' 可以匹配字符串 "dce". +// +// +// 示例 5: +// +// 输入: +//s = "acdcb" +//p = "a*c?b" +//输入: false +// Related Topics 贪心算法 字符串 动态规划 回溯算法 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public boolean isMatch(String s, String p) { + int m = s.length(); + int n = p.length(); + boolean[][] dp = new boolean[m + 1][n + 1]; + dp[0][0] = true; + for (int i = 1; i <= n; i++) { + dp[0][i] = dp[0][i - 1] && p.charAt(i - 1) == '*'; + } + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + if (p.charAt(j - 1) != '*') { + dp[i][j] = dp[i - 1][j - 1] && (s.charAt(i - 1) == p.charAt(j - 1)||p.charAt(j - 1) == '?'); + } else { + dp[i][j] = dp[i - 1][j] || dp[i][j - 1]; + } + } + } + return dp[m][n]; + } + + public static void main(String[] args) { + Solution s = new Solution(); + s.isMatch("aa","a"); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_388/LeeCode_388_45.java b/Week 08/id_388/LeeCode_388_45.java new file mode 100644 index 000000000..b98d4b5e8 --- /dev/null +++ b/Week 08/id_388/LeeCode_388_45.java @@ -0,0 +1,85 @@ +package com.company.leetcode.editor.cn; +//给定一个非负整数数组,你最初位于数组的第一个位置。 +// +// 数组中的每个元素代表你在该位置可以跳跃的最大长度。 +// +// 你的目标是使用最少的跳跃次数到达数组的最后一个位置。 +// +// 示例: +// count = 2 +// 输入: [2,3,1,1,4] +//输出: 2 +//解释: 跳到最后一个位置的最小跳跃数是 2。 +//  从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。 +// +// +// 说明: +// +// 假设你总是可以到达数组的最后一个位置。 +// Related Topics 贪心算法 数组 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_45 { + + public int jump(int[] nums) { + if (nums == null || nums.length <= 1) { + return 0; + } + int maxIndex = nums[0]; + int current = 1; + int steps = 1; + while (maxIndex < nums.length - 1) { + + int nextMax = maxIndex; + for (int i = current; i <= maxIndex; i++) { + nextMax = Math.max(nums[i] + i,nextMax); + } + steps++; + current = maxIndex; + maxIndex = nextMax; + } + return steps; + } + + public int jump1(int[] nums) { + int end = 0; + int maxIndex = 0; + int steps = 0; + + for (int i = 0; i < nums.length - 1; i++) { + maxIndex = Math.max(nums[i] + i,maxIndex); + if (i == end) { + steps++; + end = maxIndex; + } + } + + return steps; + } + + public int jump2(int[] nums) { + + int position = nums.length - 1; + int steps = 0; + while (position != 0) { + for (int i = 0; i < position; i++) { + if (nums[i] + i >= position) { + position = i; + steps++; + break; + } + } + } + + return steps; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// int[] nums = new int[]{2,3,1,1,4}; +// System.out.println(s.jump(nums)); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_388/LeeCode_388_49.java b/Week 08/id_388/LeeCode_388_49.java new file mode 100644 index 000000000..399e3eefd --- /dev/null +++ b/Week 08/id_388/LeeCode_388_49.java @@ -0,0 +1,114 @@ +package com.company.leetcode.editor.cn; +//给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 +// +// 示例: +// +// 输入: ["eat", "tea", "tan", "ate", "nat", "bat"], +//输出: +//[ +// ["ate","eat","tea"], +// ["nat","tan"], +// ["bat"] +//] +// +// 说明: +// +// +// 所有输入均为小写字母。 +// 不考虑答案输出的顺序。 +// +// Related Topics 哈希表 字符串 + + +import java.util.*; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_49 { + + public List> groupAnagrams(String[] strs) { + Map> map = new HashMap<>(); + for (int i = 0; i < strs.length; i++) { + String tmp = strs[i]; + char[] cs = tmp.toCharArray(); + Arrays.sort(cs); + //拼接key + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < cs.length; j++) { + sb.append(cs[j]).append('#'); + } + String key = sb.toString(); + List tmpList; + if (map.containsKey(key)) { + tmpList = map.get(key); + } else { + tmpList = new ArrayList(); + } + tmpList.add(tmp); + map.put(key,tmpList); + } + return new ArrayList<>(map.values()); + } + + //key自己计算 +// public List> groupAnagrams(String[] strs) { +// int[] count = new int[26]; +// Map> resMap = new HashMap<>(); +// for (int i = 0; i < strs.length; i++) { +// Arrays.fill(count,0); +// String s = strs[i]; +// //计算key +// for (int j = 0; j < s.length(); j++) { +// count[s.charAt(j) - 'a']++; +// } +// StringBuilder sb = new StringBuilder(); +// for (int j = 0; j < count.length; j++) { +// sb.append(count[j]).append("#"); +// } +// String key = sb.toString(); +// if (resMap.containsKey(key)) { +// List tmp = resMap.get(key); +// tmp.add(s); +// continue; +// } +// List tmp = new ArrayList(); +// tmp.add(s); +// resMap.put(key,tmp); +// } +// return new ArrayList<>(resMap.values()); +// } + + //counter作为map的key + public List> groupAnagrams1(String[] strs) { + + //计算每一个的counter + Map,List> counterMap = new HashMap<>(); + for (int i = 0; i < strs.length; i++) { + //计算counter + Map counter = new HashMap<>(); + for (int j = 0; j < strs[i].length(); j++) { + Character c = strs[i].charAt(j); + if (counter.containsKey(c)){ + Integer count = counter.get(c) + 1; + counter.put(c,count); + continue; + } + counter.put(c,1); + } + if (counterMap.containsKey(counter)) { + List list = counterMap.get(counter); + list.add(strs[i]); + continue; + } + List list = new ArrayList<>(); + list.add(strs[i]); + counterMap.put(counter,list); + } + List> res = new ArrayList<>(); + for (List value : counterMap.values()) { + res.add(value); + } + return res; + } + +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_388/LeeCode_388_5.java b/Week 08/id_388/LeeCode_388_5.java new file mode 100644 index 000000000..e5f4f5782 --- /dev/null +++ b/Week 08/id_388/LeeCode_388_5.java @@ -0,0 +1,65 @@ +package com.company.leetcode.editor.cn; +//给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。 +// +// 示例 1: +// count = 2 +// 输入: "babad" +//输出: "bab" +//注意: "aba" 也是一个有效答案。 +// +// +// 示例 2: +// +// 输入: "cbbd" +//输出: "bb" +// +// Related Topics 字符串 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_5 { + + public String longestPalindrome(String s) { + + if (s == null || s.length() < 1) { + return ""; + } + if (s.length() == 1) { + return s; + } + int start = 0; + int end = 0; + int maxLen = 0; + for (int i = 0; i < s.length(); i++) { + + int len1 = expand(s,i,i); + int len2 = expand(s,i,i+1); + int len = Math.max(len1,len2); + if (len > maxLen) { + start = i - (len - 1)/2; + end = i + len/2; + maxLen = len; + } + + } + + return s.substring(start,end + 1); + } + + private int expand(String s, int left, int right) { + + while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) { + left--; + right++; + } + + return right - left -1; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// System.out.println(s.longestPalindrome("cbbd")); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_388/LeeCode_388_8.java b/Week 08/id_388/LeeCode_388_8.java new file mode 100644 index 000000000..b1a586327 --- /dev/null +++ b/Week 08/id_388/LeeCode_388_8.java @@ -0,0 +1,97 @@ +package com.company.leetcode.editor.cn; + +//请你来实现一个 atoi 函数,使其能将字符串转换成整数。 +// +// 首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。 +// +// 当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。 +// +// 该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。 +// +// 注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。 +// +// 在任何情况下,若函数不能进行有效的转换时,请返回 0。 +// +// 说明: +// +// 假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。 +// 如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。 +// +// 示例 1: +// +// 输入: "42" +//输出: 42 +// +// +// 示例 2: +// +// 输入: " -42" +//输出: -42 +//解释: 第一个非空白字符为 '-', 它是一个负号。 +//  我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。 +// +// +// 示例 3: +// +// 输入: "4193 with words" +//输出: 4193 +//解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。 +// +// +// 示例 4: +// +// 输入: "words and 987" +//输出: 0 +//解释: 第一个非空字符是 'w', 但它不是数字或正、负号。 +// 因此无法执行有效的转换。 +// +// 示例 5: +// +// 输入: "-91283472332" +//输出: -2147483648 +//解释: 数字 "-91283472332" 超过 32 位有符号整数范围。 +//  因此返回 INT_MIN (−231) 。 +// +// Related Topics 数学 字符串 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_8 { + public int myAtoi(String str) { + if (str == null || str.length() == 0) { + return 0; + } + //检查空格 + int i = 0; + while (i < str.length() && str.charAt(i) == ' ') { + i++; + } + //检查正负 + int flag = 1; + if (i < str.length() && str.charAt(i) == '-') { + flag = -1; + } + if (i < str.length() && (str.charAt(i) == '+' || str.charAt(i) == '-')) { + i++; + } + int res = 0; + while (i < str.length() && str.charAt(i) >= '0' && str.charAt(i) <= '9') { + int n = str.charAt(i) - '0'; + + if (Integer.MAX_VALUE / 10 < res || (Integer.MAX_VALUE/10 == res && Integer.MAX_VALUE%10 < n)) { + return flag == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE; + } + + res = res * 10 + n; + i++; + } + return res * flag; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// System.out.println(s.myAtoi(" ")); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_388/LeeCode_388_85.java b/Week 08/id_388/LeeCode_388_85.java new file mode 100644 index 000000000..1b9eaca00 --- /dev/null +++ b/Week 08/id_388/LeeCode_388_85.java @@ -0,0 +1,65 @@ +package com.company.leetcode.editor.cn; +//给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。 +// +// 示例: +// +// 输入: +//[ +// ["1","0","1","0","0"], +// ["1","0","1","1","1"], +// ["1","1","1","1","1"], +// ["1","0","0","1","0"] +//] +//输出: 6 +// Related Topics 栈 数组 哈希表 动态规划 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_85 { + public int maximalRectangle(char[][] matrix) { + + if (matrix == null || matrix.length == 0) { + return 0; + } + + int maxarea = 0; + int rows = matrix.length; + int cols = matrix[0].length; + int[][] dp = new int[rows][cols]; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + + if (matrix[i][j] == '0') { + continue; + } + + //update width + dp[i][j] = j == 0 ? 1 : (dp[i][j - 1] + 1); + int width = dp[i][j]; + for (int k = i; k >= 0; k--) { + width = Math.min(width,dp[k][j]); + maxarea = Math.max(maxarea,width * (i - k + 1)); + } + } + } + + return maxarea; + + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// +// char[][] matrix = new char[][]{ +// new char[]{'1','0','1','0','0'}, +// new char[]{'1','0','1','1','1'}, +// new char[]{'1','1','1','1','1'}, +// new char[]{'1','0','0','1','0'} +// }; +// +// System.out.println(s.maximalRectangle(matrix)); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_388/LeeCode_388_91.java b/Week 08/id_388/LeeCode_388_91.java new file mode 100644 index 000000000..1a7ba2a86 --- /dev/null +++ b/Week 08/id_388/LeeCode_388_91.java @@ -0,0 +1,71 @@ +package com.company.leetcode.editor.cn; +//一条包含字母 A-Z 的消息通过以下方式进行了编码: +// +// 'A' -> 1 +//'B' -> 2 +//... +//'Z' -> 26 +// +// +// 给定一个只包含数字的非空字符串,请计算解码方法的总数。 +// +// 示例 1: +// +// 输入: "12" +//输出: 2 +//解释: 它可以解码为 "AB"(1 2)或者 "L"(12)。 +// +// +// 示例 2: +// +// 输入: "226" +//输出: 3 +//解释: 它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。 +// +// Related Topics 字符串 动态规划 + + +import java.util.*; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_91 { + public int numDecodings(String s) { + if (s == null || s.length() == 0) { + return 0; + } + + int[] dp = new int[s.length() + 1]; + dp[0] = 1; + if (s.charAt(0) == '0') { + return 0; + } else { + dp[1] = 1; + } + for (int i = 2; i <= s.length(); i++) { + char c = s.charAt(i - 1); + char pre = s.charAt( i - 2); + if (c == '0') { + //处理 10,20 + if (pre == '1' || pre == '2') { + dp[i] = dp[i - 2]; + continue; + } + return 0; + } + + + if ((pre == '1') || (pre == '2' && (c >= '1' && c <= '6'))) { //1* 2* + dp[i] = dp[i - 1] + dp[i - 2]; + continue; + } + dp[i] = dp[i - 1]; + } + return dp[s.length()]; + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// System.out.println(s.numDecodings("100")); +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_388/LeeCode_388_917.java b/Week 08/id_388/LeeCode_388_917.java new file mode 100644 index 000000000..0b533a49c --- /dev/null +++ b/Week 08/id_388/LeeCode_388_917.java @@ -0,0 +1,85 @@ +package com.company.leetcode.editor.cn; +//给定一个字符串 S,返回 “反转后的” 字符串,其中不是字母的字符都保留在原地,而所有字母的位置发生反转。 +// +// +// +// +// +// +// 示例 1: +// +// 输入:"ab-cd" +//输出:"dc-ba" +// +// +// 示例 2: +// +// 输入:"a-bC-dEf-ghIj" +//输出:"j-Ih-gfE-dCba" +// +// +// 示例 3: +// +// 输入:"Test1ng-Leet=code-Q!" +//输出:"Qedo1ct-eeLg=ntse-T!" +// +// +// +// +// 提示: +// +// +// S.length <= 100 +// 33 <= S[i].ASCIIcode <= 122 +// S 中不包含 \ or " +// +// Related Topics 字符串 + + +import java.util.ArrayList; +import java.util.List; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution_917 { + public String reverseOnlyLetters(String S) { + if (S == null || S.equals("")) { + return ""; + } + + if (S.length() == 1) { + return S; + } + + char[] cs = S.toCharArray(); + int i = 0; + int j = cs.length - 1; + while (i < j) { + //sweep + if (isAlpha(S.charAt(i)) && isAlpha(S.charAt(j))) { + char tmp = cs[i]; + cs[i++] = cs[j]; + cs[j--] = tmp; + } + while (i < j && !isAlpha(S.charAt(i))) { + i++; + } + while (i < j && !isAlpha(S.charAt(j))) { + j--; + } + } + + return new String(cs); + } + + private boolean isAlpha(char c) { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); + } + +// public static void main(String[] args) { +// Solution s = new Solution(); +// //Wrong Answer: input:"a-bC-dEf-ghIj" Output:"jIhg-fEd-Cb-a" Expected:"j-Ih-gfE-dCba" +// System.out.println(s.reverseOnlyLetters("a-bC-dEf-ghIj")); +// } + +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_388/NOTE.md b/Week 08/id_388/NOTE.md new file mode 100755 index 000000000..eef0375c4 --- /dev/null +++ b/Week 08/id_388/NOTE.md @@ -0,0 +1,39 @@ +# NOTE + +### 1、状态转移方程总结 + 爬楼梯:fn = f(n-1) + f(n - 2); + 不同路径:obstacleGrid[i][j] = obstacleGrid[i - 1][j] + obstacleGrid[i][j - 1]; + 打家劫舍:dp[n] = MAX( dp[n-1], dp[n-2] + num ) + 股票买卖:dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i]),dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i]) + 编辑距离:min(dp[i - 1][j],dp[i][j - 1],dp[i - 1][j - 1]) + 1 + 不同子序列: if (s.charAt(i - 1) == t.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1]; + } else { + dp[i][j] = dp[i][j - 1]; + } + 正则表达式:if (p.charAt(j) == '.') { + dp[i + 1][j + 1] = dp[i][j]; + } + if (p.charAt(j) == s.charAt(i)) { + dp[i + 1][j + 1] = dp[i][j]; + } + if (p.charAt(j) == '*') { + if (p.charAt(j-1) != s.charAt(i) && p.charAt(j - 1) != '.') { + dp[i + 1][j + 1] = dp[i + 1][j - 1]; + } else { + dp[i + 1][j + 1] = (dp[i + 1][j] || dp[i][j + 1] || dp[i + 1][j - 1]); + } + } + +### 2、java 字符串比较 + String x = “abb”; String y = “abb”; + x == y —-> false x.equals(y) —-> true + x.equalsIgnoreCase(y) —-> true + 字符串比较 + + Rabin-Karp 算法的思想: + 1.假设子串的长度为 M (pat),目标字符串的长度为 N (txt) + 2.计算子串的 hash 值 hash_pat + 3.计算目标字符串txt中每个长度为 M 的子串的 hash 值(共需要计算 N-M+1次) + 4.比较 hash 值:如果 hash 值不同,字符串必然不匹配; 如果 hash 值相同,还需要使用朴素算法再次判断 + diff --git a/Week 08/id_393/LeetCode_387_393.java b/Week 08/id_393/LeetCode_387_393.java new file mode 100644 index 000000000..f513bed11 --- /dev/null +++ b/Week 08/id_393/LeetCode_387_393.java @@ -0,0 +1,27 @@ +package no387; + +import java.util.HashMap; + +/** + * @author boyiren + * @date 2019-12-08 + */ +public class Solution { + public int firstUniqChar(String s) { + HashMap count = new HashMap(); + int n = s.length(); + // build hash map : character and how often it appears + for (int i = 0; i < n; i++) { + char c = s.charAt(i); + count.put(c, count.getOrDefault(c, 0) + 1); + } + + // find the index + for (int i = 0; i < n; i++) { + if (count.get(s.charAt(i)) == 1) { + return i; + } + } + return -1; + } +} diff --git a/Week 08/id_393/LeetCode_541_393.java b/Week 08/id_393/LeetCode_541_393.java new file mode 100644 index 000000000..b6f01b8dc --- /dev/null +++ b/Week 08/id_393/LeetCode_541_393.java @@ -0,0 +1,20 @@ +package no541; + +/** + * @author boyiren + * @date 2019-12-08 + */ +public class Solution { + public String reverseStr(String s, int k) { + char[] a = s.toCharArray(); + for (int start = 0; start < a.length; start += 2 * k) { + int i = start, j = Math.min(start + k - 1, a.length - 1); + while (i < j) { + char tmp = a[i]; + a[i++] = a[j]; + a[j--] = tmp; + } + } + return new String(a); + } +} \ No newline at end of file diff --git a/Week 08/id_393/NOTE.md b/Week 08/id_393/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_393/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_398/NOTE.md b/Week 08/id_398/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_398/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_403/NOTE.md b/Week 08/id_403/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_403/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_408/LeetCode_300_408.go b/Week 08/id_408/LeetCode_300_408.go new file mode 100644 index 000000000..d41802909 --- /dev/null +++ b/Week 08/id_408/LeetCode_300_408.go @@ -0,0 +1,23 @@ +func lengthOfLIS(nums []int) int { + size := 0 + tail := make([]int, len(nums)) + for i := 0; i < len(nums); i++ { + if size == 0 || tail[size-1] < nums[i] { + tail[size] = nums[i] + size++ + } else { + x, y := 0, size-1 + for x < y { + mid := (x + y) / 2 + if tail[mid] < nums[i] { + x = mid + 1 + } else { + y = mid + } + } + tail[x] = nums[i] + } + } + return size + +} diff --git a/Week 08/id_408/LeetCode_709_408.go b/Week 08/id_408/LeetCode_709_408.go new file mode 100644 index 000000000..150089f44 --- /dev/null +++ b/Week 08/id_408/LeetCode_709_408.go @@ -0,0 +1,10 @@ +func toLowerCase(str string) string { +strRune := []rune(str) +for i := 0; i < len(strRune); i++ { +if strRune[i] >= 'A' && strRune[i] <= 'Z' { +strRune[i] = strRune[i] + 32 +} +} +return string(strRune) + +} diff --git a/Week 08/id_408/NOTE.md b/Week 08/id_408/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_408/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_413/NOTE.md b/Week 08/id_413/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_413/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_418/LeetCode_387_418.java b/Week 08/id_418/LeetCode_387_418.java new file mode 100644 index 000000000..38d82860d --- /dev/null +++ b/Week 08/id_418/LeetCode_387_418.java @@ -0,0 +1,33 @@ +package com.ljg.leetcode.week08.a04; + +/** + * FirstUniqueCharacterInAString + */ +public class FirstUniqueCharacterInAString { + + public static void main(String[] args) { + // String s = "leetcode"; + String s = "loveleetcode"; + FirstUniqueCharacterInAString f = new FirstUniqueCharacterInAString(); + int index =f.firstUniqChar(s); + System.out.println("index=" + index); + } + + public int firstUniqChar(String s) { + if (s == null || s.trim().length() == 0) { + return -1; + } + int[] arrs = new int[26]; + int len = s.length(); + char[] chs = s.toCharArray(); + for (int i = 0; i < len; i++) { + arrs[chs[i] - 'a']++; + } + for (int i = 0; i < len; i++) { + if (arrs[chs[i] - 'a'] == 1) { + return i; + } + } + return -1; + } +} \ No newline at end of file diff --git a/Week 08/id_418/LeetCode_541_418.java b/Week 08/id_418/LeetCode_541_418.java new file mode 100644 index 000000000..4ab7c0a29 --- /dev/null +++ b/Week 08/id_418/LeetCode_541_418.java @@ -0,0 +1,48 @@ +package com.ljg.leetcode.week08.a06; + +/** + * ReverseStringII + */ +public class ReverseStringII { + + public static void main(String[] args) { + ReverseStringII rs2 = new ReverseStringII(); + //String s = "abcdefg"; + //int k = 2; + String s = "abcd"; + int k = 4; + String res = rs2.reverseStr(s, k); + System.out.println("res=" + res); + } + + public String reverseStr(String s, int k) { + if (s == null) { + return s; + } + char[] chs = s.toCharArray(); + int len = chs.length; + for (int i = 0; len - i > 0; i += 2 * k) { + if (len - i >= k) { + reverse(chs, i, i + k - 1); + } else { + reverse(chs, i, len - 1); + } + } + return new String(chs); + } + + private void reverse(char[] chs, int start, int end) { + int i = start; + int j = end; + while (i <= j) { + if (i == j) { + return; + } + char temp = chs[i]; + chs[i] = chs[j]; + chs[j] = temp; + i++; + j--; + } + } +} \ No newline at end of file diff --git a/Week 08/id_418/LeetCode_5_418.java b/Week 08/id_418/LeetCode_5_418.java new file mode 100644 index 000000000..507c39ee2 --- /dev/null +++ b/Week 08/id_418/LeetCode_5_418.java @@ -0,0 +1,35 @@ +package com.ljg.leetcode.week08.a03; + +/** + * LongestPalindromicSubstring + */ +public class LongestPalindromicSubstring { + + public static void main(String[] args) { + LongestPalindromicSubstring lps = new LongestPalindromicSubstring(); + String s = "babad"; + String res = lps.longestPalindrome(s); + System.out.println("res=" + res); + } + + public String longestPalindrome(String s) { + if (s == null || s.length() < 2) { + return s; + } + String res = ""; + s = s.trim(); + int len = s.length(); + boolean[][] arrs = new boolean[len][len]; + for (int i = len - 1; i >= 0; i--) { + for (int j = i; j < len; j++) { + arrs[i][j] = s.charAt(i) == s.charAt(j) && (j - i < 2 || arrs[i + 1][j - 1]); + + if (arrs[i][j] & (j - i + 1 > res.length())) { + res = s.substring(i, j + 1); + } + } + } + + return res; + } +} \ No newline at end of file diff --git a/Week 08/id_418/LeetCode_8_418.java b/Week 08/id_418/LeetCode_8_418.java new file mode 100644 index 000000000..2ed4a0346 --- /dev/null +++ b/Week 08/id_418/LeetCode_8_418.java @@ -0,0 +1,52 @@ +package com.ljg.leetcode.week08.a05; + +import lombok.val; + +/** + * StringToIntegerAtoi + */ +public class StringToIntegerAtoi { + + public static void main(String[] args) { + String[] arrs = new String[] { "+1", "42", " -42", "4193 with words", "words and 987", "-91283472332","-2147483647", "-2147483648","2147483648" }; + StringToIntegerAtoi sti = new StringToIntegerAtoi(); + for (String string : arrs) { + int value = sti.myAtoi(string); + System.out.println(string + "\t:" + value); + } + + } + + public int myAtoi(String str) { + if (str == null || str.trim().length() == 0) { + return 0; + } + str = str.trim(); + int len = str.length(); + char[] chs = str.toCharArray(); + + char firstChar = chs[0]; + if (!(firstChar == '-' || firstChar == '+' || (firstChar >= '0' && firstChar <= '9'))) { + return 0; + } + int flag = firstChar == '-' ? -1 : 1; + int i = (firstChar == '-' || firstChar == '+') ? 1 : 0; + int value = 0; + for (; i < len; i++) { + int curValue = chs[i] - '0'; + if (curValue > 9 || curValue < 0) { + return flag * value; + } + if (Integer.MAX_VALUE / 10 < value) { + return flag == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE; + } else if (Integer.MAX_VALUE / 10 == value) { + if (Integer.MAX_VALUE % 10 < curValue) { + return flag == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE; + } + } + value = (value * 10) + curValue; + } + + return flag * value; + } +} \ No newline at end of file diff --git a/Week 08/id_418/NOTE.md b/Week 08/id_418/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_418/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_423/LeetCode_205_423.java b/Week 08/id_423/LeetCode_205_423.java new file mode 100644 index 000000000..beb61ac0d --- /dev/null +++ b/Week 08/id_423/LeetCode_205_423.java @@ -0,0 +1,23 @@ +class Solution { + public boolean isMatch(String s, String p) { + int m = s.length(), n = p.length(); + boolean[][] f = new boolean[m + 1][n + 1]; + + f[0][0] = true; + for(int i = 1; i <= n; i++){ + f[0][i] = f[0][i - 1] && p.charAt(i - 1) == '*'; + } + + for(int i = 1; i <= m; i++){ + for(int j = 1; j <= n; j++){ + if(s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '?'){ + f[i][j] = f[i - 1][j - 1]; + } + if(p.charAt(j - 1) == '*'){ + f[i][j] = f[i][j - 1] || f[i - 1][j]; + } + } + } + return f[m][n]; + } +} \ No newline at end of file diff --git a/Week 08/id_423/LeetCode_44_423.java b/Week 08/id_423/LeetCode_44_423.java new file mode 100644 index 000000000..7d35d8536 --- /dev/null +++ b/Week 08/id_423/LeetCode_44_423.java @@ -0,0 +1,73 @@ +class Solution { + public int trap_brute_force(int[] height) { + if (0 == height.length) return 0; + int ans = 0; + int[] left_max = new int[height.length]; + int[] right_max = new int[height.length]; + for (int i = 0; i < height.length; i++){ + int max_left = 0, max_right = 0; + for (int j = i; j >= 0; j--) max_left = Math.max(max_left, height[j]); + for (int j = i; j < height.length; j++) max_right = Math.max(max_right, height[j]); + ans += Math.min(max_left, max_right) - height[i]; + } + return ans; + } + + public int trap_dp(int[] height) { + if (0 == height.length) return 0; + int ans = 0; + int[] left_max = new int[height.length]; + int[] right_max = new int[height.length]; + + left_max[0] = height[0]; + right_max[height.length - 1] = height[height.length - 1]; + + for (int i = 1; i < height.length; i++) left_max[i] = Math.max(height[i], left_max[i - 1]); + for (int i = height.length - 2; i >= 0; i--) right_max[i] = Math.max(height[i],right_max[i + 1]); + + for (int i = 0; i < height.length; i++) ans += Math.min(left_max[i], right_max[i]) - height[i]; + + return ans; + } + + public int trap_stack(int[] height) { + if (0 == height.length) return 0; + int ans = 0, current = 0; + + Stack st = new Stack<>(); + while (current < height.length) { + while (!st.empty() && height[st.peek()] < height[current]) { + int top = st.pop(); + if (st.empty()) break; + ans += (current - st.peek() - 1) * (Math.min(height[current], height[st.peek()]) - height[top]); + } + st.push(current++); + } + return ans; + } + + public int trap_double_pointer(int[] height) { + if (0 == height.length) retrun 0; + + int ans = 0; + int max_left = 0; + int max_right = 0; + int left = 0; + int right = height.length - 2; + + for (int i = 1; i < height.length - 1 ; i++) { + if (height[left-1] < height[right+1]) { + max_left = Math.max(height[left-1], max_left); + if (max_left > height[left]) + ans += max_left - height[left]; + left++; + } else { + max_right = Math.max(height[right+1], max_right); + if (max_right > height[right]) + ans += max_right - height[right]; + right++; + } + } + return ans; + } +} \ No newline at end of file diff --git a/Week 08/id_423/NOTE.md b/Week 08/id_423/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_423/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_428/LeeCode_300_428.java b/Week 08/id_428/LeeCode_300_428.java new file mode 100644 index 000000000..38d52bdda --- /dev/null +++ b/Week 08/id_428/LeeCode_300_428.java @@ -0,0 +1,21 @@ +class Solution { + public int lengthOfLIS(int[] nums) { + if (nums.length == 0) { + return 0; + } + int[] dp = new int[nums.length]; + dp[0] = 1; + int maxans = 1; + for (int i = 1; i < dp.length; i++) { + int maxval = 0; + for (int j = 0; j < i; j++) { + if (nums[i] > nums[j]) { + maxval = Math.max(maxval, dp[j]); + } + } + dp[i] = maxval + 1; + maxans = Math.max(maxans, dp[i]); + } + return maxans; + } +} \ No newline at end of file diff --git a/Week 08/id_428/LeeCode_387_428.java b/Week 08/id_428/LeeCode_387_428.java new file mode 100644 index 000000000..ead55b906 --- /dev/null +++ b/Week 08/id_428/LeeCode_387_428.java @@ -0,0 +1,18 @@ +class Solution { + public int firstUniqChar(String s) { + HashMap count = new HashMap(); + int n = s.length(); + // build hash map : character and how often it appears + for (int i = 0; i < n; i++) { + char c = s.charAt(i); + count.put(c, count.getOrDefault(c, 0) + 1); + } + + // find the index + for (int i = 0; i < n; i++) { + if (count.get(s.charAt(i)) == 1) + return i; + } + return -1; + } +} \ No newline at end of file diff --git a/Week 08/id_428/NOTE.md b/Week 08/id_428/NOTE.md new file mode 100755 index 000000000..a37b1b29f --- /dev/null +++ b/Week 08/id_428/NOTE.md @@ -0,0 +1,1074 @@ +Week_08_学习总结 + +1、学习过程 + + 1)高级动态规划 2)字符串算法 + +2、本周学习内容(待细化) + + 1)高级动态规划 + + 2)字符串算法 + +3、高级动态规划 + +一、暴力枚举 + +1、实现代码 + + `def` `fib(n):`` ``f ``=` `[``1``,``1``]`` ``for` `i ``in` `range``(``2``,n``+``1``):`` ``f.append(f[``-``1``]``+``f[``-``2``])`` ``print``(f)`` ``return` `f(n)` `fib(``5``)` + +2、输出 + + `"C:\Program Files\Python35\python.exe"` `E:``/``工作目录``/``python``/``test``/``DP.py``[``1``, ``1``, ``2``, ``3``, ``5``, ``8``]` `Process finished with exit code ``1` + +二、动态规划定义 + +1、什么是动态规划? + +动态规划的英文名,是一种分阶段求解决策略的数学思想,它不止用于编程领域,也用于管理学,经济学、生物学 + + + +2、初始为1 + +实现代码 + + `def` `LTS(x):`` ``F ``=` `[``0` `for` `_ ``in` `range``(``len``(x))]`` ``F[``0``] ``=` `1`` ``for` `k ``in` `range``(``1``,``len``(F)):`` ``max_loc ``=` `None`` ``max_num ``=` `0`` ``for` `i ``in` `range``(``1``,k):`` ``if` `x[i] < x[k]:`` ``if` `F[i] > max_num:`` ``max_loc ``=` `i`` ``max_num ``=` `F[i]`` ``F[k] ``=` `max_num ``+` `1`` ``return` `F` `print``(LTS([``1``,``7``,``2``,``8``,``3``,``5``,``2``]))` + +输出 + + `"C:\Program Files\Python35\python.exe"` `E:``/``工作目录``/``python``/``test``/``DP.py``[``1``, ``1``, ``1``, ``2``, ``2``, ``3``, ``1``]` `Process finished with exit code ``0` + +2、初始为0 + +1、实现代码 + + `def` `LIS(x):`` ``F ``=` `[``0` `for` `_ ``in` `range``(``len``(x))]`` ``#初始化`` ``F[``0``] ``=` `1`` ``for` `k ``in` `range``(``1``,``len``(F)):`` ``max_loc ``=` `None`` ``max_num ``=` `0`` ``#内层循环表示[0:R] 里所有小于x[k]的对应位置的F[i]最大值`` ``for` `i ``in` `range``(``0``,k):`` ``if` `x[i] < x[k]:`` ``if` `F[i] > max_num:`` ``max_loc ``=` `i`` ``max_num ``=` `F[i]`` ``F[k] ``=` `max_num ``+` `1`` ``return` `F` `print``(LIS([``1``,``7``,``2``,``8``,``3``,``5``,``2``]))` + +2、输出 + + `"C:\Program Files\Python35\python.exe"` `E:``/``工作目录``/``python``/``test``/``DP.py``[``1``, ``2``, ``2``, ``3``, ``3``, ``4``, ``2``]` `Process finished with exit code ``0` + +三、动态规划最长上升子序列 + + + +1、实现代码 + + `def` `fib(n):`` ``f ``=` `[``1``,``1``]`` ``for` `i ``in` `range``(``2``,n``+``1``):`` ``f.append(f[``-``1``]``+``f[``-``2``])`` ``print``(f)`` ``return` `f(n)` `# fib(5)` `def` `LIS(x):`` ``F ``=` `[``0` `for` `_ ``in` `range``(``len``(x))]`` ``p ``=` `[``-``1` `for` `_ ``in` `range``(``len``(x))]`` ``#初始化`` ``F[``0``] ``=` `1`` ``p[``0``] ``=` `-``1`` ``for` `k ``in` `range``(``1``,``len``(F)):`` ``max_loc ``=` `-``1`` ``max_num ``=` `0`` ``#内层循环表示[0:R] 里所有小于x[k]的对应位置的F[i]最大值`` ``for` `i ``in` `range``(``0``,k):`` ``if` `x[i] < x[k]:`` ``if` `F[i] > max_num:`` ``max_loc ``=` `i`` ``max_num ``=` `F[i]`` ``F[k] ``=` `max_num ``+` `1`` ``p[k] ``=` `max_loc` ` ``max_i ``=` `0`` ``for` `i ``in` `range``(``1``,``len``(F)):`` ``if` `F[i] > F[max_i]:`` ``max_i ``=` `i`` ``lis ``=` `[]`` ``i ``=` `max_i`` ``while` `i >``=` `0``:`` ``lis.append(x[i])`` ``i ``=` `p[i]`` ``lis.reverse()`` ``return` `lis` `print``(LIS([``1``,``7``,``2``,``8``,``3``,``5``,``2``]))` + +2、输出结果 + + `"C:\Program Files\Python35\python.exe"` `E:``/``工作目录``/``python``/``test``/``DP.py``[``1``, ``2``, ``3``, ``5``]` `Process finished with exit code ``0` + +最长公共子序列2 + + + + 最长公共子序列1 + + + + + +动态规划最优子结构 + + + + + +参考: + +https://www.cnblogs.com/luoahong/p/9723117.html + +4、字符串算法 + +字符串相关的一些算法,种类挺多的,特来整理一波。 + +字符串哈希(Hash) +简介 +原理 +哈希查找 +字符串哈希 +哈希的弊端 & 如何卡哈希 +KMP算法 +简介 + +1. 引言 +2. 暴力匹配算法 +3. KMP算法 +3.1 定义 +3.2 步骤 +3.3 解释 +3.3.1 寻找最长前缀后缀 +3.3.2 基于《最大长度表》匹配 +3.3.3 根据《最大长度表》求next 数组 +3.3.4 通过代码递推计算next 数组 +3.3.5 基于《next 数组》匹配 +3.3.6 基于《最大长度表》与基于《next 数组》等价 +3.3.7 Next 数组与有限状态自动机 +3.3.8 Next 数组的优化 +3.4 KMP的时间复杂度分析 +4. 扩展1:BM算法 +5. 扩展2:Sunday算法 +6. 参考文献 +7.后记 +8.重编者(也就是我)记 +字符串哈希(Hash) +简介 +哈希( HashHash )是一种神奇的查找算法,广泛运用于计算机领域,它的强大在于设计得好的哈希算法可以使对一个对象的查找时间复杂度降为 O(1)O(1) ,这是朴素查找的 O(n)O(n) 和二分查找的 O(logn)O(log⁡n) 所远不能及的。 + +第一次了解到哈希这一种技术是在吴军 dalaodalao 的《数学之美》一书上(感谢 MasterYinMasterYin 的借阅!)讲解网络爬虫的数学原理的一章。有兴趣的话可以去读一读。 + +原理 +哈希查找 +为什么哈希查找的时间复杂度可以这么低呢?考虑下面一个问题: + +设计一个程序,以完成以下输入输出: +第一行,输入两个数字 n,mn,m 。 +第二行,输入 nn 个数字。 +第三行,输入 mm 个数字。对于每一个数字,询问其是否存在于第二行输入 nn 个数中,若是则输出”YES”,若不是则输出”NO”。 +数据说明:输入的数字的范围在 1,105 。 + +对于朴素算法,我们把第二行输入的数字储存在一个数组中。对于每一次询问,我们从左到右遍历整个数组来查询,每次询问的时间复杂度为 O(n)O(n) 。 + +对于二分查找算法,我们把第二行输入的数字进行排序。对于每一次询问,我们用二分查找的方法找到这个数字处在的位置,排序的时间复杂度为 O(logn)O(log⁡n) ,每次询问的时间复杂度也为 O(logn)O(log⁡n) 。 + +可是这一题真的有这么复杂吗?我们可以使用一个大小为 105105 的辅助数组 visvis 来表示一个数字是否出现过。对于第二行中输入的每一个数字 aa ,我们在 visvis 数组中进行记录: vis(a)=truevis(a)=true 。这样,对于每一次询问,我们只用 O(1)O(1) 的时间访问 visvis 数组,就可以得到询问的结果了。而哈希,就是运用了这样的思想:我们多开一个数组,用这些多申请的空间去解决时间上的复杂。这也正是应了那句名言: + +以空间换时间。 —-蒋介石 + +字符串哈希 +那么如果我们把题改一下呢? + +设计一个程序,以完成以下输入输出: +第一行,输入两个数字 n,mn,m 。 +第二行,输入 nn 个字符串。 +第三行,输入 mm 个字符串。对于每一个字符串,询问其是否存在于第二行输入 nn 个字符串中,若是则输出”YES”,若不是则输出”NO”。 +数据说明:输入的字符串的长度范围在 1,105 。 + +如果输入的是字符串的话,我们不就不能用 visvis 数组来储存了吗?这可怎么办?当然,如果你很熟悉 STLSTL 这一神奇的模板库,那么你一定会毫不犹豫地打出一行这样的代码: + +mapvis; +1 +是的, mapmap 可以完美的解决这个问题。不过,我们可以试试用自己的力量来完成对字符串的处理:将字符串转换为数字。 + +怎样转换呢?我们知道,每一个字符都有它对应的 ASCIIASCII 码,也就是说,一个字符可以表示为一个数字。那么,如果把字符串看作是字符的连续,不就可以把每个字符的 ASCIIASCII 值连续起来表示字符串吗?我们可以运用这一点特性,将输入的字符串转换为一个 PP 进制数。在这里, PP 是一个大数。处理完后得到的 PP 进制数,就叫做这个字符串的哈希值。按照我的习惯,我会取 P=131P=131 。当然,如果你乐意,取点什么 P=233P=233 或 P=666P=666 也是可以的,不过很显然,计算难度也会随着 PP 的增大而逐步增大。 + +接下来给出一个函数 HashHash ,来获取一个字符串的哈希值: + +const int P=131; + +int Hash(string tmp) +{ + int hash=0; + for(int i=0;iv[M]; + +bool Query(string tmp) +{ + int pos=Hash(tmp); + for(int i=0;i0k>0,代表下次匹配跳到jj之前的某个字符,而不是跳到开头,且具体跳过了kk个字符。 + +转换成代码表示,则是: + +int KmpSearch(char* s, char* p) +{ + int i = 0; + int j = 0; + int sLen = strlen(s); + int pLen = strlen(p); + while (i < sLen && j < pLen) + { + //如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++ + if (j == -1 || s[i] == p[j]) + { + i++; + j++; + } + else + { + //如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j] + //next[j]即为j所对应的next值 + j = next[j]; + } + } + if (j == pLen) + return i - j; + else + return -1; +} +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +继续拿之前的例子来说,当S[10]跟P[6]匹配失败时,KMPKMP不是跟暴力匹配那样简单的把模式串右移一位,而是执行第②条指令:“如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令i不变,j = next[j]”,即j从6变到2(后面我们将求得P[6],即字符D对应的next值为2),所以相当于模式串向右移动的位数为j - next[j](j - next[j] = 6-2 = 4)。 + + + +向右移动4位后,S[10]跟P[2]继续匹配。为什么要向右移动4位呢,因为移动4位后,模式串中又有个“AB”可以继续跟S[8]、S[9]对应着,从而不用让i回溯。相当于在除去字符D的模式串子串中寻找相同的前缀和后缀,然后根据前缀后缀求出next数组,最后基于next数组进行匹配(不关心next数组是怎么求来的,只想看匹配过程是咋样的,可直接跳到下文3.3.43.3.4节)。 + + + +3.2 步骤 +①寻找前缀后缀最长公共元素长度 + +对于P=p[0,j]P=p[0,j],寻找模式串PP中长度最大且相等的前缀和后缀。如果存在p[0,k]=p[j−k,j]p[0,k]=p[j−k,j],那么在包含pjpj的模式串中有最大长度为k+1k+1的相同前缀后缀。举个例子,如果给定的模式串为“abab”,那么它的各个子串的前缀后缀的公共元素的最大长度如下表格所示: + +比如对于字符串"aba"来说,它有长度为11的相同前缀后缀a;而对于字符串abab来说,它有长度为22的相同前缀后缀ab(相同前缀后缀的长度为k+1k+1,k+1=2k+1=2)。 + +②求nextnext数组 + +nextnext数组考虑的是除当前字符外的最长相同前缀后缀,所以通过第①步骤求得各个前缀后缀的公共元素的最大长度后,只要稍作变形即可:将第①步骤中求得的值整体右移一位,然后初值赋为−1−1,如下表格所示: + +比如对于aba来说,第33个字符a之前的字符串ab中有长度为00的相同前缀后缀,所以第33个字符a对应的nextnext值为00;而对于abab来说,第44个字符b之前的字符串aba中有长度为11的相同前缀后缀a,所以第44个字符b对应的nextnext值为11(相同前缀后缀的长度为kk,k=1k=1)。 + +③根据nextnext数组进行匹配 + +匹配失配,j = next[j],模式串向右移动的位数为:j - next[j]。换言之,当模式串的后缀p[j−k,j−1]p[j−k,j−1]跟文本串s[i−k,i−1]s[i−k,i−1]匹配成功,但pjpj跟sisi匹配失败时,因为next[j] = k,相当于在不包含pjpj的模式串中有最大长度为kk的相同前缀后缀,即p[0,k−1]=p[j−k,j−1]p[0,k−1]=p[j−k,j−1],故令j = next[j],从而让模式串右移j - next[j]位,使得模式串的前缀p[0,k−1]p[0,k−1]对应着文本串s[i−k,i−1]s[i−k,i−1],而后让pkpk跟sisi继续匹配。如下图所示: + +综上,KMPKMP的nextnext数组相当于告诉我们:当模式串中的某个字符跟文本串中的某个字符匹配失配时,模式串下一步应该跳到哪个位置。如模式串中在jj处的字符跟文本串在ii处的字符匹配失配时,下一步用next[j]next[j]处的字符继续跟文本串ii处的字符匹配,相当于模式串向右移动j - next[j]位。 + +接下来,分别具体解释上述33个步骤。 + +3.3 解释 +3.3.1 寻找最长前缀后缀 +如果给定的模式串是:“ABCDABD”,从左至右遍历整个模式串,其各个子串的前缀后缀分别如下表格所示: + + + +也就是说,原模式串子串对应的各个前缀后缀的公共元素的最大长度表为(下简称《最大长度表》): + + + +3.3.2 基于《最大长度表》匹配 +因为模式串中首尾可能会有重复的字符,故可得出下述结论: + +失配时,模式串向右移动的位数为:已匹配字符数 - 失配字符的上一位字符所对应的最大长度值。 + +下面,咱们就结合之前的《最大长度表》和上述结论,进行字符串的匹配。如果给定文本串“BBC ABCDAB ABCDABCDABDE”,和模式串“ABCDABD”,现在要拿模式串去跟文本串匹配,如下图所示: + + + +因为模式串中的字符A跟文本串中的字符B、B、C、空格一开始就不匹配,所以不必考虑结论,直接将模式串不断的右移一位即可,直到模式串中的字符A跟文本串的第55个字符A匹配成功: + +继续往后匹配,当模式串最后一个字符D跟文本串匹配时失配,显而易见,模式串需要向右移动。但向右移动多少位呢?因为此时已经匹配的字符数为66个(ABCDAB),然后根据《最大长度表》可得失配字符D的上一位字符B对应的长度值为22,所以根据之前的结论,可知需要向右移动6−2=46−2=4位。 + +模式串向右移动44位后,发现C处再度失配,因为此时已经匹配了22个字符(AB),且上一位字符B对应的最大长度值为00,所以向右移动:2−0=22−0=2位。 + +A与空格失配,向右移动11位。 + +继续比较,发现D与C失配,故向右移动的位数为:已匹配的字符数66减去上一位字符B对应的最大长度22,即向右移动6−2=46−2=4位。 + +经历第55步后,发现匹配成功,过程结束。 + +通过上述匹配过程可以看出,问题的关键就是寻找模式串中最大长度的相同前缀和后缀,找到了模式串中每个字符之前的前缀和后缀公共部分的最大长度后,便可基于此匹配。而这个最大长度便正是nextnext数组要表达的含义。 + +3.3.3 根据《最大长度表》求next 数组 +由上文,我们已经知道,字符串“ABCDABD”各个前缀后缀的最大公共元素长度分别为: + + + +而且,根据这个表可以得出下述结论 + +失配时,模式串向右移动的位数为:已匹配字符数 - 失配字符的上一位字符所对应的最大长度值 +上文利用这个表和结论进行匹配时,我们发现,当匹配到一个字符失配时,其实没必要考虑当前失配的字符,更何况我们每次失配时,都是看的失配字符的上一位字符对应的最大长度值。如此,便引出了nextnext数组。 + +给定字符串“ABCDABD”,可求得它的nextnext数组如下: + + + +把nextnext数组跟之前求得的最大长度表对比后,不难发现,nextnext数组相当于“最大长度值” 整体向右移动一位,然后初始值赋为−1−1。意识到了这一点,你会惊呼原来next 数组的求解竟然如此简单:就是找最大对称长度的前缀后缀,然后整体右移一位,初值赋为−1−1(当然,你也可以直接计算某个字符对应的nextnext值,就是看这个字符之前的字符串中有多大长度的相同前缀后缀)。 + +换言之,对于给定的模式串:ABCDABD,它的最大长度表及nextnext数组分别如下: + + + +根据最大长度表求出了nextnext数组后,从而有 + +失配时,模式串向右移动的位数为:失配字符所在位置 - 失配字符对应的nextnext值 +而后,你会发现,无论是基于《最大长度表》的匹配,还是基于nextnext数组的匹配,两者得出来的向右移动的位数是一样的。为什么呢?因为: + +根据《最大长度表》,失配时,模式串向右移动的位数 = 已经匹配的字符数 - 失配字符的上一位字符的最大长度值。 + +而根据《nextnext数组》,失配时,模式串向右移动的位数 = 失配字符的位置 - 失配字符对应的nextnext值。 + +其中,从00开始计数时,失配字符的位置 = 已经匹配的字符数(失配字符不计数),而失配字符对应的nextnext值 = 失配字符的上一位字符的最大长度值,两相比较,结果必然完全一致。 +所以,你可以把《最大长度表》看做是nextnext数组的雏形,甚至就把它当做nextnext数组也是可以的,区别不过是怎么用的问题。 + +3.3.4 通过代码递推计算next 数组 +接下来,咱们来写代码求下nextnext数组。 + +基于之前的理解,可知计算nextnext数组的方法可以采用递推: + +1.如果对于值kk,已有p[0,k−1]=p[j−k,j−1]p[0,k−1]=p[j−k,j−1],相当于next[j]=knext[j]=k。 + +此意味着什么呢?究其本质,next[j] = k代表p[j]之前的模式串子串中,有长度为kk的相同前缀和后缀。有了这个nextnext数组,在KMPKMP匹配中,当模式串中j处的字符失配时,下一步用next[j]处的字符继续跟文本串匹配,相当于模式串向右移动j - next[j]位。 +举个例子,如下图,根据模式串“ABCDABD”的nextnext数组可知失配位置的字符D对应的nextnext值为22,代表字符D前有长度为22的相同前缀和后缀(这个相同的前缀后缀即为“AB”),失配后,模式串需要向右移动j - next [j] = 6 - 2 =4位。 + +向右移动44位后,模式串中的字符C继续跟文本串匹配。 + +2.下面的问题是:已知next[0,j]next[0,j],如何求出next[j+1]next[j+1]呢? +对于PP的前j+1j+1个序列字符: + +若p[k] == p[j],则next[j + 1] = next[j] + 1 = k + 1; + +若p[k] != p[j],如果此时p[next[k]] == p[j],则next[ j + 1 ] = next[k] + 1,否则继续递归前缀索引k = next[k],而后重复此过程。 相当于在字符p[j+1]之前不存在长度为k+1k+1的前缀p[0,k]p[0,k]跟后缀p[j−k,j]p[j−k,j]相等,那么是否可能存在另一个值t+1 int: + counter = Counter(s) + + for (i, c) in enumerate(s): + if counter[c] == 1: + return i + return -1 diff --git a/Week 08/id_433/LeetCode_5_433.py b/Week 08/id_433/LeetCode_5_433.py new file mode 100644 index 000000000..0c67406d6 --- /dev/null +++ b/Week 08/id_433/LeetCode_5_433.py @@ -0,0 +1,18 @@ +class Solution: + def longestPalindrome(self, s: str) -> str: + res = "" + for i in range(len(s)): + tmp = self.helper(s, i-1, i+1) + if len(tmp) > len(res): + res = tmp + tmp = self.helper(s, i, i+1) + if len(tmp) > len(res): + res = tmp + + return res + + def helper(self, s, l, r): + while l >= 0 and r < len(s) and s[l] == s[r]: + l -= 1 + r += 1 + return s[l+1: r] \ No newline at end of file diff --git a/Week 08/id_433/NOTE.md b/Week 08/id_433/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_433/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_438/NOTE.md b/Week 08/id_438/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_438/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_443/LeetCode_387_443.java b/Week 08/id_443/LeetCode_387_443.java new file mode 100644 index 000000000..f58bab26a --- /dev/null +++ b/Week 08/id_443/LeetCode_387_443.java @@ -0,0 +1,51 @@ +//给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。 +// +// 案例: +// +// +//s = "leetcode" +//返回 0. +// +//s = "loveleetcode", +//返回 2. +// +// +// +// +// 注意事项:您可以假定该字符串只包含小写字母。 +// Related Topics 哈希表 字符串 + +package com.modds.alltest.leetcode.editor.cn; + +public class LeetCode_387_443_FirstUniqueCharacterInAString { + public static void main(String[] args) { + Solution solution = new LeetCode_387_443_FirstUniqueCharacterInAString().new Solution(); + } + + + //leetcode submit region begin(Prohibit modification and deletion) + class Solution { + public int firstUniqChar(String s) { + if (s == null || s.length() == 0) return -1; + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + int count = 0; + for (int j = 0; j < s.length(); j++) { + if (c == s.charAt(j)) { + count++; + } + if (count > 1) { + break; + } + } + if (count == 1) { + return i; + } + } + return -1; + } + } +//leetcode submit region end(Prohibit modification and deletion) + +} \ No newline at end of file diff --git a/Week 08/id_443/LeetCode_541_443.java b/Week 08/id_443/LeetCode_541_443.java new file mode 100644 index 000000000..b3546cb3d --- /dev/null +++ b/Week 08/id_443/LeetCode_541_443.java @@ -0,0 +1,47 @@ + //给定一个字符串和一个整数 k,你需要对从字符串开头算起的每个 2k 个字符的前k个字符进行反转。如果剩余少于 k 个字符,则将剩余的所有全部反转。如果有小于 2k 但大于或等于 k 个字符,则反转前 k 个字符,并将剩余的字符保持原样。 +// +// 示例: +// +// +//输入: s = "abcdefg", k = 2 +//输出: "bacdfeg" +// +// +// 要求: +// +// +// 该字符串只包含小写的英文字母。 +// 给定字符串的长度和 k 在[1, 10000]范围内。 +// +// Related Topics 字符串 + +package com.modds.alltest.leetcode.editor.cn; + public class LeetCode_541_443_ReverseStringIi{ + public static void main(String[] args) { + Solution solution = new LeetCode_541_443_ReverseStringIi().new Solution(); + } + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public String reverseStr(String s, int k) { + if(s.length() == 0) return ""; + char[] cs = s.toCharArray(); + + for( int start = 0 ; start < s.length() ; start += 2 * k){ + int i=start; + int j = Math.min( start + k - 1,s.length()-1); + while( i < j){ + char tmp = cs[i]; + cs[i++] = cs[j]; + cs[j--] = tmp; + } + } + + return new String(cs); + + } +} +//leetcode submit region end(Prohibit modification and deletion) + + } \ No newline at end of file diff --git a/Week 08/id_443/NOTE.md b/Week 08/id_443/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_443/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_448/NOTE.md b/Week 08/id_448/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_448/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_453/NOTE.md b/Week 08/id_453/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_453/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_458/NOTE.md b/Week 08/id_458/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_458/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_463/NOTE.md b/Week 08/id_463/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_463/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_468/NOTE.md b/Week 08/id_468/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_468/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_473/NOTE.md b/Week 08/id_473/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_473/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_478/NOTE.md b/Week 08/id_478/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_478/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_483/NOTE.md b/Week 08/id_483/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_483/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_488/NOTE.md b/Week 08/id_488/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_488/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git "a/Week 08/id_493/387.\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\347\254\254\344\270\200\344\270\252\345\224\257\344\270\200\345\255\227\347\254\246.js" "b/Week 08/id_493/387.\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\347\254\254\344\270\200\344\270\252\345\224\257\344\270\200\345\255\227\347\254\246.js" new file mode 100644 index 000000000..26ec17367 --- /dev/null +++ "b/Week 08/id_493/387.\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\347\254\254\344\270\200\344\270\252\345\224\257\344\270\200\345\255\227\347\254\246.js" @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode.cn id=387 lang=javascript + * + * [387] 字符串中的第一个唯一字符 + */ + +// @lc code=start +/** + * @param {string} s + * @return {number} + */ +var firstUniqChar = function(s) { + if (!s && s.length == 0) { + return -1; + } + let sMap = new Map(); + for (let i = 0; i < s.length; i++) { + if (sMap.has(s[i])) { + sMap.set(s[i], sMap.get(s[i]) + 1); + } else { + sMap.set(s[i], 1); + } + } + for (let i = 0; i < s.length; i++) { + if (sMap.get(s[i]) == 1) { + return i; + } + } + return -1; +}; +// @lc code=end + diff --git "a/Week 08/id_493/91.\350\247\243\347\240\201\346\226\271\346\263\225.js" "b/Week 08/id_493/91.\350\247\243\347\240\201\346\226\271\346\263\225.js" new file mode 100644 index 000000000..8d1a84adc --- /dev/null +++ "b/Week 08/id_493/91.\350\247\243\347\240\201\346\226\271\346\263\225.js" @@ -0,0 +1,37 @@ +/* + * @lc app=leetcode.cn id=91 lang=javascript + * + * [91] 解码方法 + */ + +// @lc code=start +/** + * @param {string} s + * @return {number} + */ +var numDecodings = function(s) { + if (!s || s.length == 0 || s[0] == '0') { + return 0; + } + let preCount = 1; + let prepreCount = 1; + for (let i = 1; i < s.length; i++) { + let curCount = 0; + if (s[i] == '0' && (s[i - 1] == '1' || s[i - 1] == '2')) { + curCount = prepreCount; + } else if (s[i - 1] == '1' || (s[i - 1] == '2' && s[i] > '0' && s[i] <= '6')) { + curCount = preCount + prepreCount; + } + else if (s[i] == '0' && !(s[i - 1] == '1' || s[i - 1] == '2')) { + return 0; + } + else { + curCount = preCount; + } + prepreCount = preCount; + preCount = curCount; + } + return preCount; +}; +// @lc code=end + diff --git a/Week 08/id_493/NOTE.md b/Week 08/id_493/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_493/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_498/LeetCode_300_498.py b/Week 08/id_498/LeetCode_300_498.py new file mode 100644 index 000000000..6fa315caf --- /dev/null +++ b/Week 08/id_498/LeetCode_300_498.py @@ -0,0 +1,15 @@ +from typing import List + + +class Solution: + @staticmethod + def lengthOfLIS(nums: List[int]) -> int: + if not nums: + return 0 + dp = [1] * len(nums) + for i in range(len(nums)): + for j in range(i): + # 如果要求非严格递增,将此行 '<' 改为 '<=' 即可。 + if nums[j] < nums[i]: + dp[i] = max(dp[i], dp[j] + 1) + return max(dp) diff --git a/Week 08/id_498/LeetCode_85_498.py b/Week 08/id_498/LeetCode_85_498.py new file mode 100644 index 000000000..7ae104e11 --- /dev/null +++ b/Week 08/id_498/LeetCode_85_498.py @@ -0,0 +1,78 @@ +from typing import List + + +class Solution: + @staticmethod + def maximalRectangle(matrix: List[List[str]]) -> int: + """ + 方法:dp + """ + # 特判 + if not matrix: + return 0 + + # 初始化 + rows = len(matrix) + columns = len(matrix[0]) + # 下标是 0 开始 + left = [-1] * columns + # 下标是 columns - 1 结束 + right = [columns] * columns + height = [0] * columns + maxArea = 0 + + # 两层循环 + for y in range(rows): + current_left = -1 + current_right = columns + + # 更新高 + for x in range(columns): + # 如果碰到 1 了 + if matrix[y][x] == '1': + # 高度就累加 1 + height[x] += 1 + else: + # 没有就是 0 + height[x] = 0 + + # 更新左指针 + for x in range(columns): + # 如果碰到了 1 + if matrix[y][x] == '1': + # 对比下标,谁更靠右 + left[x] = max(left[x], current_left) + # 如果是 0 + else: + left[x] = -1 + # 记录当前下标 + current_left = x + + # 更新右指针 + for x in range(columns - 1, -1, -1): + # 如果碰到了 1 + if matrix[y][x] == '1': + # 对比下标,谁更靠左 + right[x] = min(right[x], current_right) + # 如果是 0 + else: + right[x] = columns + # 记录当前下标 + current_right = x + + # 更新面积 + for x in range(columns): + maxArea = max(maxArea, height[x] * (right[x] - left[x] - 1)) + + return maxArea + + +m = [ + ["1", "0", "1", "0", "0"], + ["1", "0", "1", "1", "1"], + ["1", "1", "1", "1", "1"], + ["1", "0", "0", "1", "0"] +] + +s = Solution.maximalRectangle(m) +print(s) diff --git a/Week 08/id_498/NOTE.md b/Week 08/id_498/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_498/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_503/NOTE.md b/Week 08/id_503/NOTE.md new file mode 100755 index 000000000..fdd80123f --- /dev/null +++ b/Week 08/id_503/NOTE.md @@ -0,0 +1,46 @@ +# 高级动态规划 +* dp 顺推模板 + ```js + function DP() { + let dp = [][] // ⼆维情况 + for (let i = 0; i < M; i++ { + for (let j = 0; j < N; j++) { + dp[i][j] = _Function(dp[i’][j’]…) + } + } + return dp[M][N]; + } + ``` +* 关键点 + * **动态规划**和**递归**或者**分治**没有根本上的区别(关键看有无最优的子结构) + * 拥有共性:找到重复子问题 + * 差异性:最优子结构、中途可以淘汰次优解 + +# 高级字符串算法 +* Longest common sequence(最长子序列) + ```js + if (s1[i-1] == s2[j-1]) + dp[i][j] = dp[i-1][j-1] + 1; + else + dp[i][j] = max(dp[i-1][j], dp[i][j-1]); + ``` +* Longest common substring (最长子串) + ```js + if (s1[i-1] == s2[j-1]) + dp[i][j] = dp[i-1][j-1] + 1; + else + dp[i][j] = 0; + ``` +# 字符串匹配算法 +* Rabin-Karp 算法 + + * 在朴素算法中,我们需要挨个比较所有字符,才知道目标字符串中是否包含子串。为了避免挨个字符对目标字符串 和子串进行比较,可以尝试一次性判断两者是否相等。因此,我们需要一个**好的哈希函数(hash function)**。通过哈希函数,我们可以算出**子串的哈希值**,然后将它和**目标字符串中的子串的哈希值进行比较**。 这个方法在速度上**比暴力法有显著提升**。 + * 核心思想 + * 假设子串的长度为 M (pat),目标字符串的长度为 N (txt) + * 计算子串的 hash 值 hash_pat + * 计算目标字符串txt中每个长度为 M 的子串的 hash 值(共需要计算 N-M+1 次) + * 比较 hash 值:如果 hash 值不同,字符串必然不匹配; 如果 hash 值相同, 还需要使用朴素算法再次判 +* KMP 算法 + * 核心思想: + * 当子串与目标字符串不匹配时,其实已经知道了前面已经匹配成功那一部分的字符(包括子串与目标字符串)。比如,当空格与 D 不匹配时,其实知道前面六个字符是 “ABCDAB”。 + * 设法利用这个已知信息,不要把“搜索位置” **移回已经比较过**的位置,继续把它向后移,提高效率 diff --git a/Week 08/id_503/leetcode_387_503.js b/Week 08/id_503/leetcode_387_503.js new file mode 100644 index 000000000..1e2a62e28 --- /dev/null +++ b/Week 08/id_503/leetcode_387_503.js @@ -0,0 +1,37 @@ +/** + * @param {string} s + * @return {number} + */ +var firstUniqChar = function (s) { + if (s === "") { + return -1; + } + const repeatMap = {}; + for (let i = 0; i < s.length; i++) { + if (repeatMap[s[i]]) { + repeatMap[s[i]]++ + continue; + } + + repeatMap[s[i]] = 1; + } + + for (let i = 0; i < s.length; i++) { + if (repeatMap[s[i]] === 1) { + return i; + } + } + + return -1; +}; + + +function DP() { + let dp = [][] // ⼆维情况 + for (let i = 0; i < M; i++ { + for (let j = 0; j < N; j++) { + dp[i][j] = _Function(dp[i’][j’]…) + } + } + return dp[M][N]; +} \ No newline at end of file diff --git a/Week 08/id_503/leetcode_91_503.js b/Week 08/id_503/leetcode_91_503.js new file mode 100644 index 000000000..89967e0aa --- /dev/null +++ b/Week 08/id_503/leetcode_91_503.js @@ -0,0 +1,31 @@ +/** + * @param {string} s + * @return {number} + */ +var numDecodings = function (s) { + + if (s === "" || s[0] === '0') { + return 0 + } + + const n = s.length; + if (n === 1) { + return 1; + } + + const dp = new Array(n + 1).fill(0); + dp[0] = 1; + + for (let i = 1; i <= n; i++) { + if (s[i - 1] !== "0") { + dp[i] += dp[i - 1]; + } + + if (i > 1 && + (s[i - 2] === "1" || (s[i - 2] == "2" && s[i - 1] <= "6"))) { + dp[i] += dp[i - 2]; + } + } + + return dp[n]; +}; \ No newline at end of file diff --git a/Week 08/id_508/LeetCode_387_508.cpp b/Week 08/id_508/LeetCode_387_508.cpp new file mode 100644 index 000000000..511d59e53 --- /dev/null +++ b/Week 08/id_508/LeetCode_387_508.cpp @@ -0,0 +1,13 @@ +class Solution { +public: + int firstUniqChar(string s) { + int bin[26]; + for(auto &i:bin) i = 0; + + for(auto i:s) bin[i-'a'] +=1; + for(int i=0;i= 0 && right < s.Length) + { + if (s[left] == s[right]) + { + if (right - left + 1 > result.Length) + { + result = s.Substring(left, right - left + 1); + } + + left--; + right++; + } + else + { + break; + } + } + + left = i; + right = i; + if (right + 1 < s.Length && s[left] == s[right + 1]) + { + right++; + while (left >= 0 && right < s.Length) + { + if (s[left] == s[right]) + { + if (right - left + 1 > result.Length) + { + result = s.Substring(left, right - left + 1); + } + + left--; + right++; + } + else + { + break; + } + } + } + } + + return result; + } +} \ No newline at end of file diff --git a/Week 08/id_523/LeetCode_8_523.cs b/Week 08/id_523/LeetCode_8_523.cs new file mode 100644 index 000000000..f88e150cb --- /dev/null +++ b/Week 08/id_523/LeetCode_8_523.cs @@ -0,0 +1,32 @@ +public class Solution +{ + public int MyAtoi(string str) + { + if (string.IsNullOrWhiteSpace(str)) return 0; + str = str.Trim(' '); + + bool isNagetive = str[0] == '-'; + + int result = 0; + for (int i = (isNagetive || str[0] == '+') ? 1 : 0; i < str.Length; i++) + { + if (str[i] < 48 || str[i] > 57) break; + + var val = str[i] - 48; + + if (!isNagetive && (result > int.MaxValue / 10 || (int.MaxValue - result * 10 - val) < 0)) + { + return int.MaxValue; + } + + if (isNagetive && (0 - result < int.MinValue / 10 || (int.MinValue + result * 10 + val) > 0)) + { + return int.MinValue; + } + + result = result * 10 + val; + } + + return isNagetive ? 0 - result : result; + } +} \ No newline at end of file diff --git a/Week 08/id_523/NOTE.md b/Week 08/id_523/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_523/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_528/NOTE.md b/Week 08/id_528/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_528/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_533/NOTE.md b/Week 08/id_533/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_533/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_538/NOTE.md b/Week 08/id_538/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_538/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_543/LeetCode_151_543.java b/Week 08/id_543/LeetCode_151_543.java new file mode 100644 index 000000000..fa4b983d5 --- /dev/null +++ b/Week 08/id_543/LeetCode_151_543.java @@ -0,0 +1,52 @@ +//给定一个字符串,逐个翻转字符串中的每个单词。 +// +// +// +// 示例 1: +// +// 输入: "the sky is blue" +//输出: "blue is sky the" +// +// +// 示例 2: +// +// 输入: "  hello world!  " +//输出: "world! hello" +//解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 +// +// +// 示例 3: +// +// 输入: "a good   example" +//输出: "example good a" +//解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 +// +// +// +// +// 说明: +// +// +// 无空格字符构成一个单词。 +// 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 +// 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 +// +// +// +// +// 进阶: +// +// 请选用 C 语言的用户尝试使用 O(1) 额外空间复杂度的原地解法。 +// Related Topics 字符串 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public String reverseWords(String s) { + String[] words = s.trim().split(" +"); + Collections.reverse(Arrays.asList(words)); + return String.join(" ",words); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_543/LeetCode_387_543.java b/Week 08/id_543/LeetCode_387_543.java new file mode 100644 index 000000000..ae5ef1a74 --- /dev/null +++ b/Week 08/id_543/LeetCode_387_543.java @@ -0,0 +1,35 @@ +//给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。 +// +// 案例: +// +// +//s = "leetcode" +//返回 0. +// +//s = "loveleetcode", +//返回 2. +// +// +// +// +// 注意事项:您可以假定该字符串只包含小写字母。 +// Related Topics 哈希表 字符串 + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int firstUniqChar(String s) { + int[] seq = new int[26]; + for (int i = 0; i < s.length(); i++){ + seq[s.charAt(i) - 'a']++; + } + for(int i = 0; i < s.length(); i++){ + if(seq[s.charAt(i) - 'a'] == 1){ + return i; + } + } + return -1; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_543/NOTE.md b/Week 08/id_543/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_543/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_548/NOTE.md b/Week 08/id_548/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_548/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_553/NOTE.md b/Week 08/id_553/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_553/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_558/LeetCode_151_558.java b/Week 08/id_558/LeetCode_151_558.java new file mode 100644 index 000000000..88e88fedb --- /dev/null +++ b/Week 08/id_558/LeetCode_151_558.java @@ -0,0 +1,25 @@ +package Week07; + + +import java.util.*; + +/** + * @see https://leetcode-cn.com/problems/reverse-words-in-a-string/ + * 翻转字符串里的单词 + */ +public class LeetCode_151_558 { + + public String reverseWords(String s) { + if (s == null || s.length() == 0) return s; + String[] ss = s.trim().split(" +"); + Collections.reverse(Arrays.asList(ss)); + return String.join(" ", ss); + } + + public static void main(String[] args) { + String s = "the sky is blue"; + System.out.println(new LeetCode_151_558().reverseWords(s)); + } + + +} diff --git a/Week 08/id_558/LeetCode_387_558.java b/Week 08/id_558/LeetCode_387_558.java new file mode 100644 index 000000000..f5fd6a089 --- /dev/null +++ b/Week 08/id_558/LeetCode_387_558.java @@ -0,0 +1,38 @@ +package Week07; + + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Set; + +/** + * @see https://leetcode-cn.com/problems/first-unique-character-in-a-string/ + * 387. 字符串中的第一个唯一字符 + */ +public class LeetCode_387_558 { + + public int firstUniqChar(String s) { + if (s == null || s.length() == 0) { + return -1; + } + int len = s.length(); + HashMap hashMap = new HashMap<>(); + for (int i = 0; i < len; i++) { + char key = s.charAt(i); + hashMap.put(key, hashMap.getOrDefault(key, 0) + 1); + } + for (int i = 0; i < len; i++) { + char key = s.charAt(i); + if (hashMap.getOrDefault(key, 0) == 1) { + return i; + } + } + return -1; + } + + public static void main(String[] args) { + + } + + +} diff --git a/Week 08/id_558/LeetCode_709_558.java b/Week 08/id_558/LeetCode_709_558.java new file mode 100644 index 000000000..b846f1c14 --- /dev/null +++ b/Week 08/id_558/LeetCode_709_558.java @@ -0,0 +1,31 @@ +package Week07; + + +/** + * @see https://leetcode-cn.com/problems/to-lower-case/ + * 转换成小写字母 + */ +public class LeetCode_709_558 { + + public static String toLowerCase(String str) { + if (str == null || str.length() == 0) { + return str; + } + String s = ""; + char[] chars = str.toCharArray(); + for (int c : chars) { + if (c >= 65 && c <= 90) { + c = c + 32; + } + s += (char)c; + } + return s; + } + + public static void main(String[] args) { + char c = 'Z'; + System.out.println(toLowerCase("A6l")); + } + + +} diff --git a/Week 08/id_558/LeetCode_771_558.java b/Week 08/id_558/LeetCode_771_558.java new file mode 100644 index 000000000..a151d3f83 --- /dev/null +++ b/Week 08/id_558/LeetCode_771_558.java @@ -0,0 +1,45 @@ +package Week07; + + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Set; + +/** + * @see https://leetcode-cn.com/problems/jewels-and-stones/ + * 宝石与石头 + */ +public class LeetCode_771_558 { + + public int numJewelsInStones(String J, String S) { + if (J == null || J.length() == 0 || S == null || S.length() == 0) { + return 0; + } + + HashMap map = new HashMap<>(); + for (int i = 0; i < J.length(); i++) { + map.put(J.charAt(i), 0); + } + + for (int i = 0; i < S.length(); i++) { + char key = S.charAt(i); + if (map.containsKey(key)) { + map.put(key, map.get(key) + 1); + } + + } + int sum = 0; + Set characters = map.keySet(); + Iterator iterator = characters.iterator(); + while (iterator.hasNext()) { + sum += map.get(iterator.next()); + } + return sum; + } + + public static void main(String[] args) { + + } + + +} diff --git a/Week 08/id_558/LeetCode_917_558.java b/Week 08/id_558/LeetCode_917_558.java new file mode 100644 index 000000000..f19a054ef --- /dev/null +++ b/Week 08/id_558/LeetCode_917_558.java @@ -0,0 +1,28 @@ +package Week07; + + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Set; + +/** + * @see https://leetcode-cn.com/problems/reverse-only-letters/submissions/ + * 仅仅反转字母 + */ +public class LeetCode_917_558 { + + public String reverseOnlyLetters(String S) { + StringBuilder ans = new StringBuilder(); + int j = S.length() - 1; + for (int i = 0; i < S.length(); ++i) { + if (Character.isLetter(S.charAt(i))) { + while (!Character.isLetter(S.charAt(j))) j--; + ans.append(S.charAt(j--)); + } else { + ans.append(S.charAt(i)); + } + } + return ans.toString(); + } + +} diff --git a/Week 08/id_558/NOTE.md b/Week 08/id_558/NOTE.md new file mode 100755 index 000000000..a81da7457 --- /dev/null +++ b/Week 08/id_558/NOTE.md @@ -0,0 +1,31 @@ +# NOTE +#### 知识回顾 +* 递归、分治、回溯、动态规划 + * 找重复逻辑 + * 代码模板 + +* 字符串操作 + * 字符串转数字 + * 异位词 + * 回文 + * 最长子串、子序列 + * 字符串匹配 + * brute force + * Rabin-karp + * KMP + +#### 处理输出 + +* [翻转字符串里的单词](https://leetcode-cn.com/problems/reverse-words-in-a-string/) +* [字符串中的第一个唯一字符](https://leetcode-cn.com/problems/first-unique-character-in-a-string/) +* [转换成小写字母](https://leetcode-cn.com/problems/to-lower-case/) +* [宝石与石头](https://leetcode-cn.com/problems/jewels-and-stones/) +* [仅仅反转字母](https://leetcode-cn.com/problems/reverse-only-letters/submissions/) +#### 遗留问题 + * 高级DP + * KMP练习 + +#### 总结改进 +* 理解-拆分-练习 +* 提高练习难度 + diff --git a/Week 08/id_563/Leecode_344_563.go b/Week 08/id_563/Leecode_344_563.go new file mode 100644 index 000000000..319382e98 --- /dev/null +++ b/Week 08/id_563/Leecode_344_563.go @@ -0,0 +1,8 @@ +func reverseString(s []byte) { + left, right := 0, len(s)-1 + for i := 0; i < len(s)/2; i++ { + s[left], s[right] = s[right], s[left] + left++ + right-- + } +} \ No newline at end of file diff --git a/Week 08/id_563/Leecode_771_563.go b/Week 08/id_563/Leecode_771_563.go new file mode 100644 index 000000000..53522ffde --- /dev/null +++ b/Week 08/id_563/Leecode_771_563.go @@ -0,0 +1,10 @@ +func numJewelsInStones(J string, S string) (ret int) { + for _, c1 := range J { + for _, c2 := range S { + if c1 == c2 { + ret++ + } + } + } + return +} \ No newline at end of file diff --git a/Week 08/id_563/NOTE.md b/Week 08/id_563/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_563/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_568/LeetCode_300_568.java b/Week 08/id_568/LeetCode_300_568.java new file mode 100644 index 000000000..4ecf9dda5 --- /dev/null +++ b/Week 08/id_568/LeetCode_300_568.java @@ -0,0 +1,20 @@ +import java.lang.reflect.Array; +import java.util.Arrays; + +public class LeetCode_300_568 { + public int lengthOfLIS(int[] nums) { + int[] dp = new int[nums.length]; + int len = 0; + for (int num : nums) { + int i = Arrays.binarySearch(dp, 0, len, num); + if (i < 0) { + i=-(i+1); + } + dp[i]=num; + if(i==len){ + len++; + } + } + return len; + } +} diff --git a/Week 08/id_568/LeetCode_387_568.java b/Week 08/id_568/LeetCode_387_568.java new file mode 100644 index 000000000..b6cbc5164 --- /dev/null +++ b/Week 08/id_568/LeetCode_387_568.java @@ -0,0 +1,18 @@ +import java.util.HashMap; + +public class LeetCode_387_568 { + public int firstUniqChar(String s) { + HashMap count = new HashMap<>(); + int n = s.length(); + for (int i = 0; i < n; i++) { + char c = s.charAt(i); + count.put(c, count.getOrDefault(c, 0) + 1); + } + for (int i = 0; i < n; i++) { + if(count.get(s.charAt(i))==1){ + return i; + } + } + return -1; + } +} diff --git a/Week 08/id_568/LeetCode_438_568.java b/Week 08/id_568/LeetCode_438_568.java new file mode 100644 index 000000000..f9a8f4d5a --- /dev/null +++ b/Week 08/id_568/LeetCode_438_568.java @@ -0,0 +1,40 @@ +import java.util.ArrayList; +import java.util.List; + +public class LeetCode_438_568 { + public List findAnagrams(String s, String p) { + if (s == null || s.length() == 0) + return new ArrayList<>(); + List res = new ArrayList<>(); + int[] needs = new int[26]; + int[] window = new int[26]; + int left = 0, right = 0, total = p.length(); + for (char ch : p.toCharArray()) { + needs[ch - 'a']++; + } + while (right < s.length()) { + char chr = s.charAt(right); + if (needs[chr - 'a'] > 0) { + window[chr - 'a']++; + if (window[chr - 'a'] <= needs[chr - 'a']) { + total--; + } + } + while (total == 0) { + if (right - left + 1 == p.length()) { + res.add(left); + } + char ch1 = s.charAt(left); + if (needs[ch1 - 'a'] > 0) { + window[ch1 - 'a']--; + if (window[ch1 - 'a'] < needs[ch1 - 'a']) { + total++; + } + } + left++; + } + right ++; + } + return res; + } +} diff --git a/Week 08/id_568/LeetCode_62_568.java b/Week 08/id_568/LeetCode_62_568.java new file mode 100644 index 000000000..def5a9da6 --- /dev/null +++ b/Week 08/id_568/LeetCode_62_568.java @@ -0,0 +1,20 @@ +import java.util.Arrays; + +public class LeetCode_62_568 { + public static int uniquePaths(int m, int n) { + if (m <= 0 && m > 100 || n < 0 && n > 100) { + return 0; + } + int less = Math.min(m, n); + int more = Math.max(m, n); + int[] dp = new int[less]; + Arrays.fill(dp, 1); + for (int i = 1; i < more; i++) { + for (int j = 1; j < less; j++) { + dp[j] = dp[j - 1] + dp[j]; + } + } + return dp[less-1]; + + } +} diff --git a/Week 08/id_568/LeetCode_63_568.java b/Week 08/id_568/LeetCode_63_568.java new file mode 100644 index 000000000..932e306b8 --- /dev/null +++ b/Week 08/id_568/LeetCode_63_568.java @@ -0,0 +1,29 @@ +public class LeetCode_63_568 { + + private int colNum; + + public int uniquePathsWithObstacles(int[][] obstacleGrid) { + int rowNum = obstacleGrid.length; + int colNum = obstacleGrid[0].length; + if (obstacleGrid[0][0] == 1) { + return 0; + } + obstacleGrid[0][0] = 1; + for (int i = 1; i < rowNum; i++) { + obstacleGrid[i][0] = (obstacleGrid[i][0] == 0 && obstacleGrid[i - 1][0] == 1) ? 1 : 0; + } + for (int i = 1; i < colNum; i++) { + obstacleGrid[0][i] = (obstacleGrid[0][i] == 0 && obstacleGrid[0][i - 1] == 1) ? 1 : 0; + } + for (int i = 1; i < rowNum; i++) { + for (int j = 1; j < colNum; j++) { + if (obstacleGrid[i][j] == 0) { + obstacleGrid[i][j] = obstacleGrid[i - 1][j] + obstacleGrid[i][j - 1]; + } else { + obstacleGrid[i][j] = 0; + } + } + } + return obstacleGrid[rowNum-1][colNum-1]; + } +} diff --git a/Week 08/id_568/NOTE.md b/Week 08/id_568/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_568/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_573/NOTE.md b/Week 08/id_573/NOTE.md new file mode 100755 index 000000000..2e1854a6f --- /dev/null +++ b/Week 08/id_573/NOTE.md @@ -0,0 +1,267 @@ +# NOTE + +### 动态规划概念 +```` +动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。 +```` + +### 动态规划分类 + +``` +动态规划一般可分为线性动规,区域动规,树形动规,背包动规四类。 +举例: + 线性动规:拦截导弹,合唱队形,挖地雷,建学校,剑客决斗等; + 区域动规:石子合并, 加分二叉树,统计单词个数,炮兵布阵等; + 树形动规:贪吃的九头龙,二分查找树,聚会的欢乐,数字三角形等; + 背包问题:01背包问题,完全背包问题,分组背包问题,二维背包,装箱问题,挤牛奶(同济ACM第1132题)等; + +应用实例: + 最短路径问题 ,项目管理,网络流优化等; +``` + + +#### 动态规划问题实例 + +``` +解决动态规划类问题,分为两步:1.确定状态,2.根据状态列状态转移方程 + 确定该状态上可以执行的操作,然后是该状态和前一个状态或者前多个状态有什么关联,通常该状态下可执行的操作必定是关联到我们之前的几个状态。 + +1、数字三角形 + +2、背包问题两讲 + 这里解决了两张背包问题,一个是确定最多可以装的下多少的背包盛放物品问题,还有一个是背包中放置的物品具有价值,要来确定其价值为多少。解决方法都是通过动态规划来解决。 + +3、公共子序列,公共子串问题 + 公共子串 + 给出两个字符串,找到最长公共子串,并返回其长度 + + 状态,字符串的每一位对应另一个字符串的每一个位置,因此通过一个二维数组来表示这每一个状态位,然后是找状态转移方程,转移方程即为其前一个位置的前一个的比对的结果累计当前的结果,如果相同则加1,否则为0 + +4、打劫房屋 + 问题描述 + 假设你是一个专业的窃贼,准备沿着一条街打劫房屋。每个房子都存放着特定金额的钱。你面临的唯一约束条件是:相邻的房子装着相互联系的防盗系统,且 当相邻的两个房子同一天被打劫时,该系统会自动报警。 + 给定一个非负整数列表,表示每个房子中存放的钱, 算一算,如果今晚去打劫,你最多可以得到多少钱 在不触动报警装置的情况下。 + +5、编辑距离 + 题目描述 + 给出两个单词word1和word2,计算出将word1 转换为word2的最少操作次数。 + + 你总共三种操作方法: + + 插入一个字符 + + 删除一个字符 + + 替换一个字符 + + 三种操作,因此我们在一个状态上面可以进行三种状态的变化,确定每一个状态,通过第二个字符串和第一个字符串的每一个位置的对应作为一个状态,处在该状态上,我们可以进行的操作,改,进行改操作,那么与之关联的前一个状态是其前一个字符对应另一个字符串的当前对应的前一个字符,增,则是说当前字符串的当前位对应到前一个字符串的前一个位置,删,则为当前字符串的当前位对应前一个字符串的前一个位置。为了增加一个增的位置,需要我们在其前面,所以我们在两个字符串的开始处设置一增加的位置。 + +6、N皇后问题 + n皇后问题是将n个皇后放置在n*n的棋盘上,皇后彼此之间不能相互攻击。 + 给定一个整数n,返回所有不同的n皇后问题的解决方案。 + 对于n皇后的问题,下一个皇后的布局位置将与之前的所有王后布局有关,因此通过动态规划,没安置一个皇后就作为一个状态,然后判断之前的已经安放的所有皇后的状态,确定是否可以按这一个皇后,通过递归的方式实现。 + +``` + +``` +### Trie +Trie 树优于哈希表的另一个理由是,随着哈希表大小增加,会出现大量的冲突,时间复杂度可能增加到 O(n)O(n),其中 nn 是插入的键的数量。与哈希表相比,Trie 树在存储多个具有相同前缀的键时可以使用较少的空间。此时 Trie 树只需要 O(m)O(m) 的时间复杂度,其中 mm 为键长。而在平衡树中查找键值需要 O(m \log n)O(mlogn) 时间复杂度。 + +### 作用 +Trie (发音为 "try") 或前缀树是一种树数据结构,用于检索字符串数据集中的键。这一高效的数据结构有多种应用: + + 1. 自动补全 + 2. 拼写检查 + 3. IP 路由 (最长前缀匹配) + 4. T9 (九宫格) 打字预测 + 5. 单词游戏 + 6. 还有其他的数据结构,如平衡树和哈希表,使我们能够在字符串数据集中搜索单词。为什么我们还需要 Trie 树呢?尽管哈希表可以在 O(1)O(1) 时间内寻找键值,却无法高效的完成以下操作: + 找到具有同一前缀的全部键值。 + 按词典序枚举字符串的数据集。 + + +### Trie 树的结点结构 + + Trie 树是一个有根的树,其结点具有以下字段: + +```` + 1、最多 RR 个指向子结点的链接,其中每个链接对应字母表数据集中的一个字母。本文中假定 RR 为 26,小写拉丁字母的数量。 + 2、布尔字段,以指定节点是对应键的结尾还是只是键前缀。 +```` + +### Trie 树中最常见的两个操作是键的插入和查找。 + + 向 Trie 树中插入键 + 我们通过搜索 Trie 树来插入一个键。我们从根开始搜索它对应于第一个键字符的链接。有两种情况: + + 链接存在。沿着链接移动到树的下一个子层。算法继续搜索下一个键字符。 + 链接不存在。创建一个新的节点,并将它与父节点的链接相连,该链接与当前的键字符相匹配。 + 重复以上步骤,直到到达键的最后一个字符,然后将当前节点标记为结束节点,算法完成。 + +### 复杂度分析 + + 时间复杂度:O(m)O(m),其中 mm 为键长。在算法的每次迭代中,我们要么检查要么创建一个节点,直到到达键尾。只需要 mm 次操作。 + 空间复杂度:O(m)O(m)。最坏的情况下,新插入的键和 Trie 树中已有的键没有公共前缀。此时需要添加 mm 个结点,使用 O(m)O(m) 空间。 + +#### 在 Trie 树中查找键 + 每个键在 trie 中表示为从根到内部节点或叶的路径。我们用第一个键字符从根开始,。检查当前节点中与键字符对应的链接。有两种情况: + + 存在链接。我们移动到该链接后面路径中的下一个节点,并继续搜索下一个键字符。 + 不存在链接。若已无键字符,且当前结点标记为 isEnd,则返回 true。否则有两种可能,均返回 false : + 还有键字符剩余,但无法跟随 Trie 树的键路径,找不到键。 + 没有键字符剩余,但当前结点没有标记为 isEnd。也就是说,待查找键只是Trie树中另一个键的前缀。 + +#### 查找 Trie 树中的键前缀 + 该方法与在 Trie 树中搜索键时使用的方法非常相似。我们从根遍历 Trie 树,直到键前缀中没有字符,或者无法用当前的键字符继续 Trie 中的路径。与上面提到的“搜索键”算法唯一的区别是,到达键前缀的末尾时,总是返回 true。我们不需要考虑当前 Trie 节点是否用 “isend” 标记,因为我们搜索的是键的前缀,而不是整个键。 + + +### DFS\BFS + + DFS即深度优先搜索,俗称不撞南墙不回头,实现方式是用栈来保存选择,然后每次取栈顶元素,并且依据栈顶元素遍历,将符合条件的元素压栈,然后如此往复。 + + BFS即广度优先搜索,特征是层次遍历,利用队列来实现。每次取队列队首元素,遍历所有节点,将所有符合条件的元素入队。循环往复。 + + 不管是DFS还是BFS,都需要一个状态标记来表示元素已经被选取,避免出现重复选择以及死循环。 + + 从上面的分析,我们可以知道DFS是很适合去做连通性搜索测试,并且如果不需要找到最短路径的话,可以直接退出,不需要存储大量路径信息(C语言也可以用递归来做);而BFS则是很适合去搜索==最短路径==,因为其会进行层次遍历,在任何一层发现了目标,那都是最短的路径上发现的。但是BFS会要求记录每一层的信息,会导致信息记录量大。(而且C语言没有队列,需要额外实现) + + 但是很明显,我们这道题就是需要用到BFS。也即根据beginWord来搜索所有与其相差仅为1个词语的单词,将其放入队列中,然后后循环搜索。不过要注意的是,存粹的BFS会超时,所以需要双端BFS,也就是从beginWord和endWord两端搜索。 + + +### 双端BFS + + +依据名字,我们要从两端都搜索,也就需要两个队列来保存各自的搜索信息。我们记为begin_word_queue和end_word_queue,并且记为A和B。双端搜索的规则如下: + +1、每次选取A和B中最少元素的来进行出队操作。如果size相等,取A中元素。 + +2、每次遍历到符合要求的元素,则进行入队操作,A搜索到的记为1, B搜索到的记为2。但是赋值方式为: + + + int selected_flag; //0 means A; 1 means B + int flag = 0; + flag |= selected_flag; + +3、当flag为3的时候代表A、B同时访问了一个节点,退出。 + +4、记录访问层次的次数,返回此次数。层次次数初始值为1 + + + +为什么这么做呢?因为BFS是层次遍历,也就是金字塔型遍历,越往后,搜索到的节点越多,信息越庞大,导致搜索时间越长。==但是结束点又只有一个,所以数据量大就会超时==。 + +### 算法分类 + +十种常见排序算法可以分为两大类: + + 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。 + 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。 + + + #### 概念 + + 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。 + 不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。 + 时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。 + 空间复杂度:是指算法在计算机 + 内执行时所需存储空间的度量,它也是数据规模n的函数。 + + +#### 1、冒泡排序(Bubble Sort) + + 冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。 + +##### 1.1 算法描述 + + 比较相邻的元素。如果第一个比第二个大,就交换它们两个; + 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数; + 针对所有的元素重复以上的步骤,除了最后一个; + 重复步骤1~3,直到排序完成。 + +``` +function bubbleSort(arr) { + var len = arr.length; + for (var i = 0; i < len - 1; 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、选择排序(Selection Sort) + 选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。 + +##### 算法描述 + + n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下: + + 初始状态:无序区为R[1..n],有序区为空; + 第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1..i]和R[i+1..n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区; + n-1趟结束,数组有序化了。 + + +``` +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; +} +``` +##### 分析 + +```` +表现最稳定的排序算法之一,因为无论什么数据进去都是O(n2)的时间复杂度,所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。理论上讲,选择排序可能也是平时排序一般人想到的最多的排序方法了吧。 +```` + + +#### 寻找旋转排序数组中的最小值 + +#### 思路: + + 旋转排序数组 numsnums 可以被拆分为 2 个排序数组 nums1nums1 , nums2nums2 ,并且 nums1任一元素 >= nums2任一元素;因此,考虑二分法寻找此两数组的分界点 nums[i]nums[i] (即第 2 个数组的首个元素)。 + 设置 leftleft, rightright 指针在 numsnums 数组两端,midmid 为每次二分的中点: + 当 nums[mid] > nums[right]时,midmid 一定在第 1 个排序数组中,ii 一定满足 mid < i <= right,因此执行 left = mid + 1; + 当 nums[mid] < nums[right] 时,midmid 一定在第 2 个排序数组中,ii 一定满足 left < i <= mid,因此执行 right = mid; + 当 nums[mid] == nums[right] 时,是此题对比 153题 的难点(原因是此题中数组的元素可重复,难以判断分界点 ii 指针区间); + 例如 [1, 0, 1, 1, 1][1,0,1,1,1] 和 [1, 1, 1, 0, 1][1,1,1,0,1] ,在 left = 0, right = 4, mid = 2 时,无法判断 midmid 在哪个排序数组中。 + 我们采用 right = right - 1 解决此问题,证明: + 此操作不会使数组越界:因为迭代条件保证了 right > left >= 0; + 此操作不会使最小值丢失:假设 nums[right]nums[right] 是最小值,有两种情况: + 若 nums[right]nums[right] 是唯一最小值:那就不可能满足判断条件 nums[mid] == nums[right],因为 mid < right(left != right 且 mid = (left + right) // 2 向下取整); + 若 nums[right]nums[right] 不是唯一最小值,由于 mid < right 而 nums[mid] == nums[right],即还有最小值存在于 [left, right - 1][left,right−1] 区间,因此不会丢失最小值。 + + 以上是理论分析,可以代入以下数组辅助思考: + [1, 2, 3][1,2,3] + [1, 1, 0, 1][1,1,0,1] + [1, 0, 1, 1, 1][1,0,1,1,1] + [1, 1, 1, 1][1,1,1,1] + 时间复杂度 O(logN)O(logN),在特例情况下会退化到 O(N)O(N)(例如 [1, 1, 1, 1][1,1,1,1])。 + + + 旋转排序数组nums可以被拆分为2个排序数组nums1, nums2,并且nums1中所有元素比nums2大(因为nums中没有重复值); + 因此,考虑二分法寻找值nums[i],满足nums[i] < nums[i-1] and nums[i] < nums[i+1] + 设置left, right指针在nums数组两端,mid为中点: + 当nums[mid] > nums[right]时,一定满足mid < i <= right,因此left = mid + 1; + 当nums[mid] < nums[right]时,一定满足left< i <= mid,因此right = mid; + 当nums[mid] == nums[right]时,说明数组长度len(num) == 1(因为计算mid向下取整);当left = right也满足,但本题left == right时跳出循环。 + + + + diff --git a/Week 08/id_573/leetcode_387_573.java b/Week 08/id_573/leetcode_387_573.java new file mode 100644 index 000000000..b1606552a --- /dev/null +++ b/Week 08/id_573/leetcode_387_573.java @@ -0,0 +1 @@ +class Solution { public int firstUniqChar(String s) { HashMap count = new HashMap(); int n = s.length(); // build hash map : character and how often it appears for (int i = 0; i < n; i++) { char c = s.charAt(i); count.put(c, count.getOrDefault(c, 0) + 1); } // find the index for (int i = 0; i < n; i++) { if (count.get(s.charAt(i)) == 1) return i; } return -1; } } \ No newline at end of file diff --git a/Week 08/id_573/leetcode_557_573.java b/Week 08/id_573/leetcode_557_573.java new file mode 100644 index 000000000..728604985 --- /dev/null +++ b/Week 08/id_573/leetcode_557_573.java @@ -0,0 +1 @@ +class Solution { public String reverseWords(String input) { final StringBuilder result = new StringBuilder(); final StringBuilder word = new StringBuilder(); for (int i = 0; i < input.length(); i++) { if (input.charAt(i) != ' ') { word.append(input.charAt(i)); } else { result.append(word.reverse()); result.append(" "); word.setLength(0); } } result.append(word.reverse()); return result.toString(); } } \ No newline at end of file diff --git a/Week 08/id_573/leetcode_917_573.go b/Week 08/id_573/leetcode_917_573.go new file mode 100644 index 000000000..481dd911e --- /dev/null +++ b/Week 08/id_573/leetcode_917_573.go @@ -0,0 +1,29 @@ +package main + +func reverseOnlyLetters(S string) string { + var str,out string + for i:=len(S)-1;i>=0;i--{ + if isLetter(S[i]) { + str += string(S[i]) + } + } + + for i,j:=0,0;i= 'a' && str <= 'z') || (str >= 'A' && str <= 'Z') { + out = true + } + return out +} diff --git a/Week 08/id_573/leetcode_917_573.java b/Week 08/id_573/leetcode_917_573.java new file mode 100644 index 000000000..742ba93fd --- /dev/null +++ b/Week 08/id_573/leetcode_917_573.java @@ -0,0 +1 @@ +class Solution { public String reverseOnlyLetters(String S) { StringBuilder ans = new StringBuilder(); int j = S.length() - 1; for (int i = 0; i < S.length(); ++i) { if (Character.isLetter(S.charAt(i))) { while (!Character.isLetter(S.charAt(j))) j--; ans.append(S.charAt(j--)); } else { ans.append(S.charAt(i)); } } return ans.toString(); } } \ No newline at end of file diff --git a/Week 08/id_578/LeetCode_115_578.java b/Week 08/id_578/LeetCode_115_578.java new file mode 100644 index 000000000..91510a1bd --- /dev/null +++ b/Week 08/id_578/LeetCode_115_578.java @@ -0,0 +1,21 @@ +package com.hand.week8; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/12/8 + */ +public class LeetCode_115_578 { + public int numDistinct(String s, String t) { + int[][] dp = new int[t.length() + 1][s.length() + 1]; + for (int j = 0; j < s.length() + 1; j++) dp[0][j] = 1; + for (int i = 1; i < t.length() + 1; i++) { + for (int j = 1; j < s.length() + 1; j++) { + if (t.charAt(i - 1) == s.charAt(j - 1)) dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1]; + else dp[i][j] = dp[i][j - 1]; + } + } + return dp[t.length()][s.length()]; + } +} diff --git a/Week 08/id_578/LeetCode_300_578.java b/Week 08/id_578/LeetCode_300_578.java new file mode 100644 index 000000000..6b7d7c25f --- /dev/null +++ b/Week 08/id_578/LeetCode_300_578.java @@ -0,0 +1,26 @@ +package com.hand.week8; + +import java.util.Arrays; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/12/8 + */ +public class LeetCode_300_578 { + public int lengthOfLIS(int[] nums) { + int length = nums.length; + if (length == 0) return 0; + int[] dp = new int[length]; + int result = 0; + Arrays.fill(dp, 1); + for (int i = 0; i < length; ++i) { + for (int j = 0; j < i; ++j) { + if (nums[j] < nums[i]) dp[i] = Math.max(dp[i], dp[j] + 1); + } + result = Math.max(result, dp[i]); + } + return result; + } +} diff --git a/Week 08/id_578/LeetCode_32_578.java b/Week 08/id_578/LeetCode_32_578.java new file mode 100644 index 000000000..8dd40d3b7 --- /dev/null +++ b/Week 08/id_578/LeetCode_32_578.java @@ -0,0 +1,25 @@ +package com.hand.week8; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/12/8 + */ +public class LeetCode_32_578 { + public int longestValidParentheses(String s) { + int result = 0; + int[] dp = new int[s.length()]; + for (int i = 1; i < s.length(); ++i) { + if (s.charAt(i) == ')') { + if (s.charAt(i - 1) == '(') { + dp[i] = (i - 2 >= 0 ? dp[i - 2] : 0) + 2; + } else if (i - dp[i - 1] - 1 >= 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + (i - dp[i - 1] - 2 >= 0 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + } + result = Math.max(result, dp[i]); + } + return result; + } +} diff --git a/Week 08/id_578/LeetCode_387_578.java b/Week 08/id_578/LeetCode_387_578.java new file mode 100644 index 000000000..961bb77ed --- /dev/null +++ b/Week 08/id_578/LeetCode_387_578.java @@ -0,0 +1,24 @@ +package com.hand.week8; + +import java.util.HashMap; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/12/8 + */ +public class LeetCode_387_578 { + public int firstUniqChar(String s) { + HashMap map = new HashMap<>(); + for (int i = 0; i < s.length(); ++i) { + map.put(s.charAt(i), map.getOrDefault(s.charAt(i), 0) + 1); + } + for (int i = 0; i < s.length(); ++i) { + if (map.get(s.charAt(i)) == 1) { + return i; + } + } + return -1; + } +} diff --git a/Week 08/id_578/LeetCode_438_578.java b/Week 08/id_578/LeetCode_438_578.java new file mode 100644 index 000000000..227c04576 --- /dev/null +++ b/Week 08/id_578/LeetCode_438_578.java @@ -0,0 +1,34 @@ +package com.hand.week8; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/12/8 + */ +public class LeetCode_438_578 { + public List findAnagrams(String s, String p) { + List result = new ArrayList<>(); + char[] sChar = s.toCharArray(); + char[] pChar = p.toCharArray(); + int[] curAToZ = new int[26]; + int[] aToZ = new int[26]; + for (char c : pChar) { + aToZ[c - 'a']++; + } + for (int i = 0; i < sChar.length; i++) { + if (i >= pChar.length) { + curAToZ[sChar[i - pChar.length] - 'a']--; + } + curAToZ[sChar[i] - 'a']++; + if (Arrays.equals(curAToZ, aToZ)) { + result.add(i - pChar.length + 1); + } + } + return result; + } +} diff --git a/Week 08/id_578/LeetCode_541_578.java b/Week 08/id_578/LeetCode_541_578.java new file mode 100644 index 000000000..68e141ecb --- /dev/null +++ b/Week 08/id_578/LeetCode_541_578.java @@ -0,0 +1,22 @@ +package com.hand.week8; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/12/8 + */ +public class LeetCode_541_578 { + public String reverseStr(String s, int k) { + char[] chars = s.toCharArray(); + for (int start = 0; start < chars.length; start += 2 * k) { + int i = start, j = Math.min(start + k - 1, chars.length - 1); + while (i < j) { + char tmp = chars[i]; + chars[i++] = chars[j]; + chars[j--] = tmp; + } + } + return new String(chars); + } +} diff --git a/Week 08/id_578/LeetCode_5_578.java b/Week 08/id_578/LeetCode_5_578.java new file mode 100644 index 000000000..de1f0c198 --- /dev/null +++ b/Week 08/id_578/LeetCode_5_578.java @@ -0,0 +1,24 @@ +package com.hand.week8; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/12/8 + */ +public class LeetCode_5_578 { + public String longestPalindrome(String s) { + int n = s.length(); + String result = ""; + boolean[][] dp = new boolean[n][n]; + for (int i = n - 1; i >= 0; --i) { + for (int j = i; j < n; ++j) { + dp[i][j] = s.charAt(i) == s.charAt(j) && (j - i < 2 || dp[i + 1][j - 1]); + if (dp[i][j] && j - i + 1 > result.length()) { + result = s.substring(i, j + 1); + } + } + } + return result; + } +} diff --git a/Week 08/id_578/LeetCode_85_578.java b/Week 08/id_578/LeetCode_85_578.java new file mode 100644 index 000000000..ab8f3ecc1 --- /dev/null +++ b/Week 08/id_578/LeetCode_85_578.java @@ -0,0 +1,28 @@ +package com.hand.week8; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/12/8 + */ +public class LeetCode_85_578 { + public int maximalRectangle(char[][] matrix) { + if (matrix == null || matrix.length == 0) return 0; + int maxArea = 0; + int[][] dp = new int[matrix.length][matrix[0].length]; + for (int i = 0; i < matrix.length; ++i) { + for (int j = 0; j < matrix[0].length; ++j) { + if (matrix[i][j] == '1') { + dp[i][j] = (j == 0) ? 1 : dp[i][j - 1] + 1; + int width = dp[i][j]; + for (int k = i; k >= 0; --k) { + width = Math.min(width, dp[k][j]); + maxArea = Math.max(maxArea, width * (i - k + 1)); + } + } + } + } + return maxArea; + } +} diff --git a/Week 08/id_578/LeetCode_91_578.java b/Week 08/id_578/LeetCode_91_578.java new file mode 100644 index 000000000..3b4ac00a2 --- /dev/null +++ b/Week 08/id_578/LeetCode_91_578.java @@ -0,0 +1,27 @@ +package com.hand.week8; + +/** + * @description: + * @version: 1.0 + * @author: xiantao.han@hand-china.com + * @Date: 2019/12/8 + */ +public class LeetCode_91_578 { + public int numDecodings(String s) { + char[] chars = s.toCharArray(); + if (chars[0] == '0') return 0; + int[] dp = new int[s.length()]; + dp[0] = 1; + for (int i = 1; i < dp.length; ++i) { + if (chars[i] == '0') { + if (chars[i - 1] == '1' || chars[i - 1] == '2') dp[i] = (i >= 2 ? dp[i - 2] : 1); + else return 0; + } else if (chars[i - 1] == '1' || (chars[i - 1] == '2' && chars[i] >= '1' && chars[i] <= '6')) { + dp[i] = dp[i - 1] + (i >= 2 ? dp[i - 2] : 1); + } else { + dp[i] = dp[i - 1]; + } + } + return dp[s.length() - 1]; + } +} diff --git a/Week 08/id_578/NOTE.md b/Week 08/id_578/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_578/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_583/NOTE.md b/Week 08/id_583/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_583/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_588/LeetCode_115_588.java b/Week 08/id_588/LeetCode_115_588.java new file mode 100644 index 000000000..aa9480cdf --- /dev/null +++ b/Week 08/id_588/LeetCode_115_588.java @@ -0,0 +1,23 @@ +/** + * 不同的子序列 + * https://leetcode-cn.com/problems/distinct-subsequences/solution/dong-tai-gui-hua-by-powcai-5/ + */ +public class LeetCode_115_588 { + + public int numDistinct(String s, String t) { + int[][] dp = new int[t.length() + 1][s.length() + 1]; + for (int j = 0; j < s.length() + 1; j++) { + dp[0][j] = 1; + } + for (int i = 1; i < t.length() + 1; i++) { + for (int j = 1; j < s.length() + 1; j++) { + if (t.charAt(i - 1) == s.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1]; + } else { + dp[i][j] = dp[i][j - 1]; + } + } + } + return dp[t.length()][s.length()]; + } +} diff --git a/Week 08/id_588/LeetCode_151_588.java b/Week 08/id_588/LeetCode_151_588.java new file mode 100644 index 000000000..e58596e11 --- /dev/null +++ b/Week 08/id_588/LeetCode_151_588.java @@ -0,0 +1,34 @@ +/** + * 反转字符串里的单词 + * https://leetcode-cn.com/problems/reverse-words-in-a-string/%E2%80%A8/ + */ +public class LeetCode_151_588 { + + // 先split + // 再从后往前添加各个单词(去除空格字符串或者空字符串) + public String reverseWords(String s) { + if (null == s) { + return null; + } + + String[] splitArr = s.trim().split(" "); + StringBuilder sb = new StringBuilder(); + for (int i = splitArr.length - 1; i >= 0; i--) { + String str = splitArr[i]; + // 去掉空格 + while (str.contains(" ")){ + str = str.replaceAll(" ",""); + } + if ("".equals(str)){ + continue; + } + sb.append(str); + if (0 != i){ + sb.append(" "); + } + } + return sb.toString(); + } + + +} diff --git a/Week 08/id_588/LeetCode_387_588.java b/Week 08/id_588/LeetCode_387_588.java new file mode 100644 index 000000000..50704f8d7 --- /dev/null +++ b/Week 08/id_588/LeetCode_387_588.java @@ -0,0 +1,26 @@ +import java.util.HashMap; +import java.util.Map; + +/** + * 字符串中的第一个唯一字符 + * https://leetcode-cn.com/problems/first-unique-character-in-a-string/ + */ +public class LeetCode_387_588 { + + public int firstUniqChar(String s) { + if (null == s || s.length() <=0 ) { + return -1; + } + Map map = new HashMap<>(); + for (int i = 0; i < s.length(); i ++) { + char ch = s.charAt(i); + map.put(ch, map.getOrDefault(ch, 0) + 1); + } + for (int i = 0; i < s.length(); i ++) { + if (1 == map.get(s.charAt(i))) { + return i; + } + } + return -1; + } +} diff --git a/Week 08/id_588/LeetCode_541_588.java b/Week 08/id_588/LeetCode_541_588.java new file mode 100644 index 000000000..68c8ee57d --- /dev/null +++ b/Week 08/id_588/LeetCode_541_588.java @@ -0,0 +1,19 @@ +/** + * 反转字符串II + * https://leetcode-cn.com/problems/reverse-string-ii/%E2%80%A8/ + */ +public class LeetCode_541_588 { + + public String reverseStr(String s, int k) { + char[] a = s.toCharArray(); + for (int start = 0; start < a.length; start += 2 * k) { + int i = start, j = Math.min(start + k - 1, a.length - 1); + while (i < j) { + char tmp = a[i]; + a[i++] = a[j]; + a[j--] = tmp; + } + } + return new String(a); + } +} diff --git a/Week 08/id_588/LeetCode_5_588.java b/Week 08/id_588/LeetCode_5_588.java new file mode 100644 index 000000000..ae37dfe46 --- /dev/null +++ b/Week 08/id_588/LeetCode_5_588.java @@ -0,0 +1,21 @@ +/** + * 最长回文子串 + * https://leetcode-cn.com/problems/longest-palindromic-substring/ + */ +public class LeetCode_5_588 { + + public String longestPalindrome(String s) { + int n = s.length(); + String res = ""; + boolean[][] dp = new boolean[n][n]; + for (int i = n - 1; i >= 0; i --) { + for (int j = i; j < n; j ++) { + dp[i][j] = s.charAt(i) == s.charAt(j) && (j - i < 2 || dp[i + 1][j - 1]); + if (dp[i][j] && j - i + 1 > res.length()) { + res = s.substring(i, j + 1); + } + } + } + return res; + } +} diff --git a/Week 08/id_588/NOTE.md b/Week 08/id_588/NOTE.md new file mode 100755 index 000000000..cf363a1a6 --- /dev/null +++ b/Week 08/id_588/NOTE.md @@ -0,0 +1,30 @@ +# 第八周学习总结 + +## 动态规划 + +- 动态规划 和 递归或者分治 没有根本上的区别(关键看有无最优的子结构) + +- 拥有共性:找到重复子问题 + +- 差异性:最优子结构、中途可以淘汰次优解 + +- 动态规划问题,解题步骤 + + - 寻找最优子结构 + + - 定义状态数组 + + - 写出状态转移方程 + +## 高阶DP问题 + +- 状态拥有更多维度(二维、三维、或者更多、甚至需要压缩) +- 状态方程更加复杂 + +## 字符串匹配算法 + +- 暴力法 + +- Rabin-Karp算法 + +- KMP算法 \ No newline at end of file diff --git a/Week 08/id_593/LeetCode_151_593.java b/Week 08/id_593/LeetCode_151_593.java new file mode 100644 index 000000000..000a30e46 --- /dev/null +++ b/Week 08/id_593/LeetCode_151_593.java @@ -0,0 +1,60 @@ +/** + * 151. 翻转字符串里的单词 + * 给定一个字符串,逐个翻转字符串中的每个单词。 + *

+ *   + *

+ * 示例 1: + *

+ * 输入: "the sky is blue" + * 输出: "blue is sky the" + * 示例 2: + *

+ * 输入: "  hello world!  " + * 输出: "world! hello" + * 解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 + * 示例 3: + *

+ * 输入: "a good   example" + * 输出: "example good a" + * 解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 + *   + *

+ * 说明: + *

+ * 无空格字符构成一个单词。 + * 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 + * 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 + *   + *

+ * 进阶: + *

+ * 请选用 C 语言的用户尝试使用 O(1) 额外空间复杂度的原地解法。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/reverse-words-in-a-string + * 链接:https://leetcode.com/problems/reverse-words-in-a-string + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + * + * @author jaryoung + */ +public class LeetCode_151_593 { + + public String reverseWords(String s) { + s = s.trim(); + String emptyStr = " "; + String[] split = s.split(emptyStr); + StringBuilder stringBuilder = new StringBuilder(); + for (int i = split.length - 1; i >= 0; i--) { + if ("".equals(split[i])) { + continue; + } + stringBuilder.append(split[i]); + if (i > 0 ) { + stringBuilder.append(emptyStr); + } + } + return stringBuilder.toString(); + } + +} diff --git a/Week 08/id_593/LeetCode_58_593.java b/Week 08/id_593/LeetCode_58_593.java new file mode 100644 index 000000000..63a32e05c --- /dev/null +++ b/Week 08/id_593/LeetCode_58_593.java @@ -0,0 +1,9 @@ +class LeetCode_58_593 { + public int lengthOfLastWord(String s) { + s = s.trim(); + if (s.length() == 0) { + return 0; + } + return s.length() - s.lastIndexOf(" ") - 1; + } +} \ No newline at end of file diff --git a/Week 08/id_593/LeetCode_5_593.java b/Week 08/id_593/LeetCode_5_593.java new file mode 100644 index 000000000..4afddc70c --- /dev/null +++ b/Week 08/id_593/LeetCode_5_593.java @@ -0,0 +1,53 @@ +/** + * 5. 最长回文子串 + *

+ * 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。 + *

+ * 示例 1: + *

+ * 输入: "babad" + * 输出: "bab" + * 注意: "aba" 也是一个有效答案。 + * 示例 2: + *

+ * 输入: "cbbd" + * 输出: "bb" + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/longest-palindromic-substring + * 链接:https://leetcode.com/problems/longest-palindromic-substring + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + *

+ * 思考: + *

a[l][r] / true 是回文子串 + *

\ false 不是回文字串 + *

a[l][r] = a[l+1][r-1] && a[l] == a[r] + * + * @author jaryoung + * @version 1.0 + */ +public class LeetCode_5_593 { + + public String longestPalindrome(String s) { + int length = s.length(); + if (length < 2) { + return s; + } + boolean[][] a = new boolean[length][length]; + String longestPalindromicSubstring = s.substring(0, 1); + int longestPalindromicSubstringLength = 1; + for (int r = 1; r < length; r++) { + for (int l = 0; l < r; l++) { + a[l][r] = s.charAt(l) == s.charAt(r) && (r - l <= 2 || a[l + 1][r - 1]); + if (a[l][r]) { + int checkLength = r - l + 1; + if (checkLength > longestPalindromicSubstringLength) { + longestPalindromicSubstringLength = checkLength; + longestPalindromicSubstring = s.substring(l, r + 1); + } + } + } + } + return longestPalindromicSubstring; + } +} diff --git a/Week 08/id_593/LeetCode_709_593.java b/Week 08/id_593/LeetCode_709_593.java new file mode 100644 index 000000000..7d8905a31 --- /dev/null +++ b/Week 08/id_593/LeetCode_709_593.java @@ -0,0 +1,12 @@ +class LeetCode_709_593 { + public String toLowerCase(String str) { + StringBuilder builder = new StringBuilder(); + for (char c : str.toCharArray()) { + if (c >= 'A' && c <= 'Z') { + c = (char) (c + 32); + } + builder.append(c); + } + return builder.toString(); + } +} \ No newline at end of file diff --git a/Week 08/id_593/LeetCode_711_593.java b/Week 08/id_593/LeetCode_711_593.java new file mode 100644 index 000000000..e76f08f38 --- /dev/null +++ b/Week 08/id_593/LeetCode_711_593.java @@ -0,0 +1,15 @@ +class LeetCode_711_593 { + public int numJewelsInStones(String J, String S) { + Set countor = new HashSet<>(J.length()); + for (char c : J.toCharArray()) { + countor.add(c); + } + int count = 0; + for (char c : S.toCharArray()) { + if (countor.contains(c)) { + count++; + } + } + return count; + } +} \ No newline at end of file diff --git a/Week 08/id_593/NOTE.md b/Week 08/id_593/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_593/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_598/LeetCode_198_598.java b/Week 08/id_598/LeetCode_198_598.java new file mode 100644 index 000000000..dff655b54 --- /dev/null +++ b/Week 08/id_598/LeetCode_198_598.java @@ -0,0 +1,26 @@ +/** + * @author northleaf + * @create 2019年12月03日 + */ +public class LeetCode_198_598 { + public int rob(int[] nums) { + if(nums==null || nums.length<1){ + return 0; + } + if(nums.length==1){ + return nums[0]; + } + if(nums.length == 2){ + return Math.max(nums[0],nums[1]); + } + + int[] dp = new int[nums.length]; + dp[0] = nums[0]; + dp[1] = Math.max(dp[0],nums[1]); + for(int i = 2;i=0;i--){ + for(int j = col-2;j>=0;j--){ + dp[i][j] = dp[i+1][j]+dp[i][j+1]; + } + } + return dp[0][0]; + } + + +} diff --git a/Week 08/id_598/LeetCode_63_598.java b/Week 08/id_598/LeetCode_63_598.java new file mode 100644 index 000000000..b401a20b5 --- /dev/null +++ b/Week 08/id_598/LeetCode_63_598.java @@ -0,0 +1,44 @@ +/** + * @author northleaf + * @create 2019年12月03日 + */ +public class LeetCode_63_598 { + /** + * obstacleGrid中的1代表障碍物,0代表空位 + * @param obstacleGrid + * @return + */ + public int uniquePathsWithObstacles(int[][] obstacleGrid) { + if(obstacleGrid==null || obstacleGrid.length < 1){ + return 0; + } + + //获取行与列 + int row = obstacleGrid.length; + int col = obstacleGrid[0].length; + + //定义一个二维数组 + //1代表有一个位置可走,0代表无路可走 + int[][] dp = new int[row][col]; + + //设定最后一个位置的值 + dp[row-1][col-1] = obstacleGrid[row-1][col-1] == 1 ? 0 : 1 ; + //设定最后一行的值 + for(int i = col-2;i>=0;i--){ + //最后一行的值,从未尾向前推,如果给定的数组中为1,则为0(无路),否则等于它的下一位的值 + dp[row-1][i]= obstacleGrid[row-1][i] == 1 ? 0 : dp[row-1][i+1]; + } + //设定最后一列的值 + for(int i = row - 2;i >= 0;i--){ + dp[i][col-1]=obstacleGrid[i][col-1] == 1 ? 0 : dp[i+1][col-1]; + } + + for(int i = row - 2;i>=0;i--){ + for(int j = col - 2;j>=0 ;j--){ + dp[i][j]=obstacleGrid[i][j] ==1 ? 0 : (dp[i+1][j]+dp[i][j+1]); + } + } + + return dp[0][0]; + } +} diff --git a/Week 08/id_598/LeetCode_64_598.java b/Week 08/id_598/LeetCode_64_598.java new file mode 100644 index 000000000..a93ea44fe --- /dev/null +++ b/Week 08/id_598/LeetCode_64_598.java @@ -0,0 +1,36 @@ +/** + * @author northleaf + * @create 2019年12月05日 + */ +public class LeetCode_64_598 { + public int minPathSum(int[][] grid) { + + + if(grid==null||grid.length<1){ + return 0; + } + + int row = grid.length; + int col = grid[0].length; + //dp中存放最小路径之和 + int[][] dp = new int[grid.length][grid[0].length]; + //设置第一个格(左上角)的值 + dp[0][0] = grid[0][0]; + //处理第一行的值 + for(int i=1;i false + +x.equals(y) --> true +x.equalsIgnoreCase(y) --> true + +## 字符串匹配算法 +- 1、暴力法 +- 2、Robin-Karp算法 +- 3、KMP算法 +- Robin-Karp和KMP算法都是在暴力法的基础上进行的优化或加速 + +### Rabin-Karp算法(在朴素算法的基础上增加了hash来做预判) +Rabin-Karp算法思想: +1、假设子串的长度为M(pat),目标字符串的长度为N(txt) +2、计算子串的hash值hash_pat +3、计算目标字符串txt中每个长度为M的子串的hash值(共需要计算N-N+1次) +4、比较hash值:如果hash值不同,字符串必然不匹配;如果hash值相同,还需要使用朴素算法再次判断 + +### KMP算法 +KMP算法思想:当子串与目标字符串不匹配时,其实你已经知道了前面已经匹配成功的那一部分字符,然后 +设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,继续把它向后移,这样就提高了效率。 +#### 算法视频 +https://www.bilibili.com/video/av11866460?from=search&seid=17425875345653862171 + + +课后了解: +Boyer-Moore算法: +http://www.ruanyifeng.com/blog/2013/05/boyer-moore_string_search_algorithm.html +Sunday算法: +https://blog.csdn.net/u012505432/article/details/52210975 + + +# 高级动态规划 +## DP顺推模板 +```python +function DP(): + dp = [][] # 二维情况 + + for i = 0 .. M { + for j = 0 .. N { + dp[i][j] = _Function(dp[i'][j']...) + } + } + + return dp[M][N] +``` + +## DP解题思维方式 +- 把问题抽象化 +- 定义成状态(一维或多维状态),写出DP转移方程 +- 套用模板,写出嵌套循环及DP方程 + +## 高阶DP复杂度来源 +- 状态拥有更多维度(二维、三维、或者更多、甚至需要压缩) +- 状态方程更加复杂 +本质:内功、逻辑思维、数学 + diff --git a/Week 08/id_613/java/src/main/Leetcode541.java b/Week 08/id_613/java/src/main/Leetcode541.java new file mode 100644 index 000000000..04ba95ebf --- /dev/null +++ b/Week 08/id_613/java/src/main/Leetcode541.java @@ -0,0 +1,45 @@ +/** + * 反转字符串II + * + * 执行用时 : * 2 ms * , 在所有 java 提交中击败了 * 55.35% * 的用户 + * 内存消耗 : * 37.3 MB * , 在所有 java 提交中击败了 * 97.74% * 的用户 + */ +public class Leetcode541 { + public String reverseStr(String s, int k) { + if (s == null) { + return null; + } + + StringBuilder result = new StringBuilder(); + for (int i = 0; i < s.length(); i += 2 * k) { + result.append(reverse(s.substring(i, Math.min(i + k, s.length())))); + result.append(s.substring(Math.min(i + k, s.length()), Math.min(i + 2 * k, s.length()))); + } + + return result.toString(); + } + + // 翻转字符串 + private String reverse(String s) { + if (s == null) { + return null; + } + + StringBuilder result = new StringBuilder(); + + for (int i = s.length() - 1; i >= 0; i--) { + result.append(s.charAt(i)); + } + + return result.toString(); + } + + public static void main(String[] args) { + String s = "hello, world!"; + Leetcode541 so = new Leetcode541(); + System.out.println(so.reverse(s)); + + s = "a"; + System.out.println(so.reverse(s)); + } +} diff --git a/Week 08/id_613/java/src/main/Leetcode746.java b/Week 08/id_613/java/src/main/Leetcode746.java new file mode 100644 index 000000000..bfc985e24 --- /dev/null +++ b/Week 08/id_613/java/src/main/Leetcode746.java @@ -0,0 +1,42 @@ +/** + * 使用最小花费爬楼梯 + * + * 执行用时 : * 1 ms * , 在所有 java 提交中击败了 * 100.00% * 的用户 + * 内存消耗 : * 39.1 MB * , 在所有 java 提交中击败了 * 66.97% * 的用户 + */ +public class Leetcode746 { + // dp[i]为从第i级台阶出发的最小花费 + // DP方程:dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2]) + public int minCostClimbingStairs(int[] cost) { + int[] re = new int[cost.length + 1]; + + for (int i = 2; i < re.length; i++) { + re[i] = Math.min(re[i - 1] + cost[i - 1], re[i - 2] + cost[i - 2]); + } + + return re[re.length - 1]; + } + + public int climbingStairs(int n) { + if (n < 2) { + return n; + } + + int[] re = new int[n]; + re[0] = 1; + re[1] = 2; + for (int i = 2; i < n; i++) { + re[i] = re[i - 1] + re[i - 2]; + } + + return re[re.length - 1]; + } + + public static void main(String[] args) { + Leetcode746 so = new Leetcode746(); + System.out.println(so.climbingStairs(3)); + + int[] cost = new int[]{10, 15, 20}; + System.out.println(so.minCostClimbingStairs(cost)); + } +} diff --git a/Week 08/id_613/java/src/test/Leetcode541Test.java b/Week 08/id_613/java/src/test/Leetcode541Test.java new file mode 100644 index 000000000..12934118c --- /dev/null +++ b/Week 08/id_613/java/src/test/Leetcode541Test.java @@ -0,0 +1,24 @@ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +/** + * + */ +public class Leetcode541Test { + @Test + public void testSolution1() { + String s = "abcdefg"; + Leetcode541 so = new Leetcode541(); + + assertEquals(so.reverseStr(s, 2), "bacdfeg"); + } + + @Test + public void testSolution2() { + String s = "a"; + Leetcode541 so = new Leetcode541(); + + assertEquals(so.reverseStr(s, 2), "a"); + } +} + diff --git a/Week 08/id_613/java/src/test/Leetcode746Test.java b/Week 08/id_613/java/src/test/Leetcode746Test.java new file mode 100644 index 000000000..858b04ddd --- /dev/null +++ b/Week 08/id_613/java/src/test/Leetcode746Test.java @@ -0,0 +1,22 @@ +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * + */ +public class Leetcode746Test { + @Test + public void testSolution1() { + Leetcode746 so = new Leetcode746(); + int[] cost = new int[]{10, 15, 20}; + assertEquals(15, so.minCostClimbingStairs(cost)); + } + + @Test + public void testSolution2() { + Leetcode746 so = new Leetcode746(); + int[] cost = new int[]{1, 100, 1, 1, 1, 100, 1, 1, 100, 1}; + assertEquals(6, so.minCostClimbingStairs(cost)); + } +} diff --git a/Week 08/id_618/LeetCode_300_618.java b/Week 08/id_618/LeetCode_300_618.java new file mode 100644 index 000000000..fb7fc10bb --- /dev/null +++ b/Week 08/id_618/LeetCode_300_618.java @@ -0,0 +1,28 @@ +class Solution { + public int lengthOfLIS(int[] nums) { + if (nums.length == 0) { + return 0; + } + + int[] dp = new int[nums.length]; + int max = 0; + + for (int i = 0; i < dp.length; i++) { + int target = nums[i]; + int maxForTarget = 0; + + for (int j = 0; j < i; j++) { + int current = nums[j]; + + if (target > current) { + maxForTarget = Math.max(maxForTarget, dp[j]); + } + } + + dp[i] = maxForTarget + 1; + max = Math.max(max, dp[i]); + } + + return max; + } +} \ No newline at end of file diff --git a/Week 08/id_618/LeetCode_44_618..java b/Week 08/id_618/LeetCode_44_618..java new file mode 100644 index 000000000..b0656c482 --- /dev/null +++ b/Week 08/id_618/LeetCode_44_618..java @@ -0,0 +1,30 @@ +class Solution { + public boolean isMatch(String s, String p) { + int m = s.length(); + int n = p.length(); + + boolean[][] dp = new boolean[m + 1][n + 1]; + + dp[0][0] = true; + for (int i = 1; i <= n; i++) { + dp[0][i] = dp[0][i - 1] && p.charAt(i - 1) == '*'; + } + + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + char cs = s.charAt(i - 1); + char cp = p.charAt(j - 1); + + if (cs == cp || cp == '?') { + dp[i][j] = dp[i - 1][j - 1]; + } + + if (cp == '*') { + dp[i][j] = dp[i][j - 1] || dp[i - 1][j]; + } + } + } + + return dp[m][n]; + } +} \ No newline at end of file diff --git a/Week 08/id_618/NOTE.md b/Week 08/id_618/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_618/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_623/NOTE.md b/Week 08/id_623/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_623/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_628/LeetCode_205_628.java b/Week 08/id_628/LeetCode_205_628.java new file mode 100644 index 000000000..7976518b6 --- /dev/null +++ b/Week 08/id_628/LeetCode_205_628.java @@ -0,0 +1,41 @@ +//ַ s tжǷͬġ +// +// s еַԱ滻õ t ôַͬġ +// +// гֵַһַ滻ͬʱַ˳ַӳ䵽ͬһַϣַӳԼ +// +// ʾ 1: +// +// : s = "egg", t = "add" +//: true +// +// +// ʾ 2: +// +// : s = "foo", t = "bar" +//: false +// +// ʾ 3: +// +// : s = "paper", t = "title" +//: true +// +// ˵: +//Լ s t ͬijȡ +// Related Topics ϣ + + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public boolean isIsomorphic(String s, String t) { + char[] arr1 = s.toCharArray(); + char[] arr2 = t.toCharArray(); + for (int i = 0; i count = new HashMap(); + int n = s.length(); + for (int i = 0; i < n; i++) { + char c = s.charAt(i); + count.put(c, count.getOrDefault(c, 0) + 1); + } + + for (int i = 0; i < n; i++) { + if (count.get(s.charAt(i)) == 1) + return i; + } + return -1; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week 08/id_628/NOTE.md b/Week 08/id_628/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_628/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_633/NOTE.md b/Week 08/id_633/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_633/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_638/LeetCode_541_638.java b/Week 08/id_638/LeetCode_541_638.java new file mode 100644 index 000000000..aded99471 --- /dev/null +++ b/Week 08/id_638/LeetCode_541_638.java @@ -0,0 +1,27 @@ +package test1.Week8; + +public class LeetCode_541_637 { + + /** + * 翻转字符串2 + * @param s + * @param k + * @return + */ + public String reverseStr(String s, int k) { + int len = s.length(); + if (s == null || len < 2)return s; + + char[] arr = s.toCharArray(); + for (int start = 0; start < arr.length; start += 2 * k) { + int i = start, j = Math.min(start + k - 1, arr.length - 1); + while (i < j) { + char tmp = arr[i]; + arr[i++] = arr[j]; + arr[j--] = tmp; + } + } + return new String(arr); + + } +} diff --git a/Week 08/id_638/LeetCode_746_638.java b/Week 08/id_638/LeetCode_746_638.java new file mode 100644 index 000000000..2a6552c95 --- /dev/null +++ b/Week 08/id_638/LeetCode_746_638.java @@ -0,0 +1,28 @@ +package test1.Week8; + +import java.util.Arrays; + +public class LeetCode_746_638 { + + /** + * 使用最小花费爬楼梯 + * @param cost + * @return + */ + public int minCostClimbingStairs(int[] cost) { + + if (cost == null || cost.length == 0) return 0; + + int[] dp = cost; + for (int i = 2;i < dp.length;i++){ + dp[i] = Math.min(dp[i-1] + cost[i],dp[i - 2] + cost[i]); + } + return Math.min(dp[dp.length - 1],dp[dp.length - 2]); + } + + public static void main(String[] args) { + LeetCode_746_638 leetCode_746_638 = new LeetCode_746_638(); + int[] cost = {10,15,20}; + System.out.println(leetCode_746_638.minCostClimbingStairs(cost));; + } +} diff --git a/Week 08/id_638/NOTE.md b/Week 08/id_638/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_638/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_643/LeetCode_387_643.java b/Week 08/id_643/LeetCode_387_643.java new file mode 100644 index 000000000..7cb0b4e7d --- /dev/null +++ b/Week 08/id_643/LeetCode_387_643.java @@ -0,0 +1,19 @@ +class Solution { + public int firstUniqChar(String s) { + HashMap count = new HashMap(); + int n = s.length(); + // build hash map : character and how often it appears + for (int i = 0; i < n; i++) { + char c = s.charAt(i); + count.put(c, count.getOrDefault(c, 0) + 1); + } + + // find the index + for (int i = 0; i < n; i++) { + if (count.get(s.charAt(i)) == 1) + return i; + } + return -1; + } +} + diff --git a/Week 08/id_643/LeetCode_8_643.java b/Week 08/id_643/LeetCode_8_643.java new file mode 100644 index 000000000..f3c942ade --- /dev/null +++ b/Week 08/id_643/LeetCode_8_643.java @@ -0,0 +1,33 @@ +class Solution { + public int myAtoi(String str) { + if(str.isEmpty()) return 0; + char[] mychar=str.toCharArray(); + long ans=0; + int i=0,sign=1,n=str.length(); + while(i='0'&&mychar[i]<='9')) { + if(ans!=(int)ans) { + return (sign==1)?Integer.MAX_VALUE:Integer.MIN_VALUE; + } + ans=ans*10+mychar[i++]-'0'; + } + + if(ans!=(int)ans) { + return (sign==1)?Integer.MAX_VALUE:Integer.MIN_VALUE; + } + + return (int)(ans*sign); + + } +} + diff --git a/Week 08/id_643/NOTE.md b/Week 08/id_643/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_643/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_648/LeetCode_151_648.java b/Week 08/id_648/LeetCode_151_648.java new file mode 100644 index 000000000..6b2a6fc57 --- /dev/null +++ b/Week 08/id_648/LeetCode_151_648.java @@ -0,0 +1,24 @@ +import java.util.Arrays; +import java.util.Collections; + +/** + * @Date 2019/12/8 + **/ +public class LeetCode_151_648 { + public String reverseWords(String s) { + String[] arr = s.trim().split(" "); + Collections.reverse(Arrays.asList(arr)); + StringBuffer sb = new StringBuffer(); + for(String str:arr){ + sb.append(str +" "); + } + return sb.toString().trim(); + } + + public static void main(String[] args) { + LeetCode_151_648 leetCode_151_648 = new LeetCode_151_648(); + String str = " hello world! "; + String reverseStr = leetCode_151_648.reverseWords(str); + System.out.println(reverseStr); + } +} diff --git a/Week 08/id_648/LeetCode_300_648.java b/Week 08/id_648/LeetCode_300_648.java new file mode 100644 index 000000000..b13f21bc8 --- /dev/null +++ b/Week 08/id_648/LeetCode_300_648.java @@ -0,0 +1,43 @@ +/** + * @Date 2019/12/8 + **/ +public class LeetCode_300_648 { + public int lengthOfLIS(int[] nums) { + + return maxLengthOfLIS(nums,Integer.MIN_VALUE,0); + } + public int maxLengthOfLIS(int[] nums,int pre,int cur){ + if(cur == nums.length){ + return 0; + } + int taken=0; + if(nums[cur]>pre){ + taken = 1+maxLengthOfLIS(nums,nums[cur],cur+1); + } + int notaken = maxLengthOfLIS(nums,pre,cur+1); + return Math.max(taken,notaken); + } + public int lengthOfLIS2(int[] nums) { + if(nums.length==0){ + return 0; + } + int[] dp = new int[nums.length]; + for(int i=1;inums[j]){ + maxnum = Math.max(maxnum,dp[j]); + } + } + dp[i] = maxnum+1; + } + + return 0; + } + public static void main(String[] args) { + LeetCode_300_648 leetCode_300_648 = new LeetCode_300_648(); + int[] arr = new int[]{10,9,2,5,3,7,101,18}; + int num = leetCode_300_648.lengthOfLIS(arr); + System.out.println(num); + } +} diff --git a/Week 08/id_648/LeetCode_387_648.java b/Week 08/id_648/LeetCode_387_648.java new file mode 100644 index 000000000..9656dde66 --- /dev/null +++ b/Week 08/id_648/LeetCode_387_648.java @@ -0,0 +1,57 @@ +import java.util.HashMap; + +/** + * @Date 2019/12/8 + **/ +public class LeetCode_387_648 { + + /** + * 暴力法 + * @param s + * @return + */ + public int firstUniqChar(String s) { + int num =0; + for(int i=0;i map = new HashMap<>(); + int num =0; + for(int i=0;i> triangle) { + int row = triangle.size(); + int[] dp = new int[row+1]; + + for (int level = row-1; level >=0 ; level--) { + for (int i = 0; i <= level; i++) { + dp[i] = triangle.get(level).get(i) + Math.min(dp[i],dp[i+1]); + } + } + + return dp[0]; + } + + public int dfs(int level,int position,List> triangle) { + if(memo[level][position]!=null) { + return memo[level][position]; + } + + if(level==row-1) { + return memo[level][position]=triangle.get(level).get(position); + } + + int left = dfs(level+1,position,triangle); + int right = dfs(level+1,position+1,triangle); + + return memo[level][position] = Math.min(left,right) + triangle.get(level).get(position); + } +} \ No newline at end of file diff --git a/Week 08/id_653/LeetCode_300_653.java b/Week 08/id_653/LeetCode_300_653.java new file mode 100644 index 000000000..728a34e87 --- /dev/null +++ b/Week 08/id_653/LeetCode_300_653.java @@ -0,0 +1,27 @@ +class Solution { + public int lengthOfLIS(int[] nums) { + + int[] top = new int[nums.length]; + int piles = 0; + for (int i = 0; i < nums.length; i++) { + int val = nums[i]; + + int left = 0,right = piles; + // 通过二分法将 val 放入堆中 + + while (left>1; + if (top[mid] charCountMap = new HashMap(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + charCountMap.put(c, charCountMap.getOrDefault(c, 0) + 1); + } + + for (int i = 0; i < s.length(); i++) { + if (charCountMap.get(s.charAt(i)) == 1) + return i; + } + return -1; + } +} +// @lc code=end diff --git a/Week 08/id_658/LeetCode_557_658.java b/Week 08/id_658/LeetCode_557_658.java new file mode 100644 index 000000000..e3f7c7bd8 --- /dev/null +++ b/Week 08/id_658/LeetCode_557_658.java @@ -0,0 +1,17 @@ +/* + * @lc app=leetcode.cn id=557 lang=java + * + * [557] 反转字符串中的单词 III + */ + +// @lc code=start +class Solution { + public String reverseWords(String s) { + String words[] = s.split(" "); + StringBuilder res = new StringBuilder(); + for (String word : words) + res.append(new StringBuffer(word).reverse().toString() + " "); + return res.toString().trim(); + } +} +// @lc code=end diff --git a/Week 08/id_658/LeetCode_917_658.java b/Week 08/id_658/LeetCode_917_658.java new file mode 100644 index 000000000..1aa071d62 --- /dev/null +++ b/Week 08/id_658/LeetCode_917_658.java @@ -0,0 +1,26 @@ +/* + * @lc app=leetcode.cn id=917 lang=java + * + * [917] 仅仅反转字母 + */ + +// @lc code=start +class Solution { + public String reverseOnlyLetters(String S) { + Stack letters = new Stack(); + for (char c : S.toCharArray()) + if (Character.isLetter(c)) + letters.push(c); + + StringBuilder ans = new StringBuilder(); + for (char c : S.toCharArray()) { + if (Character.isLetter(c)) + ans.append(letters.pop()); + else + ans.append(c); + } + + return ans.toString(); + } +} +// @lc code=end diff --git a/Week 08/id_658/LeetCode_91_658.java b/Week 08/id_658/LeetCode_91_658.java new file mode 100644 index 000000000..782e7bced --- /dev/null +++ b/Week 08/id_658/LeetCode_91_658.java @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode.cn id=91 lang=java + * + * [91] 解码方法 + */ + +// @lc code=start +class Solution { + public int numDecodings(String s) { + if (s.charAt(0) == '0') + return 0; + + int[] dp = new int[s.length() + 1]; + dp[0] = dp[1] = 1; + + for (int i = 2; i <= s.length(); i++) { + if (s.charAt(i - 1) != '0') { + dp[i] += dp[i - 1]; + } + if ((s.charAt(i - 2) == '1') || (s.charAt(i - 2) == '2' && s.charAt(i - 1) <= '6')) { + dp[i] += dp[i - 2]; + } + } + return dp[s.length()]; + } +} +// @lc code=end diff --git a/Week 08/id_658/NOTE.md b/Week 08/id_658/NOTE.md new file mode 100755 index 000000000..544faca1c --- /dev/null +++ b/Week 08/id_658/NOTE.md @@ -0,0 +1,179 @@ +# 第八周学习总结 + +## 高级动态规划 + +### 动态规划 + +- 本质上还是寻找重复性(计算机指令集) +- 分治的过程中能够在中间每一步淘汰次优解就变成了动态规划 +- 将一个复杂问题分解成各个简单的子问题(分治思想) +- 有一个最优子结构 +- 很多时候状态本身可以进行顺推(从下到上往上推)`动态递推` + +#### DP 顺推模板 + +```python +function DP(): + dp = [][] # 0. dp 状态的定义 一维情况、二维情况、三维情况 + + # 1.本身是一个嵌套循环 + for i = 0 .. M { + for j = 0 .. N { + dp[i][j] = _Function(dp[i'][j']...) # 2.从之前的 dp 的状态,推到最新的 dp[i][j] 的状态 + } + } + return dp[M][N] # 3. dp[M][N] 反映最后的最终结果 +``` + +- `0. dp 状态的定义` 需要把现实的问题定义成一个数组,里面保存状态 +- `2. 状态转移方程` 类似 Fibonacci 数列 `dp[i] = dp[i - 1] + dp[i - 2]`,更多情况下是求最小值、累加累减或者是有一层小的循环从之前的 k 个状态中找出它的最值 + +### 动态规划的状态转移方程 + +#### 爬楼梯问题 + +- 本质上可以转换成 Fibonacci 问题 +- 和硬币置换问题异曲同工(一次上一步上两步相当于每次用1面值2面值的硬币) +- 递归公式 `f(n) = f(n - 1) + f(n - 2), f(0) = 0, f(1) = 1` + +#### 不同路径 + +- 递归公式 `f(x, y) = f(x - 1, y) + f(x, y - 1)` + +#### 打家劫舍 + +- 不能偷连续的两个房子 +- dp[i] 的状态表示打劫从 A[0] 到 A[i] 的房子最多可以获得多少钱 `max $ of robbing A[0 -> i]` + - i 有可能被打劫,也可能没被打劫 + - `dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])` +- dp[i][0] 的状态表示打劫从 A[0] 到 A[i] 的房子最多可以获得多少钱,并且没打劫 num[i] +- dp[i][1] 的状态表示打劫从 A[0] 到 A[i] 的房子最多可以获得多少钱,并且打劫了 num[i] + - `dp[i][0] = max(dp[i - 1][0], dp[i - 1][1])` + - `dp[i][1] = dp[i - 1][0] + nums[i]` + +#### 最小路径和 + +- dp[i][j] 的状态表示:`minPath(A[i -> i][1 -> j])` +- `dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + A[i][j]` + +#### 股票买卖 + +- [一个方法团灭6道股票问题](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/yi-ge-fang-fa-tuan-mie-6-dao-gu-piao-wen-ti-by-l-3/) +- dp[i][k][0 or 1] (i >= 0 && i <= n - 1, k >= 1 && k <= K) + - i 为天数 + - k 为最多交易次数 + - [0, 1] 为是否持有股票 +- 总状态数:`n * K * 2` 种 + +```python +for 0 <= i < n : + for 1 <= k <= K : + for s in {0, 1} : + dp[i][k][s] = max(buy, sell, rest) +``` + +- 状态转移方程 + - `dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i])` + - max(选择 rest, 选择 sell) + - 今天我没持有股票,有两种可能: + 1. 我昨天就没有持有,然后今天选择 rest,所以我今天还是没有持有股票 + 2. 我昨天持有股票,但是今天我 sell 了,所以我今天没有持有股票了 + - `dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i])` + - max(选择 rest, 选择 buy) + - 今天我持有着股票,有两种可能: + 1. 我昨天就持有着股票,然后今天选择 rest, 所以我今天还持有着股票 + 2. 我昨天本没有持有股票,但今天我选择 buy,所以今天我就持有股票了 + +### 动态规划进阶 + +#### 复杂度来源 + +- 状态拥有更多维度(二维、三维或者更多,甚至需要压缩) +- 状态方程更加复杂 +- 本质上是内功、逻辑思维能力、数学能力 + +## 字符串 + +- Java 中字符串 String 是不可变的 +- Java 中的 `x == y` 比较的是指向的内存地址,用 `x.equals(y)` 方法比较 x 和 y 的值是否相同,可以用 `x.equalsIgnoreCase(y)` 忽略大小写比较 + +### 字符串匹配算法 + +#### 暴力法 (brute force) + +- 挨个比较所有的字符才知道目标字符串中是否包含子串 + +```java +public static int forceSearch(String txt, String pat) { + int M = txt.length(); + int N = pat.length(); + for (int i = 0; i <= M - N; i++) { + int j; + for (j = 0; j < N; j++) { + if (txt.charAt(i + j) != pat.charAt(j)) { + break; + } + } + if (j == N) { + return i; + } + // 更加聪明? + // 1. 预先判断子串是否相同 hash(txt.subString(i, M)) == hash(pat) + // 2. KMP 已经匹配的片段的最大前缀和最大后缀的长度 + } + return -1; +} +``` + +#### Rabin-Karp 算法 + +- 为了避免挨个字符对目标字符串和子串进行比较,我们可以尝试一次性判断两者是否相等 +- 需要通过一个哈希函数算出子串的哈希值,然后将它和目标字符串中的子串的哈希值进行比较 +- 思想: + 1. 假设子串 pat 的长度为 M,目标字符串 txt 的长度为 N + 2. 计算子串的 hash 值 hash_pat + 3. 计算目标字符串 txt 中每个长度为 M 的子串的 hash 值,共需要计算 `N - M + 1` 次 + 4. 比较 hash 值:如果 hash 值不同,字符串必然不匹配;如果 hash 值相同,还需要使用朴素算法再次判断 + +```java +public final static int D = 256; // 每一位权重就是256的相应的次方 +public final static int Q = 9997; // 防止 hash 值过大,每次 % 一个素数 +static int RabinKarpSearch(String txt, String pat) { + int M = pat.length(); + int N = txt.length(); + int i, j; + int patHash = 0, txtHash = 0; + + for (i = 0; i < M; i++) { + patHash = (D * patHash + pat.charAt(i)) % Q; + txtHash = (D * txtHash + txt.charAt(i)) % Q; + } + int highestPow = 1; // pow(256, M - 1) + for (i = 0; i < M - 1; i++) + highestPow - (highestPow * D) % Q; + + for (i = 0; i <= N - M; i++) { //枚举起点 + if (patHash == txtHash) { + for (j = 0; j < M; j++) { + if (txt.charAt(i + j) != pat.charAt(j)) + break; + } + if (j == M) + return i; + } + if (i < N - M) { + txtHash = (D * (txtHash - txt.charAt(i) * highestPow) + txt.charAt(i + M)) % Q; + if (txtHash < 0) + txtHash += Q; + } + } + return -1; +} +``` + +#### KMP 算法 + +- Knuth-Morris-Pratt 的思想就是,当子串与目标字符串不匹配时,其实你已经知道了前面已经匹配成功那一部分的字符(包括子串与目标字符串) +- 利用这个已知信息,不要把`搜索位置`移回已经比较过的位置,而是继续把它向后移,这样就提高了效率 +- [KMP 字符串匹配算法](https://www.bilibili.com/video/av11866460) +- [字符串匹配的 KMP 算法](http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html) diff --git a/Week 08/id_663/NOTE.md b/Week 08/id_663/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_663/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_668/NOTE.md b/Week 08/id_668/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_668/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_668/leetcode_151_668.py b/Week 08/id_668/leetcode_151_668.py new file mode 100644 index 000000000..9a00f3aa2 --- /dev/null +++ b/Week 08/id_668/leetcode_151_668.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: 151_reverse_words_in_a_string.py + @time: 2019/12/5 16:14 +""" + + +class Solution(object): + """ + 给定一个字符串,逐个翻转字符串中的每个单词。 + + 示例 1: + + 输入: "the sky is blue" + 输出: "blue is sky the" + 示例 2: + + 输入: "  hello world!  " + 输出: "world! hello" + 解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 + 示例 3: + + 输入: "a good   example" + 输出: "example good a" + 解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 + + """ + + def reverse_words(self, s): + """ + :type s: str + :rtype: str + """ + ''' + 函数说明: + split(...) + S.split([sep [,maxsplit]]) -> list of strings + + Return a list of the words in the string S, using sep as the + delimiter string. If maxsplit is given, at most maxsplit + splits are done. If sep is not specified or is None, any + whitespace string is a separator and empty strings are removed + from the result. + + >>> s = " the sky is blue " + >>> s + ' the sky is blue ' + >>> + >>> s.split() + ['the', 'sky', 'is', 'blue'] + + 可知,如果split函数没有传递sep,则字符串中所有的空格与空字符都会被清除 + ''' + # slist = [e for e in s.strip().split(' ') if e] + # return ' '.join(slist[::-1]) + + return ' '.join(s.split()[::-1]) # s.split()获取以空格为分隔符形成的真值列表,然后对此取反 + + def reverse_words2(self, s): + """ + :type s: str + :rtype: str + + 分析: + 1. 先对s整体反转 + 2. 再对每个单词进行反转 + """ + return ' '.join([e[::-1] for e in s[::-1].split()]) diff --git a/Week 08/id_668/leetcode_300_668.py b/Week 08/id_668/leetcode_300_668.py new file mode 100644 index 000000000..9f85a612c --- /dev/null +++ b/Week 08/id_668/leetcode_300_668.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: 300_longest_increasing_subsequence.py + @time: 2019/12/4 16:39 +""" + + +class Solution(object): + """ + 给定一个无序的整数数组,找到其中最长上升子序列的长度。 + + 示例: + 输入: [10,9,2,5,3,7,101,18] + 输出: 4 + 解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。 + + 说明: + 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。 + 你算法的时间复杂度应该为 O(n2) 。 + 进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗? + + """ + + def length_of_lis(self, nums): + """ + :type nums: List[int] + :rtype: int + + DP 自底向上思考 + 数组nums的i位置,能够形成从{0...i-1}的各个位置到i位置的多个子序列。 + dp[i]表示i位置的最长上升子序列的长度,0 <= j < i表示i位置之前的各个位置的最长上升子序列的长度,记作dp[j]。 + 因此dp[i] = max(dp[i], dp[j] + 1 for j in range(i) if nums[j] < nums[i])。 + + """ + if len(nums) == 0: + return 0 + + dp = [1] * len(nums) # 所有元素置1,是因为每个元素至少可以单独成为子序列,此时长度都为1 + + for i in range(len(nums)): + for j in range(i): # 依次查看从0位置至i位置与i位置形成的最长子序列的情况 + if nums[j] < nums[i]: + dp[i] = max(dp[i], dp[j] + 1) + + return max(dp) # nums数组每个位置都求出该位置的最长上升子序列,然后取最大值即可 + + def length_of_lis2(self, nums): + """ + :type nums: List[int] + :rtype: int + + 子序列的共同特点是:它们的结尾是所有相同长度的"上升子序列"里面最小的。 + """ + + if len(nums) < 2: + return len(nums) + + # tail 数组的定义:长度为 i + 1 的上升子序列的末尾最小是几, + # tail数组一直是一个升序的数组且结尾元素是所有相同长度的上升子序列最小的 + # 遍历第 1 个数,直接放在有序数组 tail 的开头 + tail = [nums[0]] + + for i in range(1, len(nums)): + ''' + 对于当前数字nums[i],判断其值是否在tail数组升序的范围内;如果在其中,则替换其最小小于的元素,比如数组 + tail为[2,3,7,18],这个时候nums[i]值为4,元素4在tail中,且4最小小于的元素为7,因此这里将7替换成4 + 因此最终tail数组变为[2,3,4,18]。下面的二分法主要用于操作tail数组 + ''' + + left, right = 0, len(tail) # 因为有可能num比tail数组中的最后一个元素还要大,所以右边界应该设置为tail数组的长度 + + while left < right: + # 非标准二分查找 + # 选左中位数不是偶然,而是有原因的,原因请见 LeetCode 第 35 题题解 + mid = (left + right) >> 1 + + if tail[mid] < nums[i]: + # 中位数肯定不是要找的数,把它写在分支的前面 + left = mid + 1 + else: + right = mid + + if left == len(tail): + tail.append(nums[i]) + else: + # 因为【逻辑 1】,因此一定能找到第 1 个大于等于 nums[i] 的元素,因此无需再单独判断,直接更新即可 + # left位置的值是nums[i]最小小于的元素 + tail[left] = nums[i] + + return len(tail) diff --git a/Week 08/id_668/leetcode_387_668.py b/Week 08/id_668/leetcode_387_668.py new file mode 100644 index 000000000..6f119e752 --- /dev/null +++ b/Week 08/id_668/leetcode_387_668.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: 387_first_unique_character_in_a_string.py + @time: 2019/12/5 11:04 +""" + + +class Solution(object): + """ + 给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。 + + 案例: + s = "leetcode" + 返回 0. + + s = "loveleetcode", + 返回 2. + + 注意事项:您可以假定该字符串只包含小写字母。 + """ + + def first_uniq_char(self, s): + """ + :type s: str + :rtype: int + """ + counter = [0] * 26 + + for e in s: + counter[ord(e) - ord('a')] += 1 + + for i in range(len(s)): + if counter[ord(s[i]) - ord('a')] == 1: + return i + + return -1 diff --git a/Week 08/id_668/leetcode_72_668.py b/Week 08/id_668/leetcode_72_668.py new file mode 100644 index 000000000..be20d378d --- /dev/null +++ b/Week 08/id_668/leetcode_72_668.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: 72_edit_distance.py + @time: 2019/12/04 22:21 +""" + + +class Solution(object): + """ + 给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。 + 你可以对一个单词进行如下三种操作: + 插入一个字符 + 删除一个字符 + 替换一个字符 + + 示例 1: + 输入: word1 = "horse", word2 = "ros" + 输出: 3 + 解释: + horse -> rorse (将 'h' 替换为 'r') + rorse -> rose (删除 'r') + rose -> ros (删除 'e') + + 示例 2: + 输入: word1 = "intention", word2 = "execution" + 输出: 5 + 解释: + intention -> inention (删除 't') + inention -> enention (将 'i' 替换为 'e') + enention -> exention (将 'n' 替换为 'x') + exention -> exection (将 'n' 替换为 'c') + exection -> execution (插入 'u') + """ + + def min_distance(self, word1, word2): + """ + :type word1: str + :type word2: str + :rtype: int + + DP 自底向上思考 + 解决两个单词之间的问题,主要的思路是: + 1. 一个单词当作行,另一个单词当作列,这样就构建了一个二维数组,然后在这个二维数组框架中进行思考 + 2. 通常从两个单词的尾部元素开始向前进行思考、递推,这也是DP自底向上的思考方式 + + 题意说明word1可经过插入、删除、替换字符的方式转换成word2,然后求最少的转换次数。 + DP状态转义:dp[i][j]表示word1中从头到i位置形成的字符串sub_word1与word2中从头到j位置形成的字符串 + sub_word2之间的编辑距离。分别从两个单词的尾字符开始向前比较,如果两个字符(假设ch1、ch2)相同, + 则两个单词的编辑距离dp[i][j]=dp[i-1][j-1],因为两个字符已经相同,不需要任何方式的转换。 + 如果两个字符不同,则对于word1来说有三种处理方式,1.将ch1替换成ch2,此时编辑距离dp[i][j] = dp[i-1][j-1] + 1, + 1表示替换的一次操作;2.将ch1删除,此时编辑距离dp[i][j] = dp[i-1][j] + 1;3.在ch1尾部插入ch2,此时编辑距离 + dp[i][j] = dp[i][j-1] + 1;这三种方式均可使得word1更接近word2一步,因为求最小转换路径,因此 + dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1。 + 因此DP状态方程为:dp[i][j] = dp[i-1][j-1] if ch1 = ch2 else min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1 + + 谨记: + 1. 编辑距离比较的是两个从头到当前位置的子字符串的编辑距离,不是当前字符的编辑距离 + 2. dp[i][j]表示word1中从头到i位置形成的字符串sub_word1与word2中从头到j位置形成的字符串sub_word2之间的编辑距离 + 3. word1、word2当前比较的位置分别为i、j;dp[i-1][j-1]表示i位置字符替换成j位置字符的操作;dp[i-1][j]表示i位置字符 + 删除操作;dp[i][j-1]表示i位置后面插入j位置元素的操作 + """ + m, n = len(word1), len(word2) # 选定word1表示行数,word2表示列数 + dp = [[0] * (n + 1) for _ in range(m + 1)] # 因为要考虑到其中一个单词为空的情况,因此这里创建多一行多一列的dp二维数组 + + # 第一行 空字符串word1变换成word2 + for j in range(1, n + 1): + dp[0][j] = dp[0][j - 1] + 1 + + # 第一列 word1变换成空字符串word2 + for i in range(1, m + 1): + dp[i][0] = dp[i - 1][0] + 1 + + for i in range(1, m + 1): + for j in range(1, n + 1): + if word1[i - 1] == word2[j - 1]: # 因为二维举证是从1位置开始,而单词还是从0位置开始,因此单词索引的操作要分别-1 + dp[i][j] = dp[i - 1][j - 1] + else: + dp[i][j] = min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1 + + return dp[-1][-1] diff --git a/Week 08/id_668/leetcode_746_668.py b/Week 08/id_668/leetcode_746_668.py new file mode 100644 index 000000000..9daa82c17 --- /dev/null +++ b/Week 08/id_668/leetcode_746_668.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: 746_min_cost_climbing_stairs.py + @time: 2019/12/4 10:23 +""" + + +class Solution(object): + """ + 数组的每个索引做为一个阶梯,第 i个阶梯对应着一个非负数的体力花费值 cost[i](索引从0开始)。 + 每当你爬上一个阶梯你都要花费对应的体力花费值,然后你可以选择继续爬一个阶梯或者爬两个阶梯。 + 您需要找到达到楼层顶部的最低花费。在开始时,你可以选择从索引为 0 或 1 的元素作为初始阶梯。 + + 示例 1: + 输入: cost = [10, 15, 20] + 输出: 15 + 解释: 最低花费是从cost[1]开始,然后走两步即可到阶梯顶,一共花费15。 + + 示例 2: + 输入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] + 输出: 6 + 解释: 最低花费方式是从cost[0]开始,逐个经过那些1,跳过cost[3],一共花费6。 + + 注意: + cost 的长度将会在 [2, 1000]。 + 每一个 cost[i] 将会是一个Integer类型,范围为 [0, 999]。 + """ + + def min_cost_climbing_stairs(self, cost): + """ + :type cost: List[int] + :rtype: int + + DP 自底向上的思考方式 从数组尾部向前思考 + 以台阶为基本点思考:假设有n阶台阶,因为每次可以走一个台阶也可以走两个台阶,所以n阶走一步到达楼顶,n-1阶走两步到达楼顶。 + 第n阶min_cost为自身值,第n-1阶min_cost也为自身值,第n-2阶min_cost等于其自身值+min(min_cost(n-1), min_cost(n)) + 因此dp状态方程为:min_cost[i] = min(min_cost(i+1), min_cost(i+2)) + cost[i] + dp方程为:dp[i] = min(dp[i+1], dp[i+2]) + dp[i] + """ + if len(cost) == 0 or len(cost) == 1: # cost=1相等于就一个台阶,因为可以跨两步直接跨过,所以能量值消耗为0 + return 0 + + dp = cost + + for i in range(len(dp) - 3, -1, -1): + dp[i] += min(dp[i + 1], dp[i + 2]) + + return min(dp[0], dp[1]) # 这里比较dp[0], dp[1]是因为开始选择有两种,可以走一步,也可以走两步 + + def min_cost_climbing_stairs2(self, cost): + """ + :type cost: List[int] + :rtype: int + + DP 自底向上的思考方式 从数组尾部向前思考 + 以台阶能量消耗为基本思考点:因为可以走一步或者两步, + 因此第i阶台阶的能量消耗min_cost[i] = cost[i] + min(min_cost[i-1], min_cost[i-2]) + dp方程为:dp[i] = dp[i] + min(dp[i-1], dp[i-2]) + """ + if len(cost) == 0 or len(cost) == 1: + return 0 + + dp = cost + + for i in range(2, len(dp)): + dp[i] += min(dp[i - 1], dp[i - 2]) + + return min(dp[-2], dp[-1]) # 因为倒数第一个台阶、倒数第二个台阶都可以到达楼顶,因此最终比较这两个能量消耗值 + + def min_cost_climbing_stairs3(self, cost): + """ + :type cost: List[int] + :rtype: int + + 同样是dp思想,只是简化了代码的实现 + """ + a = b = 0 + + for e in cost: + print a, b + a, b = b, min(a + e, b + e) + + return min(a, b) diff --git a/Week 08/id_668/leetcode_8_668.py b/Week 08/id_668/leetcode_8_668.py new file mode 100644 index 000000000..56b43ac0e --- /dev/null +++ b/Week 08/id_668/leetcode_8_668.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +""" + @file: 8_string_to_integer.py + @time: 2019/12/5 13:58 +""" + + +class Solution(object): + """ + 请你来实现一个 atoi 函数,使其能将字符串转换成整数。 + 首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。 + 当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号; + 假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。 + 该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。 + + 注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。 + 在任何情况下,若函数不能进行有效的转换时,请返回 0。 + + 说明: + 假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231,  231 − 1]。如果数值超过这个范围, + 请返回  INT_MAX (231 − 1) 或 INT_MIN (−231) 。 + + 示例 1: + 输入: "42" + 输出: 42 + + 示例 2: + 输入: " -42" + 输出: -42 + 解释: 第一个非空白字符为 '-', 它是一个负号。 +   我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。 + + 示例 3: + 输入: "4193 with words" + 输出: 4193 + 解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。 + + 示例 4: + 输入: "words and 987" + 输出: 0 + 解释: 第一个非空字符是 'w', 但它不是数字或正、负号。 + 因此无法执行有效的转换。 + + 示例 5: + 输入: "-91283472332" + 输出: -2147483648 + 解释: 数字 "-91283472332" 超过 32 位有符号整数范围。 +   因此返回 INT_MIN (−231) 。 + + """ + + def my_atoi(self, s): + """ + :type s: str + :rtype: int + + python编程小记: + 1. ''.strip == ''(空字符串的strip结果依然是空字符串) + 2. 字符串类型的数字转成数字,如果不允许用int函数,则可通过ord函数转换,比如ord('8') - ord('0') + 3. isdigit函数用于判断字符串是否为数字,比如'8'.isdigit()==True,'a7'.isdigit()==False + 4. python中的int类型最大值可通过sys.maxint获取到,范围是[-9223372036854775808, 9223372036854775807] + 5. 超出int类型的数字,python自动转成long型 + """ + sstrip = s.strip() # 去除首尾空格 + + if len(sstrip) == 0: + return 0 + + sign = -1 if sstrip[0] == '-' else 1 + + if sstrip[0] in ('-', '+'): + chrlist = list(sstrip[1:]) + else: + chrlist = list(sstrip) + + ret, i = 0, 0 + + while i < len(chrlist) and chrlist[i].isdigit(): + ''' + 以chrlist = ['7', '8', '9']为例,说明如下的转换逻辑 + ['7', '8', '9']转换成数字应该为789;遍历到字符7时,通过ord('7')-ord('0')将'7' -> 7,先当做个位处理, + 因此ret开始为0,ret = 0 * 10 + 7 = 7;接着遍历到'8',此时ret = 7 * 10 + ord('8') - ord('0') = 78; + 自然原来的7就变成十位,接着遍历字符9,此时res = 78 * 10 + ord('9') - ord('0') = 789 + ''' + ret = ret * 10 + ord(chrlist[i]) - ord('0') # 通过ord函数将字符串类型的数字转换为数字,比如ord('5') - ord('0') = 5 + i += 1 + + return max(-2 ** 31, min(sign * ret, 2 ** 31 - 1)) # 通过max、min函数计算满足范围的整数 diff --git a/Week 08/id_673/NOTE.md b/Week 08/id_673/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_673/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_673/maximal-rectangle.cpp b/Week 08/id_673/maximal-rectangle.cpp new file mode 100644 index 000000000..531aa913a --- /dev/null +++ b/Week 08/id_673/maximal-rectangle.cpp @@ -0,0 +1,77 @@ +class Solution { + +public: + + void update( vector< vector< vector>>& record, int begin_i, int begin_j){ + + int line_min = record[begin_i][begin_j][0]; + + int row = record[begin_i][begin_j][1]; + + + + for( int count = 0; count < row; count++){ + + line_min = min( line_min, record[ begin_i-count][begin_j][0]); + + record[begin_i][begin_j][2] = max( record[begin_i][begin_j][2], line_min * (count + 1)); + + } + + } + + + + int maximalRectangle(vector>& matrix) { + + if( !matrix.size()) + + return 0; + + vector< vector< vector>> record( matrix.size(), vector>( matrix[0].size(), {0,0,0})); + + int res = 0; + + //dp 过程 + + for( int i = 0; i < matrix.size(); i++) + + for( int j = 0; j < matrix[0].size(); j++){ + + if( matrix[i][j] == '0') + + ; + + else + + if( i == 0 && j == 0) + + record[i][j] = { 1, 1, 1}; + + else if( i == 0) + + record[i][j] = {record[i][j-1][0] + 1, 1, record[i][j-1][2] + 1}; + + else if( j == 0) + + record[i][j] = {1, record[i-1][j][1] + 1, record[i-1][j][2] + 1}; + + else{ + + record[i][j][0] = record[i][j-1][0] + 1, record[i][j][1] = record[i-1][j][1] + 1; + + update( record, i, j); + + } + + res = max( res, record[i][j][2]); + + } + + + + return res; + + } + +}; diff --git a/Week 08/id_673/reverse-words-in-a-string.cpp b/Week 08/id_673/reverse-words-in-a-string.cpp new file mode 100644 index 000000000..7f8e32b67 --- /dev/null +++ b/Week 08/id_673/reverse-words-in-a-string.cpp @@ -0,0 +1,43 @@ +class Solution { + +public: + + string reverseWords(string s) { + + stack st; + + int pos = 0; + + + + while (true) { + + pos = s.find_first_not_of(' ', pos); // 指向单词首字母 + + if (pos == string::npos) + + break; + + st.push(s.substr(pos, s.find(' ', pos) - pos)); // 储存单词 + + pos = s.find(' ', pos); // 找到单词后的位置 + + } + + string out; + + while (!st.empty()) { + + out += st.top() + " "; + + st.pop(); + + } + + out.pop_back(); //删除多余空格 + + return out; + + } + +}; diff --git a/Week 08/id_678/NOTE.md b/Week 08/id_678/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_678/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_683/LeetCode_1143_683.java b/Week 08/id_683/LeetCode_1143_683.java new file mode 100644 index 000000000..cbacb4431 --- /dev/null +++ b/Week 08/id_683/LeetCode_1143_683.java @@ -0,0 +1,20 @@ +class Solution { + public int longestCommonSubsequence(String text1, String text2) { + if (text1 == null || text2 == null) return 0; + int n = text1.length(); + int m = text2.length(); + + int[][] dp = new int[n + 1][m + 1]; + + for (int i = 1; i <= n; ++i) { + for (int j = 1; j <= m; ++j) { + if (text1.charAt(i - 1) == text2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]); + } + } + } + return dp[n][m]; + } +} diff --git a/Week 08/id_683/LeetCode_72_683.java b/Week 08/id_683/LeetCode_72_683.java new file mode 100644 index 000000000..5c5668399 --- /dev/null +++ b/Week 08/id_683/LeetCode_72_683.java @@ -0,0 +1,30 @@ +class Solution { + public int minDistance(String word1, String word2) { + int n = word1.length(); + int m = word2.length(); + + if (n * m == 0) { + return n + m; + } + + int[][] dp = new int[n + 1][m + 1]; + + for (int i = 0; i <= n; ++i) { + dp[i][0] = i; + } + for (int i = 0; i <= m; ++i) { + dp[0][i] = i; + } + + for (int i = 1; i <= n; ++i) { + for (int j = 1; j <= m; ++j) { + if (word1.charAt(i - 1) == word2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = Math.min(dp[i - 1][j - 1] + 1, Math.min(dp[i][j - 1] + 1, dp[i - 1][j] + 1)); + } + } + } + return dp[n][m]; + } +} diff --git a/Week 08/id_683/NOTE.md b/Week 08/id_683/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_683/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_688/NOTE.md b/Week 08/id_688/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_688/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_693/LeetCode_10_693.java b/Week 08/id_693/LeetCode_10_693.java new file mode 100644 index 000000000..42ffafc85 --- /dev/null +++ b/Week 08/id_693/LeetCode_10_693.java @@ -0,0 +1,72 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 10. 正则表达式匹配 https://leetcode-cn.com/problems/regular-expression-matching/ + * @Date 2019/12/06 + */ +public class LeetCode_10_693 { + /** + * 规律字符p是否包含字符s + * 回溯:思路: + * 。 1、判断是否相等 且 源字符不能为null + * 。 2、存在 * ,判断长度是否大于等于2 且索引 1 等于 * + * 。 枚举如下两种情况 + * 。 a、符合条件1,且枚举源字符 进 1 的结果,都是true 为true + * 。 b、直接考虑 * 匹配零个前字符,所以直接移动规律字符索引 进 2 (c* 如果匹配0 就是"") + * 。 3、第二位 不存在 * ,则直接进入 下一步,且都移动源字符和规律字符索引 1 + */ + class Solution { + public boolean isMatch(String s,String p) { + if (p.isEmpty()) { + return s.isEmpty(); + } + boolean firstMath = (!s.isEmpty() && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.')); + if (p.length() >= 2 && p.charAt(1) == '*') { + return firstMath && isMatch(s.substring(1),p)//匹配一个或多个 + || isMatch(s,p.substring(2));//匹配0个 + } + return firstMath && isMatch(s.substring(1),p.substring(1)); + } + } + + /** + * 动态规划 top-down + */ + class Solution2 { + public boolean isMatch(String s,String p) { + int sLen = s.length(); + int pLen = p.length(); + boolean[][] dp = new boolean[sLen + 1][pLen + 1]; + dp[0][0] = true; + for (int i = 0; i <= sLen; i++) { + for (int j = 1; j <= pLen; j++) { + if (p.charAt(j - 1) == '*') { + boolean firstMath = i > 0 && + (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.'); + dp[i][j] = (firstMath && dp[i - 1][j]) || dp[i][j - 2]; + } else { + boolean firstMath = i > 0 && + (s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '.'); + dp[i][j] = firstMath && dp[i - 1][j - 1]; + } + } + } + return dp[sLen][pLen]; + } + } + + public static void main(String[] args) { + System.out.println(new LeetCode_10_693().new Solution2().isMatch("aa","a*")); + System.out.println(new LeetCode_10_693().new Solution2().isMatch("abc","abc")); + System.out.println(new LeetCode_10_693().new Solution2().isMatch("abc","a.c")); + System.out.println(new LeetCode_10_693().new Solution2().isMatch("abc","ab*c")); + System.out.println(new LeetCode_10_693().new Solution2().isMatch("abcccde","abc*de")); + System.out.println(new LeetCode_10_693().new Solution2().isMatch("aaaa","a*a")); + System.out.println(new LeetCode_10_693().new Solution2().isMatch("aaaac","a*a")); + System.out.println(new LeetCode_10_693().new Solution2().isMatch("asadfasfaaa",".*")); + System.out.println(new LeetCode_10_693().new Solution2().isMatch("ab",".*c")); + System.out.println(new LeetCode_10_693().new Solution2().isMatch("abcdsaas","e*abcdsaas")); + System.out.println(new LeetCode_10_693().new Solution2().isMatch("abcdsaas","esabcdsaas")); + } +} diff --git a/Week 08/id_693/LeetCode_115_693.java b/Week 08/id_693/LeetCode_115_693.java new file mode 100644 index 000000000..efe982837 --- /dev/null +++ b/Week 08/id_693/LeetCode_115_693.java @@ -0,0 +1,31 @@ +package id_693; + +import java.util.Arrays; + +/** + * @Author 李雷(KyLin) + * @Desc 115. 不同的子序列 https://leetcode-cn.com/problems/distinct-subsequences/ + * @Date 2019/12/04 + */ +public class LeetCode_115_693 { + /** + * 动态规划 dp方程:不同:f(i,j) = f(i,j-1) 相同f(i,j) = f(i,j-1) + f(i -1.j-1) + */ + class Solution { + public int numDistinct(String s,String t) { + int sLen = s.length(); + int tLen = t.length(); + int[][] dp = new int[tLen + 1][sLen + 1]; + Arrays.fill(dp[0],1);//考虑"" + for (int i = 1; i <= tLen; i++) { + for (int j = 1; j <= sLen; j++) { + dp[i][j] = dp[i][j - 1]; + if (s.charAt(j - 1) == t.charAt(i - 1)) { + dp[i][j] += dp[i - 1][j - 1]; + } + } + } + return dp[tLen][sLen]; + } + } +} diff --git a/Week 08/id_693/LeetCode_14_693.java b/Week 08/id_693/LeetCode_14_693.java new file mode 100644 index 000000000..0fc151f89 --- /dev/null +++ b/Week 08/id_693/LeetCode_14_693.java @@ -0,0 +1,79 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 14. 最长公共前缀 https://leetcode-cn.com/problems/longest-common-prefix/ + * @Date 2019/12/04 + */ +public class LeetCode_14_693 { + + /** + * 随机选取一个字符串,然后一直截取前部分直到indexOf 为0,循环所有字符串,最后的截取出的字符就是结果 + */ + class Solution { + public String longestCommonPrefix(String[] strs) { + int len = strs.length; + if (len == 0) return ""; + String sub = strs[0]; + for (String str : strs) { + while (str.indexOf(sub) != 0) { + sub = sub.substring(0,sub.length() - 1); + if (sub.length() == 0) { + return ""; + } + } + } + return sub; + } + } + + /** + * 字符树解法,可以继续优化,因为后面发现total不等于0 那么就没必要继续新建了。因为代表这里已经不同了,剪枝 + */ + class Solution2 { + public String longestCommonPrefix(String[] strs) { + int len = strs.length; + String res = ""; + if (len == 0) return res; + Trie trie = new Trie(); + trie.build(strs); + Node node = trie.root; + for (char ch : strs[0].toCharArray()) { + node = node.children[ch - 'a']; + if (node.total != strs.length) return res; + res += ch; + } + return res; + + } + + class Trie { + Node root = new Node(); + + public void build(String[] strs) { + for (String str : strs) { + Node temp = root; + temp.total++; + for (char ch : str.toCharArray()) { + if (temp.children[ch - 'a'] == null) { + temp.children[ch - 'a'] = new Node(); + } + temp = temp.children[ch - 'a']; + temp.total++; + } + + } + } + } + + class Node { + int total = 0; + Node[] children; + + public Node() { + children = new Node[26]; + } + } + } + +} diff --git a/Week 08/id_693/LeetCode_151_693.java b/Week 08/id_693/LeetCode_151_693.java new file mode 100644 index 000000000..4aa93e8a3 --- /dev/null +++ b/Week 08/id_693/LeetCode_151_693.java @@ -0,0 +1,62 @@ +package id_693; + +import java.util.Arrays; +import java.util.Collections; + +/** + * @Author 李雷(KyLin) + * @Desc 151. 翻转字符串里的单词 https://leetcode-cn.com/problems/reverse-words-in-a-string/ + * @Date 2019/12/05 + */ +public class LeetCode_151_693 { + /** + * 普通的单词转换 + */ + class Solution { + public String reverseWords(String s) { + String[] strs = s.trim().split(" +"); +// String[] strs = s.trim().split(" +"); 使用" +" 会很影响性能,极大的影响,加了8秒 不加2秒,只是需要处理下""的情况 + int i = 0; + int j = strs.length - 1; + while (i < j) { + String temp = strs[i]; + strs[i++] = strs[j]; + strs[j--] = temp; + } + StringBuilder str = new StringBuilder(); + for (String ss : strs) { + if (ss.length() == 0 )continue; + str.append(ss); + str.append(" "); + } + return str.toString().trim(); + } + } + + /** + * 直接内置函数加身,复杂度同上,因为用的" +" 所以性能leetcode是8秒,其他处理了的只有2秒 + */ + class Solution2 { + public String reverseWords(String s) { + String[] strs = s.trim().split(" +"); + Collections.reverse(Arrays.asList(strs)); + return String.join(" ",strs); + } + } + + class Solution3 { + public String reverseWords(String s) { + String[] strs = s.trim().split(" +"); + StringBuilder stringBuilder = new StringBuilder(s.length()); + for (int i = strs.length - 1;i >= 0;i--) { + if (strs[i].length() != 0) { + stringBuilder.append(strs[i]).append(" "); + } + } + if (stringBuilder.length() > 0) { + stringBuilder.deleteCharAt(stringBuilder.length() - 1); + } + return stringBuilder.toString(); + } + } +} diff --git a/Week 08/id_693/LeetCode_300_693.java b/Week 08/id_693/LeetCode_300_693.java new file mode 100644 index 000000000..da7ebd268 --- /dev/null +++ b/Week 08/id_693/LeetCode_300_693.java @@ -0,0 +1,73 @@ +package id_693; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +/** + * @Author 李雷(KyLin) + * @Desc 300. 最长上升子序列 https://leetcode-cn.com/problems/longest-increasing-subsequence/ + * @Date 2019/12/04 + */ +public class LeetCode_300_693 { + /** + * dp方程 + */ + class Solution { + public int lengthOfLIS(int[] nums) { + int len = nums.length; + if (len == 0) return 0; + int[] dp = new int[len]; + Arrays.fill(dp,1); + int max = 0; + for (int i = 0; i < len; i++) { + for (int j = 0; j < i; j++) { + if (nums[i] > nums[j]) { + dp[i] = Math.max(dp[i],dp[j] + 1); + } + } + max = Math.max(dp[i],max); + } + return max; + } + } + + /** + * 动态规划 + 二分 + */ + class Solution2 { + public int lengthOfLIS(int[] nums) { + int len = nums.length; + if (len == 0) return 0; + int[] dp = new int[len]; + int res = 0; + for (int num : nums) { + int i = 0; + int j = res; + while (i < j) { + int m = i + (j - i) / 2; + if (dp[m] < num) { + i = m + 1; + } else { + j = m; + } + } + dp[i] = num; + if (j == res) { + res++; + } + } + return res; + } + } + + @Test + public void test() { + System.out.println(new Solution().lengthOfLIS(new int[]{10,9,2,5,3,7,101,18})); + } + + @Test + public void test2() { + System.out.println(new Solution2().lengthOfLIS(new int[]{10,9,2,5,3,7,101,18})); + } +} diff --git a/Week 08/id_693/LeetCode_344_693.java b/Week 08/id_693/LeetCode_344_693.java new file mode 100644 index 000000000..e98c2c821 --- /dev/null +++ b/Week 08/id_693/LeetCode_344_693.java @@ -0,0 +1,23 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 344. 反转字符串 https://leetcode-cn.com/problems/reverse-string/ + * @Date 2019/12/04 + */ +public class LeetCode_344_693 { + /** + * 没啥说的。直接替换就好了。 + */ + class Solution { + public void reverseString(char[] s) { + int i = 0; + int j = s.length - 1; + while (i < j) { + s[i] ^= s[j]; + s[j] ^= s[i]; + s[i++] ^= s[j--]; + } + } + } +} diff --git a/Week 08/id_693/LeetCode_387_693.java b/Week 08/id_693/LeetCode_387_693.java new file mode 100644 index 000000000..24770c890 --- /dev/null +++ b/Week 08/id_693/LeetCode_387_693.java @@ -0,0 +1,117 @@ +package id_693; + +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +/** + * @Author 李雷(KyLin) + * @Desc 387. 字符串中的第一个唯一字符 https://leetcode-cn.com/problems/first-unique-character-in-a-string/ + * @Date 2019/12/04 + */ +public class LeetCode_387_693 { + /** + * hash表去重保存 时间复杂度O(2N) 空间复杂度O(N) + */ + class Solution { + public int firstUniqChar(String s) { + int len = s.length(); + Map map = new HashMap<>(len,1); + for (int i = 0; i < len; i++) { + map.put(s.charAt(i),map.getOrDefault(s.charAt(i),0) + 1); + } + for (int i = 0; i < len; i++) { + if (map.get(s.charAt(i)) == 1) return i; + } + return -1; + } + } + + /** + * 使用String的内置函数 substring和indexOf 时间复杂度 O(N^2) 空间O(1) + */ + class Solution2 { + public int firstUniqChar(String s) { + int len = s.length(); + for (int i = 0; i < len; i++) { + String temp = s.substring(0,i); + String temp1 = s.substring(i + 1); + if (temp.indexOf(s.charAt(i)) == -1 && temp1.indexOf(s.charAt(i)) == -1) { + return i; + } + } + return -1; + } + } + + /** + * 针对第二种解法 取消内置函数,时间复杂度 O(N * (N - 1)) + */ + class Solution3 { + public int firstUniqChar(String s) { + int len = s.length(); + for (int i = 0; i < len; i++) { + boolean f = true; + for (int j = 0; j < i; j++) { + if (!f || s.charAt(i) == s.charAt(j)) { + f = false; + break; + } + } + for (int j = i + 1; j < s.length(); j++) { + if (!f || s.charAt(i) == s.charAt(j)) { + f = false; + break; + } + } + if (f) return i; + } + return -1; + } + + } + + /** + * 直接使用String的内置函数,因为indexOf 是从前开始查找,而lastIndexOf 从后面找,只需要看两个是否一样即可,一样则只有一次 + */ + class Solution4 { + public int firstUniqChar(String s) { + for (int i = 0; i < s.length(); i++) { + int dex = s.indexOf(s.charAt(i)); + if (s.lastIndexOf(s.charAt(i)) == dex) { + return i; + } + } + return -1; + } + } + + /** + * 利用只有小写字母,直接写死,这样时间复杂度就是 indexOf 最坏是O(N) 不用加上 lastIndexOf ,因为出现需要这个的时候,说明前面已经找到了, 所以最后的时间复杂度是O(N) + */ + class Solution5 { + public int firstUniqChar(String s) { + int res = Integer.MAX_VALUE; + for (char i = 'a'; i <= 'z'; i++) { + int index = s.indexOf(i); + if (index != -1 && s.lastIndexOf(i) == index) { + res = Math.min(index,res); + } + } + return res == Integer.MAX_VALUE ? -1 :res; + } + } + + @Test + public void test() { + System.out.println(new Solution().firstUniqChar("leetcode")); + } + + @Test + public void test2() { + System.out.println(new Solution5().firstUniqChar("cc")); + System.out.println(new Solution5().firstUniqChar("leetcode")); + System.out.println(new Solution5().firstUniqChar("loveleetcode")); + } +} diff --git a/Week 08/id_693/LeetCode_438_693.java b/Week 08/id_693/LeetCode_438_693.java new file mode 100644 index 000000000..bbc8daed1 --- /dev/null +++ b/Week 08/id_693/LeetCode_438_693.java @@ -0,0 +1,81 @@ +package id_693; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @Author 李雷(KyLin) + * @Desc 438. 找到字符串中所有字母异位词 https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/ + * @Date 2019/12/05 + */ +public class LeetCode_438_693 { + /** + * 暴力破解,直接移动窗体思维(AC) 效率低下 + */ + class Solution { + public List findAnagrams(String s,String p) { + List result = new ArrayList<>(); + char[] sChars = s.toCharArray(); + char[] pChars = p.toCharArray(); + Arrays.sort(pChars); + String pp = String.valueOf(pChars); + for (int i = 0; i <= (sChars.length - pChars.length); i++) { + System.arraycopy(sChars,i,pChars,0,pChars.length); + Arrays.sort(pChars); + if (pp.equals(String.valueOf(pChars))) { + result.add(i); + } + } + return result; + } + } + + /** + * 淫技-值得思考 + */ + class Solution2 { + public List findAnagrams(String s, String p) { + //转化为char array + char[] s_arr = s.toCharArray(); + char[] p_arr = p.toCharArray(); + List list = new ArrayList<>(); + + //定义两个hash数组 + char[] s_letter = new char[26]; // 滑动窗口hash + char[] p_letter = new char[26]; //目标数组hash + //hash p数组 + for(int i = 0; i < p_arr.length;i++){ + p_letter[p_arr[i] - 'a']++; + } + //滑动窗口 + int left = 0; + int right = 0; + //当窗口右边出界 则退出 + while (right < s_arr.length){ + //获取当前最右边的字符的hash值 + int current_char_index = s_arr[right++] - 'a'; + //当前最优字符加入hash + s_letter[current_char_index]++; + + //当前字符的hash值 大于 目标数组(溢出) 1.当前字符不在目标数组中 2.当前字符有重复数组 左边界右移缩小窗口 + //主要技巧点 可以进行手动验证 + while (p_letter[current_char_index] < s_letter[current_char_index]){ + s_letter[s_arr[left++] - 'a']--; + } + //当出现窗口大小等于目标数组的大小的时候 匹配成功+1 + if(right - left == p_arr.length){ + list.add(left); + } + } + return list; + } + } + + @Test + public void test() { + System.out.println(new LeetCode_438_693().new Solution2().findAnagrams("cbaebabacd","abc")); + } +} diff --git a/Week 08/id_693/LeetCode_44_693.java b/Week 08/id_693/LeetCode_44_693.java new file mode 100644 index 000000000..116fb4652 --- /dev/null +++ b/Week 08/id_693/LeetCode_44_693.java @@ -0,0 +1,35 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 44. 通配符匹配 https://leetcode-cn.com/problems/wildcard-matching/ + * @Date 2019/12/06 + */ +public class LeetCode_44_693 { + /** + * 动态规划 相同或者是?:dp[i][j] = dp[i - 1][j - 1]; + * 是* :dp[i][j] = dp[i][j - 1] || dp[i - 1][j]; + */ + class Solution { + public boolean isMatch(String s,String p) { + int m = s.length(); + int n = p.length(); + boolean[][] dp = new boolean[m + 1][n + 1]; + dp[0][0] = true;//防止"" + for (int i = 1; i <= n; i++) { + if (p.charAt(i - 1) != '*') break; + dp[0][i] = true;//对前面的* 都设置为true + } + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + if (s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '?') { + dp[i][j] = dp[i - 1][j - 1]; + } else if (p.charAt(j - 1) == '*') { + dp[i][j] = dp[i - 1][j] || dp[i][j - 1]; + } + } + } + return dp[m][n]; + } + } +} diff --git a/Week 08/id_693/LeetCode_541_693.java b/Week 08/id_693/LeetCode_541_693.java new file mode 100644 index 000000000..8513cb23e --- /dev/null +++ b/Week 08/id_693/LeetCode_541_693.java @@ -0,0 +1,32 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 541. 反转字符串 II https://leetcode-cn.com/problems/reverse-string-ii/ + * @Date 2019/12/05 + */ +public class LeetCode_541_693 { + /** + * 直接利用reverse,然后结合规则的每2 * k 个字符前面的k个字符旋转即可(注意k需要- 1,索引问题) + */ + class Solution { + public String reverseStr(String s,int k) { + char[] str = s.toCharArray(); + int len = str.length; + for (int i = 0; i < len; i += 2 * k) { + int j = i + k - 1 < len ? i + k - 1 : len - 1; + //int j = Math.min(i + k - 1, len - 1); + reverse(str,i,j); + } + return String.valueOf(str); + } + + private void reverse(char[] str,int i,int j) { + while (i < j) { + str[i] ^= str[j]; + str[j] ^= str[i]; + str[i++] ^= str[j--]; + } + } + } +} diff --git a/Week 08/id_693/LeetCode_557_693.java b/Week 08/id_693/LeetCode_557_693.java new file mode 100644 index 000000000..0bf4d6e79 --- /dev/null +++ b/Week 08/id_693/LeetCode_557_693.java @@ -0,0 +1,61 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 557. 反转字符串中的单词 III https://leetcode-cn.com/problems/reverse-words-in-a-string-iii/ + * @Date 2019/12/05 + */ +public class LeetCode_557_693 { + /** + * 结合之前的反转思想 + */ + class Solution { + public String reverseWords(String s) { + String[] strs = s.split(" "); + StringBuilder result = new StringBuilder(s.length()); + for (String str : strs) { + char[] chars = str.toCharArray(); + int i = 0; + int j = chars.length - 1; + while (i < j) { + chars[i] ^= chars[j]; + chars[j] ^= chars[i]; + chars[i++] ^= chars[j--]; + } + result.append(String.valueOf(chars)).append(" "); + } + if (result.length() > 0) { + result.deleteCharAt(result.length() - 1); + } + return result.toString(); + } + } + + /** + * 效率高于上一个解法,直接用一个chars数组,然后用双指针 一直招" " 循环修改。 + */ + class Solution2 { + public String reverseWords(String s) { + char[] chars = s.toCharArray(); + int i = 0; + int len = s.length(); + while (i < len) { + int j = s.indexOf(" ",i + 1); + if (j < 0) { + j = len; + } + reverse(chars,i,j - 1); + i = j + 1; + } + return String.valueOf(chars); + } + + private void reverse(char[] chars,int i,int j) { + while (i < j) { + chars[i] ^= chars[j]; + chars[j] ^= chars[i]; + chars[i++] ^= chars[j--]; + } + } + } +} diff --git a/Week 08/id_693/LeetCode_58_693.java b/Week 08/id_693/LeetCode_58_693.java new file mode 100644 index 000000000..05025e45e --- /dev/null +++ b/Week 08/id_693/LeetCode_58_693.java @@ -0,0 +1,34 @@ +package id_693; + +import org.junit.jupiter.api.Test; + +/** + * @Author 李雷(KyLin) + * @Desc 58. 最后一个单词的长度 https://leetcode-cn.com/problems/length-of-last-word/ + * @Date 2019/12/04 + */ +public class LeetCode_58_693 { + class Solution { + public int lengthOfLastWord(String s) { + char[] chars = s.toCharArray(); + int res = 0; + for (int i = s.length() - 1; i >= 0; i--) { + if (res != 0 && chars[i] == ' ') { + break; + } else if (chars[i] != ' ') { + res++; + } + } + return res; + } + } + + @Test + public void test() { + System.out.println(new Solution().lengthOfLastWord(" ")); + System.out.println(new Solution().lengthOfLastWord("a ")); + System.out.println(new Solution().lengthOfLastWord(" a ")); + System.out.println(new Solution().lengthOfLastWord(" Hello ")); + System.out.println(new Solution().lengthOfLastWord("Hellollll Hello ")); + } +} diff --git a/Week 08/id_693/LeetCode_5_693.java b/Week 08/id_693/LeetCode_5_693.java new file mode 100644 index 000000000..6d3245733 --- /dev/null +++ b/Week 08/id_693/LeetCode_5_693.java @@ -0,0 +1,66 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 5. 最长回文子串 https://leetcode-cn.com/problems/longest-palindromic-substring/ + * @Date 2019/12/05 + */ +public class LeetCode_5_693 { + /** + * 中心扩展法 采用字符串 + */ + class Solution { + public String longestPalindrome(String s) { + String res = ""; + for (int i = 0; i < s.length(); i++) { + String res1 = longestPalindrome(s,i,i); + String res2 = longestPalindrome(s,i,i + 1); + String max = res1.length() > res2.length() ? res1 : res2; + if (max.length() > res.length()) { + res = max; + } + } + return res; + } + + private String longestPalindrome(String s,int i,int j) { + while (i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)) { + i--; + j++; + } + return s.substring(i + 1,j); + } + } + + /** + * 动态规划 和647. 回文子串 https://leetcode-cn.com/problems/palindromic-substrings 一摸一样,只是用2个指针获得最大值而已 + * . dp方程: dp[j][i] = (s[i]==s[j] && (i-j==1 || dp[j+1][i-1])) ? true : false + * . 动态规划更新双指针:i - j > end - start; + */ + class Solution2 { + public String longestPalindrome(String s) { + int len = s.length(); + if (len == 0) return ""; + int start = 0; + int end = 0; + boolean[][] dp = new boolean[len][len]; + for (int i = 0; i < len; i++) { + for (int j = 0; j <= i; j++) { + if (i == j || (s.charAt(i) == s.charAt(j) && (i - j == 1 || dp[j + 1][i - 1]))) { + dp[j][i] = true; + if (i - j > end - start) { + end = i; + start = j; + } + } + } + } + return s.substring(start,end + 1); + } + } + + public static void main(String[] args) { + System.out.println(new LeetCode_5_693().new Solution().longestPalindrome("babad")); + System.out.println(new LeetCode_5_693().new Solution().longestPalindrome("abbs")); + } +} diff --git a/Week 08/id_693/LeetCode_680_693.java b/Week 08/id_693/LeetCode_680_693.java new file mode 100644 index 000000000..e3db82c7f --- /dev/null +++ b/Week 08/id_693/LeetCode_680_693.java @@ -0,0 +1,40 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 680. 验证回文字符串 Ⅱ https://leetcode-cn.com/problems/valid-palindrome-ii/ + * @Date 2019/12/05 + */ +public class LeetCode_680_693 { + /** + * 双指针解法,AC + */ + class Solution { + public boolean validPalindrome(String s) { + char[] chars = s.toCharArray(); + int i = 0; + int j = chars.length - 1; + //双指针循环找出不等于的字符索引 + while (i < j && chars[i] == chars[j]) { + i++; + j--; + } + //如果还有字符串,则删除左边循环判断 + if (isValid(chars,i + 1,j)) return true; + //如果还有字符串,则删除右边循环判断 + if (isValid(chars,i,j - 1)) return true; + //如果上面都是false,那么结果肯定是false + return false; + } + + //验证是否是回文 + private boolean isValid(char[] chars,int i,int j) { + while (i < j) { + if (chars[i++] != chars[j--]) { + return false; + } + } + return true; + } + } +} diff --git a/Week 08/id_693/LeetCode_709_693.java b/Week 08/id_693/LeetCode_709_693.java new file mode 100644 index 000000000..840a51480 --- /dev/null +++ b/Week 08/id_693/LeetCode_709_693.java @@ -0,0 +1,33 @@ +package id_693; + +import org.junit.jupiter.api.Test; + +/** + * @Author 李雷(KyLin) + * @Desc 709. 转换成小写字母 https://leetcode-cn.com/problems/to-lower-case/ + * @Date 2019/12/04 + */ +public class LeetCode_709_693 { + /** + * 直接判断是否是大写字符,是就直接加32 + */ + class Solution { + public String toLowerCase(String str) { + char[] chars = str.toCharArray(); + for (int i = 0; i < chars.length; i++) { + if (chars[i] >= 65 && chars[i] <= 90) { + chars[i] += 32; + } + } + return String.valueOf(chars); + } + } + + @Test + public void test() { + System.out.println(Integer.valueOf('A')); + System.out.println(Integer.valueOf('Z')); + System.out.println(Integer.valueOf('a')); + System.out.println(Integer.valueOf('z')); + } +} diff --git a/Week 08/id_693/LeetCode_771_693.java b/Week 08/id_693/LeetCode_771_693.java new file mode 100644 index 000000000..37693f65a --- /dev/null +++ b/Week 08/id_693/LeetCode_771_693.java @@ -0,0 +1,35 @@ +package id_693; + +import org.junit.jupiter.api.Test; + +/** + * @Author 李雷(KyLin) + * @Desc 771. 宝石与石头 https://leetcode-cn.com/problems/jewels-and-stones/ + * @Date 2019/12/04 + */ +public class LeetCode_771_693 { + /** + * 计数排序思想 + */ + class Solution { + public int numJewelsInStones(String J,String S) { + //J 是宝石,不重复 S 是我的石头 + boolean[] jElements = new boolean[123]; + for (int i = 0; i < J.length(); i++) { + jElements[J.charAt(i)] = true; + } + int res = 0; + for (int i = 0; i < S.length(); i++) { + if (jElements[S.charAt(i)]) { + res++; + } + } + return res; + } + } + + @Test + public void test() { + System.out.println(Integer.valueOf('z')); + } +} diff --git a/Week 08/id_693/LeetCode_85_693.java b/Week 08/id_693/LeetCode_85_693.java new file mode 100644 index 000000000..7f062998c --- /dev/null +++ b/Week 08/id_693/LeetCode_85_693.java @@ -0,0 +1,59 @@ +package id_693; + +import java.util.Arrays; + +/** + * @Author 李雷(KyLin) + * @Desc 85. 最大矩形 https://leetcode-cn.com/problems/maximal-rectangle/ + * @Date 2019/12/04 + */ +public class LeetCode_85_693 { + /** + * 动态规划 - 每个点的最大高度 + */ + class Solution { + public int maximalRectangle(char[][] matrix) { + if (matrix.length == 0 || matrix[0].length == 0) return 0; + int m = matrix.length; + int n = matrix[0].length; + int[] leftMin = new int[n]; + int[] rightMin = new int[n]; + int[] height = new int[n]; + Arrays.fill(leftMin,-1); + Arrays.fill(rightMin,n); + int areaMax = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (matrix[i][j] == '1') { + height[j] += 1; + } else { + height[j] = 0; + } + } + int boundary = -1; + for (int j = 0; j < n; j++) { + if (matrix[i][j] == '1') { + leftMin[j] = Math.max(leftMin[j],boundary); + } else { + leftMin[j] = -1; + boundary = j; + } + } + boundary = n; + for (int j = n - 1; j >= 0; j--) { + if (matrix[i][j] == '1') { + rightMin[j] = Math.min(rightMin[j],boundary); + } else { + rightMin[j] = n; + boundary = j; + } + } + for (int j = 0; j < n; j++) { + int area = (rightMin[j] - leftMin[j] - 1) * height[j]; + areaMax = Math.max(area,areaMax); + } + } + return areaMax; + } + } +} diff --git a/Week 08/id_693/LeetCode_8_693.java b/Week 08/id_693/LeetCode_8_693.java new file mode 100644 index 000000000..ace9c208b --- /dev/null +++ b/Week 08/id_693/LeetCode_8_693.java @@ -0,0 +1,44 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 8. 字符串转换整数 (atoi) https://leetcode-cn.com/problems/string-to-integer-atoi/ + * @Date 2019/12/04 + */ +public class LeetCode_8_693 { + class Solution { + public int myAtoi(String str) { + int len = str.length(); + if (len == 0) return 0; + int sign = 1; + int index = 0; + int total = 0; + //寻找第一个空格 + while (index < len && str.charAt(index) == ' ') { + index++; + } + //确定正负数 + if (index < len && (str.charAt(index) == '+' || str.charAt(index) == '-')) { + sign = str.charAt(index) == '+' ? 1 : -1; + index++; + } + //转换数字,并且防止溢出 + while (index < len) { + int digit = str.charAt(index) - '0'; + if (digit < 0 || digit > 9) { + break; + } + //最大值/10 小于total那么下次* 10 就会溢出,或者 等于,那么就看digit 是否大于最大值%10 的结果 , + // 所以这里专门处理 会溢出的数 + if (Integer.MAX_VALUE / 10 < total || + (Integer.MAX_VALUE / 10 == total && Integer.MAX_VALUE % 10 < digit)) { + return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE; + } + total = total * 10 + digit; + index++; + } + return sign * total; + } + } +} + diff --git a/Week 08/id_693/LeetCode_917_693.java b/Week 08/id_693/LeetCode_917_693.java new file mode 100644 index 000000000..0c82a750a --- /dev/null +++ b/Week 08/id_693/LeetCode_917_693.java @@ -0,0 +1,34 @@ +package id_693; + +/** + * @Author 李雷(KyLin) + * @Desc 917. 仅仅反转字母 https://leetcode-cn.com/problems/reverse-only-letters/ + * @Date 2019/12/05 + */ +public class LeetCode_917_693 { + /** + * 直接转会为char数组,然后双指针跑路 + */ + class Solution { + public String reverseOnlyLetters(String s) { + char[] chars = s.toCharArray(); + int i = 0; + int j = chars.length - 1; + while (i < j) { + while (i < j && !Character.isLowerCase(chars[i]) && !Character.isUpperCase(chars[i])) i++; + while (i < j && !Character.isLowerCase(chars[j]) && !Character.isUpperCase(chars[j])) j--; + if (i < j) { + chars[i] ^= chars[j]; + chars[j] ^= chars[i]; + chars[i++] ^= chars[j--]; + } + } + return String.valueOf(chars); + } + } + + + public static void main(String[] args) { + new LeetCode_917_693().new Solution().reverseOnlyLetters("a-bC-dEf-ghIj"); + } +} diff --git a/Week 08/id_693/NOTE.md b/Week 08/id_693/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_693/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_698/LeetCode_115_698.java b/Week 08/id_698/LeetCode_115_698.java new file mode 100644 index 000000000..20fbd5757 --- /dev/null +++ b/Week 08/id_698/LeetCode_115_698.java @@ -0,0 +1,28 @@ +/** + * 给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。 + * + * @author gning (id=698) + */ + + public class LeetCode_115_698 { + + public int numDistinct(String s, String t) { + int[][] dp = new int[t.length()+1][s.length()+1]; + + for(int j=0; j=0; i--) { + String word = words[i]; + + if(word.isEmpty()) { + continue; + } + + buf.append(word.replace(space, "")); + + if(i>0) + buf.append(space); + } + + return buf.toString(); + } +} \ No newline at end of file diff --git a/Week 08/id_698/NOTE.md b/Week 08/id_698/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_698/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_703/LeetCode_387_703.py b/Week 08/id_703/LeetCode_387_703.py new file mode 100644 index 000000000..ce549d8d8 --- /dev/null +++ b/Week 08/id_703/LeetCode_387_703.py @@ -0,0 +1,23 @@ +# +# @lc app=leetcode.cn id=387 lang=python3 +# +# [387] 字符串中的第一个唯一字符 +# + +# @lc code=start +class Solution: + def firstUniqChar(self, s: str) -> int: + + count = collections.Counter(s) + + index = 0 + for ch in s: + if count[ch] == 1: + return index + else: + index += 1 + return -1 + + +# @lc code=end + diff --git a/Week 08/id_703/LeetCode_541_703.py b/Week 08/id_703/LeetCode_541_703.py new file mode 100644 index 000000000..358289f61 --- /dev/null +++ b/Week 08/id_703/LeetCode_541_703.py @@ -0,0 +1,17 @@ +# +# @lc app=leetcode.cn id=541 lang=python3 +# +# [541] 反转字符串 II +# + +# @lc code=start +class Solution: + def reverseStr(self, s: str, k: int) -> str: + a = list(s) + for i in range(0, len(a), 2*k): + a[i:i+k] = reversed(a[i:i+k]) + return "".join(a) + + +# @lc code=end + diff --git a/Week 08/id_703/NOTE.md b/Week 08/id_703/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_703/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_708/LeetCode_58_708.java b/Week 08/id_708/LeetCode_58_708.java new file mode 100644 index 000000000..b2afb1c68 --- /dev/null +++ b/Week 08/id_708/LeetCode_58_708.java @@ -0,0 +1,13 @@ +class Solution { + public int lengthOfLastWord(String s) { + if (s.length() == 0) return 0; + int ans = 0; + int pos = s.length() - 1; + while (pos >= 0 && s.charAt(pos) == ' ') pos--; + while (pos >= 0 && s.charAt(pos) != ' ') { + ans++; + pos--; + } + return ans; + } +} \ No newline at end of file diff --git a/Week 08/id_708/LeetCode_709_708.java b/Week 08/id_708/LeetCode_709_708.java new file mode 100644 index 000000000..91207c808 --- /dev/null +++ b/Week 08/id_708/LeetCode_709_708.java @@ -0,0 +1,13 @@ +class Solution { + public String toLowerCase(String str) { + StringBuilder ret = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c >= 'A' && c <= 'Z') { + ret.append((char)(c+32)); + } + else ret.append(c); + } + return ret.toString(); + } +} \ No newline at end of file diff --git a/Week 08/id_708/LeetCode_771_708.java b/Week 08/id_708/LeetCode_771_708.java new file mode 100644 index 000000000..797d1da96 --- /dev/null +++ b/Week 08/id_708/LeetCode_771_708.java @@ -0,0 +1,10 @@ +class Solution { + public int numJewelsInStones(String J, String S) { + int ans = 0; + Set set = new HashSet<>(); + for (char c : J.toCharArray()) set.add(c); + for (char c : S.toCharArray()) + if (set.contains(c)) ans++; + return ans; + } +} \ No newline at end of file diff --git a/Week 08/id_708/NOTE.md b/Week 08/id_708/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_708/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_713/LeetCode_14_LongestCommonPrefix.java b/Week 08/id_713/LeetCode_14_LongestCommonPrefix.java new file mode 100644 index 000000000..72a9c183f --- /dev/null +++ b/Week 08/id_713/LeetCode_14_LongestCommonPrefix.java @@ -0,0 +1,49 @@ +package id_713; + +/** + * 14. 最长公共前缀 + */ +public class LeetCode_14_LongestCommonPrefix { + + + /* + 编写一个函数来查找字符串数组中的最长公共前缀。 + + 如果不存在公共前缀,返回空字符串 ""。 + + 示例 1: + + 输入: ["flower","flow","flight"] + 输出: "fl" + + 示例 2: + + 输入: ["dog","racecar","car"] + 输出: "" + 解释: 输入不存在公共前缀。 + + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/longest-common-prefix + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + + /* + 水平扫描法 + */ + public String longestCommonPrefix(String[] strs) { + if (strs == null || strs.length == 0) return ""; + + for (int i = 0; i < strs[0].length(); i++) { + char c = strs[0].charAt(i); + for (int j = 0; j < strs.length; j++) { + if (i == strs[j].length() || strs[j].charAt(j) != c) { + return strs[0].substring(0, i); + } + } + } + + return strs[0]; + } +} diff --git a/Week 08/id_713/LeetCode_14_ReverseString.java b/Week 08/id_713/LeetCode_14_ReverseString.java new file mode 100644 index 000000000..478760cc8 --- /dev/null +++ b/Week 08/id_713/LeetCode_14_ReverseString.java @@ -0,0 +1,48 @@ +package id_713; + +/** + * 344. 反转字符串 + */ +public class LeetCode_14_ReverseString { + + + /* + 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。 + + 不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 + + 你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。 + + + + 示例 1: + + 输入:["h","e","l","l","o"] + 输出:["o","l","l","e","h"] + + 示例 2: + + 输入:["H","a","n","n","a","h"] + 输出:["h","a","n","n","a","H"] + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/reverse-string + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + + public void reverseString(char[] s) { + if (s == null || s.length == 0) return; + + int i = 0, j = s.length - 1; + + while (i < j) { + char tmp = s[i]; + s[i] = s[j]; + s[j] = tmp; + i++; + j--; + } + } + +} diff --git a/Week 08/id_713/LeetCode_151_ReverseWordsInAString.java b/Week 08/id_713/LeetCode_151_ReverseWordsInAString.java new file mode 100644 index 000000000..8ba93afb0 --- /dev/null +++ b/Week 08/id_713/LeetCode_151_ReverseWordsInAString.java @@ -0,0 +1,133 @@ +package id_713; + +import java.util.Arrays; +import java.util.Collections; + +/** + * 151. 翻转字符串里的单词 + */ +public class LeetCode_151_ReverseWordsInAString { + + /* + 给定一个字符串,逐个翻转字符串中的每个单词。 + + + + 示例 1: + + 输入: "the sky is blue" + 输出: "blue is sky the" + + 示例 2: + + 输入: " hello world! " + 输出: "world! hello" + 解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 + + 示例 3: + + 输入: "a good example" + 输出: "example good a" + 解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 + + + + 说明: + + 无空格字符构成一个单词。 + 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 + 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 + + + + 进阶: + + 请选用 C 语言的用户尝试使用 O(1) 额外空间复杂度的原地解法。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/reverse-words-in-a-string + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + public String reverseWords(String s) { + if (s == null) return null; + + String[] words = s.trim().split(" +"); + Collections.reverse(Arrays.asList(words)); + return String.join(" ", words); + } + + + public String reverseWords2(String s) { + StringBuilder sb = new StringBuilder(); + int i = s.length() - 1; + + while (i >= 0) { + if (s.charAt(i) == ' ') { + i--; + continue; + } + + int start = s.lastIndexOf(' ', i); + sb.append(" "); + sb.append(s.substring(start + 1, i + 1)); + i = start - 1; + + + } + + if (sb.length() > 0) { + sb.deleteCharAt(0); + } + + return sb.toString(); + } + + + public String reverseWords3(String s) { + if (s == null) return null; + + char[] chars = s.toCharArray(); + int n = chars.length; + + reverse(chars, 0, n - 1); + reverseWord(chars, n); + return cleanSpaces(chars, n); + + } + + private void reverseWord(char[] chars, int n) { + int i = 0, j = 0; + + while (i < n) { + while (i < j || i < n && chars[i] == ' ') i++; + while (j < i || j < n && chars[j] == ' ') j++; + reverse(chars, i, j - 1); + } + } + + private void reverse(char[] chars, int i, int j) { + while (i < j) { + char tmp = chars[i]; + chars[i] = chars[j]; + chars[j] = tmp; + } + } + + + private String cleanSpaces(char[] chars, int n) { + int i = 0, j = 0; + while (j < n) { + while (j < n && chars[j] == ' ') j++; + while (j < n && chars[j] != ' ') chars[i++] = chars[j++]; + while (j < n && chars[j] == ' ') j++; + + if (j < n) { + chars[i++] = ' '; + } + } + + return new String(chars).substring(0, i); + } + +} diff --git a/Week 08/id_713/LeetCode_300_LongestIncreasingSubsequence.java b/Week 08/id_713/LeetCode_300_LongestIncreasingSubsequence.java new file mode 100644 index 000000000..77db783a3 --- /dev/null +++ b/Week 08/id_713/LeetCode_300_LongestIncreasingSubsequence.java @@ -0,0 +1,103 @@ +package id_713; + +/** + * 300. 最长上升子序列 + */ +public class LeetCode_300_LongestIncreasingSubsequence { + + /* + 给定一个无序的整数数组,找到其中最长上升子序列的长度。 + + 示例: + + 输入: [10,9,2,5,3,7,101,18] + 输出: 4 + 解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。 + + 说明: + + 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。 + 你算法的时间复杂度应该为 O(n2) 。 + + 进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗? + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/longest-increasing-subsequence + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + + + /* + + + 参考: https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/zui-chang-shang-sheng-zi-xu-lie-dong-tai-gui-hua-2/ + + 朴素动态规划: + 状态定义: + dp[i]的值表示 nums 前i个数字的最长子序列长度 + 转移方程 + 设 j 属于 [0, i), 考虑每轮计算新的dp[i]时, 遍历[0, i)列表区间, 做以下判断 + 1. 当 nums[i] > nums[j]时, nums[i]可以接在nums[j]之后, 此情况下最长上升子序列长度为 dp[j] + 1 + 2. 当 nums[i] <= nums[j]时, nums[i] 无法接在nums[i]之后, 此情况上升序列不成立, 跳过 + + 上述所有1情况下 计算出的 dp[j] + 1的最大值, 为直到i的上升子序列长度(dp[i]) + 实现方式为 遍历j时, 每轮执行 dp[i] = max(dp[i], dp[j] + 1) + + 转移方程: dp[i] = max(dp[i], dp[j] + 1) for j in [0, i) + 初始状态: dp[i]所有元素置1, 含义是: 每个元素都至少可以单独成为子序列, 长度为1 + 返回值: 返回dp数组中最大值, 即可得到全局最长上升子序列长度 + + 高级动态规划 + 1. 动态规划中, 通过线性遍历来计算dp的复杂度无法降低 + 2. 每轮计算中, 需要通过线性遍历 [0, k)区间元素来得到 dp[k]. 考虑, 是否可以通过重新设计状态定义, 使整个dp为一个排序列表 + 这样在计算每个dp[k]时, 就可以通过二分查找法遍历 [0, k)区间元素, 将此部分复杂度有 O(N) 降低至 O(logN) + + 新状态定义 + 维护一个列表 dp, 其中每个元素 dp[k]的值代表 长度为 k+1的子序列尾部元素值 + 如 [1,4,6]序列, 长度为1,2,3的子序列尾部元素值分别为 dp = [1,4,6] + + 状态转移设计 + 设常量数字N, 和随机数x, 可以推出: 当N越小时, N < x的纪律越大 + 如 N = 0 比 N = 1000更可能满足 N < x + + 在遍历计算每个 tails[k],不断更新长度为 [1,k]的子序列尾部元素值,始终保持每个尾部元素值最小 (例如 [1,5,3]], 遍历到元素 555 时,长度为 222 的子序列尾部元素值为 555;当遍历到元素 333 时,尾部元素值应更新至 333,因为 333 遇到比它大的数字的几率更大)。 + + + + + + [10 9 2 5 3 7 21 18] + + [0 0 0 0 0 0 0 0] + [10 0 0 0 0 0 0 0] + [9 0 0 0 0 0 0 0] + [2 0 0 0 0 0 0 0] + [2 5 0 0 0 0 0 0] + [2 3 0 0 0 0 0 0] + [2 3 7 0 0 0 0 0] + [2 3 7 21 0 0 0 0] + [2 3 7 18 0 0 0 0] + + 数组的长度, 即为最大上升子序列 + */ + + public int lengthOfLIS(int[] nums) { + + int[] tails = new int[nums.length]; + int res = 0; + + for (int num : nums) { + int i = 0, j = res; + while (i < j) { + int m = (i + j) / 2; + if (tails[m] < num) i = m + 1; + else j = m; + } + tails[i] = num; + if (res == j) res++; + } + + return res; + } +} diff --git a/Week 08/id_713/LeetCode_387_FirstUniqueCharacterInAString.java b/Week 08/id_713/LeetCode_387_FirstUniqueCharacterInAString.java new file mode 100644 index 000000000..b94d64c2b --- /dev/null +++ b/Week 08/id_713/LeetCode_387_FirstUniqueCharacterInAString.java @@ -0,0 +1,42 @@ +package id_713; + +import java.util.HashMap; + +/** + * 387. 字符串中的第一个唯一字符 + */ +public class LeetCode_387_FirstUniqueCharacterInAString { + + /* + 给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。 + + 案例: + + s = "leetcode" + 返回 0. + + s = "loveleetcode", + 返回 2. + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/first-unique-character-in-a-string + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + public int firstUniqChar(String s) { + HashMap map = new HashMap<>(); + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + map.put(c, map.getOrDefault(c, 0) + 1); + } + + for (int i = 0; i < s.length(); i++) { + if (map.get(s.charAt(i)) == 1) { + return i; + } + } + + return -1; + } +} diff --git a/Week 08/id_713/LeetCode_8_StringToIntegerAtoi.java b/Week 08/id_713/LeetCode_8_StringToIntegerAtoi.java new file mode 100644 index 000000000..d1be1331c --- /dev/null +++ b/Week 08/id_713/LeetCode_8_StringToIntegerAtoi.java @@ -0,0 +1,95 @@ +package id_713; + +/** + * 8. 字符串转换整数 (atoi) + */ +public class LeetCode_8_StringToIntegerAtoi { + + /* + 请你来实现一个 atoi 函数,使其能将字符串转换成整数。 + + 首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。 + + 当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。 + + 该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。 + + 注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。 + + 在任何情况下,若函数不能进行有效的转换时,请返回 0。 + + 说明: + + 假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。 + + 示例 1: + + 输入: "42" + 输出: 42 + + 示例 2: + + 输入: " -42" + 输出: -42 + 解释: 第一个非空白字符为 '-', 它是一个负号。 + 我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。 + + 示例 3: + + 输入: "4193 with words" + 输出: 4193 + 解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。 + + 示例 4: + + 输入: "words and 987" + 输出: 0 + 解释: 第一个非空字符是 'w', 但它不是数字或正、负号。 + 因此无法执行有效的转换。 + + 示例 5: + + 输入: "-91283472332" + 输出: -2147483648 + 解释: 数字 "-91283472332" 超过 32 位有符号整数范围。 + 因此返回 INT_MIN (−231) 。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/string-to-integer-atoi + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + public int myAtoi(String str) { + int index = 0, sign = 1, total = 0; + + // 如果是空串 + if(str == null || str.length() == 0) return 0; + + // 去除空格, 如果使用trim的话, 会使用额外的空间, 原因: java的String是不可变的 + while (str.charAt(index) == ' ' && index < str.length()) + index++; + + // 处理符号 + if(str.charAt(index) == '+' || str.charAt(index) == '-'){ + sign = str.charAt(index) == '+' ? 1 : -1; + index++; + } + + + // 转换 + while (index < str.length()) { + int digit = str.charAt(index) - '0'; + if (digit < 0 || digit > 9) break; + + if (Integer.MAX_VALUE / 10 < total || Integer.MAX_VALUE / 10 == total && Integer.MAX_VALUE % 10 < digit) { + return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE; + } + + total = 10 * total + digit; + index++; + } + + return total * sign; + } + +} diff --git a/Week 08/id_713/LeetCode_91_DecodeWays.java b/Week 08/id_713/LeetCode_91_DecodeWays.java new file mode 100644 index 000000000..4fc047c54 --- /dev/null +++ b/Week 08/id_713/LeetCode_91_DecodeWays.java @@ -0,0 +1,130 @@ +package id_713; + +/** + * 91. 解码方法 + */ +public class LeetCode_91_DecodeWays { + + /* + 一条包含字母 A-Z 的消息通过以下方式进行了编码: + + 'A' -> 1 + 'B' -> 2 + ... + 'Z' -> 26 + + 给定一个只包含数字的非空字符串,请计算解码方法的总数。 + + 示例 1: + + 输入: "12" + 输出: 2 + 解释: 它可以解码为 "AB"(1 2)或者 "L"(12)。 + + 示例 2: + + 输入: "226" + 输出: 3 + 解释: 它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。 + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/decode-ways + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + + /* + 分析: + + 以22067举例, 从后往前遍历 + + 1. 首先提取如果为7, 只有1种 -> G + 2. 其次为67, 也只有一种 -> FG + 3. 067 -> 0 + 4. 2067 -> (20 67) + (2 067) = (20 67) -> TFG + 5. 22067 -> (2 2067) + (22 067) + (2 2067) -> BTFG + + 得到规律: + 如果开始为0, 结果为0 + 如果开始的数加上第二个数 <= 26. 结果为 (start + 1) + (start + 2) + 如果开始的数加上第二个数 > 26. 结果为 (start + 1) + + 以上是递归的思路, 导致了大量重复的计算 + + 引入一个dp数组, 用来计算以某个字符为开始的解码数量, 动态规划是一个填表的过程 + 递推方程: + 如果 nums[i] = 0, dp[i] = 0 + 如果 nums[i] + nums[i + 1] <= 26 + dp[i] =dp[i + 1] + dp[i + 2] + 否则 + dp[i] = dp[i + 1] + + + 以2206为例, 假设此处 nums[]={2,2,0,6}, 新建一个数组 + int[] dp = new int[len + 1]; + 之所以长度为len+1, 因为这样不需要将最后一个6单独处理, 并将dp[len]=1 + + 0 1 2 3 4 + [0 0 0 0 1] // 将 dp[len]=1, 算上0, 此时长度为 len + 1, 所以索引是 len + [0 0 0 1 1] // 此时处理 dp[3], 按照递推式, 因为 nums[3] = 6,所以 dp[3] = 1 + [0 0 0 1 1] // 此时处理 dp[2], 按照递推式, 因为 nums[2] = 0,所以 dp[2] = 0 + [0 1 0 1 1] // 此时处理 dp[1], 按照递推式, 因为 nums[1]*10 + nums[2] = 20, 所以dp[1] = dp[2] + dp[3] = 1 + [1 1 0 1 1] // 此时处理 dp[0], 按照递推式, 因为 nums[0]*10 + nums[1] = 22, 所以dp[0] = dp[1] + dp[2] = 1 + + */ + + + public int numDecodings(String s) { + if (s == null || s.length() == 0) return 0; + + // 提取s的长度 + int n = s.length(); + + int[] dp = new int[n + 1]; + dp[n] = 1; + dp[n - 1] = (s.charAt(n - 1) == '0' ? 0 : 1); + + for (int i = n - 2; i >= 0; i--) { + if (s.charAt(i) == '0') { + dp[i] = 0; + continue; + } + + if ((s.charAt(i) - '0' * 10 + s.charAt(i + 1) - '0') <= 26) { + dp[i] = dp[i + 1] + dp[i + 2]; + } else { + dp[i] = dp[i + 1]; + } + } + + return dp[0]; + } + + public int numDecodingsPlus(String s) { + if (s == null || s.length() == 0) return 0; + + // 提取s的长度 + int len = s.length(); + + int help = 1; // 相当于 初始化 dp[len + 1[ = 1 的意思 + int res = (s.charAt(len - 1) != '0' ? 1 : 0); // 倒数第2个值, 也就是是 + + for (int i = len - 2; i >= 0; i--) { + if (s.charAt(i) == '0') { + help = res; + res = 0; + continue; + } + + if ((s.charAt(i - '0') * 10 + s.charAt(i + 1 - '0')) <= 26) { + res += help; + help = res - help; // help用于存储res之前的值 + } else { + help = res; + } + } + + return res; + } + +} diff --git a/Week 08/id_713/NOTE.md b/Week 08/id_713/NOTE.md new file mode 100644 index 000000000..9e96a1bc3 --- /dev/null +++ b/Week 08/id_713/NOTE.md @@ -0,0 +1,146 @@ +# NOTE + + + + + +# 高级动态规划 + +## 常见DP题目和状态转移方程 + +### 爬楼梯问题 + +* 转换成斐波那契数列 +* f(n) = f(n - 1) + f(n - 2), 其中 f(1) = 1, f(2) = 1 + +```python +# 朴素 +def f(n): + if n <= 1: return 1; + return f(n - 1) + f(n - 2) + +# 带缓存 +def f(n): + if n <= 1: return 1 + if n not in mem: + mem[n] = f(n - 1) + f(n - 2) + return mem[n] + +def f(n): + x, y = 1, 1 + for i in ranges (1, n): + y, x = x + y, y + return y +``` + + + +### 不同路径问题 + +* f(x, y) = f(x - 1, y) + f(x, y - 1) + +```python +def (x, y): + dp[[0] * (m + 1) for _ in range(n + 1)] + dp[1][1] = 1 + for i in range(1, y + 1): + for j in range(1, x + 1): + dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + return dp[y][x] +``` + + + +### 打家劫舍 + +* dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]) + +```python +dp[i][0] = max(dp[i - 1][0], dp[i - 1][1]) +dp[i][1] = dp[i - 1][0] + +``` + + + +### 最小路径和 + +* dp[i] [j]状态定义: minPath(A[1 -> i] [1 -> j]) +* dp[i] [j] = min(dp[i - 1] [j], dp[i] [j - 1]) +A[i] [j] + + + +### 股票买卖 + +```python +dp[i][k][0 or 1] 0 <= i <= n-1, 1 <= k <= K +# i为天数 +# k为最多交易次数 +# [0, 1]是否持有股票 +# 总状态数: n * K * 2种状态 + +for 0 <= i < n: + for i <= k <= K: + for s in [0, 1]: + dp[i][k][s] = max(buy, sell, rest) + +dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i]) +# 解释: 今天没有持有股票, 有2种可能 +# 1.昨天就没有持有, 然后今天选择rest +# 2.昨天持有了股票, 今天卖掉了 +dp[i][k][1] = max(dp[i-1[k][1]], dp[i-1][k-1][0] - prices[i]) +# 解释: 今天持有股票, 有2种可能 +# 1.昨天持有股票, 今天rest +# 2.昨天没有持有, 今天买入 +``` + + + +--- + +## 复杂度来源 + +* 拥有更多维度 +* dp方程复杂 + + + + + +### 爬楼梯变种 + +* 一次可以上 一级,二级,三级 + +```python +# 上一级, 上二级, 上三级 +# 状态转移方程 +a[i] = a[i - 1] + a[i - 2] + a[i -3] + +a[0] = 1 +a[1] = 2 +a[2] = 3 + +for (int i = 2; i < n; i++) { + a[i] = a[i - 1] + a[i - 2] + a[i - 3] +} + +return a[n - 1] +``` + + + +### 编辑距离 + +```python +# 如果word1[i]与word2[j]相同, 显然dp[i][j] = dp[i - 1][j - 1] + +# 如果word1[i]与word2[j], 不同那么dp[i][j]可以通过 +# 1.在dp[i - 1][j - 1]的基础上做replace操作达到目的 +# 2.在dp[i - 1][j]的基础上做insert操作达到目的 +# 3.在dp[i][j - 1]的基础上做delete操作达到目的 +# 取三者最小情况即可 + +``` + + + diff --git a/Week 08/id_713/practise/LeetCode_58_LengthOfLastWord.java b/Week 08/id_713/practise/LeetCode_58_LengthOfLastWord.java new file mode 100644 index 000000000..d6f121579 --- /dev/null +++ b/Week 08/id_713/practise/LeetCode_58_LengthOfLastWord.java @@ -0,0 +1,46 @@ +package id_713.practise; + +/** + * 58. 最后一个单词的长度 + */ +public class LeetCode_58_LengthOfLastWord { + /* + 给定一个仅包含大小写字母和空格 ' ' 的字符串,返回其最后一个单词的长度。 + + 如果不存在最后一个单词,请返回 0 。 + + 说明:一个单词是指由字母组成,但不包含任何空格的字符串。 + + 示例: + + 输入: "Hello World" + 输出: 5 + + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/length-of-last-word + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + + */ + + public int lengthOfLastWord(String s) { + if (s == null) return 0; + s = s.trim(); + if (s.length() == 0) return 0; + + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == ' ') + return s.length() - 1 - i; + } + + return s.length(); + } + + public int lengthOfLastWord2(String s) { + if (s == null || s.length() == 0) return 0; + s = s.trim(); + int lastSpace = s.lastIndexOf(" "); + return s.length() - 1 - lastSpace; + } + +} diff --git a/Week 08/id_713/practise/LeetCode_709_ToLowerCase.java b/Week 08/id_713/practise/LeetCode_709_ToLowerCase.java new file mode 100644 index 000000000..18ad20fbd --- /dev/null +++ b/Week 08/id_713/practise/LeetCode_709_ToLowerCase.java @@ -0,0 +1,47 @@ +package id_713.practise; + +/** + * 709. 转换成小写字母 + */ +public class LeetCode_709_ToLowerCase { + /* + 实现函数 ToLowerCase(),该函数接收一个字符串参数 str,并将该字符串中的大写字母转换成小写字母,之后返回新的字符串。 + + 示例 1: + + 输入: "Hello" + 输出: "hello" + + 示例 2: + + 输入: "here" + 输出: "here" + + 示例 3: + + 输入: "LOVELY" + 输出: "lovely" + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/to-lower-case + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + public String toLowerCase(String str) { + + char[] chars = new char[str.length()]; + + for (int i = 0; i < str.length(); i++) { + chars[i] = str.charAt(i) >= 65 && str.charAt(i) <= 90 + ? (char) (str.charAt(i) + 32) + : str.charAt(i); + } + + return new String(chars); + } + + public String toLowerCase2(String str) { + return str.toLowerCase(); + } + +} diff --git a/Week 08/id_713/practise/LeetCode_771_JewelsAndStones.java b/Week 08/id_713/practise/LeetCode_771_JewelsAndStones.java new file mode 100644 index 000000000..df0db4858 --- /dev/null +++ b/Week 08/id_713/practise/LeetCode_771_JewelsAndStones.java @@ -0,0 +1,57 @@ +package id_713.practise; + +import java.util.HashSet; +import java.util.Set; + +/** + * 771. 宝石与石头 + */ +public class LeetCode_771_JewelsAndStones { + /* + 给定字符串J 代表石头中宝石的类型,和字符串 S代表你拥有的石头。 S 中每个字符代表了一种你拥有的石头的类型,你想知道你拥有的石头中有多少是宝石。 + + J 中的字母不重复,J 和 S中的所有字符都是字母。字母区分大小写,因此"a"和"A"是不同类型的石头。 + + 示例 1: + + 输入: J = "aA", S = "aAAbbbb" + 输出: 3 + + 示例 2: + + 输入: J = "z", S = "ZZ" + 输出: 0 + + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/jewels-and-stones + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + + */ + + public int numJewelsInStones(String J, String S) { + Set set = new HashSet<>(); + + for (char c : J.toCharArray()) { + set.add(c); + } + + int res = 0; + for (char c : S.toCharArray()) { + if (set.contains(c)) + res++; + } + return res; + } + + public int numJewelsInStones2(String J, String S) { + int res = 0; + for (char s : S.toCharArray()) { + if(J.indexOf(s) != -1) + res++; + } + + return res; + } + +} diff --git a/Week 08/id_718/NOTE.md b/Week 08/id_718/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_718/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_723/NOTE.md b/Week 08/id_723/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_723/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_728/NOTE.md b/Week 08/id_728/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_728/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_733/LeetCode_115_733.go b/Week 08/id_733/LeetCode_115_733.go new file mode 100644 index 000000000..8f787ee6f --- /dev/null +++ b/Week 08/id_733/LeetCode_115_733.go @@ -0,0 +1,26 @@ +func numDistinct(s string, t string) int { + sLen, tLen := len(s), len(t) + dp := make([][]int, 2) + for i := 0; i < len(dp); i++ { + dp[i] = make([]int, sLen+1) + } + + for j := 0; j <= sLen; j++ { + dp[0][j] = 1 + } + + for i := 0; i < tLen; i++ { + for j := 0; j < sLen; j++ { + if t[i] == s[j] { + dp[1][j+1] = dp[0][j] + dp[1][j] + } else { + dp[1][j+1] = dp[1][j] + } + } + + dp[0], dp[1] = dp[1], dp[0] + dp[1][0] = 0 + } + + return dp[0][sLen] +} diff --git a/Week 08/id_733/LeetCode_151_733.go b/Week 08/id_733/LeetCode_151_733.go new file mode 100644 index 000000000..b91fdd81b --- /dev/null +++ b/Week 08/id_733/LeetCode_151_733.go @@ -0,0 +1,35 @@ +func reverseWords(s string) string { + res := make([]byte, len(s)+1) + i, idx := len(s)-1, 0 + for { + for i >= 0 && s[i] == ' ' { + i-- + } + + if i < 0 { + break + } + + end := i + for { + i-- + if i < 0 || s[i] == ' ' { + break + } + } + + for k := i + 1; k <= end; k++ { + res[idx] = s[k] + idx++ + } + + res[idx] = ' ' + idx++ + } + + if idx == 0 { + return "" + } + + return string(res[:idx-1]) +} diff --git a/Week 08/id_733/LeetCode_205_733.go b/Week 08/id_733/LeetCode_205_733.go new file mode 100644 index 000000000..a5fb771e1 --- /dev/null +++ b/Week 08/id_733/LeetCode_205_733.go @@ -0,0 +1,14 @@ +func isIsomorphic(s string, t string) bool { + sMap := make([]int, 256) + tMap := make([]int, 256) + for i := range s { + if sMap[s[i]] != tMap[t[i]] { + return false + } + + sMap[s[i]] = i + 1 + tMap[t[i]] = i + 1 + } + + return true +} diff --git a/Week 08/id_733/LeetCode_300_733.go b/Week 08/id_733/LeetCode_300_733.go new file mode 100644 index 000000000..4b61ebbe1 --- /dev/null +++ b/Week 08/id_733/LeetCode_300_733.go @@ -0,0 +1,60 @@ +func lengthOfLIS(nums []int) int { + dp := make([]int, len(nums)) + res := 0 + for i := 0; i < len(nums); i++ { + for j := 0; j < i; j++ { + if nums[i] > nums[j] { + dp[i] = max(dp[i], dp[j]) + } + } + + dp[i] += 1 + res = max(res, dp[i]) + } + + return res +} + +func max(x, y int) int { + if x > y { + return x + } + + return y +} + +func lengthOfLISV2(nums []int) int { + dp := make([]int, len(nums)) + l := 0 + for _, n := range nums { + pos := searchInsertPos(dp, 0, l-1, n) + if pos < 0 { + continue + } + + dp[pos] = n + if l == pos { + l++ + } + } + + return l +} + +func searchInsertPos(nums []int, left, right, target int) int { + for left <= right { + mid := left + (right-left)>>1 + value := nums[mid] + if value == target { + return -1 + } + + if value > target { + right = mid - 1 + } else { + left = mid + 1 + } + } + + return left +} diff --git a/Week 08/id_733/LeetCode_387_733.go b/Week 08/id_733/LeetCode_387_733.go new file mode 100644 index 000000000..b0fe7d2d6 --- /dev/null +++ b/Week 08/id_733/LeetCode_387_733.go @@ -0,0 +1,14 @@ +func firstUniqChar(s string) int { + count := make([]int, 26) + for _, r := range s { + count[r-'a']++ + } + + for i, r := range s { + if count[r-'a'] == 1 { + return i + } + } + + return -1 +} diff --git a/Week 08/id_733/LeetCode_438_733.go b/Week 08/id_733/LeetCode_438_733.go new file mode 100644 index 000000000..fc71e82a8 --- /dev/null +++ b/Week 08/id_733/LeetCode_438_733.go @@ -0,0 +1,58 @@ +func findAnagrams(s string, p string) []int { + if len(s) < len(p) { + return nil + } + + var res []int + a := newAnagramRecognizer(p) + left, right := 0, 0 + for ; right < len(p)-1; right++ { + a.push(s[right]) + } + + for ; right < len(s); right++ { + a.push(s[right]) + if a.isAnagram() { + res = append(res, left) + } + + a.pop(s[left]) + left++ + } + + return res +} + +type anagramRecognizer struct { + counter []int + total int +} + +func newAnagramRecognizer(target string) *anagramRecognizer { + a := &anagramRecognizer{} + a.counter = make([]int, int('z')+1) + for _, r := range target { + a.counter[r]++ + a.total++ + } + + return a +} + +func (a *anagramRecognizer) push(r byte) { + a.counter[r]-- + if a.counter[r] >= 0 { + a.total-- + } +} + +func (a *anagramRecognizer) pop(r byte) { + a.counter[r]++ + if a.counter[r] > 0 { + a.total++ + } +} + +func (a *anagramRecognizer) isAnagram() bool { + return a.total == 0 +} diff --git a/Week 08/id_733/LeetCode_44_733.go b/Week 08/id_733/LeetCode_44_733.go new file mode 100644 index 000000000..d84243737 --- /dev/null +++ b/Week 08/id_733/LeetCode_44_733.go @@ -0,0 +1,27 @@ +func isMatch(s string, p string) bool { + dp := make([][]bool, len(s)+1) + for i := range dp { + dp[i] = make([]bool, len(p)+1) + } + + dp[0][0] = true + for j := 0; j < len(p); j++ { + if p[j] == '*' { + dp[0][j+1] = true + } else { + break + } + } + + for i := 0; i < len(s); i++ { + for j := 0; j < len(p); j++ { + if p[j] == '*' { + dp[i+1][j+1] = dp[i+1][j] || dp[i][j+1] + } else { + dp[i+1][j+1] = dp[i][j] && (s[i] == p[j] || p[j] == '?') + } + } + } + + return dp[len(s)][len(p)] +} diff --git a/Week 08/id_733/LeetCode_541_733.go b/Week 08/id_733/LeetCode_541_733.go new file mode 100644 index 000000000..f3858192c --- /dev/null +++ b/Week 08/id_733/LeetCode_541_733.go @@ -0,0 +1,17 @@ +func reverseStr(s string, k int) string { + str := []rune(s) + for i := 0; i < len(str); i += 2 * k { + left, right := i, i+k-1 + if right >= len(str) { + right = len(str) - 1 + } + + for left < right { + str[left], str[right] = str[right], str[left] + left++ + right-- + } + } + + return string(str) +} diff --git a/Week 08/id_733/LeetCode_557_733.go b/Week 08/id_733/LeetCode_557_733.go new file mode 100644 index 000000000..b06438b96 --- /dev/null +++ b/Week 08/id_733/LeetCode_557_733.go @@ -0,0 +1,17 @@ +func reverseWords3(s string) string { + str := []rune(s) + for i := 0; i < len(s); i++ { + left := i + for i < len(s) && str[i] != ' ' { + i++ + } + + for right := i - 1; left < right; { + str[left], str[right] = str[right], str[left] + left++ + right-- + } + } + + return string(str) +} diff --git a/Week 08/id_733/LeetCode_5_733.go b/Week 08/id_733/LeetCode_5_733.go new file mode 100644 index 000000000..f43d8cce1 --- /dev/null +++ b/Week 08/id_733/LeetCode_5_733.go @@ -0,0 +1,22 @@ +func longestPalindrome(s string) string { + dp := make([][]bool, len(s)) + for i := range dp { + dp[i] = make([]bool, len(s)) + } + + start, length := 0, 0 + for i := len(s) - 1; i >= 0; i-- { + for j := i; j < len(s); j++ { + dp[i][j] = s[i] == s[j] && (j-i < 3 || dp[i+1][j-1]) + if dp[i][j] { + l := j - i + 1 + if l > length { + length = l + start = i + } + } + } + } + + return s[start : start+length] +} diff --git a/Week 08/id_733/LeetCode_680_733.go b/Week 08/id_733/LeetCode_680_733.go new file mode 100644 index 000000000..f7f030dfe --- /dev/null +++ b/Week 08/id_733/LeetCode_680_733.go @@ -0,0 +1,26 @@ +func validPalindrome(s string) bool { + left, right := 0, len(s)-1 + for left < right { + if s[left] == s[right] { + left++ + right-- + } else { + return isPalindrome(s, left+1, right) || isPalindrome(s, left, right-1) + } + } + + return true +} + +func isPalindrome(s string, left, right int) bool { + for left < right { + if s[left] != s[right] { + return false + } + + left++ + right-- + } + + return true +} diff --git a/Week 08/id_733/LeetCode_818_733.go b/Week 08/id_733/LeetCode_818_733.go new file mode 100644 index 000000000..130d11d88 --- /dev/null +++ b/Week 08/id_733/LeetCode_818_733.go @@ -0,0 +1,36 @@ +const IntMax = int(^uint(0) >> 1) + +func racecar(target int) int { + dp := make([]int, target+1) + for i := 1; i <= target; i++ { + dp[i] = IntMax + j, cnt := 1, 1 + for j < i { + rev, revCnt := 0, 0 + for rev < j { + dp[i] = min(dp[i], cnt+revCnt+2+dp[i-(j-rev)]) + revCnt++ + rev = 1<= 0; j-- { + if matrix[i][j] == '1' { + right[j] = min(right[j], curRight) + } else { + curRight = j + right[j] = n + } + } + + for j, curLeft := 0, 0; j < n; j++ { + if matrix[i][j] == '1' { + left[j] = max(left[j], curLeft) + height[j]++ + res = max(res, (right[j]-left[j])*height[j]) + } else { + curLeft = j + 1 + left[j] = 0 + height[j] = 0 + } + } + } + + return res +} + +func max(x, y int) int { + if x > y { + return x + } + + return y +} + +func min(x, y int) int { + if x < y { + return x + } + + return y +} diff --git a/Week 08/id_733/LeetCode_8_733.go b/Week 08/id_733/LeetCode_8_733.go new file mode 100644 index 000000000..d2b1ea53a --- /dev/null +++ b/Week 08/id_733/LeetCode_8_733.go @@ -0,0 +1,41 @@ +func myAtoi(str string) int { + i, neg := 0, false + for ; i < len(str); i++ { + if str[i] == ' ' { + continue + } + + if str[i] == '-' { + neg = true + i++ + } else if str[i] == '+' { + i++ + } + + break + } + + var num int32 + for ; i < len(str); i++ { + digit := int32(str[i]) - '0' + if digit < 0 || digit > 9 { + break + } + + if num > math.MaxInt32/10 || (num == math.MaxInt32/10 && digit > math.MaxInt32%10) { + if neg { + return math.MinInt32 + } + + return math.MaxInt32 + } + + num = num*10 + digit + } + + if neg { + num *= -1 + } + + return int(num) +} diff --git a/Week 08/id_733/LeetCode_917_733.go b/Week 08/id_733/LeetCode_917_733.go new file mode 100644 index 000000000..1632c4f7f --- /dev/null +++ b/Week 08/id_733/LeetCode_917_733.go @@ -0,0 +1,20 @@ +func reverseOnlyLetters(S string) string { + str := []rune(S) + for i, j := 0, len(str)-1; i < j; { + if !unicode.IsLetter(str[i]) { + i++ + continue + } + + if !unicode.IsLetter(str[j]) { + j-- + continue + } + + str[i], str[j] = str[j], str[i] + i++ + j-- + } + + return string(str) +} diff --git a/Week 08/id_733/LeetCode_91_733.go b/Week 08/id_733/LeetCode_91_733.go new file mode 100644 index 000000000..5183a74dd --- /dev/null +++ b/Week 08/id_733/LeetCode_91_733.go @@ -0,0 +1,21 @@ +func numDecodings(s string) int { + prev1, prev2 := 1, 1 + for i := 0; i < len(s); i++ { + cur := 0 + if s[i] > '0' { + cur += prev1 + } + + if i > 0 && (s[i-1] == '1' || (s[i-1] == '2' && s[i] <= '6')) { + cur += prev2 + } + + if cur == 0 { + return 0 + } + + prev1, prev2 = cur, prev1 + } + + return prev1 +} diff --git a/Week 08/id_733/NOTE.md b/Week 08/id_733/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_733/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_738/LeetCode_1143_738.py b/Week 08/id_738/LeetCode_1143_738.py new file mode 100644 index 000000000..aa5a9fea4 --- /dev/null +++ b/Week 08/id_738/LeetCode_1143_738.py @@ -0,0 +1,42 @@ +class Solution(object): + def longestCommonSubsequence(self, text1, text2): + """ + 最长公共子序列:https://leetcode-cn.com/problems/longest-common-subsequence/ + + :type text1: str + :type text2: str + :rtype: int + """ + # DP + m = len(text1) + n = len(text2) + dp = [[0] * (n + 1) for _ in range(m + 1)] + for i in range(1, m + 1): + for j in range(1, n + 1): + if text1[i - 1] == text2[j - 1]: + dp[i][j] = dp[i - 1][j - 1] + 1 + else: + dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]) + return dp[-1][-1] + + # 暴力法,提交超时,字符串小的时候没问题 + # 找出text1的所有子序列 + # q = [text1] + # while q: + # l = [] + # while q: + # s = q.pop() + # # 如果是子序列则return + # i = 0 + # for j in range(len(text2)): + # if s[i] == text2[j]: + # i += 1 + # if i == len(s): + # return i + # l.append(s) + # for s in l: + # for i in range(len(s)): + # new_s = s[:i] + s[i + 1:] + # if new_s and new_s not in q: + # q.append(new_s) + # return 0 \ No newline at end of file diff --git a/Week 08/id_738/LeetCode_115_738.py b/Week 08/id_738/LeetCode_115_738.py new file mode 100644 index 000000000..07805613d --- /dev/null +++ b/Week 08/id_738/LeetCode_115_738.py @@ -0,0 +1,63 @@ +class Solution(object): + def numDistinct(self, s, t): + """ + 不同的子序列:https://leetcode-cn.com/problems/distinct-subsequences/ + + :type s: str + :type t: str + :rtype: int + """ + # 如何计算字符串S中出现T的个数?(很重要) + # 如果S[0] == T[0]:那么等价于计算"S[1:],T[1:]的不同子序列问题" + "S[1:],T[:]的不同子序列问题"(重复性) + # 如果S[0] != T[0]:那么等价于计算"S[1:],T[:]的不同子序列问题"(重复性) + # 基于上述理论我们: + # 定义状态dp[i][j]:表示S[i:]和T[j:]的不同子序列数量 + # 那么状态转移方程: + # if S[i] == T[j]: dp[i][j] = dp[i + 1][j + 1] + dp[i + 1][j] + # if S[i] != T[j]: dp[i][j] = dp[i + 1][j] + # 可以看到上面递推关系是从后到前的,直到求解到dp[0][0],问题得解 + # 开始写代码: + # 由于上面涉及到i+1,j+1的长度,所以dp定义为S和T字符串长度+1,这样dp才对i+1,j+1才不会越界 + dp = [[0] * (len(t) + 1) for _ in range(len(s) + 1)] + # 思考:dp[:][-1],dp[-1][:]的值是什么? + # 由于dp[i][j]:S[i:]和T[j:]的不同子序列 + # 所以当i==len(S),dp表示S[len(S):]字符串(即"")出现T的子序列的个数,很明显值为0 + # 当 j==len(T), dp表示S字符串出现T[len(T):]字符串(即"")的子序列的个数,很明显为1 + # 当 i==len(S) and j==len(T)的时候,明显为1 + # 所以初始化: + for i in range(len(s) + 1): + dp[i][-1] = 1 + # 开始填充二维数组dp(时刻记住,i为s的下标,j为t的下标) + # 从上面可以看到只有当计算出dp[i + 1][j + 1]和dp[i + 1][j]后,才能计算dp[i][j] + # 所以我们循环要"从后往前",而循环顺序要"先j后i"(还是迷糊直接画表格秒懂)(时刻记住,i为s的下标,j为t的下标) + # 附上一个例子的dp最终数值直接看就明白循环顺序: + # 输入S:"rabbbit" + # 输入T:"rabbit" + # r a b b i t "" + # r [3, 3, 3, 3, 1, 1, 1] --> i = 0 + # a [0, 3, 3, 3, 1, 1, 1] --> i = 1 + # b [0, 0, 3, 3, 1, 1, 1] --> i = 2 + # b [0, 0, 1, 2, 1, 1, 1] --> i = 3 + # b [0, 0, 0, 1, 1, 1, 1] --> i = 4 + # i [0, 0, 0, 0, 1, 1, 1] --> i = 5 + # t [0, 0, 0, 0, 0, 1, 1] --> i = 6 + # ""[0, 0, 0, 0, 0, 0, 1] --> i = 7 + # | | | | | | | + # j= 0 1 2 3 4 5 6 + for j in range(len(t))[::-1]: + for i in range(len(s))[::-1]: + if t[j] == s[i]: + dp[i][j] = dp[i + 1][j + 1] + dp[i + 1][j] + else: + dp[i][j] = dp[i + 1][j] + return dp[0][0] + + + + + + + + + + diff --git a/Week 08/id_738/LeetCode_300_738.py b/Week 08/id_738/LeetCode_300_738.py new file mode 100644 index 000000000..d786ccbfc --- /dev/null +++ b/Week 08/id_738/LeetCode_300_738.py @@ -0,0 +1,19 @@ +class Solution(object): + def lengthOfLIS(self, nums): + """ + 最长上升子序列: https://leetcode-cn.com/problems/longest-increasing-subsequence/submissions/ + + :type nums: List[int] + :rtype: int + """ + # 定义dp[i]为 对于每个 k = 0..i - 1,nums[0..k]中的最长子序列包含上nums[i]也成为最长子序列的大小 + # 而最终的最长子序列就是dp[i]中的最大值 + if not nums: + return 0 + dp = [1] * len(nums) + for i in range(0, len(nums)): + for k in range(0, i): + if nums[i] > nums[k]: + dp[i] = max(dp[i], dp[k] + 1) + return max(dp) + diff --git a/Week 08/id_738/LeetCode_32_738.py b/Week 08/id_738/LeetCode_32_738.py new file mode 100644 index 000000000..9a2aa364e --- /dev/null +++ b/Week 08/id_738/LeetCode_32_738.py @@ -0,0 +1,27 @@ +class Solution(object): + def longestValidParentheses(self, s): + """ + :type s: str + :rtype: int + """ + # 定义dp[i]为s[0...i],如果是连续有效括号,则为括号数,如果不是连续有效括号则为0 + # 那么有如下规律: + # 1. s[i]为(,则可以忽略s[i],因为(不可能与之前成为有效括号对,但是可能与后面构成连续有效括号串,从而导致连接两个有效括号串的效果 + # 2. s[i]为),则如果s[i - 1]为(,则dp[i] = dp[i - 2] + 2 + # 3. s[i]为),则如果s[i - 1]为),则当s[i-1]之前如果有(也可以没有)连续有效括号,并且在s[i-1]之前的连续有效括号再前一个位置的字符必须是(,才能使s[i]融入连续有效括号串中。否则s[i]必然不能与前面的字符串构成连续有效括号串 + dp = [0] * len(s) + res = 0 + for i in range(1, len(s)): + if s[i] == ")": + if s[i - 1] == "(": + dp[i] = dp[i - 2] + 2 + elif s[i - 1] == ")" and i - dp[i - 1] - 1 >=0 and s[i - dp[i - 1] - 1] == "(": + dp[i] = dp[i - 1] + 2 + dp[i - dp[i - 1] - 1 - 1] + if res < dp[i]: + res = dp[i] + return res + + + + + diff --git a/Week 08/id_738/LeetCode_5_738.py b/Week 08/id_738/LeetCode_5_738.py new file mode 100644 index 000000000..d72f8dff3 --- /dev/null +++ b/Week 08/id_738/LeetCode_5_738.py @@ -0,0 +1,25 @@ +class Solution(object): + def longestPalindrome(self, s): + """ + 最长回文子串:https://leetcode-cn.com/problems/longest-palindromic-substring/ + + :type s: str + :rtype: str + """ + # 动态规划: + # 这里只有一个字符串,不能定义一个二维dp状态,分别为一个字符串 + # 对于自身和自身比较的字符串,定义dp[i][j]表示s[i:j + 1]的状态,这里的状态就是是否是回文串:true, false + # dp方程:s[i: j + 1]是回文串的条件就是: + # s[i] == s[j]并且s[i + 1, j]也是回文串 + # dp[i][j] = dp[i + 1][j - 1] && s[i] == s[j] + length = len(s) + dp = [[False] * length for _ in range(length)] + res = "" + for i in range(length)[::-1]: + for j in range(i, length): + dp[i][j] = (j - i <= 1 or dp[i + 1][j - 1]) and s[i] == s[j] + res = s[i: j + 1] if dp[i][j] and len(res) < (j - i + 1) else res + return res + + + diff --git a/Week 08/id_738/LeetCode_72_738.py b/Week 08/id_738/LeetCode_72_738.py new file mode 100644 index 000000000..18cb46d54 --- /dev/null +++ b/Week 08/id_738/LeetCode_72_738.py @@ -0,0 +1,32 @@ +class Solution(object): + def minDistance(self, word1, word2): + """ + 编辑距离:https://leetcode-cn.com/problems/edit-distance/ + + :type word1: str + :type word2: str + :rtype: int + """ + # 定义dp[i][j]为word1[0...i]和word2[0...j]的最小变换距离 + # dp方程有如下情形: + # 解释,如果两个单词最后一个字母相同,那么直接排除这2个字母,必然保证最短编辑距离不变 + # 如果最后一个字母不同,那么变成距离必定是word1删掉i或者word2删掉j或者同时删掉i,j形成的子串的最短编程距离+1 + # 1. if word1[i] == word2[j]: dp[i][j] = dp[i - 1][j - 1] + # 2. if word1[i] != word2[j]: dp[i][j] = min(dp[i - 1][j - 1] + 1, dp[i - 1][j] + 1, dp[i][j - 1] + 1) + dp = [[i] * (len(word1) + 1) for i in range(len(word2) + 1)] + for i in range(len(word1) + 1)[::-1]: + dp[0][i] = i + for i in range(1, len(word2) + 1): + for j in range(1, len(word1) + 1): + if word2[i - 1] == word1[j - 1]: + dp[i][j] = dp[i - 1][j - 1] + else: + dp[i][j] = min(dp[i - 1][j - 1] + 1, dp[i - 1][j] + 1, dp[i][j - 1] + 1) + return dp[-1][-1] + + + + + + + diff --git a/Week 08/id_738/LeetCode_746_738.py b/Week 08/id_738/LeetCode_746_738.py new file mode 100644 index 000000000..61aab60ff --- /dev/null +++ b/Week 08/id_738/LeetCode_746_738.py @@ -0,0 +1,28 @@ +class Solution(object): + def minCostClimbingStairs(self, cost): + """ + 使用最小花费爬楼梯:https://leetcode-cn.com/problems/min-cost-climbing-stairs/ + + :type cost: List[int] + :rtype: int + """ + # dp表示到这级楼梯需要耗费的体力 + dp = [0 for _ in range(len(cost) + 1)] + # 由于开始可以站在第0或者第1级楼梯,所以初始化为0(不需要走) + dp[0] = dp[1] = 0 + for i in range(2, len(cost) + 1): + dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]) + return dp[-1] + + +################################################ + # 空间压缩 + # dp表示到这级楼梯需要耗费的体力 + dp = [0 for _ in range(len(cost) + 1)] + # 由于开始可以站在第0或者第1级楼梯,所以初始化为0(不需要走) + a = b = 0 + for i in range(2, len(cost) + 1): + a, b = b, min(b + cost[i - 1], a + cost[i - 2]) + return b + + diff --git a/Week 08/id_738/LeetCode_91_738.py b/Week 08/id_738/LeetCode_91_738.py new file mode 100644 index 000000000..c8a21e273 --- /dev/null +++ b/Week 08/id_738/LeetCode_91_738.py @@ -0,0 +1,26 @@ +class Solution(object): + def numDecodings(self, s): + """ + 解码方法:https://leetcode-cn.com/problems/decode-ways/ + + :type s: str + :rtype: int + """ + if s[0] == "0": + return 0 + if len(s) == 1: + return 1 + a, b = 1, 0 + if s[1] != "0": b += 1 + if s[0] == "1" or (s[0] == "2" and "0" <= s[1] <= "6"): + b += 1 + for i in range(2, len(s)): + temp = 0 + if s[i] != "0": + temp = b + if s[i - 1] == "1" or (s[i - 1] == "2" and "0" <= s[i] <= "6"): + temp += a + a, b = b, temp + return b + + diff --git a/Week 08/id_738/NOTE.md b/Week 08/id_738/NOTE.md new file mode 100755 index 000000000..1ec343feb --- /dev/null +++ b/Week 08/id_738/NOTE.md @@ -0,0 +1,144 @@ +# 第八周学习总结 +## 动态规划、状态转移方程串讲 +- 一定具有重复性,本质是寻找重复性 + - 1. 将一个问题分解成多个简单子问题 + - 2. 分治 + 最优子结构 + - 3. 顺推形式:动态递推 +- DP递推模板 +```java +function DP(): + // 第一步复杂的地方:如何将现实问题定义为一个N维数组? + dp = [][] # 二维情况 + for i = 0..M { + for j = 0..N { + // 第二步复杂的地方:这个动态转移方程要怎么写? + dp[i][j] = _Function(dp[i’][j’]...) + } + } + return dp[M][N]; +``` + +- 重要题目,一个方法团灭全部股票买卖问题 + - [股票买卖](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/) + +## 高级动态规划 +- 复杂度来源 + - 1. 状态拥有更多维度(二维、三维、或者更多甚至需要压缩) + - 2. 状态方程更加复杂 +- 本质:内功、逻辑思维、数学 +- 国际编程比赛到最后都是动态规划压轴 + +## 字符串基础知识 +- 可变与不可变 + - 不可变字符串 + - Python和Java的String是不可变的。 +- 字符串比较 + - Java的常量池,equal方法 + +## 高级字符串算法 +- 面试很多,实际算法也经常会用的 + - 动态规划和字符串相结合 + - 最长子串、子序列 + - 两个字符串的问题,建立二维dp状态,两个字符串长度 + 1 各为一维 + - [编辑距离](https://leetcode-cn.com/problems/edit-distance/) + - [最长公共子序列](https://leetcode-cn.com/problems/longest-common-subsequence/) + - 最长子串(连续的子串, LeetCode没有题目) + - dp[i][j] = dp[i-1][j-1] + 1 if s1[i-1] == s2[j-1] else dp[i][j] = 0 + - [最长回文子串](https://leetcode-cn.com/problems/longest-palindromic-substring/) + - 暴力O(n^3) + - 中间向两边扩张O(n^2) + - 动态规划 + - [不同子序列](https://leetcode-cn.com/problems/distinct-subsequences/) + +## 字符串匹配算法 +- 暴力法 + - 代码 +```python +def forceSearch(txt, par): + m = len(txt) + n = len(par) + for i in range(m - n): + j = 0 + while j < n: + if txt[i + j] != par[j]: + break + j += 1 + if j == n: + return i + # 更加聪明? + # 1. 预先判断 hash(txt.substring(i, M)) == hash(pat) + # 2. KMP + return -1 +``` + +- Rabin-Karp算法 + - 基于暴力法,用子串的hash值进行判断子串是否可能一样,如果可能一样再用逐个比较字符串 + - 1. 假设子串长度为M(pat),目标字符串的长度为N(txt) + - 2. 计算子串的hash值hast_pat + - 3. 计算目标字符串txt中每个长度为M的子串的hash值(共需要计算N-M+1次)(注意,如果用系统的hash函数,时间复杂度还是高,因为要逐个字符串弄出来计算hash值,如何找hash函数?) + - 4. 比较hash值:如果hash值不相同,字符串必然不匹配;如果hash值相同,则进一步比较子字符串的每一位 + - Rabin-Karp 代码示例 +```java +public final static int D = 256; // 256进制 +public final static int Q = 9997; // 最好是一个素数 +static int RabinKarpSerach(String txt, String pat) { + int M = pat.length(); + int N = txt.length(); + int i, j; + int patHash = 0, txtHash = 0; + for (i = 0; i < M; i++) { + // 模上素数Q,防止数值过大溢出 + patHash = (D * patHash + pat.charAt(i)) % Q; + txtHash = (D * txtHash + txt.charAt(i)) % Q; + } + int highestPow = 1; + // 计算出txtHash最高位字符的hash的权重:256的M-1次方 (记得每位都要模Q) + for (i = 0; i < M - 1; i++) + highestPow = (highestPow * D) % Q; + + for (i = 0; i <= N - M; i++) { // 枚举起点 + if (patHash == txtHash) { + for (j = 0; j < M; j++) { + if (txt.charAt(i + j) != pat.charAt(j)) + break; + } + if (j == M) + return i; + } + // 利用上一个txtHash计算下一个txtHash: + // 逻辑是:将最高位(最左边)字符移除(即减去该位字符的值),然后再加入最低位(活动窗口下一位)的字符值,就是新的txtHash值 + if (i < N - M) { + txtHash = (D * (txtHash - txt.charAt(i) * highestPow) + txt.charAt(i + M)) % Q; + // txtHash可能为负数,如果为负数,矫正为正数即可,这不影响Hash的效果,可以容忍 + if (txtHash < 0) + txtHash += Q; + } + } + return -1; +} +``` + +- KMP算法 + - 当子串与目标字符串不匹配时,其实你已经知道了前面已经匹配为成功那一部分的字符(包括子串与目标字符串)。 + - 以阮一峰的文章为例,当空格与D不匹配时,你其实已经知道前面六个字符是”ABCDAB”。KMP算法的想法是:设法利用这个已知信息,不要把”搜索位置”移回已经比较过的位置,继续把它向后移,这样就提高了效率 + - [KMP 字符串匹配算法视频](https://www.bilibili.com/video/av11866460?from=search&seid=17425875345653862171) + - [字符串匹配的 KMP 算法](http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html) +- 课后了解: + - [Boyer-Moore算法](http://xn--https-ni33a//www.ruanyifeng.com/blog/2013/05/boyer-moore_string_search_algorithm.html) + - [Sunday算法](https://blog.csdn.net/u012505432/article/details/52210975) + + + + + + + + + + + + + + + + diff --git a/Week 08/id_743/NOTE.md b/Week 08/id_743/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_743/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_748/NOTE.md b/Week 08/id_748/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_748/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_753/NOTE.md b/Week 08/id_753/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_753/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_758/NOTE.md b/Week 08/id_758/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_758/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_763/NOTE.md b/Week 08/id_763/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_763/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_768/NOTE.md b/Week 08/id_768/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_768/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_773/NOTE.md b/Week 08/id_773/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_773/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_778/NOTE.md b/Week 08/id_778/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_778/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_783/NOTE.md b/Week 08/id_783/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_783/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_788/NOTE.md b/Week 08/id_788/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_788/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_793/NOTE.md b/Week 08/id_793/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_793/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git a/Week 08/id_798/NOTE.md b/Week 08/id_798/NOTE.md new file mode 100755 index 000000000..a6321d6e2 --- /dev/null +++ b/Week 08/id_798/NOTE.md @@ -0,0 +1,4 @@ +# NOTE + + + diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_003/\346\225\260\346\215\256\347\273\223\346\236\204.png" "b/Week \351\242\204\344\271\240\345\221\250/id_003/\346\225\260\346\215\256\347\273\223\346\236\204.png" new file mode 100644 index 000000000..eb979c1ca Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_003/\346\225\260\346\215\256\347\273\223\346\236\204.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_003/\347\256\227\346\263\225.png" "b/Week \351\242\204\344\271\240\345\221\250/id_003/\347\256\227\346\263\225.png" new file mode 100644 index 000000000..7db2d4dbf Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_003/\347\256\227\346\263\225.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_008/\350\204\221\345\233\276.jpg" "b/Week \351\242\204\344\271\240\345\221\250/id_008/\350\204\221\345\233\276.jpg" new file mode 100644 index 000000000..cce515604 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_008/\350\204\221\345\233\276.jpg" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_028/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_028/NOTE.md" index a6321d6e2..b8c447245 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_028/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_028/NOTE.md" @@ -1,4 +1,99 @@ # NOTE +# 预习周 第一课 +### 精通一个领域 +#### Chunk it up 切碎知识点 +>庖丁解牛 +>脉络连接 +> +>人脑不适合记忆和理解孤立的知识,一定要把它弄成一个脑图 +> +>每个部分单独拎出来练习,经过很多次不断得反复练习和刻意练习,可以达到顶尖水平 + +>数据结构: +> +>一维 +> +>基础:数组、链表 +> +>高级:栈、队列、双端队列、集合、映射等 +> +>二维 +> +>基础:树、图 +> +>高级:二叉搜索树、堆、并查集、字典树 +> +>特殊 +> +>位运算、布隆过滤器 +> +>LRU Cache +> + +>算法: +> +>if-else,switch +> +>for,while loop +> +>递归 +> +>搜索: +> +>深度优先搜索、广度优先搜索、A* +> +>动态规划 +> +>二分查找 +> +>贪心算法 +> +>数学、几何 + +>化繁为简:找到重复单元 + +#### Delibrate Practise 刻意练习 +>基本功是区别业余和职业转手的根本 +> +>五遍刷题法 +> +>练习弱项 +> +#### Feedback 反馈 +>即时反馈 +> +>主动性反馈(自己去找):高手代码 第一视角直播 +> +>被动式反馈:code review、教练打给你看 + +### 切题四件套 +>1、多看题目,确保自己理解正确 +> +>2、想所有可能的解法 +> +>所有可能的解法过一下,比较时间、空间复杂度 +> +>找出最优解法 +> +>3、写代码 +> +>4、例举测试样例 + +### 五遍刷题法 +>1、5分钟 读题+思考 => 基础弱可以10~15分钟 +>实在没有思路 => 直接看解法,比较多种解法的优劣性 +> +>2、自己写代码 每种解法都要比较 +> +>3、过一天后,再重新做一遍前一天做过的题目 +> +>4、过一周后,反过来练习 +> +>5、面试前一周恢复性训练 + + + + + - diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_028/\350\204\221\345\233\276_028_\346\225\260\346\215\256\347\273\223\346\236\204.png" "b/Week \351\242\204\344\271\240\345\221\250/id_028/\350\204\221\345\233\276_028_\346\225\260\346\215\256\347\273\223\346\236\204.png" new file mode 100644 index 000000000..3f776e9d6 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_028/\350\204\221\345\233\276_028_\346\225\260\346\215\256\347\273\223\346\236\204.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_028/\350\204\221\345\233\276_028_\347\256\227\346\263\225.png" "b/Week \351\242\204\344\271\240\345\221\250/id_028/\350\204\221\345\233\276_028_\347\256\227\346\263\225.png" new file mode 100644 index 000000000..f7c259225 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_028/\350\204\221\345\233\276_028_\347\256\227\346\263\225.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_033/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_033/NOTE.md" index a6321d6e2..62b6f4e4e 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_033/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_033/NOTE.md" @@ -1,4 +1,6 @@ -# NOTE +# 预习周作业如下 + +[数据与算法结构脑图](http://naotu.baidu.com/file/dc954a45daef91a3bda643155a715989?token=5ac3fad0701211e5) diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_048/048_Week08.md" "b/Week \351\242\204\344\271\240\345\221\250/id_048/048_Week08.md" new file mode 100644 index 000000000..8086cd490 --- /dev/null +++ "b/Week \351\242\204\344\271\240\345\221\250/id_048/048_Week08.md" @@ -0,0 +1,51 @@ +动态规划(Dynamic programming,简称DP) +一. 思想与性质: +动态规划最重要的是掌握他的思想,动态规划的核心思想是把原问题分解成子问题进行求解,也就是分治的思想。 +二. 过程: +动态规划问题,大致可以通过以下四部进行解决。 + +1.划分状态,即划分子问题 +2.状态表示,即如何让计算机理解子问题。 +3.状态转移,即父问题是如何由子问题推导出来的。 +4.确定边界,确定初始状态是什么?最小的子问题?最终状态又是什么。 + +三.经典模型: + +1.线性模型 + +最经典的问题就是斐波那楔数列的问题,每个数的值都是一个状态,可以用F[i]表示表示第i个数的值是多少。每个数都是由F[i-1]+F[i-2]转移而来。 + +另外一个经典的问题就是最长上升自序列(LIS),有一串序列,要求找出它的一串子序列,这串子序列可以不连续,但必须满足它是严格的单调递増的且为最长的。把这个长度输出。示例:1 7 3 5 9 4 8 结果为4。 + +我们非常容易表示他的状态,我们用f[i]表示以第i个数结尾的,最大的上升自序列是多少?那么它是怎么转移的呢?非常容易想到肯定是从左边的的数转移而来,能转移的数满足什么条件呢?肯定是比a[i]更小的。 + +线性模式还可以拓展成二维问题,例如背包问题,用f[i][j]表示前i个物品,凑成大小为j的背包,最大的价值是多少。 + +这类问题非常的多,但是思路都是这样,无非就是从左往右,从上到下,从低维到高维进行转移。 + +2.区间模型 + +对于每个问题,都是由子区间推导过来的,我们称之为区间模型,下面是一个例子。 + +我们有一个连续的序列,每个序列上面都是一个数字c[i],每次我们都能够消灭一个连续的回文子序列,消灭之后左右会合并,成为一个新序列,问最少需要多少次才能够把整个序列消灭掉。回文就是从左到有从右到左读到的序列都是一样的。题目比较抽象,我们通过一些例子来说明这个问题吧?例如一开始的序列是1 4 4 2 3 2 1,那么我们最少需要2次,先消灭掉4 4 , 然后再消灭调1 2 3 2 1.第二个例子是 1 2 3 4 5 5 3 1,我们先消灭掉2 然后再消灭掉4, 最后消灭 1 3 5 5 3 1, 需要3次。 + +我们经常用f[i][j]来表示消灭i,j区间需要的代价,文末有链接详细叙述这个问题,大家感兴趣的可以看一看。 + +3.树状模型 + +我们在数据结构树上面进行最求最优解、最大值等问题,上述我们讲的这个绩效考核就是一个树状模型,具体不再累叙。 + +四.实现的套路: + +我们实现动态规划算法,常用的是2个实现套路,一个是自底向上,另外一个是自顶向下。无论是何种方式,我们都要明确动态规划的过程,把状态表示、状态转移、边界都考虑好。 + +1.自底向上,简单来说就是根据初始状态,逐步推导到最终状态,而这个转移的过程,必定是一个拓扑序。如何理解这个拓扑序问题呢,甲总监下面有X,Y,Z两个小组,甲总监不会一拿到X组最优秀的三个人,就立马去跟A经理汇报,而是要等到Y,Z小组也选出来之后,也就是自己下面所有子问题都解决了,才会继续向汇报。如果推导的过程不是一个拓扑序,那么要么得到错误的结果,要么算法就要退化。 + +自底向上一般用来解决什么问题呢?那就是可以轻松确定拓扑序的问题,例如线性模型,都是从左往右进行转移,区间模型,一般都是从小区间推导到大区间。自底向上的一个经典实现是斐波那楔数列的递推实现,即F[i] = F[i - 1] + F[i - 2] 。 + +2.自顶向下,也就是从最终状态出发,如果遇到一个子问题还未求解,那么就先求解子问题。如果子问题已经求解,那么直接使用子问题的解,所以自顶向下动态规划又有一个形象生动的名字,叫做记忆化搜索,一般我们采用递归的方式进行求解。 + + +自顶向下,我们一般用在树上面,因为我们根据父亲结点,很容易找到所有的子问题,也就是所有的子结点,而自底向上的话,我们要去统计这个结点的所有兄弟结点是否已经实现。会稍微复杂一点,而且比较难理解。 + +无论是自顶向下还是自底向上,都只是代码实现的一种套路,随便你采用哪一个,都是可以解的,只是看你的选择而已。 diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_048/LeetCode_115_048.java" "b/Week \351\242\204\344\271\240\345\221\250/id_048/LeetCode_115_048.java" new file mode 100644 index 000000000..ca2031adc --- /dev/null +++ "b/Week \351\242\204\344\271\240\345\221\250/id_048/LeetCode_115_048.java" @@ -0,0 +1,61 @@ +package com.leetcode.week08; + +/** + * Created by tim on 2019/12/7. + * 115 不同的子序列 + * 给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。 + + 一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是) + + 示例 1: + + 输入: S = "rabbbit", T = "rabbit" + 输出: 3 + 解释: + + 如下图所示, 有 3 种可以从 S 中得到 "rabbit" 的方案。 + (上箭头符号 ^ 表示选取的字母) + + rabbbit + ^^^^ ^^ + rabbbit + ^^ ^^^^ + rabbbit + ^^^ ^^^ + 示例 2: + + 输入: S = "babgbag", T = "bag" + 输出: 5 + 解释: + + 如下图所示, 有 5 种可以从 S 中得到 "bag" 的方案。 + (上箭头符号 ^ 表示选取的字母) + + babgbag + ^^ ^ + babgbag + ^^ ^ + babgbag + ^ ^^ + babgbag + ^ ^^ + babgbag + ^^^ + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/distinct-subsequences + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_115_048 { + public int numDistinct(String s, String t) { + int[][] dp = new int[t.length() + 1][s.length() + 1]; + for (int j = 0; j < s.length() + 1; j++) dp[0][j] = 1; + for (int i = 1; i < t.length() + 1; i++) { + for (int j = 1; j < s.length() + 1; j++) { + if (t.charAt(i - 1) == s.charAt(j - 1)) dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1]; + else dp[i][j] = dp[i][j - 1]; + } + } + return dp[t.length()][s.length()]; + } +} diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_048/LeetCode_32_048.java" "b/Week \351\242\204\344\271\240\345\221\250/id_048/LeetCode_32_048.java" new file mode 100644 index 000000000..ce42b1b74 --- /dev/null +++ "b/Week \351\242\204\344\271\240\345\221\250/id_048/LeetCode_32_048.java" @@ -0,0 +1,39 @@ +package com.leetcode.week08; + +/** + * Created by tim on 2019/12/7. + * 32. 最长有效括号 + * 给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。 + + 示例 1: + + 输入: "(()" + 输出: 2 + 解释: 最长有效括号子串为 "()" + 示例 2: + + 输入: ")()())" + 输出: 4 + 解释: 最长有效括号子串为 "()()" + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/longest-valid-parentheses + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_32_048 { + public int longestValidParentheses(String s) { + int maxans = 0; + int dp[] = new int[s.length()]; + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) == ')') { + if (s.charAt(i - 1) == '(') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + maxans = Math.max(maxans, dp[i]); + } + } + return maxans; + } +} diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_048/LeetCode_44_048.java" "b/Week \351\242\204\344\271\240\345\221\250/id_048/LeetCode_44_048.java" new file mode 100644 index 000000000..c71c45fbd --- /dev/null +++ "b/Week \351\242\204\344\271\240\345\221\250/id_048/LeetCode_44_048.java" @@ -0,0 +1,131 @@ +package com.leetcode.week08; + +/** + * Created by tim on 2019/12/7. + * 44. 通配符匹配 + * 给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 '?' 和 '*' 的通配符匹配。 + + '?' 可以匹配任何单个字符。 + '*' 可以匹配任意字符串(包括空字符串)。 + 两个字符串完全匹配才算匹配成功。 + + 说明: + + s 可能为空,且只包含从 a-z 的小写字母。 + p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *。 + 示例 1: + + 输入: + s = "aa" + p = "a" + 输出: false + 解释: "a" 无法匹配 "aa" 整个字符串。 + 示例 2: + + 输入: + s = "aa" + p = "*" + 输出: true + 解释: '*' 可以匹配任意字符串。 + 示例 3: + + 输入: + s = "cb" + p = "?a" + 输出: false + 解释: '?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。 + 示例 4: + + 输入: + s = "adceb" + p = "*a*b" + 输出: true + 解释: 第一个 '*' 可以匹配空字符串, 第二个 '*' 可以匹配字符串 "dce". + 示例 5: + + 输入: + s = "acdcb" + p = "a*c?b" + 输入: false + + 来源:力扣(LeetCode) + 链接:https://leetcode-cn.com/problems/wildcard-matching + 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class LeetCode_44_048 { + public boolean isMatch(String s, String p) { + if (p.equals("*")) + return true; + char[] s_array = s.toCharArray(); + char[] p_array = p.toCharArray(); + int s_index = 0; + int p_index = 0; + boolean result = false; + boolean flag = false; + while (s_index< s_array.length && p_index < p_array.length){ + //处理正常情况 + if (p_array[p_index]!='?'&&p_array[p_index]!='*'){ + if (s_array[s_index]!=p_array[p_index]){ + return false; + } + } + //处理 * 情况 + if (p_array[p_index]=='*'){ + flag = true; + //当有很多个*连在一起的时候,等同于一个* + while (p_index= p_array.length ) + return true; + else { + while (s_index < s_array.length){ + int temp_s_index = s_index; + int temp_p_index = p_index; + //当相等时,下标向前走 + while (s_indexbranch +* For, while loop —>iteration +* 递归recursion(Divide & Conquer, Backtrace) +高级三种: +* 搜索search; 深度优先搜索Depth first search, 广度优先搜索Breadth first search, A*, etc +* 动态规划:Dynamic Programming +* 二分查找 binary Search +* 贪心算法 greedy +* 数学 math,几何 geometry + +最大误区:做LeetCode只做一遍就是最大误区,需要多做几遍,用不同方法。 + +刷题应该用如下方法,简称五毒神掌。 + + + +刷题第一遍 + • 5分钟:读题 + 思考 + • 直接看解法:注意!多解法,比较解法优劣 • 背诵、默写好的解法 +刷题第二遍 + • 马上自己写 —> LeetCode 提交 + • 多种解法比较、体会 —> 优化! +刷题第三遍 + • 过了一天后,再重复做题 + • 不同解法的熟练程度 —> 专项练习 +刷题第四遍 + 一周后,反复回来联系相同题目 +刷题第五遍 + 面试前一周恢复性训练 + + +切题四件套: +Clarification:反复明确题目要求 +Possible solutions + --compare(Time/space) + --optimal(加强) +Coding(开始写代码) +Test cases(测试用例) \ No newline at end of file diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_103/\346\225\260\346\215\256\347\273\223\346\236\204\350\204\221\345\233\276.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_103/\346\225\260\346\215\256\347\273\223\346\236\204\350\204\221\345\233\276.xmind" new file mode 100644 index 000000000..df1e404ac Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_103/\346\225\260\346\215\256\347\273\223\346\236\204\350\204\221\345\233\276.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_103/\347\256\227\346\263\225\350\204\221\345\233\276.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_103/\347\256\227\346\263\225\350\204\221\345\233\276.xmind" new file mode 100644 index 000000000..3edb11551 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_103/\347\256\227\346\263\225\350\204\221\345\233\276.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_118/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_118/NOTE.md" index a6321d6e2..474ba3d8f 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_118/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_118/NOTE.md" @@ -1,4 +1,11 @@ # NOTE - +## Data Structure Mindmap +1. url: [Data Structure](http://v3.processon.com/view/link/5da2b2c7e4b0c9f9da46d579) +2. password: geekbang + +## Algorithm Mindmap + +1. url: [Algorithm](http://v3.processon.com/view/link/5da2b05fe4b0c9f9da46d576) +2. password: geekbang diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_123/3\347\261\273\346\225\260\346\215\256\347\273\223\346\236\204.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_123/3\347\261\273\346\225\260\346\215\256\347\273\223\346\236\204.xmind" new file mode 100644 index 000000000..97d1d0d98 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_123/3\347\261\273\346\225\260\346\215\256\347\273\223\346\236\204.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_123/\347\256\227\346\263\225.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_123/\347\256\227\346\263\225.xmind" new file mode 100644 index 000000000..dca26d9f7 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_123/\347\256\227\346\263\225.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_138/\346\225\260\346\215\256\347\273\223\346\236\204.pdf" "b/Week \351\242\204\344\271\240\345\221\250/id_138/\346\225\260\346\215\256\347\273\223\346\236\204.pdf" new file mode 100644 index 000000000..a5467a149 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_138/\346\225\260\346\215\256\347\273\223\346\236\204.pdf" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_138/\347\256\227\346\263\225.pdf" "b/Week \351\242\204\344\271\240\345\221\250/id_138/\347\256\227\346\263\225.pdf" new file mode 100644 index 000000000..c773fa83a Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_138/\347\256\227\346\263\225.pdf" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_143/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_143/NOTE.md" index a6321d6e2..a67936e47 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_143/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_143/NOTE.md" @@ -1,4 +1,9 @@ -# NOTE +# 预习周体会 - +本周通过入学的摸底考试后,给了我自己一个很大的震惊,考了一个比我年龄还小1的分数。 +总结起来有两方面的原因。一是毕业十几年,概念性的内存都模模糊糊了,问题出在记忆上;二是有一些感觉 +平时也曾接受的东西摆放一起后迷糊了,应该是没做好总结。 +正好借着这个机会,有覃老师,有校长,班长,助教,还有这么多优秀的伙伴相陪。是时候把自己的底子补牢。 +指不定以后教给我的两个宝贝呢!! + 画好图,加深一下记忆,检查一下工具,准备开战啦!小伙伴们,大家准备好了吗? diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_143/\346\225\260\346\215\256\347\273\223\346\236\204.png" "b/Week \351\242\204\344\271\240\345\221\250/id_143/\346\225\260\346\215\256\347\273\223\346\236\204.png" new file mode 100644 index 000000000..ed3954fb5 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_143/\346\225\260\346\215\256\347\273\223\346\236\204.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_143/\347\256\227\346\263\225.png" "b/Week \351\242\204\344\271\240\345\221\250/id_143/\347\256\227\346\263\225.png" new file mode 100644 index 000000000..9f12f8ff2 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_143/\347\256\227\346\263\225.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_153/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_153/NOTE.md" index a6321d6e2..d7caa661d 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_153/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_153/NOTE.md" @@ -1,4 +1,32 @@ # NOTE - +## 目标 +真正理解`数据结构`和`算法`,不追求速度,弄懂一道是一道。 + +## 输出 +1. 学习总结 +2. 时间复杂度、空间复杂度分析 +3. `Java`和`JavaScript`代码各一份 +## 方法 +五遍刷题法 +1. 第一遍 + * 5分钟:读题 + 思考 + * 直接看解法:比较不同解法的优劣,分析时间复杂度 + * 背诵、默写 +2. 第二遍 + * 自己写,不参考任何资料,提交 LeetCode + * 多种解法都写一遍,体会 -> 优化 +3. 第三遍 + * 过了一天后,再重复做题 + * 不同解法的熟练程度 -> 专项练习 +4. 第四遍 + * 过了一周,反复回来练习相同题目 +5. 第五遍 + * 面试前一周恢复性训练 + +## 编程思想 +自顶向下编程 +1. 先抽象,只写出需要用到的方法名,参数等 +2. 利用编辑器自带的代码生成工具,生成函数 +3. 再具体,补全各个方法中的具体细节 diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_153/\346\225\260\346\215\256\347\273\223\346\236\204.png" "b/Week \351\242\204\344\271\240\345\221\250/id_153/\346\225\260\346\215\256\347\273\223\346\236\204.png" new file mode 100644 index 000000000..773e4a944 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_153/\346\225\260\346\215\256\347\273\223\346\236\204.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_153/\347\256\227\346\263\225.png" "b/Week \351\242\204\344\271\240\345\221\250/id_153/\347\256\227\346\263\225.png" new file mode 100644 index 000000000..2a85cc37b Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_153/\347\256\227\346\263\225.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_173/Algorithm.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_173/Algorithm.xmind" new file mode 100644 index 000000000..e98e48cfd Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_173/Algorithm.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_173/DataStructure.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_173/DataStructure.xmind" new file mode 100644 index 000000000..5aff619c6 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_173/DataStructure.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_173/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_173/NOTE.md" index a6321d6e2..60364e3f5 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_173/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_173/NOTE.md" @@ -1,4 +1,3 @@ # NOTE - - +  通过绘制数据结构和算法脑图,不仅让自己回顾了之前所学的知识,也使自己认识到了很多不足,特别是在算法方面,所以在接下来的时间里,要格外注重这一方面的学习,同时对自己已经掌握的知识也要进行不断的复习,加深理解。 diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_198/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_198/NOTE.md" index a6321d6e2..795d51fa3 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_198/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_198/NOTE.md" @@ -1,4 +1,5 @@ # NOTE - +## 参考 +- [Algorithm and its types](https://www.includehelp.com/data-structure-tutorial/algorithm-and-its-types.aspx) diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_198/algorithms.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_198/algorithms.xmind" new file mode 100644 index 000000000..d3c75ac0d Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_198/algorithms.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_198/data-structure.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_198/data-structure.xmind" new file mode 100644 index 000000000..2dd66c17a Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_198/data-structure.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_203/\346\225\260\346\215\256\347\273\223\346\236\204.png" "b/Week \351\242\204\344\271\240\345\221\250/id_203/\346\225\260\346\215\256\347\273\223\346\236\204.png" new file mode 100644 index 000000000..720cc8890 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_203/\346\225\260\346\215\256\347\273\223\346\236\204.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_203/\347\256\227\346\263\225.png" "b/Week \351\242\204\344\271\240\345\221\250/id_203/\347\256\227\346\263\225.png" new file mode 100644 index 000000000..b289595d7 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_203/\347\256\227\346\263\225.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_213/dataStructureAndAlgorithm_XMind/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_213/dataStructureAndAlgorithm_XMind/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" new file mode 100644 index 000000000..6cee75a51 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_213/dataStructureAndAlgorithm_XMind/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_213/dataStructureAndAlgorithm_XMind/\347\256\227\346\263\225.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_213/dataStructureAndAlgorithm_XMind/\347\256\227\346\263\225.xmind" new file mode 100644 index 000000000..d751c84f4 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_213/dataStructureAndAlgorithm_XMind/\347\256\227\346\263\225.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_218/README.md" "b/Week \351\242\204\344\271\240\345\221\250/id_218/README.md" new file mode 100644 index 000000000..ddbe45b6f --- /dev/null +++ "b/Week \351\242\204\344\271\240\345\221\250/id_218/README.md" @@ -0,0 +1,67 @@ +# NOTE +###预习周学习总结: + +一:Chunk it up,切碎知识点 +算法分为: + 1:一维数据结构 + 基础:数组array;链表linked list + 高级:栈stack;队列queue;双端队列deque;集合set;映射map(hash or map)等 + 2:二维数据结构 + 基础:数tree;图graph + 高级:二叉搜索树binary search tree(红黑树、AVL);堆 heap; 并查集disjoint tree;字典树trie等 + 3:特殊数据结构 + 位运算Bitwise;布隆过滤器BloomFilter + LRU cache +算法的方法(所有复杂的算法都是找他们的重复单元是什么。): + 1: IF-ELSE + 2:FOR WHILE 循环 + 3:递归(分支,backtrace) + 4:搜索Search:深度优先搜索Depth first search,广度优先搜索Breadth first search, A*等 + 5:动态规划Dynamic Programming + 6:二分查找 Binary Search(有序的) + 7:贪心 Greedy + 8:数学 Math, 几何Geometry + 注意:在头脑中回忆上面每种算法的思想和代码模板。 + + +二:刻意练习 + 目标:基本功提升。基础动作分解训练,且反复训练。 + 刻意练习 --- 过遍数 (5遍刷题法)练习大家的弱项。找到自己的相关弱点。 + +三:反馈 + 主动的反馈(自己去找) + 高手代码(GitHub,LeetCode等) + 第一视角直播 + 被动的反馈(高手给你指点) + code review + 教练代打 +四:刷题技巧: +切题四件套: + Clarification:面试官,反复沟通,保证对题目的理解是正确的。 + Possible solutions:所有的可能解决问题的方法。 + compare(time/space) + optimal(加强) + Coding 写代码 + Test case 测试样例 + +五毒神掌:至少做5遍 +第一遍: + 1:5分钟:读题 + 思考 <15分钟。 + 2:如果没有思路:直接看解法;注意:多解法,比较解法优劣; + 3:背诵、默写好的解法。 +第二遍: + 自己写代码; + 跑Letcode 看情况,反复修改 +第三遍:在第二遍的24小时之后再做的。 + 重新做一遍前一天做过的题目。 + 对自己不熟悉,非专项的程序,进行专项训练。 +第四遍:一周之后 + 反复练习。 +第五遍:面试前一个星期做恢复型面试。 + + + + + + + diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_218/algorithm.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_218/algorithm.xmind" new file mode 100644 index 000000000..671568cae Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_218/algorithm.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_223/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_223/NOTE.md" index a6321d6e2..f215a1851 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_223/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_223/NOTE.md" @@ -1,4 +1,101 @@ # NOTE - +# 学习方法 +## 拆分知识点 chunk it up + +### 把知识点切碎 + +### 玩游戏举例 + +- 单独的项目,分解出来,一项一项的练习 + +### 数据结构 + +- 一维 + - 基础 + - 数组 + - array + - 链表 + - link list + - 高级 + - 栈stack + - 队列queue + - 双端队列deque + - 集合 map + - 映射 set +- 二维 + - 基础 + - 树 tree + - 图 graph + - 高级 + - 二叉搜索树 binary search tree + - 红黑树 red black tree + - 堆 heap + - 字典树 trie +- 特殊 + - 位运算 Bitwise + - 缓存 LRU Cache + +### 算法 + +- if else +- while loop +- 递归 +- 高级 + - 搜索 + - 深度优先搜索 depth first search + - 广度优先搜索 breadth first search + - 动态规划 Dynamic Programming + - 二分查找 Binary Search + - 贪心 Greedy + - 数学 Math + - 几何 Geometry + +## 刻意练习 + +### 过遍数(五毒神掌) + +### 练习缺陷,弱点 + +### 不舒服,不爽是成长的点 + +### 切题四件套 + +- clarification + - 和面试官沟通题意 +- possible solutions + - 比较时间复杂度和空间复杂度 compare + - 加强练习 optimal +- coding +- test cases + +### 五毒神掌 + +- 5分钟 读题+思考 +- 刷题第一遍 + - 直接看解法 +- 刷题第二遍 + - 马上自己写 leetcode上提交 + - 多种解法比较、体会、优化 +- 刷题第三遍 + - 过了一天后,再重复做题 + - 不同解法熟练程度,专项练习 +- 刷题第四遍 + - 过了一周:反复练习相同的题目 +- 刷题第五遍 + - 面试前一周恢复性训练 + +## 寻求反馈 feedback + +### 即时反馈 + +### 主动型反馈 + +- 高手代码 +- 第一视角直播 + +### 被动式反馈 + +- code review +- 教练看着你打,给你反馈 diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_223/\346\225\260\346\215\256\347\273\223\346\236\204-Self.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_223/\346\225\260\346\215\256\347\273\223\346\236\204-Self.xmind" new file mode 100644 index 000000000..bd41bdb74 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_223/\346\225\260\346\215\256\347\273\223\346\236\204-Self.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_223/\346\225\260\346\215\256\347\273\223\346\236\204.png" "b/Week \351\242\204\344\271\240\345\221\250/id_223/\346\225\260\346\215\256\347\273\223\346\236\204.png" new file mode 100644 index 000000000..88dd88ce5 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_223/\346\225\260\346\215\256\347\273\223\346\236\204.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_223/\347\256\227\346\263\225.png" "b/Week \351\242\204\344\271\240\345\221\250/id_223/\347\256\227\346\263\225.png" new file mode 100644 index 000000000..91e4ca1d0 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_223/\347\256\227\346\263\225.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_223/\347\256\227\346\263\225\350\204\221\345\233\276-Self.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_223/\347\256\227\346\263\225\350\204\221\345\233\276-Self.xmind" new file mode 100644 index 000000000..29e196382 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_223/\347\256\227\346\263\225\350\204\221\345\233\276-Self.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_233/Algorithme_Freemind_233.png" "b/Week \351\242\204\344\271\240\345\221\250/id_233/Algorithme_Freemind_233.png" new file mode 100644 index 000000000..0268ebd7a Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_233/Algorithme_Freemind_233.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_233/DataStructure_Freemind_233.png" "b/Week \351\242\204\344\271\240\345\221\250/id_233/DataStructure_Freemind_233.png" new file mode 100644 index 000000000..f1298ddd4 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_233/DataStructure_Freemind_233.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_243/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_243/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" new file mode 100644 index 000000000..d91cbc1cb Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_243/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_243/\347\256\227\346\263\225.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_243/\347\256\227\346\263\225.xmind" new file mode 100644 index 000000000..a4167130b Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_243/\347\256\227\346\263\225.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_273/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_273/NOTE.md" index a6321d6e2..b37d3ac64 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_273/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_273/NOTE.md" @@ -1,4 +1,28 @@ # NOTE +## 切题四件套 +1. 看清题目 +2. 思考所有可能的解法,比较性能优劣,找出最优实现 +3. code +4. 考虑各种情况进行测试 +## 五遍刷题法 +第一遍:用5min~15min读题并思考,若不会直接看大神的解法,并比较多种解法之间的优劣,理解之后后背、默好的解法 +第二遍:自己写代码,实现多种解法,并尝试优化 +第三遍:隔一天之后再做一遍 +第四遍:一周后再做一遍 +第五遍:面试前的恢复训练 + +## 欲要善其工必先利器 +1. 尽可能采用Googole搜索引擎 +2. 学会使用Microsoft new Terminal +3. 安装LeetCode插件并学会使用 +4. 注意Code Style +5. 学会自顶而下的编程方式 +6. 熟悉编译器的快捷操作 +7. 在学习之后,定期进行总结,并按照自己的理解绘制Xmind + +## 复杂度分析 +1. 自己写程序的时候习惯分析时间复杂度和空间复杂度 +2. 斐波那契实现在面试的时候一定不要采用最捞的递归,因为其复杂度为O(2^n) \ No newline at end of file diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_273/datastructAndAlgorithm_273.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_273/datastructAndAlgorithm_273.xmind" new file mode 100644 index 000000000..a8d9daaeb Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_273/datastructAndAlgorithm_273.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_308/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_308/NOTE.md" index a6321d6e2..ca19f7301 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_308/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_308/NOTE.md" @@ -1,4 +1,118 @@ -# NOTE +作业 + +[数据结构脑图](http://naotu.baidu.com/file/6db9b3f894b01a952786cb0080454edc?token=4593bb918fca7f49) +[算法脑图](http://naotu.baidu.com/file/96ccd6e5b3e2a4e6724cff20cbfd7d97?token=57b2b51543403a71) + +笔记 + +### 数据结构与算法总览 + +#### 如何系统的学习算法和数据结构 + + - 注重预习 + - 课堂中一起思考、回答 + - 按照切题方法完成作业 + +#### 如何精通一个理论 + + - Chunk it up 切碎知识点 + * 庖丁解牛 + * 脉络相连 + - Deliberate Practicing 刻意练习 + - Feedback 反馈 + + #### 数据结构 + + - 一维 + * 基础: 数组 array(String)、链表 linked list + * 高级:栈 stack、队列 queue、双端队列 deque、 集合 Set、映射 map (hash or map),etc + + - 二维 + * 基础: 树 tree、图 graph + * 高级: 二叉搜索树 binary search tree(red-black tree)、堆 heap、 并查集 disjoint set、字典树 trie,etc + - 特殊 + * 位运算 Bitwise、布隆过滤器 BloomFilter + * LRU Cache + +#### 算法 + + - if-else,switch -> branch + - for,while loop -> iteration + - 递归 Recursion(Divide & Conquer,Backtrace) + - 搜索 Seach: 深度有限搜索 Depth first search,广度优先算法 Breadth first search,A*,etc + - 动态规划 dynamic Programming + - 二分查找 Binary Search + - 贪心 Greedy + - 数学 Math,几何 Geometry + + #### 刻意练习 + + - 过遍数:(五毒神掌) + - 练习缺陷、弱点的地方 + +#### Feedback + - 即时反馈 + - 主动型反馈 + * 高手代码(Github,LeetCode,.etc.) + * 第一视角直播 + - 被动型反馈 + * code review + * 教练给反馈 + +#### 切题四件套 + + - Clarification 保证理解清楚题目 + - Possible solution 所有可能的解法 + * compare(time/space) 最优解法 + * optimal(加强) + - Coding(多写) + - Test cases 测试用例 + + +#### 五毒神掌 + +`刷题第一遍` + - 5分钟: 读题+思考 + - 直接看解法: 多解法比较解法优劣 + - 背诵、默写好的解法 + +`刷题第二遍` + - 马上默写 -> LeetCode提交 + - 多种解法比较、体会->优化 + +`刷题第三遍` + - 隔天练习 + +`刷题第四遍` + - 隔周练习 + +`刷题第五遍` + - 面试前练习 + + + +### 时间复杂度和空间复杂度 + +#### 时间复杂度 + + - O(1): Constant Complexity 常数复杂度 + - O(log n): Logarithmic Complexity 对数复杂度 + - O(n): Linear Complexity 线性时间复杂度 + - O(n^2): N square Complexity 平方 + - O(n^3): N cube Complexity 立方 + - O(2^n): Exponential Growth 指数 + - O(n!): Factorial 阶乘 + +#### Master Theorem + - 二分查找: T(n) = T(n/2) + O(1) -> log(n) + - 二叉树的遍历: T(n) = 2T(n/2) + O(1) -> O(n) + - 在排好序的二维矩阵二分查找: T(n) = 2T(n/2) + O(log n) -> O(n) + - 归并排序: T(n) = 2T(n/2) + O(n) -> O(n log n) + + - 二叉树前序/中序/后序遍历的时间复杂度: O(n) -> n为总节点数 + - 图的遍历时间复杂度:O(n) -> n为图的节点总数 + - 搜索算法DFS/BFS的时间复杂度: O(n) -> n为搜索空间里的节点总数 + - 二分查找时间复杂度是: O(log n) diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_318/\346\225\260\346\215\256\347\273\223\346\236\204\345\222\214\347\256\227\346\263\225\347\237\245\350\257\206\350\204\211\347\273\234\345\222\214\345\255\246\344\271\240\346\226\271\346\263\225.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_318/\346\225\260\346\215\256\347\273\223\346\236\204\345\222\214\347\256\227\346\263\225\347\237\245\350\257\206\350\204\211\347\273\234\345\222\214\345\255\246\344\271\240\346\226\271\346\263\225.xmind" new file mode 100644 index 000000000..425435c3a Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_318/\346\225\260\346\215\256\347\273\223\346\236\204\345\222\214\347\256\227\346\263\225\347\237\245\350\257\206\350\204\211\347\273\234\345\222\214\345\255\246\344\271\240\346\226\271\346\263\225.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_363/\346\225\260\346\215\256\347\273\223\346\236\204\345\222\214\347\256\227\346\263\225.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_363/\346\225\260\346\215\256\347\273\223\346\236\204\345\222\214\347\256\227\346\263\225.xmind" new file mode 100644 index 000000000..ed4c00be5 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_363/\346\225\260\346\215\256\347\273\223\346\236\204\345\222\214\347\256\227\346\263\225.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_388/DataStructuresAndAlgorithms.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_388/DataStructuresAndAlgorithms.xmind" new file mode 100644 index 000000000..b9e1ccd1f Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_388/DataStructuresAndAlgorithms.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_388/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_388/NOTE.md" index a6321d6e2..2b2e78547 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_388/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_388/NOTE.md" @@ -1,4 +1,7 @@ # NOTE +## 预习周 + 通过画脑图发现了好多不知道的知识点,后续会着重学习。 + diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_393/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_393/NOTE.md" index a6321d6e2..e12da9a78 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_393/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_393/NOTE.md" @@ -2,3 +2,6 @@ +本次预习周画思维导图。这里的思维导图将随着课程的学习持续更新。 + +不会照搬老师已经画好的思维导图。 \ No newline at end of file diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_393/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_393/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225.xmind" new file mode 100644 index 000000000..a7845a2b3 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_393/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_423/week-00.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_423/week-00.xmind" new file mode 100644 index 000000000..37b95981a Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_423/week-00.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_428/\350\204\221\345\233\276_\346\225\260\346\215\256\347\273\223\346\236\204.png" "b/Week \351\242\204\344\271\240\345\221\250/id_428/\350\204\221\345\233\276_\346\225\260\346\215\256\347\273\223\346\236\204.png" new file mode 100644 index 000000000..a0aeb5b31 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_428/\350\204\221\345\233\276_\346\225\260\346\215\256\347\273\223\346\236\204.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_428/\350\204\221\345\233\276_\347\256\227\346\263\225\345\255\246\344\271\240.png" "b/Week \351\242\204\344\271\240\345\221\250/id_428/\350\204\221\345\233\276_\347\256\227\346\263\225\345\255\246\344\271\240.png" new file mode 100644 index 000000000..5937cc762 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_428/\350\204\221\345\233\276_\347\256\227\346\263\225\345\255\246\344\271\240.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_443/\346\225\260\346\215\256\347\273\223\346\236\204\350\204\221\345\233\276.md" "b/Week \351\242\204\344\271\240\345\221\250/id_443/\346\225\260\346\215\256\347\273\223\346\236\204\350\204\221\345\233\276.md" new file mode 100644 index 000000000..f28b057be --- /dev/null +++ "b/Week \351\242\204\344\271\240\345\221\250/id_443/\346\225\260\346\215\256\347\273\223\346\236\204\350\204\221\345\233\276.md" @@ -0,0 +1,2 @@ + +[数据结构](https://naotu.baidu.com/file/37ef08788b784d85922ecae8c1a236b3) diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_443/\347\256\227\346\263\225\350\204\221\345\233\276.md" "b/Week \351\242\204\344\271\240\345\221\250/id_443/\347\256\227\346\263\225\350\204\221\345\233\276.md" new file mode 100644 index 000000000..f451c2db1 --- /dev/null +++ "b/Week \351\242\204\344\271\240\345\221\250/id_443/\347\256\227\346\263\225\350\204\221\345\233\276.md" @@ -0,0 +1,2 @@ + +[算法](https://naotu.baidu.com/file/5b9f59e96d470e2b52714bb863da145a) diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_468/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_468/NOTE.md" index a6321d6e2..3ecfc166b 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_468/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_468/NOTE.md" @@ -1,4 +1,4 @@ # NOTE - +- 468-week 00 diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_468/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_468/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" new file mode 100644 index 000000000..a995d8c0d Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_468/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_468/\347\256\227\346\263\225.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_468/\347\256\227\346\263\225.xmind" new file mode 100644 index 000000000..ac160f3d5 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_468/\347\256\227\346\263\225.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_473/473-Week 00.png" "b/Week \351\242\204\344\271\240\345\221\250/id_473/473-Week 00.png" new file mode 100644 index 000000000..5c07d5d54 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_473/473-Week 00.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_503/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_503/NOTE.md" index a6321d6e2..1c5259aae 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_503/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_503/NOTE.md" @@ -1,4 +1,57 @@ -# NOTE - +## 精通算法学习方法论 +* Chunk it up 切碎知识点 + * 分解数据结构 + * 分解算法 +* Deliberate Practicing 刻意练习 + * 最大误区:只练习一遍 + * 切题四件套 + 1. Clarification:审题,真正理解题目意思 + 2. Possible solutions:思考尽可能的解法,分析并比较每种解放的时间、空间复杂度,找出最优解法 + * optimal(加强)**** + * compare (time/space) + 3. Coding(多写):思考完后才开始写代码 + 4. Test cases: 写完后,列举几个测试用例,验证代码正确性 + * 五步刷题法 + * 每道算法题至少练习 5 遍 (五毒神掌) + 1. 第一遍 + * 5~15分钟:读题 + 思考 + * 直接看解法:注意!多解法,比较解法优劣 + * 背诵、默写好的解法 + 2. 第二遍 + * 马上自己写 —> LeetCode 提交 + * 多种解法比较、体会 —> 优化! + 3. 第三遍 + * 过了一天后,再重复做题 + * 不同解法的熟练程度 —> 专项练习 + 4. 第四遍 + * 过了一周:反复回来练习相同题目 + 5. 第五遍 + * 面试前一周恢复性训练 +* Feedback 反馈 + * 主动型反馈 + * 学习高手的代码 + * GitHub + * LeetCode + * 源码 + * 被动式反馈 + * 让别人对自己的代码 code review +## 工欲善其事,必先利利其器器 +* 对常用工具进行配置 +* 刻意练习基本功和编程指法 +* ⾃自顶向下的编程⽅方式 +## 时间复杂度 +* Big O notation:大 O 表示法 +* 常见的算法时间复杂度 + * O(1): Constant Complexity 常数复杂度 + * O(log n): Logarithmic Complexity 对数复杂度 + * O(n): Linear Complexity 线性时间复杂度 + * O(n^2): N square Complexity 平⽅方 + * O(n^3): N square Complexity ⽴立⽅方 + * O(2^n): Exponential Growth 指数 + * O(n!): Factorial 阶乘
 +* 主定理 Master Theorem + * 参考资料 + * [Master Theorem](https://en.wikipedia.org/wiki/Master_theorem_(analysis_of_algorithms)) + * [主定理](https://zh.wikipedia.org/wiki/%E4%B8%BB%E5%AE%9A%E7%90%86) \ No newline at end of file diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_503/mindmap.md" "b/Week \351\242\204\344\271\240\345\221\250/id_503/mindmap.md" new file mode 100644 index 000000000..1bcd4eeee --- /dev/null +++ "b/Week \351\242\204\344\271\240\345\221\250/id_503/mindmap.md" @@ -0,0 +1,7 @@ +## 说明 +* 列出了将要学习和巩固的大纲(只怪自己知识薄弱) +* 每周学习课程后进行完善和更新 + +[数据结构脑图](https://www.processon.com/view/link/5da29588e4b09df5500dd7e8) + +[算法脑图](https://www.processon.com/view/link/5da32c15e4b09df5500f0e90) diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_508/\346\225\260\346\215\256\347\273\223\346\236\204.png" "b/Week \351\242\204\344\271\240\345\221\250/id_508/\346\225\260\346\215\256\347\273\223\346\236\204.png" new file mode 100644 index 000000000..d1210b633 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_508/\346\225\260\346\215\256\347\273\223\346\236\204.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_508/\347\256\227\346\263\225.png" "b/Week \351\242\204\344\271\240\345\221\250/id_508/\347\256\227\346\263\225.png" new file mode 100644 index 000000000..9f5316bad Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_508/\347\256\227\346\263\225.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_533/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_533/NOTE.md" index a6321d6e2..34ba9da48 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_533/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_533/NOTE.md" @@ -1,4 +1,40 @@ -# NOTE +### 收获 - ++ 学习方法: + 1. 切碎知识点 + 2. 刻意练习(对于知识的熟练掌握真的很重要) + 3. 主动反馈(自己去找)与被动反馈(高手给的指点),有助于完善知识体系、拓宽视野 + ++ 切题四件套: + 1. Clarification + 2. Possible solutions + 3. Coding + 4. Test cases + ++ 五毒神掌 + 1. 刷题第一遍 + 5分钟(10到15分钟也可):读题+思考,如果无思路-->直接看解法:注意!多解法,比较优劣。背诵、默写好的解法 + 2. 第二遍 + 马上自己写-->LeetCode提交,多种解法比较、体会-->优化! + 3. 第三遍 + 一天之后 重做一遍 + LeetCode国际站看most votes自己使用语言的前三回答。针对不同熟练程度的解法-->专项练习 + 4. 第四遍 + 过了一周:再回来练习一遍 + LeetCode国际站看most votes自己使用语言的前三回答。针对不同熟练程度的解法-->专项练习 + 5. 第五遍 + 面试前一周恢复性训练 + ++ 遵循严格的代码规范 ++ 养成良好的IDE使用习惯 + ++ 自顶向下的编程方式,最关键的函数写在最上面,细节放在后面 + +### 关于作业 +1. 预习周的作业脑图没有分为数据结构与算法,是因为算法是作用在数据结构之上的,联系比较紧密 +2. 排序分类里目前没有希尔排序,后面可以找时间学习加上 +3. 还有一些不熟和不懂的知识会随着课程深入补充整理 + +### 疑问 +为什么堆属于受限线性表 \ No newline at end of file diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_533/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225.mmap" "b/Week \351\242\204\344\271\240\345\221\250/id_533/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225.mmap" new file mode 100644 index 000000000..144760b51 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_533/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225.mmap" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_558/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_558/NOTE.md" index a6321d6e2..aa9465f81 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_558/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_558/NOTE.md" @@ -1,4 +1,6 @@ # NOTE - - - +关于作业:数据结构和算法脑图不够完善,后续学习中不断迭代优化... +预习周总结: +1、构建数据结构和算法知识体系,后续按图索骥去学习 +2、明确了解算法题的策略技巧(切题四件套、五遍刷题法)->刻意练习 +3、工欲善其事必先利其器,配置了练习算法的编程环境 \ No newline at end of file diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_558/\346\225\260\346\215\256\347\273\223\346\236\204&\347\256\227\346\263\225.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_558/\346\225\260\346\215\256\347\273\223\346\236\204&\347\256\227\346\263\225.xmind" new file mode 100644 index 000000000..8ac148f50 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_558/\346\225\260\346\215\256\347\273\223\346\236\204&\347\256\227\346\263\225.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_583/\346\225\260\346\215\256\347\273\223\346\236\204\350\204\221\345\233\276.png" "b/Week \351\242\204\344\271\240\345\221\250/id_583/\346\225\260\346\215\256\347\273\223\346\236\204\350\204\221\345\233\276.png" new file mode 100644 index 000000000..0f1c7b6ca Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_583/\346\225\260\346\215\256\347\273\223\346\236\204\350\204\221\345\233\276.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_583/\347\256\227\346\263\225.png" "b/Week \351\242\204\344\271\240\345\221\250/id_583/\347\256\227\346\263\225.png" new file mode 100644 index 000000000..8ff728808 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_583/\347\256\227\346\263\225.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_593/\347\256\227\346\263\225\345\222\214\346\225\260\346\215\256\347\273\223\346\236\204.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_593/\347\256\227\346\263\225\345\222\214\346\225\260\346\215\256\347\273\223\346\236\204.xmind" new file mode 100644 index 000000000..a4e2e179e Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_593/\347\256\227\346\263\225\345\222\214\346\225\260\346\215\256\347\273\223\346\236\204.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_608/\346\225\260\346\215\256\347\273\223\346\236\204.png" "b/Week \351\242\204\344\271\240\345\221\250/id_608/\346\225\260\346\215\256\347\273\223\346\236\204.png" new file mode 100644 index 000000000..b5489a64a Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_608/\346\225\260\346\215\256\347\273\223\346\236\204.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_608/\347\256\227\346\263\225.png" "b/Week \351\242\204\344\271\240\345\221\250/id_608/\347\256\227\346\263\225.png" new file mode 100644 index 000000000..995e5045f Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_608/\347\256\227\346\263\225.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_628/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_628/NOTE.md" index a6321d6e2..547ac6067 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_628/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_628/NOTE.md" @@ -2,3 +2,89 @@ +# ݽṹ + Ա + + + + ˫ + ѭ + ̬ + Ա + + С + 󶥶 + 쳲 + ջ + ˳ջ + ʽջ + + ͨ + ˫߶ + ȼ + + + ȫ + + + ƽ + AVL + + · + B + B+ + 2-3 + 2-3-4 + + ֵ + ͼ + ͼ + ͼ + ݽṹ + + ɢб + λͼ +# 㷨 + 㷨˼ + ̰㷨 + 㷨 + 㷨 + ö㷨 + ̬滮 + ֦㷨 + 㷨 + + O(n^2 + ð + + ѡ + ϣ + O(nlogn + 鲢 + + + O(n + + + Ͱ + + DFS-- + BFS-- + A*ʽ + + + ƥ + KMP + ACԶ + Brute Force + BM + Rabin-Karp + 㷨 + + · + λͼ + ͳ + ռ + 鼯 + ¡ + LRU \ No newline at end of file diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_628/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225\350\204\221\345\233\276\344\275\234\344\270\232.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_628/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225\350\204\221\345\233\276\344\275\234\344\270\232.xmind" new file mode 100644 index 000000000..62069cb97 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_628/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225\350\204\221\345\233\276\344\275\234\344\270\232.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_633/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_633/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" new file mode 100644 index 000000000..81289504a Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_633/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_633/\347\256\227\346\263\225.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_633/\347\256\227\346\263\225.xmind" new file mode 100644 index 000000000..cd8a59ed7 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_633/\347\256\227\346\263\225.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_643/\351\242\204\344\271\240\345\221\250\346\200\235\347\273\264\345\257\274\345\233\276.png" "b/Week \351\242\204\344\271\240\345\221\250/id_643/\351\242\204\344\271\240\345\221\250\346\200\235\347\273\264\345\257\274\345\233\276.png" new file mode 100644 index 000000000..5c0319d73 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_643/\351\242\204\344\271\240\345\221\250\346\200\235\347\273\264\345\257\274\345\233\276.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_688/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_688/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" new file mode 100644 index 000000000..ce53b857a Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_688/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_688/\347\256\227\346\263\225\344\270\216\346\225\260\346\215\256\347\273\223\346\236\204.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_688/\347\256\227\346\263\225\344\270\216\346\225\260\346\215\256\347\273\223\346\236\204.xmind" new file mode 100644 index 000000000..271c2d576 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_688/\347\256\227\346\263\225\344\270\216\346\225\260\346\215\256\347\273\223\346\236\204.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_693/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_693/NOTE.md" index a6321d6e2..3138f5ba9 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_693/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_693/NOTE.md" @@ -1,4 +1,4 @@ # NOTE - + 1 diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_698/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_698/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" new file mode 100644 index 000000000..1389ae6da Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_698/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_698/\347\256\227\346\263\225.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_698/\347\256\227\346\263\225.xmind" new file mode 100644 index 000000000..f378bc2b8 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_698/\347\256\227\346\263\225.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_703/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_703/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" new file mode 100644 index 000000000..eb79ffad4 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_703/\346\225\260\346\215\256\347\273\223\346\236\204.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_703/\347\256\227\346\263\225.xmind" "b/Week \351\242\204\344\271\240\345\221\250/id_703/\347\256\227\346\263\225.xmind" new file mode 100644 index 000000000..c639a9359 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_703/\347\256\227\346\263\225.xmind" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_713/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_713/NOTE.md" index a6321d6e2..c96a86693 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_713/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_713/NOTE.md" @@ -2,3 +2,96 @@ +* **最大误区: 只做一遍** +* 拆分知识点 +* 刻意练习 => 过遍数 + * 五遍刷题法则 + * 弱项,缺陷 + * 不舒服,不爽是成长的点 +* 寻求反馈 + +### 所有复杂的算法本质是找重复的单元是什么 +--- + +### 数据结构 +* 一维 + * 基础 + * 数组 array + * 链表 linked list + * 高级 + * 栈 stack + * 队列 queue + * 双端队列 deque + * 集合 set + * 映射 hashmap +* 二维 + * 基础 + * 树 tree + * 图 graph + * 特殊 + * 二叉搜索树 binary search tree + * 堆 heap + * 字典树 trie + * 并查集 disjoint set +* 特殊 + * 位运算 bitwise + * 布隆过滤器 bloom filter + * LRU cache + +### 算法 +* if-else 逻辑切换 +* loop 循环 +* recursive 递归 -> 自己调用自己, 处理不当容易栈溢出 + * Divide & conquer 分治 + * backtrace 回溯 +* 搜索 + * DFS(deep first search) 深度优先搜索 + * BFS(breadth first search) 广度优先搜索 + * A* 启发式搜索 +* 动态规划 DP(Dynamic Programming) +* 二分查找 binary search +* 贪心 greedy +* 数学 Math, 几何 Geometry + + +### 反馈 +* 主动式(自己寻找) + * 高手代码(LeetCode, GitHub) + * 第一视角直播 +* 被动式 + * 高手给你指点 + * code review + +### 切题四件套 遇题四步思考方式 +* Clarification 明确题目 +* Possible Solutions 所有可能解题方法,分析时间/空间复杂度, 找最优化 +* Coding 编码 +* Test Cases 测试用例 + +### 五遍做题法 +#### 第一遍 +* 5分钟 读题+思考 +* 直接看解法: 多种解法, 比较优劣 +* 背诵&默写好的写法 +#### 第二遍 +* 马上自己写, LeetCode提交 +* 多种解法比较, 体会, 优化 +#### 第三遍 +* 过了24h之后再做一遍 +* 不同解法熟练程度的专项练习 +#### 第四遍 +* 过了一周之后再训练 +#### 第五遍 +* 面试前一周康复性训练 + + +​ + +--- +#### 备注 +* git提交作业方法 + * https://shimo.im/docs/9ty8pjk6ckxGrkQt/read +* 脑图 + * https://naotu.baidu.com/home +* 入学考试 + * https://exam.kaoshixing.com/exam/wrong_topic \ No newline at end of file diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_713/\346\225\260\346\215\256\347\273\223\346\236\204.png" "b/Week \351\242\204\344\271\240\345\221\250/id_713/\346\225\260\346\215\256\347\273\223\346\236\204.png" new file mode 100644 index 000000000..5f57ee165 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_713/\346\225\260\346\215\256\347\273\223\346\236\204.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_713/\347\256\227\346\263\225.png" "b/Week \351\242\204\344\271\240\345\221\250/id_713/\347\256\227\346\263\225.png" new file mode 100644 index 000000000..5e38dbc48 Binary files /dev/null and "b/Week \351\242\204\344\271\240\345\221\250/id_713/\347\256\227\346\263\225.png" differ diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_738/NOTE.md" "b/Week \351\242\204\344\271\240\345\221\250/id_738/NOTE.md" index a6321d6e2..adf52b521 100644 --- "a/Week \351\242\204\344\271\240\345\221\250/id_738/NOTE.md" +++ "b/Week \351\242\204\344\271\240\345\221\250/id_738/NOTE.md" @@ -1,4 +1,18 @@ # NOTE - +通过对自己已经掌握的知识进行回顾,画出了目前能想到的数据结构脑图和算法脑图。 + +- 数据结构脑图 + +http://naotu.baidu.com/file/29302ef8a3e99525f676038268b96164?token=c62dfd016a1e6b86 + +- 算法脑图 + +http://naotu.baidu.com/file/d8b27845676b8e2628aad98f4000e1f6?token=26109667f0915a6e + +再和老师画出的数据结构脑图和算法脑图对比,发现自己在算法上面很欠缺。老师列出的算法很多都没能够第一时间列举出来 + +后面的学习里,要对这些没列举出来的部分进行着重训练,对已经画出来的部分,要更加熟练的掌握编程技巧 + + diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_738/\346\225\260\346\215\256\347\273\223\346\236\204.svg" "b/Week \351\242\204\344\271\240\345\221\250/id_738/\346\225\260\346\215\256\347\273\223\346\236\204.svg" new file mode 100644 index 000000000..8e7c2ef11 --- /dev/null +++ "b/Week \351\242\204\344\271\240\345\221\250/id_738/\346\225\260\346\215\256\347\273\223\346\236\204.svg" @@ -0,0 +1 @@ +数据结构二叉树实现方式顺序实现链式实现遍历方式广度优先深度优先前序遍历中序遍历后序遍历特点深度为h的二叉树最多有2^h-1个节点完全二叉树除了最后一层,其他层的节点数都是最大个数,且最后一层的叶子节点都连续靠在最左方是平衡二叉树的一个特例n个节点的完全二叉树,深度k为log(n) + 1 [向下取整]采用顺序存储,那么节点序号(第一个节点序号为1)为k,如果有左右子节点,那么左子节点序号为2^k,右子节点序号为2^k+1采用顺序存储,那么节点序号(第一个节点序号为1)为k,父节点序号为K/2 [向下取整]平衡二叉树左右子树的高度差不超过1,且左右子树都为平衡二叉树 满二叉树根节点算第0层,那么第k层有2^k个节点二叉排序树中序遍历得到有序节点线性表数组插入删除O(n)访问元素O(1)需要捕获越界异常链表双向链表单向链表循环链表先进后出数组和链表都可以实现队列单向队列双端队列优先级队列(LRU)大顶堆构建时间复杂度为O(n)排序时间复杂度为0(n*log(n))空间复杂度为O(1)插入后重调大顶堆时间复杂度为log(n)小顶堆哈希表哈希冲突解决拉链法开放地址法构造:除留余数法集合Set字典MapHashSetLinkedHashSetTreeSetHashMap \ No newline at end of file diff --git "a/Week \351\242\204\344\271\240\345\221\250/id_738/\347\256\227\346\263\225.svg" "b/Week \351\242\204\344\271\240\345\221\250/id_738/\347\256\227\346\263\225.svg" new file mode 100644 index 000000000..268df4ca0 --- /dev/null +++ "b/Week \351\242\204\344\271\240\345\221\250/id_738/\347\256\227\346\263\225.svg" @@ -0,0 +1 @@ +算法排序归并排序快速排序冒泡排序堆排序希尔排序插入排序选择排序二分查找动态规划递归优化:尾递归位运算LRUHash表+双向链表get\set都是O(1)时间复杂度不通数据结构适用不通的算法 \ No newline at end of file diff --git "a/\350\256\262\345\270\210\350\257\276\344\273\266/01\346\236\201\345\256\242\345\244\247\345\255\246-\347\256\227\346\263\225\350\256\255\347\273\203\350\220\245-\350\246\203\350\266\205-\347\254\254\344\270\200\350\257\276.pdf" "b/\350\256\262\345\270\210\350\257\276\344\273\266/01\346\236\201\345\256\242\345\244\247\345\255\246-\347\256\227\346\263\225\350\256\255\347\273\203\350\220\245-\350\246\203\350\266\205-\347\254\254\344\270\200\350\257\276.pdf" deleted file mode 100644 index 1cb731ace..000000000 Binary files "a/\350\256\262\345\270\210\350\257\276\344\273\266/01\346\236\201\345\256\242\345\244\247\345\255\246-\347\256\227\346\263\225\350\256\255\347\273\203\350\220\245-\350\246\203\350\266\205-\347\254\254\344\270\200\350\257\276.pdf" and /dev/null differ diff --git "a/\350\256\262\345\270\210\350\257\276\344\273\266/02\346\236\201\345\256\242\345\244\247\345\255\246-\347\256\227\346\263\225\350\256\255\347\273\203\350\220\245-\350\246\203\350\266\205-\347\254\254\344\272\214\350\257\276.pdf" "b/\350\256\262\345\270\210\350\257\276\344\273\266/02\346\236\201\345\256\242\345\244\247\345\255\246-\347\256\227\346\263\225\350\256\255\347\273\203\350\220\245-\350\246\203\350\266\205-\347\254\254\344\272\214\350\257\276.pdf" deleted file mode 100644 index 4461afc43..000000000 Binary files "a/\350\256\262\345\270\210\350\257\276\344\273\266/02\346\236\201\345\256\242\345\244\247\345\255\246-\347\256\227\346\263\225\350\256\255\347\273\203\350\220\245-\350\246\203\350\266\205-\347\254\254\344\272\214\350\257\276.pdf" and /dev/null differ diff --git "a/\350\256\262\345\270\210\350\257\276\344\273\266/\346\236\201\345\256\242\345\244\247\345\255\246-\347\256\227\346\263\225\350\256\255\347\273\203\350\220\245-\350\246\203\350\266\205-\347\254\254\344\270\211\350\257\276.pdf" "b/\350\256\262\345\270\210\350\257\276\344\273\266/\346\236\201\345\256\242\345\244\247\345\255\246-\347\256\227\346\263\225\350\256\255\347\273\203\350\220\245-\350\246\203\350\266\205-\347\254\254\344\270\211\350\257\276.pdf" deleted file mode 100644 index eef4cfaa6..000000000 Binary files "a/\350\256\262\345\270\210\350\257\276\344\273\266/\346\236\201\345\256\242\345\244\247\345\255\246-\347\256\227\346\263\225\350\256\255\347\273\203\350\220\245-\350\246\203\350\266\205-\347\254\254\344\270\211\350\257\276.pdf" and /dev/null differ diff --git "a/\350\256\262\345\270\210\350\257\276\344\273\266/\346\236\201\345\256\242\345\244\247\345\255\246-\347\256\227\346\263\225\350\256\255\347\273\203\350\220\245-\350\246\203\350\266\205-\347\254\254\345\233\233\350\257\276.pdf" "b/\350\256\262\345\270\210\350\257\276\344\273\266/\346\236\201\345\256\242\345\244\247\345\255\246-\347\256\227\346\263\225\350\256\255\347\273\203\350\220\245-\350\246\203\350\266\205-\347\254\254\345\233\233\350\257\276.pdf" deleted file mode 100644 index 972f64848..000000000 Binary files "a/\350\256\262\345\270\210\350\257\276\344\273\266/\346\236\201\345\256\242\345\244\247\345\255\246-\347\256\227\346\263\225\350\256\255\347\273\203\350\220\245-\350\246\203\350\266\205-\347\254\254\345\233\233\350\257\276.pdf" and /dev/null differ