From 58ba05110e5aa6ec65cdb3f4e19342b6f6224589 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sat, 13 Jun 2020 23:12:30 -0400 Subject: [PATCH 01/48] two-sum, move-zeroes and plus-one solution --- Week01/move-zeroes.py | 55 +++++++++++++++++++++++++++++++++++++++++++ Week01/plus-one.py | 27 +++++++++++++++++++++ Week01/two-sum.py | 54 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 Week01/move-zeroes.py create mode 100644 Week01/plus-one.py create mode 100644 Week01/two-sum.py diff --git a/Week01/move-zeroes.py b/Week01/move-zeroes.py new file mode 100644 index 000000000..667dcaea9 --- /dev/null +++ b/Week01/move-zeroes.py @@ -0,0 +1,55 @@ +""" +Leetcode 283. Move Zeroes +https://leetcode.com/problems/move-zeroes/ +""" +from typing import List + +class Solution: + def moveZeroes1(self, nums: List[int]) -> None: + """ + Solution #1: use append & remove API + Time: O(n^2): append O(1) + remove O(n) worst case + Space: O(1) + """ + for elem in nums: + if elem == 0: + nums.append(0) + nums.remove(0) + return nums + + def moveZeroes2(self, nums: List[int]) -> None: + """ + Solution #2: remember the next zero position and swap + Time: O(n) + Space: O(1) + """ + next_zero_spot = 0 + for i in range(len(nums)): + if nums[i] != 0: + nums[i], nums[next_zero_spot] = nums[next_zero_spot], nums[i] + next_zero_spot += 1 + return nums + + def moveZeroes3(self, nums: List[int]) -> None: + """ + Solution #3: snowball solution by https://leetcode.com/olsh + Time: O(n) + Space: O(1) + """ + snowBallSize = 0 + for i in range(len(nums)): + if nums[i] == 0: + snowBallSize += 1 # the snowball gets bigger + elif snowBallSize > 0: + # swap the most left 0 with the element + nums[i], nums[i - snowBallSize] = nums[i - snowBallSize], nums[i] + return nums + +solution = Solution() +ans1 = solution.moveZeroes1([0,1,0,3,12]) +ans2 = solution.moveZeroes1([0,1,0,3,12]) +ans3 = solution.moveZeroes1([0,1,0,3,12]) + +print(ans1) # [1,3,12,0,0] +print(ans2) # [1,3,12,0,0] +print(ans3) # [1,3,12,0,0] \ No newline at end of file diff --git a/Week01/plus-one.py b/Week01/plus-one.py new file mode 100644 index 000000000..213ce93ca --- /dev/null +++ b/Week01/plus-one.py @@ -0,0 +1,27 @@ +""" +Leetcode 66. Plus One +https://leetcode.com/problems/plus-one/ +""" +from typing import List + +class Solution: + def plusOne(self, digits: List[int]) -> List[int]: + """ + Time: O(n) + Space: O(1) + """ + n = len(digits) - 1 + while n >= 0: + if digits[n] == 9: + digits[n] = 0 + n -= 1 + else: + digits[n] += 1 + break + + return [1] + digits[:] if digits[0] == 0 else digits + +solution = Solution() +ans = solution.plusOne([9,9,9]) + +print(ans) # [1,0,0,0] \ No newline at end of file diff --git a/Week01/two-sum.py b/Week01/two-sum.py new file mode 100644 index 000000000..a616e6170 --- /dev/null +++ b/Week01/two-sum.py @@ -0,0 +1,54 @@ +""" +Leetcode 1. Two Sum +https://leetcode.com/problems/two-sum +""" +from typing import List + +class Solution: + def twoSum1(self, nums: List[int], target: int) -> List[int]: + """ + Solution #1: brute-force + Time: O(n^2) + Space: O(1) + """ + l = len(nums) + for i in range(0, l - 1): + for j in range(i + 1, l): + if nums[i] + nums[j] == target: + return [i, j] + + def twoSum2(self, nums: List[int], target: int) -> List[int]: + """ + Solution #2: cache, 2 loops + Time: O(n) + Space: O(n) + """ + dic = {} + for i in range(len(nums)): + dic[target - nums[i]] = i + + for i, num in enumerate(nums): + if num in dic and dic[num] != i: + return [i, dic[num]] + + def twoSum3(self, nums: List[int], target: int) -> List[int]: + """ + Solution #3: cache, 1 loop + Time: O(n) + Space: O(n) + """ + dic = {} + for i, n in enumerate(nums): + if n in dic: + return [dic.get(n), i] + else: + dic[target - n] = i + +solution = Solution() +ans1 = solution.twoSum1([2, 7, 11, 15], 9) +ans2 = solution.twoSum1([2, 7, 11, 15], 9) +ans3 = solution.twoSum1([2, 7, 11, 15], 9) + +print(ans1) # [0, 1] +print(ans2) # [0, 1] +print(ans3) # [0, 1] \ No newline at end of file From 6951cf111b4871c4913a73146e5167396b3fcde1 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sat, 13 Jun 2020 23:32:50 -0400 Subject: [PATCH 02/48] week01 learning notes --- Week01/NOTE.md | 82 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/Week01/NOTE.md b/Week01/NOTE.md index 50de30414..67040346a 100644 --- a/Week01/NOTE.md +++ b/Week01/NOTE.md @@ -1 +1,81 @@ -学习笔记 \ No newline at end of file +Learning Notes Week 01 +====================== + +Array +----- + +Basic Concepts +- Arrays are lists, lists are mutable sequences, tuples are immutable sequences +- list is dynamically-resized, there is no upper bound +- Values can be deleted and inserted at arbitrary locations + +Time Complexity of array operations +- Retrieving O(1) +- Updating O(1) +- Insertion O(n) +- Deletion O(n-i) + +List Operations +```python +a.append(x) +a.extend(iterable) +a.insert(i, x) +a.remove(x) +a.pop([i]) +a.clear() +a.count(x) +a.reverse() +a.copy() +a.index(x[, start[, end]]) +a.sort(key=None, reverse=False) +``` + +Common Sequence Operations +```python +x in s # True if an item of s is equal to x, else False +x not in s # False if an item of s is equal to x, else True +s + t # the concatenation of s and t +s * n or n * s # equivalent to adding s to itself n times +s[i] # ith item of s, origin 0 +s[i:j] # slice of s from i to j +s[i:j:k] # slice of s from i to j with step k +len(s) # length of s +min(s) # smallest item of s +max(s) # largest item of s +reversed(s) # return a reverse iterator +sorted(s) # return a new sorted list from the items in iterable +``` + +List Comprehensions +```python +vec = [-4, -2, 0, 2, 4] + +[x*2 for x in vec] # [-8, -4, 0, 4, 8] + +[x for x in vec if x >= 0] # [0, 2, 4] + +[abs(x) for x in vec] # [4, 2, 0, 2, 4] + +[(x, x**2) for x in range(6)] # create a list of 2-tuples (number, square) + +vec = [[1,2,3], [4,5,6], [7,8,9]] + +[num for elem in vec for num in elem] # flatten a list +``` + +Linked Lists +------------ + +A list implements an ordered collection of values, which may include repetitions. + +Singly linked list: `L -> 2 -> 3 -> 5 -> 4 -> EOL` +- 2 is linked list head +- 4 is linked list tail +- 2's next is 3, 3's next is 5 and 5's next is 4, and 4's next is None +- L sometimes is used as a "dummy" head + +Doubly linked list: `L -> x <- 2 <-> 3 <-> 5 <-> 4 -> x` +- 2's prev is None, 2's next is 3 +- 3's prev is 2 , 3's next is 5 +- 4's prev is 3 , 5's next is 4 +- 4's prev is 5 , 4's next is None From 5075ece0f565dc3cdccf4760dd7873c839d20fa0 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 14 Jun 2020 22:20:37 -0400 Subject: [PATCH 03/48] add .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..c791ff59a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] \ No newline at end of file From 7e9f4b314014263ac982db58d7c60afa6dd70945 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 14 Jun 2020 22:21:09 -0400 Subject: [PATCH 04/48] merge two sorted lists --- Week01/linked_list.py | 31 +++++++++++++++++++ Week01/merge-two-sorted-lists.py | 51 ++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 Week01/linked_list.py create mode 100644 Week01/merge-two-sorted-lists.py diff --git a/Week01/linked_list.py b/Week01/linked_list.py new file mode 100644 index 000000000..aa13c5c17 --- /dev/null +++ b/Week01/linked_list.py @@ -0,0 +1,31 @@ +""" +Singly Linked List Data Structure +""" + +class ListNode: + """ + Linked List Node + """ + def __init__(self, val=0, next=None): + self.val = val + self.next = next + +def insert_node(node, new_node): + new_node.next = node.next + node.next = new_node + +def remove_node(node): + node.next = node.next.next + +def create_list(iterable = ()): + dummy = ListNode() + for elem in reversed(iterable): + insert_node(dummy, ListNode(elem)) + return dummy.next + +def print_list(head): + result, p = [], head + while p: + result.append(p.val) + p = p.next + print(result) diff --git a/Week01/merge-two-sorted-lists.py b/Week01/merge-two-sorted-lists.py new file mode 100644 index 000000000..ff715f7a7 --- /dev/null +++ b/Week01/merge-two-sorted-lists.py @@ -0,0 +1,51 @@ +""" +21. Merge Two Sorted Lists +https://leetcode-cn.com/problems/merge-two-sorted-lists/ +""" +from linked_list import ListNode, create_list, print_list + +class Solution: + def mergeTwoLists1(self, l1: ListNode, l2: ListNode) -> ListNode: + """ + Solution #1: iteratively + Time: O(n) + Space: O(1) + """ + dummy = ListNode() + p = dummy + while l1 and l2: + if l1.val < l2.val: + p.next, l1 = l1, l1.next + else: + p.next, l2 = l2, l2.next + p = p.next + p.next = l1 or l2 + return dummy.next + + def mergeTwoLists2(self, l1: ListNode, l2: ListNode) -> ListNode: + """ + Solution #2: recursively + Time: O(n) + Space: O(1) + """ + if not l1: return l2 + if not l2: return l1 + + if l1.val < l2.val: + l1.next = self.mergeTwoLists2(l1.next, l2) + return l1 + else: + l2.next = self.mergeTwoLists2(l1, l2.next) + return l2 + +solution = Solution() + +l1 = create_list([1,2,3]) +l2 = create_list([1,2,4]) +ans1 = solution.mergeTwoLists1(l1, l2) +print_list(ans1) # [1,1,2,2,3,4] + +l1 = create_list([1,2,3]) +l2 = create_list([3,4,5,6,7]) +ans2 = solution.mergeTwoLists2(l1, l2) +print_list(ans2) # [1, 2, 3, 3, 4, 5, 6, 7] \ No newline at end of file From ee4569fa261449ad4b3b8f8fea948ec004a0330e Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 14 Jun 2020 22:21:53 -0400 Subject: [PATCH 05/48] house keeping --- Week01/move-zeroes.py | 9 +++++---- Week01/plus-one.py | 2 +- Week01/two-sum.py | 16 +++++++++------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Week01/move-zeroes.py b/Week01/move-zeroes.py index 667dcaea9..eb645333b 100644 --- a/Week01/move-zeroes.py +++ b/Week01/move-zeroes.py @@ -1,5 +1,5 @@ """ -Leetcode 283. Move Zeroes +283. Move Zeroes https://leetcode.com/problems/move-zeroes/ """ from typing import List @@ -46,9 +46,10 @@ def moveZeroes3(self, nums: List[int]) -> None: return nums solution = Solution() -ans1 = solution.moveZeroes1([0,1,0,3,12]) -ans2 = solution.moveZeroes1([0,1,0,3,12]) -ans3 = solution.moveZeroes1([0,1,0,3,12]) +lst = [0,1,0,3,12] +ans1 = solution.moveZeroes1(lst) +ans2 = solution.moveZeroes1(lst) +ans3 = solution.moveZeroes1(lst) print(ans1) # [1,3,12,0,0] print(ans2) # [1,3,12,0,0] diff --git a/Week01/plus-one.py b/Week01/plus-one.py index 213ce93ca..2e60baeec 100644 --- a/Week01/plus-one.py +++ b/Week01/plus-one.py @@ -1,5 +1,5 @@ """ -Leetcode 66. Plus One +66. Plus One https://leetcode.com/problems/plus-one/ """ from typing import List diff --git a/Week01/two-sum.py b/Week01/two-sum.py index a616e6170..3376eb510 100644 --- a/Week01/two-sum.py +++ b/Week01/two-sum.py @@ -1,6 +1,6 @@ """ -Leetcode 1. Two Sum -https://leetcode.com/problems/two-sum +1. Two Sum +https://leetcode.com/problems/two-sum/ """ from typing import List @@ -19,7 +19,7 @@ def twoSum1(self, nums: List[int], target: int) -> List[int]: def twoSum2(self, nums: List[int], target: int) -> List[int]: """ - Solution #2: cache, 2 loops + Solution #2: cache, 2 iteration Time: O(n) Space: O(n) """ @@ -33,7 +33,7 @@ def twoSum2(self, nums: List[int], target: int) -> List[int]: def twoSum3(self, nums: List[int], target: int) -> List[int]: """ - Solution #3: cache, 1 loop + Solution #3: cache, 1 iteration Time: O(n) Space: O(n) """ @@ -45,9 +45,11 @@ def twoSum3(self, nums: List[int], target: int) -> List[int]: dic[target - n] = i solution = Solution() -ans1 = solution.twoSum1([2, 7, 11, 15], 9) -ans2 = solution.twoSum1([2, 7, 11, 15], 9) -ans3 = solution.twoSum1([2, 7, 11, 15], 9) +lst = [2, 7, 11, 15] +target = 9 +ans1 = solution.twoSum1(lst, target) +ans2 = solution.twoSum1(lst, target) +ans3 = solution.twoSum1(lst, target) print(ans1) # [0, 1] print(ans2) # [0, 1] From bd2206f122fdf1fa49ae3a960bcf942bbb895c61 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 14 Jun 2020 23:14:33 -0400 Subject: [PATCH 06/48] merge two sorted array --- Week01/merge-sorted-array.py | 59 ++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 Week01/merge-sorted-array.py diff --git a/Week01/merge-sorted-array.py b/Week01/merge-sorted-array.py new file mode 100644 index 000000000..1788320ca --- /dev/null +++ b/Week01/merge-sorted-array.py @@ -0,0 +1,59 @@ +""" +88. Merge Sorted Array +https://leetcode.com/problems/merge-sorted-array/ +""" +from typing import List + +class Solution: + def merge1(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: + """ + Solution #1: Two pointers, forwards + Time: O(m+n) + Space O(m): need a copy for nums1 + """ + nums1_copy = nums1[:m] + nums1[:] = [] + + p1, p2 = 0, 0 + while p1 < m and p2 < n: + if nums1_copy[p1] < nums2[p2]: + nums1.append(nums1_copy[p1]) + p1 += 1 + else: + nums1.append(nums2[p2]) + p2 += 1 + + # reminder + if p1 < m: nums1[p1+p2:] = nums1_copy[p1:] + if p2 < n: nums1[p1+p2:] = nums2[p2:] + + def merge2(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: + """ + Solution #2: Three pointers, backwards + Time: O(m+n) + Space O(1) + """ + p1, p2, p = m-1, n-1, len(nums1) - 1 + + while p1 >= 0 and p2 >= 0: + if nums1[p1] < nums2[p2]: + nums1[p] = nums2[p2] + p2 -= 1 + else: + nums1[p] = nums1[p1] + p1 -= 1 + p -= 1 + + nums1[:p2+1] = nums2[:p2+1] + +solution = Solution() + +nums1 = [1,2,3,0,0,0] +nums2 = [2,5,6] +solution.merge1(nums1, 3, nums2, 3) +print(nums1) # [1, 2, 2, 3, 5, 6] + +nums1 = [1,2,3,0,0,0] +nums2 = [2,5,6] +solution.merge2(nums1, 3, nums2, 3) +print(nums1) # [1, 2, 2, 3, 5, 6] \ No newline at end of file From 4207effbb9afa7531df172110cad32150d136029 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 14 Jun 2020 23:29:12 -0400 Subject: [PATCH 07/48] remove duplicates --- Week01/remove-duplicates.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Week01/remove-duplicates.py diff --git a/Week01/remove-duplicates.py b/Week01/remove-duplicates.py new file mode 100644 index 000000000..6e52deeb0 --- /dev/null +++ b/Week01/remove-duplicates.py @@ -0,0 +1,26 @@ +""" +26. Remove Duplicates from Sorted Array +https://leetcode.com/problems/remove-duplicates-from-sorted-array/ +""" +from typing import List + +class Solution: + def removeDuplicates(self, nums: List[int]) -> int: + """ + Solution #1: Two Pointers + """ + if len(nums) == 0: + return 0 + + p = 0 + for q in range(1, len(nums)): + if nums[q] > nums[p]: + p += 1 + nums[p] = nums[q] + + return p + 1 + +solution = Solution() +nums = [0,0,1,1,1,2,2,3,3,4] +ans = solution.removeDuplicates(nums) +print(nums[:ans]) # [0, 1, 2, 3, 4] \ No newline at end of file From 58c659009a06d74b987218ae6e5bd7b2f959a35d Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 14 Jun 2020 23:29:42 -0400 Subject: [PATCH 08/48] fix an issue in move zereos --- Week01/move-zeroes.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Week01/move-zeroes.py b/Week01/move-zeroes.py index eb645333b..7ed30a329 100644 --- a/Week01/move-zeroes.py +++ b/Week01/move-zeroes.py @@ -15,7 +15,6 @@ def moveZeroes1(self, nums: List[int]) -> None: if elem == 0: nums.append(0) nums.remove(0) - return nums def moveZeroes2(self, nums: List[int]) -> None: """ @@ -28,7 +27,6 @@ def moveZeroes2(self, nums: List[int]) -> None: if nums[i] != 0: nums[i], nums[next_zero_spot] = nums[next_zero_spot], nums[i] next_zero_spot += 1 - return nums def moveZeroes3(self, nums: List[int]) -> None: """ @@ -43,14 +41,17 @@ def moveZeroes3(self, nums: List[int]) -> None: elif snowBallSize > 0: # swap the most left 0 with the element nums[i], nums[i - snowBallSize] = nums[i - snowBallSize], nums[i] - return nums solution = Solution() -lst = [0,1,0,3,12] -ans1 = solution.moveZeroes1(lst) -ans2 = solution.moveZeroes1(lst) -ans3 = solution.moveZeroes1(lst) -print(ans1) # [1,3,12,0,0] -print(ans2) # [1,3,12,0,0] -print(ans3) # [1,3,12,0,0] \ No newline at end of file +nums = [0,1,0,3,12] +solution.moveZeroes1(nums) +print(nums) # [1,3,12,0,0] + +nums = [0,0,0,0,1] +solution.moveZeroes2(nums) +print(nums) # [1, 0, 0, 0, 0] + +nums = [1,0,2,0,3,0] +solution.moveZeroes3(nums) +print(nums) # [1, 2, 3, 0, 0, 0] \ No newline at end of file From d2acc4b2a7de65bc69eaf3327c9e1ec77e1406f7 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Tue, 16 Jun 2020 13:00:48 -0400 Subject: [PATCH 09/48] rotate array --- Week01/rotate-array.py | 85 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 Week01/rotate-array.py diff --git a/Week01/rotate-array.py b/Week01/rotate-array.py new file mode 100644 index 000000000..f6b27368f --- /dev/null +++ b/Week01/rotate-array.py @@ -0,0 +1,85 @@ +""" +189. Rotate Array +https://leetcode.com/problems/rotate-array/ +""" +from typing import List + +class Solution: + def rotate1(self, nums: List[int], k: int) -> None: + """ + Solution #1: Brute Force, rotate k times + Time: O(n*k) + Space: O(1) + """ + for i in range(k): + prev = nums[len(nums) - 1] + for j in range(len(nums)): + nums[j], prev = prev, nums[j] + + def rotate2(self, nums: List[int], k: int) -> None: + """ + Solution #2: Extra Array + Time: O(n) + Space: O(n) + """ + n = len(nums) + res = [0] * n + for i in range(n): + res[(i+k) % n] = nums[i] + for i in range(n): + nums[i] = res[i] + + def rotate3(self, nums: List[int], k: int) -> None: + """ + Solution #3: Cyclic Replacement + Time: O(n) + Space: O(1) + """ + n = len(nums) + k %= n + start = count = 0 + while count < n: + curr, prev = start, nums[start] + while True: + next_idx = (curr + k) % n + nums[next_idx], prev = prev, nums[next_idx] + curr = next_idx + count += 1 + if start == curr: + break + start += 1 + + def rotate4(self, nums: List[int], k: int) -> None: + """ + Solution #3: Reverse + Time: O(n): n elements are reversed a total of three times. + Space: O(1) + """ + n = len(nums) + k %= n + self.reverse(nums, 0, n - 1) + self.reverse(nums, 0, k - 1) + self.reverse(nums, k, n - 1) + + def reverse(self, nums: list, start: int, end: int) -> None: + while start < end: + nums[start], nums[end] = nums[end], nums[start] + start, end = start + 1, end - 1 + +solution = Solution() + +nums = [1,2,3,4,5,6,7] +solution.rotate1(nums, 1) +print(nums) # [7, 1, 2, 3, 4, 5, 6] + +nums = [1,2,3,4,5,6,7] +solution.rotate2(nums, 2) +print(nums) # [6, 7, 1, 2, 3, 4, 5] + +nums = [1,2,3,4,5,6,7] +solution.rotate3(nums, 3) +print(nums) # [5, 6, 7, 1, 2, 3, 4] + +nums = [1,2,3,4,5,6,7] +solution.rotate4(nums, 4) +print(nums) # [4, 5, 6, 7, 1, 2, 3] From fbd26cb72bad2e08e225e3a256a60bfe462cab55 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Tue, 16 Jun 2020 13:01:28 -0400 Subject: [PATCH 10/48] house keeping --- Week01/merge-sorted-array.py | 14 +++++--------- Week01/merge-two-sorted-lists.py | 1 - Week01/plus-one.py | 2 +- Week01/remove-duplicates.py | 4 ++-- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/Week01/merge-sorted-array.py b/Week01/merge-sorted-array.py index 1788320ca..46b8bc2bc 100644 --- a/Week01/merge-sorted-array.py +++ b/Week01/merge-sorted-array.py @@ -13,8 +13,7 @@ def merge1(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: """ nums1_copy = nums1[:m] nums1[:] = [] - - p1, p2 = 0, 0 + p1 = p2 = 0 while p1 < m and p2 < n: if nums1_copy[p1] < nums2[p2]: nums1.append(nums1_copy[p1]) @@ -22,7 +21,6 @@ def merge1(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: else: nums1.append(nums2[p2]) p2 += 1 - # reminder if p1 < m: nums1[p1+p2:] = nums1_copy[p1:] if p2 < n: nums1[p1+p2:] = nums2[p2:] @@ -34,7 +32,6 @@ def merge2(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: Space O(1) """ p1, p2, p = m-1, n-1, len(nums1) - 1 - while p1 >= 0 and p2 >= 0: if nums1[p1] < nums2[p2]: nums1[p] = nums2[p2] @@ -43,7 +40,6 @@ def merge2(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: nums1[p] = nums1[p1] p1 -= 1 p -= 1 - nums1[:p2+1] = nums2[:p2+1] solution = Solution() @@ -53,7 +49,7 @@ def merge2(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: solution.merge1(nums1, 3, nums2, 3) print(nums1) # [1, 2, 2, 3, 5, 6] -nums1 = [1,2,3,0,0,0] -nums2 = [2,5,6] -solution.merge2(nums1, 3, nums2, 3) -print(nums1) # [1, 2, 2, 3, 5, 6] \ No newline at end of file +nums1 = [1,2,3,4,0,0] +nums2 = [3,4] +solution.merge2(nums1, 4, nums2, 2) +print(nums1) # [1, 2, 3, 3, 4, 4] \ No newline at end of file diff --git a/Week01/merge-two-sorted-lists.py b/Week01/merge-two-sorted-lists.py index ff715f7a7..7a16014b1 100644 --- a/Week01/merge-two-sorted-lists.py +++ b/Week01/merge-two-sorted-lists.py @@ -30,7 +30,6 @@ def mergeTwoLists2(self, l1: ListNode, l2: ListNode) -> ListNode: """ if not l1: return l2 if not l2: return l1 - if l1.val < l2.val: l1.next = self.mergeTwoLists2(l1.next, l2) return l1 diff --git a/Week01/plus-one.py b/Week01/plus-one.py index 2e60baeec..075e22634 100644 --- a/Week01/plus-one.py +++ b/Week01/plus-one.py @@ -7,6 +7,7 @@ class Solution: def plusOne(self, digits: List[int]) -> List[int]: """ + Solution #1 Time: O(n) Space: O(1) """ @@ -23,5 +24,4 @@ def plusOne(self, digits: List[int]) -> List[int]: solution = Solution() ans = solution.plusOne([9,9,9]) - print(ans) # [1,0,0,0] \ No newline at end of file diff --git a/Week01/remove-duplicates.py b/Week01/remove-duplicates.py index 6e52deeb0..52c904f0f 100644 --- a/Week01/remove-duplicates.py +++ b/Week01/remove-duplicates.py @@ -8,16 +8,16 @@ class Solution: def removeDuplicates(self, nums: List[int]) -> int: """ Solution #1: Two Pointers + Time: O(n) + Space: O(1) """ if len(nums) == 0: return 0 - p = 0 for q in range(1, len(nums)): if nums[q] > nums[p]: p += 1 nums[p] = nums[q] - return p + 1 solution = Solution() From c1cdbfc0de6096fd8a41a6fab304fbc477a6ba2f Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Tue, 16 Jun 2020 13:01:54 -0400 Subject: [PATCH 11/48] better summary note --- Week01/NOTE.md | 95 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 17 deletions(-) diff --git a/Week01/NOTE.md b/Week01/NOTE.md index 67040346a..38318f6ef 100644 --- a/Week01/NOTE.md +++ b/Week01/NOTE.md @@ -17,20 +17,20 @@ Time Complexity of array operations List Operations ```python -a.append(x) -a.extend(iterable) -a.insert(i, x) -a.remove(x) -a.pop([i]) -a.clear() -a.count(x) -a.reverse() -a.copy() -a.index(x[, start[, end]]) +a.append(x) # appends x to the end of the sequence (same as s[len(s):len(s)] = [x]) +a.extend(iterable) # extends s with the contents of t (same as s += t) +a.insert(i, x) # inserts x into s at the index given by i (same as s[i:i] = [x]) +a.remove(x) # remove the first item from s where s[i] is equal to x +a.pop([i]) # retrieves the item at i and also removes it from s +a.clear() # removes all items from s (same as del s[:]) +a.count(x) # total number of occurrences of x in s +a.reverse() # reverses the items of s in place +a.copy() # creates a shallow copy of s (same as s[:]) +a.index(x[, start[, end]]) # index of the first occurrence of x in s a.sort(key=None, reverse=False) ``` -Common Sequence Operations +Common Sequence Operations (list, tuple, range, etc.) ```python x in s # True if an item of s is equal to x, else False x not in s # False if an item of s is equal to x, else True @@ -46,21 +46,49 @@ reversed(s) # return a reverse iterator sorted(s) # return a new sorted list from the items in iterable ``` -List Comprehensions +String Operations ```python -vec = [-4, -2, 0, 2, 4] +string.digits # The string '0123456789' +string.hexdigits # The string '0123456789abcdefABCDEF' +string.octdigits # The string '01234567' + +ord(c) # Unicode code representation of the char +ord(c) - ord('a') # Position of the char in 26 letters +chr(i) # String representation of the char unicode code + +s.strip([chars]) +s.startswith(prefix) +s.endswith(prefix) +s.slipt(delimiter) +s.lower() +s.upper() +'Hello {name}'.format(name='World') +``` +Coding Techniques +```python +# List comprehension +vec = [-4, -2, 0, 2, 4] [x*2 for x in vec] # [-8, -4, 0, 4, 8] - [x for x in vec if x >= 0] # [0, 2, 4] - [abs(x) for x in vec] # [4, 2, 0, 2, 4] -[(x, x**2) for x in range(6)] # create a list of 2-tuples (number, square) +# Create a list of 2-tuples (number, square) +[(x, x**2) for x in range(4)] # [(0, 0), (1, 1), (2, 4), (3, 9)] +# Flatten a 2-D list vec = [[1,2,3], [4,5,6], [7,8,9]] +[num for elem in vec for num in elem] + +# Rotate array by k times (right shift) +nums = [1,2,3,4,5,6,7] +for i in range(len(nums)): + res[(i+k) % len(nums)] = nums[i] +res # [5,6,7,1,2,3,4] -[num for elem in vec for num in elem] # flatten a list +# Useful functions +functools.reduce(func, iter, [initial_value]) +itertools.groupby(iterable[, key]) ``` Linked Lists @@ -79,3 +107,36 @@ Doubly linked list: `L -> x <- 2 <-> 3 <-> 5 <-> 4 -> x` - 3's prev is 2 , 3's next is 5 - 4's prev is 3 , 5's next is 4 - 4's prev is 5 , 4's next is None + +Coding Techniques +```python +# Linked list recursion (think backwards) +def reverseList(self, head: ListNode) -> ListNode: + if not head or not head.next: return head # end condition + p = self.reverseList(head.next) # rest of the nodes are already reversed + head.next.next = head # start to reverse the first two nodes + head.next = None + return p # return head +``` + +Stacks and Queues +----------------- + +- Stack: First In Last Out (FILO) +- Queue: First In First Out (FIFO) + +Stack Libraries (Stack uses the build-in list-type) +```python +stack.append(e) # push +stack[-1] # peek +stack.pop() # pop +len(stack) # len +``` + +Queue Libraries (Queue uses the collection.deque class) +```python +queue.append(e) # push +queue[0] # peek front +queue[-1] # peek back +queue.popleft() # pop +``` \ No newline at end of file From 6fb7d0bbec9a7d0143392cbdbbc7e8edd62d3899 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Thu, 18 Jun 2020 01:12:58 -0400 Subject: [PATCH 12/48] update notes --- Week01/NOTE.md | 146 +++++++++++++++++++++++++++++++------------------ Week02/NOTE.md | 118 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 209 insertions(+), 55 deletions(-) diff --git a/Week01/NOTE.md b/Week01/NOTE.md index 38318f6ef..cd4d94c91 100644 --- a/Week01/NOTE.md +++ b/Week01/NOTE.md @@ -15,8 +15,24 @@ Time Complexity of array operations - Insertion O(n) - Deletion O(n-i) +Common Sequence Operations (list, tuple, range, etc.) +```py +x in s # True if an item of s is equal to x, else False +x not in s # False if an item of s is equal to x, else True +s + t # the concatenation of s and t +s * n or n * s # equivalent to adding s to itself n times +s[i] # ith item of s, origin 0 +s[i:j] # slice of s from i to j +s[i:j:k] # slice of s from i to j with step k +len(s) # length of s +min(s) # smallest item of s +max(s) # largest item of s +reversed(s) # return a reverse iterator +sorted(s) # return a new sorted list from the items in iterable +``` + List Operations -```python +```py a.append(x) # appends x to the end of the sequence (same as s[len(s):len(s)] = [x]) a.extend(iterable) # extends s with the contents of t (same as s += t) a.insert(i, x) # inserts x into s at the index given by i (same as s[i:i] = [x]) @@ -30,43 +46,25 @@ a.index(x[, start[, end]]) # index of the first occurrence of x in s a.sort(key=None, reverse=False) ``` -Common Sequence Operations (list, tuple, range, etc.) -```python -x in s # True if an item of s is equal to x, else False -x not in s # False if an item of s is equal to x, else True -s + t # the concatenation of s and t -s * n or n * s # equivalent to adding s to itself n times -s[i] # ith item of s, origin 0 -s[i:j] # slice of s from i to j -s[i:j:k] # slice of s from i to j with step k -len(s) # length of s -min(s) # smallest item of s -max(s) # largest item of s -reversed(s) # return a reverse iterator -sorted(s) # return a new sorted list from the items in iterable -``` - String Operations -```python -string.digits # The string '0123456789' -string.hexdigits # The string '0123456789abcdefABCDEF' -string.octdigits # The string '01234567' - -ord(c) # Unicode code representation of the char -ord(c) - ord('a') # Position of the char in 26 letters -chr(i) # String representation of the char unicode code - -s.strip([chars]) -s.startswith(prefix) -s.endswith(prefix) -s.slipt(delimiter) -s.lower() -s.upper() +```py +string.digits # the string '0123456789' +string.hexdigits # the string '0123456789abcdefABCDEF' +string.octdigits # the string '01234567' +ord(c) # the unicode code representation of the char +ord(c) - ord('a') # the position of the char in 26 letters +chr(i) # string representation of the char unicode code +s.strip([chars]) # return a copy of the string with the leading and trailing characters removed. +s.startswith(prefix) # return True if string starts with the prefix, False otherwise. +s.endswith(prefix) # return True if string starts with the prefix, False otherwise. +s.slipt(delimiter) # return a list of the words of the string s. +s.lower() # return a copy of the string with all the lowercase characters +s.upper() # return a copy of the string with all the uppercase characters 'Hello {name}'.format(name='World') ``` Coding Techniques -```python +```py # List comprehension vec = [-4, -2, 0, 2, 4] [x*2 for x in vec] # [-8, -4, 0, 4, 8] @@ -82,9 +80,10 @@ vec = [[1,2,3], [4,5,6], [7,8,9]] # Rotate array by k times (right shift) nums = [1,2,3,4,5,6,7] -for i in range(len(nums)): - res[(i+k) % len(nums)] = nums[i] -res # [5,6,7,1,2,3,4] +n = len(nums) +for i in range(n): + res[(i + k) % n] = nums[i] +print(res) # [5,6,7,1,2,3,4] # Useful functions functools.reduce(func, iter, [initial_value]) @@ -94,7 +93,7 @@ itertools.groupby(iterable[, key]) Linked Lists ------------ -A list implements an ordered collection of values, which may include repetitions. +> A list implements an ordered collection of values, which may include repetitions. Singly linked list: `L -> 2 -> 3 -> 5 -> 4 -> EOL` - 2 is linked list head @@ -108,15 +107,48 @@ Doubly linked list: `L -> x <- 2 <-> 3 <-> 5 <-> 4 -> x` - 4's prev is 3 , 5's next is 4 - 4's prev is 5 , 4's next is None +LinkedList Node +```py +class ListNode: + def __init__(self, val = 0, next = None): + self.val = val + self.next = next +``` + Coding Techniques -```python -# Linked list recursion (think backwards) +```py +# Linked List iteration framework +prev, curr = None, head +while curr: + # do something with prev and curr + if prev is None: + # when curr is head + else: + # when curr is not head + # move prev and curr + prev = curr + curr = curr.next + +def reverseList(self, head: ListNode) -> ListNode: + prev, curr = None, head + while curr: + cnext = curr.next + if prev is None: + curr.next = None + else: + curr.next = prev + prev = curr + curr = cnext + return prev + +# Linked List recursion framework def reverseList(self, head: ListNode) -> ListNode: - if not head or not head.next: return head # end condition + if not head or not head.next: # end condition + return head p = self.reverseList(head.next) # rest of the nodes are already reversed - head.next.next = head # start to reverse the first two nodes + head.next.next = head # start to reverse the first two nodes head.next = None - return p # return head + return p # return head ``` Stacks and Queues @@ -125,18 +157,24 @@ Stacks and Queues - Stack: First In Last Out (FILO) - Queue: First In First Out (FIFO) -Stack Libraries (Stack uses the build-in list-type) -```python -stack.append(e) # push -stack[-1] # peek +Stack Operations (Stack uses the build-in list-type) +```py +stack.append(x) # push stack.pop() # pop -len(stack) # len +stack[-1] # peek ``` -Queue Libraries (Queue uses the collection.deque class) -```python -queue.append(e) # push -queue[0] # peek front -queue[-1] # peek back -queue.popleft() # pop -``` \ No newline at end of file +Queue Operations (Queue uses the collection.deque class) +```py +q.append(x) # add x to the right side of the deque +q.appendleft(x) # add x to the left side of the deque +q.pop() # remove and return an element from the right side of the deque +q.popleft() # remove and return an element from the left side of the deque +q[0] # peek left side of the deque +q[-1] # peek right side of the deque +``` + +Skip List +--------- + +[TODO] \ No newline at end of file diff --git a/Week02/NOTE.md b/Week02/NOTE.md index 50de30414..209c038dd 100644 --- a/Week02/NOTE.md +++ b/Week02/NOTE.md @@ -1 +1,117 @@ -学习笔记 \ No newline at end of file +Learning Notes Week 02 +====================== + +Binary Trees +------------ + +> A binary tree is either empty, or a root node r together with a left binary tree and a right binary tree. + +``` + Height Depth Level + __A__ ----> 4 0 1 + / \ + __B C ----> 3 1 2 + / \ / \ + D E F G ----> 2 2 3 + / \ +H I ----> 1 3 4 +``` +- Node `A` is Root +- Node `A` has 2 children: Node `B` and Node `C` +- Node `B` is Node `A`'s left child +- Node `C` is Node `A`'s right child +- Node `A` is Node `B` and Node `C`'s parent +- Node `H` and Node `I` are a Leaf Nodes +- Node `A` is Node `H`'s ancestor +- Node `I` is Node `A`'s decedent + +Binary tree +> A tree has a root node and every node has at most 2 children + +Full Binary Tree +> A tree in which every node has either 0 or 2 children + +Perfect Binary Tree +> A full binary tree in which all leaves are at the same depth, and in which every parent has 2 children + +Complete Binary Tree +> A binary tree in which every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible. + +``` + __A__ + / \ + B C + / \ / \ + D E F G + / +H + +[_,A,B,C,D,E,F,G,H] +``` + +A complete binary tree has `2^k` nodes at every depth `k < n` and between `2^n` and `2^n+1 - 1` nodes altogether. It can be efficiently implemented as an array, where a node at index `i` has children at indexes `2i` and `2i+1` and a parent at index `i/2`, with 1-based indexing. + +Below are **NOT** Complete Binary Trees +``` + __A__ __A__ ______A______ + / \ / \ / \ + B C B C __B__ __C__ + / \ / \ / \ / \ / \ + D E D E F G D E F G + / \ \ \ \ / \ / \ / \ / \ +F G H H I H I J K L M N O +``` + +Binary Tree Traversal +--------------------- + +BinaryTree Node +```py +class BinaryTreeNode: + def __init__(self, val, left=None, right=None): + self.val = val + self.left = left + self.right = right +``` + + +DFS Pre-order Traversal: `root -> left -> right` +```py +def preorder(self, root): + if root: + self.visit(root.val) + self.preorder(left) + self.preorder(right) +``` + +DFS In-order Traversal: `left -> root -> right` +```py +def inorder(self, root): + if root: + self.inorder(left) + self.visit(root.val) + self.inorder(right) +``` + +DFS Post-order Traversal: `left -> right -> root` +```py +def postorder(self, root): + if root: + self.postorder(left) + self.postorder(right) + self.visit(root.val) +``` + +BFS Level Order Traversal: `top -> bottom, left -> right` +```py +def levelorder(root): + queue = collections.deque() + queue.append(root) + while queue: + node = queue.popleft() + visit(node) + if node.left: + queue.append(node.left) + if node.right: + queue.append(node.right) +``` From 03d949126d04c25e21abb04503a56b4bc25d45d8 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Fri, 19 Jun 2020 01:28:02 -0400 Subject: [PATCH 13/48] more notes --- Week01/NOTE.md | 41 ++++++++++++------ Week02/NOTE.md | 115 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 130 insertions(+), 26 deletions(-) diff --git a/Week01/NOTE.md b/Week01/NOTE.md index cd4d94c91..35153fbc7 100644 --- a/Week01/NOTE.md +++ b/Week01/NOTE.md @@ -4,16 +4,18 @@ Learning Notes Week 01 Array ----- -Basic Concepts +> Array is a contiguous block of memory. It is usually used to represent sequences. + - Arrays are lists, lists are mutable sequences, tuples are immutable sequences -- list is dynamically-resized, there is no upper bound +- Lists are dynamically-resized, there is no upper bound - Values can be deleted and inserted at arbitrary locations -Time Complexity of array operations -- Retrieving O(1) -- Updating O(1) -- Insertion O(n) -- Deletion O(n-i) +| Operation | Time Complexity | +| ---------- | :-------------: | +| Access | O(1) | +| Search | O(1) | +| Insertion | O(n) | +| Deletion | O(n) | Common Sequence Operations (list, tuple, range, etc.) ```py @@ -107,6 +109,13 @@ Doubly linked list: `L -> x <- 2 <-> 3 <-> 5 <-> 4 -> x` - 4's prev is 3 , 5's next is 4 - 4's prev is 5 , 4's next is None +| Operation | Time Complexity | +| ---------- | :-------------: | +| Access | O(n) | +| Search | O(n) | +| Insertion | O(1) | +| Deletion | O(1) | + LinkedList Node ```py class ListNode: @@ -117,7 +126,7 @@ class ListNode: Coding Techniques ```py -# Linked List iteration framework +# Linked List iteration framework (Double Pointers) prev, curr = None, head while curr: # do something with prev and curr @@ -141,7 +150,7 @@ def reverseList(self, head: ListNode) -> ListNode: curr = cnext return prev -# Linked List recursion framework +# Linked List recursion framework (Think Backwards) def reverseList(self, head: ListNode) -> ListNode: if not head or not head.next: # end condition return head @@ -154,17 +163,23 @@ def reverseList(self, head: ListNode) -> ListNode: Stacks and Queues ----------------- -- Stack: First In Last Out (FILO) -- Queue: First In First Out (FIFO) +> Stacks support first-in, last-out (FILO) for inserts and deletes, whereas queues are first-in first-out (FIFO) + +| Operation | Time Complexity | +| ---------- | :-------------: | +| Access | O(n) | +| Search | O(n) | +| Insertion | O(1) | +| Deletion | O(1) | -Stack Operations (Stack uses the build-in list-type) +Stack Operations (List data types represent the implementation of stacks) ```py stack.append(x) # push stack.pop() # pop stack[-1] # peek ``` -Queue Operations (Queue uses the collection.deque class) +Queue Operations (collection.deque - class represent the implementation of double ended queues) ```py q.append(x) # add x to the right side of the deque q.appendleft(x) # add x to the left side of the deque diff --git a/Week02/NOTE.md b/Week02/NOTE.md index 209c038dd..ea6f5d5fd 100644 --- a/Week02/NOTE.md +++ b/Week02/NOTE.md @@ -1,10 +1,48 @@ Learning Notes Week 02 ====================== +Hash Table +---------- + +> A hash table is a data structure used to store keys, optionally, with corresponding values. + +- A mapping object maps hashable values to arbitrary objects. Mappings are mutable objects. The only standard mapping type in python is Dictionary. +- A dictionary’s keys are almost arbitrary values. Values that are not hashable (like lists, dictionaries or other mutable types) may not be used as keys. +- Dictionaries can be created by placing a comma-separated list of key: value pairs within braces or by the dict() constructor. +- A set is an unordered collection with no duplicate elements. Curly braces or the set() function can be used to create sets. (empty set must use set()) + +| Operation | Time Complexity | +| ---------- | :-------------: | +| Access | N/A | +| Search | O(1) | +| Insertion | O(1) | +| Deletion | O(1) | + +Dictionary Operations +```py +d[key] # Return the item of d with key key. Raises a KeyError if key is not in the map. +d[key] = value # Set d[key] to value. +del d[key] # Remove d[key] from d. Raises a KeyError if key is not in the map. +key in d # Return True if d has a key key, else False. +key not in d # Equivalent to not key in d. +d.clear() # Remove all items from the dictionary. +d.get(key[, default]) # Return the value for key if key is in the dictionary, else default. +d.items() # Return a new view of the dictionary’s items ((key, value) pairs). +d.keys() # Return a new view of the dictionary’s keys. +d.values() # Return a new view of the dictionary’s values. +``` + Binary Trees ------------ -> A binary tree is either empty, or a root node r together with a left binary tree and a right binary tree. +> A binary tree is either empty, or a root node together with a left binary tree and a right binary tree. + +| Operation | Time Complexity | +| ---------- | :-------------: | +| Access | O(log(n)) | +| Search | O(log(n)) | +| Insertion | O(log(n)) | +| Deletion | O(log(n)) | ``` Height Depth Level @@ -46,7 +84,7 @@ Complete Binary Tree / H -[_,A,B,C,D,E,F,G,H] +[A,B,C,D,E,F,G,H] ``` A complete binary tree has `2^k` nodes at every depth `k < n` and between `2^n` and `2^n+1 - 1` nodes altogether. It can be efficiently implemented as an array, where a node at index `i` has children at indexes `2i` and `2i+1` and a parent at index `i/2`, with 1-based indexing. @@ -62,9 +100,6 @@ Below are **NOT** Complete Binary Trees F G H H I H I J K L M N O ``` -Binary Tree Traversal ---------------------- - BinaryTree Node ```py class BinaryTreeNode: @@ -74,23 +109,24 @@ class BinaryTreeNode: self.right = right ``` +Binary Tree Traversal DFS Pre-order Traversal: `root -> left -> right` ```py def preorder(self, root): if root: - self.visit(root.val) - self.preorder(left) - self.preorder(right) + visit(root) + preorder(left) + preorder(right) ``` DFS In-order Traversal: `left -> root -> right` ```py def inorder(self, root): if root: - self.inorder(left) - self.visit(root.val) - self.inorder(right) + inorder(left) + visit(root) + inorder(right) ``` DFS Post-order Traversal: `left -> right -> root` @@ -99,19 +135,72 @@ def postorder(self, root): if root: self.postorder(left) self.postorder(right) - self.visit(root.val) + self.visit(root) ``` BFS Level Order Traversal: `top -> bottom, left -> right` ```py +def levelOrder(root): + if root: + self.visit(root) + if node.left: + levelOrder(root.left) + if node.right: + + def levelorder(root): queue = collections.deque() queue.append(root) while queue: node = queue.popleft() - visit(node) + self.visit(node) if node.left: queue.append(node.left) if node.right: queue.append(node.right) ``` + +Heap +---- + +> A heap is a complete binary tree, and is represent by array. The children of the node at index i are at indices 2i + 1 and 2i + 2. + +Given element in a heap at position `i`: +- parent position: `(i-1) >> 1` +- left child position: `2*i + 1` +- right child position: `2*i + 2` + +max-heap: the key at each node is at least as great as the keys at it's children. +``` + _____561_____ + / \ + ___314_ _401_ + / \ / \ + _28 156 359 271 + / \ +11 3 +``` + +min-heap: the key at each node is at least as small as the keys at it's children. +```py + _____3____ + / \ + _____11_ _28_ + / \ / \ + _156_ 314 561 401 + / \ +359 271 +``` + +collections.heapq +```py +heap = [] # creates an empty heap +heap[0] # smallest element on the heap without popping it +heapq.heapify(L) # transforms list into a heap, in-place, in linear time +heapq.heappush(h, e) # pushes a new element on the heap +heapq.heappop(h) # pops the smallest item from the heap +heapq.heappushpop(h, a) # pushes a on the heap and then pops and returns the smallest element +heapq.heapreplace(h, e) # pops and returns smallest item, and adds new item; the heap size is unchanged +heapq.nlargest(L) # Find the n largest elements in a dataset. Same as sorted(iterable, key=key, reverse=True)[:n] +heapq.nsmallest(L) # Find the n smallest elements in a dataset. Same as sorted(iterable, key=key)[:n] +``` \ No newline at end of file From 440f7edd008c92da94ca1980c6851f5d48ad3f81 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sat, 20 Jun 2020 02:54:51 -0400 Subject: [PATCH 14/48] binary heap impl --- Week02/NOTE.md | 34 +++++++------- Week02/binary_heap.py | 106 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 17 deletions(-) create mode 100644 Week02/binary_heap.py diff --git a/Week02/NOTE.md b/Week02/NOTE.md index ea6f5d5fd..5c5066f3a 100644 --- a/Week02/NOTE.md +++ b/Week02/NOTE.md @@ -140,14 +140,6 @@ def postorder(self, root): BFS Level Order Traversal: `top -> bottom, left -> right` ```py -def levelOrder(root): - if root: - self.visit(root) - if node.left: - levelOrder(root.left) - if node.right: - - def levelorder(root): queue = collections.deque() queue.append(root) @@ -170,6 +162,14 @@ Given element in a heap at position `i`: - left child position: `2*i + 1` - right child position: `2*i + 2` +| Operation | Time Complexity | +| ---------- | :-------------: | +| find-min | O(1) | +| delete-min | O(logn) | +| Insert | O(logn) | +| K largest | O(nlogK) | +| K smallest | O(nlogK) | + max-heap: the key at each node is at least as great as the keys at it's children. ``` _____561_____ @@ -182,7 +182,7 @@ max-heap: the key at each node is at least as great as the keys at it's children ``` min-heap: the key at each node is at least as small as the keys at it's children. -```py +``` _____3____ / \ _____11_ _28_ @@ -192,15 +192,15 @@ min-heap: the key at each node is at least as small as the keys at it's children 359 271 ``` -collections.heapq +**heapq** Operations ```py -heap = [] # creates an empty heap -heap[0] # smallest element on the heap without popping it -heapq.heapify(L) # transforms list into a heap, in-place, in linear time -heapq.heappush(h, e) # pushes a new element on the heap -heapq.heappop(h) # pops the smallest item from the heap +heap = [] # creates an empty heap +heap[0] # smallest element on the heap without popping it +heapq.heapify(L) # transforms list into a heap, in-place, in linear time +heapq.heappush(h, e) # pushes a new element on the heap +heapq.heappop(h) # pops the smallest item from the heap heapq.heappushpop(h, a) # pushes a on the heap and then pops and returns the smallest element heapq.heapreplace(h, e) # pops and returns smallest item, and adds new item; the heap size is unchanged -heapq.nlargest(L) # Find the n largest elements in a dataset. Same as sorted(iterable, key=key, reverse=True)[:n] -heapq.nsmallest(L) # Find the n smallest elements in a dataset. Same as sorted(iterable, key=key)[:n] +heapq.nlargest(n, L) # Find the n largest elements in a dataset. +heapq.nsmallest(n, L) # Find the n smallest elements in a dataset. ``` \ No newline at end of file diff --git a/Week02/binary_heap.py b/Week02/binary_heap.py new file mode 100644 index 000000000..5293f2287 --- /dev/null +++ b/Week02/binary_heap.py @@ -0,0 +1,106 @@ +""" +My Implementation of Heap +""" +class BinaryHeap: + def __init__(self, capacity = 10): + self.heapsize = 0 + self.heap = [-1] * (capacity + 1) + + def is_empty(self): + return self.heapsize == 0 + + def is_full(self): + return self.heapsize == len(self.heap) + + def insert(self, elem): + """ + Inserts new element in to heap + Time Complexity: O(log N) + """ + if self.is_full(): + raise Exception("Heap is full, No space to insert new element") + self.heap[self.heapsize] = elem # 1. append new element to the end of heap. + self.heapsize += 1 # 2. heap size + 1. + endpos = self.heapsize - 1 + self._siftup(endpos) # 3. bubble up the larger child until hitting root. + + def _siftup(self, i): + """ + Maintains the heap property while inserting an element at position i + """ + newitem = self.heap[i] + while i > 0: + parentpos = (i - 1) >> 1 # parent position + parent = self.heap[parentpos] + if newitem > parent: + self.heap[i] = parent # Move the larger child up. + i = parentpos + continue + break + self.heap[i] = newitem + + def delete(self, i): + """ + Remove an element at position i + """ + if self.is_empty(): + raise Exception("Heap is empty, No element to delete") + delitem = self.heap[i] + endpos = self.heapsize - 1 + self.heap[i] = self.heap[endpos] # 1. replace the element at position i with the last element. + self.heapsize -= 1 # 2. heap size - 1 + self._siftdown(i) # 3. move down the new element until the end of heap. + return delitem + + def _siftdown(self, i): + """ + Maintains the heap property while deleting an element. + """ + target = self.heap[i] + endpos = self.heapsize + childpos = 2 * i + 1 # leftmost child position + while childpos < endpos: + rightpos = childpos + 1 + if rightpos < endpos and self.heap[rightpos] > self.heap[childpos]: + childpos = rightpos + if target >= self.heap[childpos]: + break + self.heap[i] = self.heap[childpos] # Move the smaller child down. + i = childpos + childpos = 2 * i + 1 + self.heap[i] = target + + def find_max(self): + if self.is_empty(): + raise Exception("Heap is empty.") + return self.heap[0] + + def printheap(self): + n = self.heapsize + depth = n // 2 + line = '' + for i in range(depth + 1): + j = 0 + while j < 2**i and (j + 2**i - 1) < n: + padding = ' ' * (2**(depth - i) - 1) + line += padding + str(self.heap[j + 2**i - 1]) + padding + ' ' + j += 1 + line = line[:-1] + '\n' + print(line) + +max_heap = BinaryHeap(10) +max_heap.insert(10) +max_heap.insert(4) +max_heap.insert(9) +max_heap.insert(1) +max_heap.insert(7) +max_heap.insert(5) +max_heap.insert(3) + +max_heap.printheap() +max_heap.delete(5) +max_heap.printheap() +print(max_heap.find_max()) +max_heap.delete(0) +max_heap.printheap() +print(max_heap.find_max()) \ No newline at end of file From 3e9171bcec5f7320974e9a3534c3e127f747398a Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 21 Jun 2020 00:28:40 -0400 Subject: [PATCH 15/48] some updates on notes and binary heap --- Week01/NOTE.md | 6 +++++ Week02/binary_heap.py | 59 +++++++++++++++++++++++++------------------ 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/Week01/NOTE.md b/Week01/NOTE.md index 35153fbc7..5b45348fc 100644 --- a/Week01/NOTE.md +++ b/Week01/NOTE.md @@ -80,6 +80,12 @@ vec = [-4, -2, 0, 2, 4] vec = [[1,2,3], [4,5,6], [7,8,9]] [num for elem in vec for num in elem] +# Get sliding windows of size k in an array of nums +nums, k = [1,2,3,4,5,6], 3 +n = len(nums) +windows = [nums[i:i+k] for i in range(n-k+1)] +print(windows) # [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]] + # Rotate array by k times (right shift) nums = [1,2,3,4,5,6,7] n = len(nums) diff --git a/Week02/binary_heap.py b/Week02/binary_heap.py index 5293f2287..29ecad6dc 100644 --- a/Week02/binary_heap.py +++ b/Week02/binary_heap.py @@ -4,7 +4,7 @@ class BinaryHeap: def __init__(self, capacity = 10): self.heapsize = 0 - self.heap = [-1] * (capacity + 1) + self.heap = [-1] * capacity def is_empty(self): return self.heapsize == 0 @@ -19,56 +19,68 @@ def insert(self, elem): """ if self.is_full(): raise Exception("Heap is full, No space to insert new element") - self.heap[self.heapsize] = elem # 1. append new element to the end of heap. - self.heapsize += 1 # 2. heap size + 1. + # 1. append new element to the end of heap. + self.heap[self.heapsize] = elem + # 2. increase the heapsize by 1. + self.heapsize += 1 + # 3. bubble up the larger child until hitting root. endpos = self.heapsize - 1 - self._siftup(endpos) # 3. bubble up the larger child until hitting root. + self._siftup(endpos) def _siftup(self, i): """ Maintains the heap property while inserting an element at position i """ - newitem = self.heap[i] while i > 0: - parentpos = (i - 1) >> 1 # parent position + newitem = self.heap[i] + parentpos = (i - 1) >> 1 parent = self.heap[parentpos] if newitem > parent: - self.heap[i] = parent # Move the larger child up. + # newitem is bigger, move it up. + newitem, parent = parent, newitem + # process the next element (parentpos). i = parentpos continue + # parent is bigger, we are done. break - self.heap[i] = newitem def delete(self, i): """ - Remove an element at position i + Remove an element at position i from the heap + Time Complexity: O(log N) """ if self.is_empty(): raise Exception("Heap is empty, No element to delete") delitem = self.heap[i] endpos = self.heapsize - 1 - self.heap[i] = self.heap[endpos] # 1. replace the element at position i with the last element. - self.heapsize -= 1 # 2. heap size - 1 - self._siftdown(i) # 3. move down the new element until the end of heap. + # 1. replace the element at position i with the last element. + self.heap[i] = self.heap[endpos] + # 2. decrease heapsize by 1 (so the last item is removed). + self.heapsize -= 1 + # 3. move down the new element until the end of heap. + self._siftdown(i) return delitem def _siftdown(self, i): """ Maintains the heap property while deleting an element. """ - target = self.heap[i] - endpos = self.heapsize - childpos = 2 * i + 1 # leftmost child position - while childpos < endpos: - rightpos = childpos + 1 - if rightpos < endpos and self.heap[rightpos] > self.heap[childpos]: + leftpos = 2 * i + 1 + while leftpos < self.heapsize: + delitem = self.heap[i] + # select the bigger one from leftchild and rightchild. + childpos = leftpos + rightpos = leftpos + 1 + if rightpos < self.heapsize and self.heap[rightpos] > self.heap[leftpos]: childpos = rightpos - if target >= self.heap[childpos]: + # delitem is bigger than child, we are done. + if delitem >= self.heap[childpos]: break - self.heap[i] = self.heap[childpos] # Move the smaller child down. + # delitem is smaller, move it down. + self.heap[i], self.heap[childpos] = self.heap[childpos], self.heap[i] + # process the next element (childpos). i = childpos - childpos = 2 * i + 1 - self.heap[i] = target + leftpos = 2 * i + 1 def find_max(self): if self.is_empty(): @@ -100,7 +112,6 @@ def printheap(self): max_heap.printheap() max_heap.delete(5) max_heap.printheap() -print(max_heap.find_max()) max_heap.delete(0) max_heap.printheap() -print(max_heap.find_max()) \ No newline at end of file +print("heapmax = ", max_heap.find_max()) \ No newline at end of file From c2cd77878c6cb8d4e86eede65abbea7a94b06098 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 21 Jun 2020 23:27:19 -0400 Subject: [PATCH 16/48] week2 works --- Week01/linked_list.py | 1 - Week02/NOTE.md | 45 ++++++++++- Week02/binary-tree-traversal.py | 129 ++++++++++++++++++++++++++++++++ Week02/binary_heap.py | 6 +- Week02/group_anagrams.py | 37 +++++++++ Week02/n-ary_tree_traversal.py | 91 ++++++++++++++++++++++ Week02/tree.py | 109 +++++++++++++++++++++++++++ Week02/valid_anagram.py | 34 +++++++++ 8 files changed, 449 insertions(+), 3 deletions(-) create mode 100644 Week02/binary-tree-traversal.py create mode 100644 Week02/group_anagrams.py create mode 100644 Week02/n-ary_tree_traversal.py create mode 100644 Week02/tree.py create mode 100644 Week02/valid_anagram.py diff --git a/Week01/linked_list.py b/Week01/linked_list.py index aa13c5c17..79eb0095e 100644 --- a/Week01/linked_list.py +++ b/Week01/linked_list.py @@ -1,7 +1,6 @@ """ Singly Linked List Data Structure """ - class ListNode: """ Linked List Node diff --git a/Week02/NOTE.md b/Week02/NOTE.md index 5c5066f3a..fc7b87b94 100644 --- a/Week02/NOTE.md +++ b/Week02/NOTE.md @@ -203,4 +203,47 @@ heapq.heappushpop(h, a) # pushes a on the heap and then pops and returns the sma heapq.heapreplace(h, e) # pops and returns smallest item, and adds new item; the heap size is unchanged heapq.nlargest(n, L) # Find the n largest elements in a dataset. heapq.nsmallest(n, L) # Find the n smallest elements in a dataset. -``` \ No newline at end of file +``` + +Graph +----- + +> A graph is a finite set of vertices and connected by a set of edges. + +``` +(1:M)---(0:E) + | \ | + | \ | + | \ | +(2:B)---(3:L) + \ + \ + (4:P) +``` + +Types of graphs: +- Undirected Graph: nodes are connected by edges that are all bidirectional. +- Directed: nodes are connected by directed edges – they only go in one direction. + +Graph Adjacency List Representation +- The size of the array is equal to the number of nodes. +- A single index, array[i] represents the list of nodes adjacent to the ith node. +``` +0 -> 1 -> 3# +1 -> 0 -> 2 -> 3# +2 -> 1 -> 3 -> 4# +3 -> 0 -> 1 -> 2# +4 -> 2# +``` + +Graph Adjacency Matrix Representation +- An Adjacency Matrix is a 2D array of size V x V where V is the number of nodes in a graph. +- A slot matrix[i][j] = 1 indicates that there is an edge from node i to node j. + +| | 0 | 1 | 2 | 3 | 4 | +|-------|---|---|---|---|---| +| **0** | 0 | 1 | 0 | 1 | 0 | +| **1** | 1 | 0 | 1 | 1 | 0 | +| **2** | 0 | 1 | 0 | 1 | 1 | +| **3** | 1 | 1 | 1 | 0 | 0 | +| **4** | 0 | 0 | 1 | 0 | 0 | \ No newline at end of file diff --git a/Week02/binary-tree-traversal.py b/Week02/binary-tree-traversal.py new file mode 100644 index 000000000..72afec825 --- /dev/null +++ b/Week02/binary-tree-traversal.py @@ -0,0 +1,129 @@ +""" +144. Binary Tree Preorder Traversal +https://leetcode.com/problems/binary-tree-preorder-traversal/ + +94. Binary Tree Inorder Traversal +https://leetcode.com/problems/binary-tree-inorder-traversal/ + +145. Binary Tree Postorder Traversal +https://leetcode.com/problems/binary-tree-postorder-traversal/ +""" +from typing import List +from tree import BinaryTreeNode, create_binary_tree, print_binary_tree + +class Solution: + def preorderTraversal1(self, root: BinaryTreeNode) -> List[int]: + """ + Solution #1: Recursively + """ + def traverse(root): + if not root: return + res.append(root.val) + traverse(root.left) + traverse(root.right) + + res = [] + traverse(root) + return res + + def preorderTraversal2(self, root: BinaryTreeNode) -> List[int]: + """ + Solution #2: Iteratively + """ + WHITE, GREY = 0, 1 + res = [] + stack = [(WHITE, root),] + while stack: + color, node = stack.pop() + if not node: + continue + if color == WHITE: + stack.append((WHITE, node.right)) + stack.append((WHITE, node.left)) + stack.append((GREY, node)) + else: + res.append(node.val) + return res + + def inorderTraversal1(self, root: BinaryTreeNode) -> List[int]: + """ + Solution #1: Recursively + """ + def traverse(root): + if not root: return + traverse(root.left) + res.append(root.val) + traverse(root.right) + + res = [] + traverse(root) + return res + + def inorderTraversal2(self, root: BinaryTreeNode) -> List[int]: + """ + Solution #2: Iteratively + """ + WHITE, GREY = 0, 1 + res = [] + stack = [(WHITE, root),] + while stack: + color, node = stack.pop() + if not node: + continue + if color == WHITE: + stack.append((WHITE, node.right)) + stack.append((GREY, node)) + stack.append((WHITE, node.left)) + else: + res.append(node.val) + return res + + def postorderTraversal1(self, root: BinaryTreeNode) -> List[int]: + """ + Solution #1: Recursively + """ + def traverse(root): + if not root: return + traverse(root.left) + traverse(root.right) + res.append(root.val) + + res = [] + traverse(root) + return res + + def postorderTraversal2(self, root: BinaryTreeNode) -> List[int]: + """ + Solution #1: Iteratively + """ + WHITE, GREY = 0, 1 + res = [] + stack = [(WHITE, root),] + while stack: + color, node = stack.pop() + if not node: + continue + if color == WHITE: + stack.append((GREY, node)) + stack.append((WHITE, node.right)) + stack.append((WHITE, node.left)) + else: + res.append(node.val) + return res + +t = create_binary_tree([0,1,2,3,4,5,6,7,8,9]) +print_binary_tree(t) + +solution = Solution() +ans = solution.preorderTraversal1(t) +print('Binary Tree Preorder Traversal Recursively: ', ans) +ans = solution.preorderTraversal2(t) +print('Binary Tree Preorder Traversal Iteratively: ', ans) +ans = solution.inorderTraversal1(t) +print('Binary Tree Inorder Traversal Recursively: ', ans) +ans = solution.inorderTraversal2(t) +print('Binary Tree Inorder Traversal Iteratively: ', ans) +ans = solution.postorderTraversal1(t) +print('Binary Tree Postorder Traversal Recursively: ', ans) +ans = solution.postorderTraversal2(t) +print('Binary Tree Postorder Traversal Iteratively: ', ans) \ No newline at end of file diff --git a/Week02/binary_heap.py b/Week02/binary_heap.py index 29ecad6dc..21b4cee2c 100644 --- a/Week02/binary_heap.py +++ b/Week02/binary_heap.py @@ -1,5 +1,5 @@ """ -My Implementation of Heap +My Implementation of Max Heap """ class BinaryHeap: def __init__(self, capacity = 10): @@ -83,6 +83,10 @@ def _siftdown(self, i): leftpos = 2 * i + 1 def find_max(self): + """ + Return the Max Element (Heap Top) + Time Complexity: O(1) + """ if self.is_empty(): raise Exception("Heap is empty.") return self.heap[0] diff --git a/Week02/group_anagrams.py b/Week02/group_anagrams.py new file mode 100644 index 000000000..d94ba3791 --- /dev/null +++ b/Week02/group_anagrams.py @@ -0,0 +1,37 @@ +""" +49. Group Anagrams +https://leetcode.com/problems/valid-anagram/ +""" +from typing import List +from collections import defaultdict + +class Solution: + def groupAnagrams1(self, strs: List[str]) -> List[List[str]]: + """ + Solution #1: Sort + Time: O(N*K*logK): N = len(strs), K = len(longest string in strs) + Space: O(N*K): the dictionary + """ + ans = defaultdict(list) + for s in strs: + ans[tuple(sorted(s))].append(s) + return ans.values() + + def groupAnagrams2(self, strs: List[str]) -> List[List[str]]: + """ + Solution #2: Count + Time: O(N*K): N = len(strs), K = len(longest string in strs) + Space: O(N*K): the dictionary + """ + ans = 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() + +solution = Solution() +print(solution.groupAnagrams1(["eat", "tea", "tan", "ate", "nat", "bat"])) +print(solution.groupAnagrams2(["eat", "tea", "tan", "ate", "nat", "bat"])) +# [['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']] diff --git a/Week02/n-ary_tree_traversal.py b/Week02/n-ary_tree_traversal.py new file mode 100644 index 000000000..3cb6ea33c --- /dev/null +++ b/Week02/n-ary_tree_traversal.py @@ -0,0 +1,91 @@ +""" +589. N-ary Tree Preorder Traversal +https://leetcode.com/problems/n-ary-tree-preorder-traversal/ + +590. N-ary Tree Postorder Traversal +https://leetcode.com/problems/n-ary-tree-postorder-traversal/ + +429. N-ary Tree Level Order Traversal +https://leetcode.com/problems/n-ary-tree-level-order-traversal/ +""" +from typing import List +from tree import TreeNode, simple_tree + +class Solution: + def preorder1(self, root: TreeNode) -> List[int]: + """ + Solution #1: Recursively + """ + def traverse(root): + if not root: return + res.append(root.val) + if root.children is not None: + for child in root.children: + traverse(child) + + res = [] + traverse(root) + return res + + def preorder2(self, root: TreeNode) -> List[int]: + """ + Solution #2: Iteratively + """ + if not root: + return [] + res = [] + stack = [root] + while stack: + root = stack.pop() + if root.children is not None: + # from right to left append child + for child in root.children[::-1]: + stack.append(child) + if root is not None: + res.append(root.val) + return res + + def postorder1(self, root: TreeNode) -> List[int]: + """ + Solution #1: Recursively + """ + def traverse(root): + if not root: return + if root.children is not None: + for child in root.children: + traverse(child) + res.append(root.val) + + res = [] + traverse(root) + return res + + def postorder2(self, root: TreeNode) -> List[int]: + """ + Solution #2: Iteratively + """ + if not root: + return [] + res = [] + stack = [root] + while stack: + root = stack.pop() + if root is not None: + res.append(root.val) + if root.children is not None: + # from left to right append child + for child in root.children: + stack.append(child) + return res[::-1] # the result is reversed + +t = simple_tree() + +solution = Solution() +ans = solution.preorder1(t) +print('N-ary Tree Preorder Traversal Recursively: ', ans) +ans = solution.preorder2(t) +print('N-ary Tree Preorder Traversal Iteratively: ', ans) +ans = solution.postorder1(t) +print('N-ary Tree Postorder Traversal Recursively: ', ans) +ans = solution.postorder2(t) +print('N-ary Tree Postorder Traversal Iteratively: ', ans) \ No newline at end of file diff --git a/Week02/tree.py b/Week02/tree.py new file mode 100644 index 000000000..f1f6ca795 --- /dev/null +++ b/Week02/tree.py @@ -0,0 +1,109 @@ +""" +Tree Data Structure +""" +class TreeNode: + def __init__(self, val=None, children=None): + self.val = val + self.children = children + +def simple_tree(): + """ Create a simple tree + + ___1___ + / | \ + __3__ 2 4 + / \ + 5 6 + """ + return TreeNode(1, [TreeNode(3, [TreeNode(5), TreeNode(6)]), TreeNode(2), TreeNode(4)]) + +class BinaryTreeNode: + def __init__(self, val=None, left=None, right=None): + self.val = val + self.left = None + self.right = None + +def create_binary_tree(iterable = ()): + """ Given in iterable, create a binary tree in level order + Example: create_tree([0,1,2,3,4,5,6,7,8,9]) + + ____0__ + / \ + __1__ 2 + / \ / \ + 3 4 5 6 + / \ / + 7 8 9 + """ + def insert(node, i): + if i < len(iterable) and iterable[i] is not None: + node = BinaryTreeNode(iterable[i]) + node.left = insert(node.left, 2 * i + 1) + node.right = insert(node.right, 2 * i + 2) + return node + return insert(None, 0) + +def print_binary_tree(root, index=False): + """ Pretty-print the binary tree. + Inspired by https://pypi.org/project/binarytree/ + """ + def build_tree_string(root, current, index=False, delimiter='-'): + if root is None: + return [], 0, 0, 0 + + line1 = [] + line2 = [] + if index: + node_repr = '{}{}{}'.format(current, delimiter, root.val) + else: + node_repr = str(root.val) + + new_root_width = gap_size = len(node_repr) + + # Get the left and right sub-boxes, their widths, and root repr positions + l_box, l_box_width, l_root_start, l_root_end = \ + build_tree_string(root.left, 2 * current + 1, index, delimiter) + r_box, r_box_width, r_root_start, r_root_end = \ + build_tree_string(root.right, 2 * current + 2, index, delimiter) + + # Draw the branch connecting the current root node to the left sub-box + # Pad the line with whitespaces where necessary + if l_box_width > 0: + l_root = (l_root_start + l_root_end) // 2 + 1 + line1.append(' ' * (l_root + 1)) + line1.append('_' * (l_box_width - l_root)) + line2.append(' ' * l_root + '/') + line2.append(' ' * (l_box_width - l_root)) + new_root_start = l_box_width + 1 + gap_size += 1 + else: + new_root_start = 0 + + # Draw the representation of the current root node + line1.append(node_repr) + line2.append(' ' * new_root_width) + + # Draw the branch connecting the current root node to the right sub-box + # Pad the line with whitespaces where necessary + if r_box_width > 0: + r_root = (r_root_start + r_root_end) // 2 + line1.append('_' * r_root) + line1.append(' ' * (r_box_width - r_root + 1)) + line2.append(' ' * r_root + '\\') + line2.append(' ' * (r_box_width - r_root)) + gap_size += 1 + new_root_end = new_root_start + new_root_width - 1 + + # Combine the left and right sub-boxes with the branches drawn above + gap = ' ' * gap_size + new_box = [''.join(line1), ''.join(line2)] + for i in range(max(len(l_box), len(r_box))): + l_line = l_box[i] if i < len(l_box) else ' ' * l_box_width + r_line = r_box[i] if i < len(r_box) else ' ' * r_box_width + new_box.append(l_line + gap + r_line) + + # Return the new box, its width and its root repr positions + return new_box, len(new_box[0]), new_root_start, new_root_end + + lines = build_tree_string(root, 0, index)[0] + print('\n' + '\n'.join((line.rstrip() for line in lines))) diff --git a/Week02/valid_anagram.py b/Week02/valid_anagram.py new file mode 100644 index 000000000..c7a560e75 --- /dev/null +++ b/Week02/valid_anagram.py @@ -0,0 +1,34 @@ +""" +242. Valid Anagram +https://leetcode.com/problems/valid-anagram/ +""" +from typing import List + +class Solution: + def isAnagram1(self, s: str, t: str) -> bool: + """ + Solution #1: Sort + Time: O(N*logN): sort + Space: O(1) + """ + return sorted(s) == sorted(t) + + def isAnagram2(self, s: str, t: str) -> bool: + """ + Solution #2: Dictionary + Time: O(1) + Space: O(K): K is the common character in s and t + """ + if len(s) != len(t): return False + d = {} + for c in s: + d[ord(c)] = d.get(ord(c), 0) + 1 + for c in t: + d[ord(c)] = d.get(ord(c), 0) - 1 + return not [x for x in d if d[x] != 0] + +solution = Solution() +print(solution.isAnagram1("anagram", "nagaram")) # True +print(solution.isAnagram1("anagram", "nagara")) # False +print(solution.isAnagram2("anagram", "nagaram")) # True +print(solution.isAnagram2("anagram", "nagara")) # False From d9afe505362802d2338a97cb9c0e20690df7719b Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 21 Jun 2020 23:46:33 -0400 Subject: [PATCH 17/48] tree level order traversal --- Week02/binary-tree-traversal.py | 64 ++++++++++++++++++++++++++++----- Week02/n-ary_tree_traversal.py | 52 ++++++++++++++++++++++++--- 2 files changed, 103 insertions(+), 13 deletions(-) diff --git a/Week02/binary-tree-traversal.py b/Week02/binary-tree-traversal.py index 72afec825..989941492 100644 --- a/Week02/binary-tree-traversal.py +++ b/Week02/binary-tree-traversal.py @@ -7,9 +7,13 @@ 145. Binary Tree Postorder Traversal https://leetcode.com/problems/binary-tree-postorder-traversal/ + +102. Binary Tree Level Order Traversal +https://leetcode.com/problems/binary-tree-level-order-traversal/ """ from typing import List from tree import BinaryTreeNode, create_binary_tree, print_binary_tree +from collections import deque class Solution: def preorderTraversal1(self, root: BinaryTreeNode) -> List[int]: @@ -32,7 +36,7 @@ def preorderTraversal2(self, root: BinaryTreeNode) -> List[int]: """ WHITE, GREY = 0, 1 res = [] - stack = [(WHITE, root),] + stack = [(WHITE, root)] while stack: color, node = stack.pop() if not node: @@ -65,7 +69,7 @@ def inorderTraversal2(self, root: BinaryTreeNode) -> List[int]: """ WHITE, GREY = 0, 1 res = [] - stack = [(WHITE, root),] + stack = [(WHITE, root)] while stack: color, node = stack.pop() if not node: @@ -98,7 +102,7 @@ def postorderTraversal2(self, root: BinaryTreeNode) -> List[int]: """ WHITE, GREY = 0, 1 res = [] - stack = [(WHITE, root),] + stack = [(WHITE, root)] while stack: color, node = stack.pop() if not node: @@ -111,19 +115,63 @@ def postorderTraversal2(self, root: BinaryTreeNode) -> List[int]: res.append(node.val) return res + def levelOrder1(self, root: BinaryTreeNode) -> List[List[int]]: + """ + Solution #1: Recursively + """ + def traverse(node, level): + if not root: + return + if len(res) == level: + res.append([]) + res[level].append(node.val) + if node.left: + traverse(node.left, level+1) + if node.right: + traverse(node.right, level+1) + + res = [] + traverse(root, 0) + return res + + def levelOrder2(self, root: BinaryTreeNode) -> List[List[int]]: + """ + Solution #2: Iteratively + """ + if not root: + return [] + res = [] + queue = deque([root]) + while queue: + n = len(queue) + level = [] + for _ in range(n): + root = queue.popleft() + level.append(root.val) + if root.left: + queue.append(root.left) + if root.right: + queue.append(root.right) + res.append(level) + return res + t = create_binary_tree([0,1,2,3,4,5,6,7,8,9]) print_binary_tree(t) solution = Solution() ans = solution.preorderTraversal1(t) -print('Binary Tree Preorder Traversal Recursively: ', ans) +print('Binary Tree Preorder Traversal Recursively: ', ans) ans = solution.preorderTraversal2(t) -print('Binary Tree Preorder Traversal Iteratively: ', ans) +print('Binary Tree Preorder Traversal Iteratively: ', ans) ans = solution.inorderTraversal1(t) -print('Binary Tree Inorder Traversal Recursively: ', ans) +print('Binary Tree Inorder Traversal Recursively: ', ans) ans = solution.inorderTraversal2(t) -print('Binary Tree Inorder Traversal Iteratively: ', ans) +print('Binary Tree Inorder Traversal Iteratively: ', ans) ans = solution.postorderTraversal1(t) print('Binary Tree Postorder Traversal Recursively: ', ans) ans = solution.postorderTraversal2(t) -print('Binary Tree Postorder Traversal Iteratively: ', ans) \ No newline at end of file +print('Binary Tree Postorder Traversal Iteratively: ', ans) +ans = solution.levelOrder1(t) +print('Binary Tree Level Order Traversal Recursively: ', ans) +ans = solution.levelOrder2(t) +print('Binary Tree Level Order Traversal Iteratively: ', ans) \ No newline at end of file diff --git a/Week02/n-ary_tree_traversal.py b/Week02/n-ary_tree_traversal.py index 3cb6ea33c..6bec37a00 100644 --- a/Week02/n-ary_tree_traversal.py +++ b/Week02/n-ary_tree_traversal.py @@ -10,6 +10,7 @@ """ from typing import List from tree import TreeNode, simple_tree +from collections import deque class Solution: def preorder1(self, root: TreeNode) -> List[int]: @@ -17,7 +18,8 @@ def preorder1(self, root: TreeNode) -> List[int]: Solution #1: Recursively """ def traverse(root): - if not root: return + if not root: + return res.append(root.val) if root.children is not None: for child in root.children: @@ -50,7 +52,8 @@ def postorder1(self, root: TreeNode) -> List[int]: Solution #1: Recursively """ def traverse(root): - if not root: return + if not root: + return if root.children is not None: for child in root.children: traverse(child) @@ -78,14 +81,53 @@ def postorder2(self, root: TreeNode) -> List[int]: stack.append(child) return res[::-1] # the result is reversed + def levelOrder1(self, root: TreeNode) -> List[List[int]]: + """ + Solution #1: Recursively + """ + def traverse(root, level): + if not root: + return + if len(res) == level: + res.append([]) + res[level].append(root.val) + if root.children is not None: + for child in root.children: + traverse(child, level + 1) + + res = [] + traverse(root, 0) + return res + + def levelOrder2(self, root: TreeNode) -> List[List[int]]: + if not root: + return [] + res = [] + queue = deque([root]) + while queue: + n = len(queue) + level = [] + for _ in range(n): + node = queue.popleft() + level.append(node.val) + if node.children is not None: + for child in node.children: + queue.append(child) + res.append(level) + return res + t = simple_tree() solution = Solution() ans = solution.preorder1(t) -print('N-ary Tree Preorder Traversal Recursively: ', ans) +print('N-ary Tree Preorder Traversal Recursively: ', ans) ans = solution.preorder2(t) -print('N-ary Tree Preorder Traversal Iteratively: ', ans) +print('N-ary Tree Preorder Traversal Iteratively: ', ans) ans = solution.postorder1(t) print('N-ary Tree Postorder Traversal Recursively: ', ans) ans = solution.postorder2(t) -print('N-ary Tree Postorder Traversal Iteratively: ', ans) \ No newline at end of file +print('N-ary Tree Postorder Traversal Iteratively: ', ans) +ans = solution.levelOrder1(t) +print('N-ary Tree Level Order Traversal Recursively: ', ans) +ans = solution.levelOrder2(t) +print('N-ary Tree Level Order Traversal Iteratively: ', ans) \ No newline at end of file From 08090376af8568f15c412ebe240375e8b626d15f Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Tue, 23 Jun 2020 23:58:27 -0400 Subject: [PATCH 18/48] week 02 revisit --- Week02/NOTE.md | 143 ++++++++++++++++++++++++----- Week02/binary-tree-traversal.py | 2 +- Week02/binary_search_tree.py | 125 +++++++++++++++++++++++++ Week02/{tree.py => binary_tree.py} | 18 +--- Week02/n-ary_tree_traversal.py | 15 ++- Week03/NOTE.md | 4 +- 6 files changed, 263 insertions(+), 44 deletions(-) create mode 100644 Week02/binary_search_tree.py rename Week02/{tree.py => binary_tree.py} (89%) diff --git a/Week02/NOTE.md b/Week02/NOTE.md index fc7b87b94..5cf2dc37f 100644 --- a/Week02/NOTE.md +++ b/Week02/NOTE.md @@ -4,11 +4,11 @@ Learning Notes Week 02 Hash Table ---------- -> A hash table is a data structure used to store keys, optionally, with corresponding values. +> A hash table is a data structure used to store vals, optionally, with corresponding values. - A mapping object maps hashable values to arbitrary objects. Mappings are mutable objects. The only standard mapping type in python is Dictionary. -- A dictionary’s keys are almost arbitrary values. Values that are not hashable (like lists, dictionaries or other mutable types) may not be used as keys. -- Dictionaries can be created by placing a comma-separated list of key: value pairs within braces or by the dict() constructor. +- A dictionary’s vals are almost arbitrary values. Values that are not hashable (like lists, dictionaries or other mutable types) may not be used as vals. +- Dictionaries can be created by placing a comma-separated list of val: value pairs within braces or by the dict() constructor. - A set is an unordered collection with no duplicate elements. Curly braces or the set() function can be used to create sets. (empty set must use set()) | Operation | Time Complexity | @@ -20,15 +20,15 @@ Hash Table Dictionary Operations ```py -d[key] # Return the item of d with key key. Raises a KeyError if key is not in the map. -d[key] = value # Set d[key] to value. -del d[key] # Remove d[key] from d. Raises a KeyError if key is not in the map. -key in d # Return True if d has a key key, else False. -key not in d # Equivalent to not key in d. +d[val] # Return the item of d with val val. Raises a valError if val is not in the map. +d[val] = value # Set d[val] to value. +del d[val] # Remove d[val] from d. Raises a valError if val is not in the map. +val in d # Return True if d has a val val, else False. +val not in d # Equivalent to not val in d. d.clear() # Remove all items from the dictionary. -d.get(key[, default]) # Return the value for key if key is in the dictionary, else default. -d.items() # Return a new view of the dictionary’s items ((key, value) pairs). -d.keys() # Return a new view of the dictionary’s keys. +d.get(val[, default]) # Return the value for val if val is in the dictionary, else default. +d.items() # Return a new view of the dictionary’s items ((val, value) pairs). +d.vals() # Return a new view of the dictionary’s vals. d.values() # Return a new view of the dictionary’s values. ``` @@ -37,13 +37,6 @@ Binary Trees > A binary tree is either empty, or a root node together with a left binary tree and a right binary tree. -| Operation | Time Complexity | -| ---------- | :-------------: | -| Access | O(log(n)) | -| Search | O(log(n)) | -| Insertion | O(log(n)) | -| Deletion | O(log(n)) | - ``` Height Depth Level __A__ ----> 4 0 1 @@ -75,6 +68,9 @@ Perfect Binary Tree Complete Binary Tree > A binary tree in which every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible. +- A complete binary tree has `2^k` nodes at every depth `k < n` and between `2^n` and `2^n+1 - 1` nodes altogether. +- It can be efficiently implemented as an array, where a node at index `i` has children at indexes `2i` and `2i+1` and a parent at index `i/2`, with 1-based indexing. + ``` __A__ / \ @@ -84,11 +80,9 @@ Complete Binary Tree / H -[A,B,C,D,E,F,G,H] +[_,A,B,C,D,E,F,G,H] ``` -A complete binary tree has `2^k` nodes at every depth `k < n` and between `2^n` and `2^n+1 - 1` nodes altogether. It can be efficiently implemented as an array, where a node at index `i` has children at indexes `2i` and `2i+1` and a parent at index `i/2`, with 1-based indexing. - Below are **NOT** Complete Binary Trees ``` __A__ __A__ ______A______ @@ -152,6 +146,109 @@ def levelorder(root): queue.append(node.right) ``` +Binary Search Tree (BST) + +> A BST is a rooted binary tree whose internal nodes each store a val greater than all the vals in the node's left subtree and less than those in its right subtree. + +| Operation | Time Complexity | +| ---------- | :-------------: | +| Access | O(log(n)) | +| Search | O(log(n)) | +| Insertion | O(log(n)) | +| Deletion | O(log(n)) | + +BST Operations + +Search +```py +def search(root, val): + if root is None: + return None + if val < root.val: + return search(root.left, val) + elif val > root.val: + return search(root.right, val) + return root +``` + +Insert +``` + 100 100 + / \ Insert(40) / \ + 20 500 ---------> 20 500 + / \ / \ +10 30 10 30 + \ + 40 +``` + +```py +def insert(root, val): + if root is None: + return BinaryTreeNode(val) + if val < root.val: + root.left = insert(root.left, val) + elif val > root.val: + root.right = insert(node.right, val) + return root +``` + +Delete +``` +1) Node to be deleted is leaf: Simply remove from the tree. + 50 50 + / \ Delete(20) / \ + 30 70 ---------> 30 70 + / \ / \ \ / \ +20 40 60 80 40 60 80 + +2) Node to be deleted has only one child: Copy the child to the node and delete the child. + 50 50 + / \ Delete(30) / \ + 30 70 ---------> 40 70 + \ / \ / \ + 40 60 80 60 80 + +3) Node to be deleted has two children: Find inorder successor of the node. Copy contents of the inorder successor to the node and delete the inorder successor. + 50 60 + / \ Delete(50) / \ + 40 70 ---------> 40 70 + / \ \ + 60 80 80 +``` + +```py +def deleteNode(self, root: TreeNode, key: int) -> TreeNode: + if root is None: + return None + if key < root.val: + root.left = self.deleteNode(root.left, key) + elif key > root.val: + root.right = self.deleteNode(root.right, key) + else: + if root.left is None: + # has only right child + child = root.right + root = None + return child + elif root.right is None: + # has only left child + child = root.left + root = None + return child + # has two children + child = self.minChild(root.right) + root.val = child.val + root.right = self.deleteNode(root.right , child.val) + return root + +def minChild(self, node: TreeNode) -> TreeNode: + curr = node + while curr.left is not None: + curr = curr.left + return curr +``` + Heap ---- @@ -170,7 +267,7 @@ Given element in a heap at position `i`: | K largest | O(nlogK) | | K smallest | O(nlogK) | -max-heap: the key at each node is at least as great as the keys at it's children. +max-heap: the val at each node is at least as great as the vals at it's children. ``` _____561_____ / \ @@ -181,7 +278,7 @@ max-heap: the key at each node is at least as great as the keys at it's children 11 3 ``` -min-heap: the key at each node is at least as small as the keys at it's children. +min-heap: the val at each node is at least as small as the vals at it's children. ``` _____3____ / \ diff --git a/Week02/binary-tree-traversal.py b/Week02/binary-tree-traversal.py index 989941492..9a45e88f7 100644 --- a/Week02/binary-tree-traversal.py +++ b/Week02/binary-tree-traversal.py @@ -12,7 +12,7 @@ https://leetcode.com/problems/binary-tree-level-order-traversal/ """ from typing import List -from tree import BinaryTreeNode, create_binary_tree, print_binary_tree +from binary_tree import BinaryTreeNode, create_binary_tree, print_binary_tree from collections import deque class Solution: diff --git a/Week02/binary_search_tree.py b/Week02/binary_search_tree.py new file mode 100644 index 000000000..e24ea1f8a --- /dev/null +++ b/Week02/binary_search_tree.py @@ -0,0 +1,125 @@ +""" +700. Search in a Binary Search Tree +https://leetcode.com/problems/search-in-a-binary-search-tree/ + +701. Insert into a Binary Search Tree +https://leetcode.com/problems/insert-into-a-binary-search-tree/ + +450. Delete Node in a BST +https://leetcode.com/problems/delete-node-in-a-bst/ +""" +from binary_tree import BinaryTreeNode, create_binary_tree, print_binary_tree + +class Solution: + def searchBST1(self, root: BinaryTreeNode, val: int) -> BinaryTreeNode: + """ + Solution #1: Recursively + """ + if root is None: + return root + if val < root.val: + return self.searchBST1(root.left, val) + if val > root.val: + return self.searchBST1(root.right, val) + # val == root.val + return root + + def searchBST2(self, root: BinaryTreeNode, val: int) -> BinaryTreeNode: + """ + Solution #2: Iteratively + """ + curr = root + while curr: + if val < curr.val: + curr = curr.left + elif val > curr.val: + curr = curr.right + else: # val == root.val + break + return curr + + def insertIntoBST1(self, root: BinaryTreeNode, val: int) -> BinaryTreeNode: + if root is None: + return BinaryTreeNode(val) + if val < root.val: + root.left = self.insertIntoBST1(root.left, val) + else: # val > root.val + root.right = self.insertIntoBST1(root.right, val) + return root + + def insertIntoBST2(self, root: BinaryTreeNode, val: int) -> BinaryTreeNode: + if root is None: + return BinaryTreeNode(val) + parent, curr = None, root + while curr: + parent = curr + if val < curr.val: + curr = curr.left + elif val > curr.val: + curr = curr.right + else: # val == root.val + break + if val < parent.val: + parent.left = BinaryTreeNode(val) + elif val > parent.val: + parent.right = BinaryTreeNode(val) + return root + + def deleteNode(self, root: BinaryTreeNode, key: int) -> BinaryTreeNode: + if root is None: + return None + if key < root.val: + root.left = self.deleteNode(root.left, key) + elif key > root.val: + root.right = self.deleteNode(root.right, key) + else: + if root.left is None: + # has only right child + child = root.right + root = None + return child + elif root.right is None: + # has only left child + child = root.left + root = None + return child + # has two children + child = self.minChild(root.right) + root.val = child.val + root.right = self.deleteNode(root.right , child.val) + return root + + def minChild(self, node: BinaryTreeNode) -> BinaryTreeNode: + curr = node + while curr.left is not None: + curr = curr.left + return curr + +t = create_binary_tree([5,3,7,2,4,6]) +print_binary_tree(t) + +solution = Solution() +print('Search BST Recursively: ') +ans = solution.searchBST1(t, 3) +print_binary_tree(ans) +print('Search BST Iteratively: ') +ans = solution.searchBST2(t, 7) +print_binary_tree(ans) +print('Insert into BST Recursively: ') +ans = solution.insertIntoBST1(t, 1) +print_binary_tree(ans) +print('Insert into BST Iteratively: ') +ans = solution.insertIntoBST2(t, 8) +print_binary_tree(ans) +print('Delete Node (leaf): ') +ans = solution.deleteNode(t, 1) +print_binary_tree(ans) +print('Delete Node (leaf): ') +ans = solution.deleteNode(t, 4) +print_binary_tree(ans) +print('Delete Node (one child): ') +ans = solution.deleteNode(t, 3) +print_binary_tree(ans) +print('Delete Node (two children): ') +ans = solution.deleteNode(t, 5) +print_binary_tree(ans) diff --git a/Week02/tree.py b/Week02/binary_tree.py similarity index 89% rename from Week02/tree.py rename to Week02/binary_tree.py index f1f6ca795..a053ed7bd 100644 --- a/Week02/tree.py +++ b/Week02/binary_tree.py @@ -1,22 +1,6 @@ """ -Tree Data Structure +Binary Tree Data Structure """ -class TreeNode: - def __init__(self, val=None, children=None): - self.val = val - self.children = children - -def simple_tree(): - """ Create a simple tree - - ___1___ - / | \ - __3__ 2 4 - / \ - 5 6 - """ - return TreeNode(1, [TreeNode(3, [TreeNode(5), TreeNode(6)]), TreeNode(2), TreeNode(4)]) - class BinaryTreeNode: def __init__(self, val=None, left=None, right=None): self.val = val diff --git a/Week02/n-ary_tree_traversal.py b/Week02/n-ary_tree_traversal.py index 6bec37a00..a17c50508 100644 --- a/Week02/n-ary_tree_traversal.py +++ b/Week02/n-ary_tree_traversal.py @@ -9,9 +9,13 @@ https://leetcode.com/problems/n-ary-tree-level-order-traversal/ """ from typing import List -from tree import TreeNode, simple_tree from collections import deque +class TreeNode: + def __init__(self, val=None, children=None): + self.val = val + self.children = children + class Solution: def preorder1(self, root: TreeNode) -> List[int]: """ @@ -116,7 +120,14 @@ def levelOrder2(self, root: TreeNode) -> List[List[int]]: res.append(level) return res -t = simple_tree() +t = TreeNode(1, [TreeNode(3, [TreeNode(5), TreeNode(6)]), TreeNode(2), TreeNode(4)]) +""" + ___1___ + / | \ + __3__ 2 4 + / \ +5 6 +""" solution = Solution() ans = solution.preorder1(t) diff --git a/Week03/NOTE.md b/Week03/NOTE.md index 50de30414..95359fd02 100644 --- a/Week03/NOTE.md +++ b/Week03/NOTE.md @@ -1 +1,3 @@ -学习笔记 \ No newline at end of file +Learning Notes Week 03 +====================== + From eb947b7e74bb3d2934a2e79ee7ff04bec1957b6c Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 28 Jun 2020 01:43:43 -0400 Subject: [PATCH 19/48] week 3 notes and homeworks --- Week03/NOTE.md | 63 ++++++++++++++++++++++++++++++++++++++++++ Week03/combinations.py | 27 ++++++++++++++++++ Week03/permutations.py | 55 ++++++++++++++++++++++++++++++++++++ Week03/subsets.py | 24 ++++++++++++++++ 4 files changed, 169 insertions(+) create mode 100644 Week03/combinations.py create mode 100644 Week03/permutations.py create mode 100644 Week03/subsets.py diff --git a/Week03/NOTE.md b/Week03/NOTE.md index 95359fd02..9e62e4be1 100644 --- a/Week03/NOTE.md +++ b/Week03/NOTE.md @@ -1,3 +1,66 @@ Learning Notes Week 03 ====================== +Recursion +--------- + +Recursion Template +```py +def 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 +``` + +Divide and Conquer Template +```py +def divide_conquer(problem, param1, param2, ...): + # recursion terminator + if problem is None: + process_result + return + + # prepare data (key is to how to split the problem) + 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 +``` + +Backtrack Template +```py +result = [] +def backtrack(path = [], choices): + if end condition: + result.add(path[:]) # have to make a new copy + return + + for choice in choices: + if exclusive condition: # get rid of the illegal choices + continue + path.append() # Make the choice + backtrack(path, choices) # enter the next decision tree + path.pop() # Remove the choice (since the choice it's already made) +``` + +- Time complexity: O(N * 2^N) +- Space complexity: O(N) +- Backtrack is a decision tree, updating the result is actually a preorder recursion diff --git a/Week03/combinations.py b/Week03/combinations.py new file mode 100644 index 000000000..3b9369c93 --- /dev/null +++ b/Week03/combinations.py @@ -0,0 +1,27 @@ +""" +77. Combinations +https://leetcode.com/problems/combinations/ +""" +from typing import List + +class Solution: + def combine(self, n: int, k: int) -> List[List[int]]: + res = [] + if k <= 0 or n <= 0: + return res + + def backtrack(start = 1, path = []): + if len(path) == k: + res.append(path[:]) + return + + for i in range(start, n + 1): + path.append(i) + backtrack(i + 1, path) + path.pop() + + backtrack() + return res + +solution = Solution() +print(solution.combine(4, 2)) \ No newline at end of file diff --git a/Week03/permutations.py b/Week03/permutations.py new file mode 100644 index 000000000..fd5c76f0a --- /dev/null +++ b/Week03/permutations.py @@ -0,0 +1,55 @@ +""" +46. Permutations +https://leetcode.com/problems/permutations/ + +47. Permutations II +https://leetcode.com/problems/permutations-ii/ +""" +from typing import List + +class Solution: + def permute(self, nums: List[int]) -> List[List[int]]: + res = [] + n = len(nums) + + def backtrack(path = []): + if len(path) == n: + res.append(path[:]) + return + + for i in range(n): + if nums[i] in path: + continue + path.append(nums[i]) + backtrack(path) + path.pop() + + backtrack() + return res + + def permuteUnique(self, nums: List[int]) -> List[List[int]]: + res = [] + n = len(nums) + used = [False] * n + + def backtrack(path = []): + if len(path) == n: + res.append(path[:]) + return + + for i in range(n): + if not used[i]: + if i > 0 and nums[i] == nums[i - 1] and not used[i - 1]: + continue + path.append(nums[i]) + used[i] = True + backtrack(path) + path.pop() + used[i] = False + + backtrack() + return res + +solution = Solution() +print(solution.permute([1,2,3])) +print(solution.permuteUnique([1,1,2])) \ No newline at end of file diff --git a/Week03/subsets.py b/Week03/subsets.py new file mode 100644 index 000000000..3d382b4f6 --- /dev/null +++ b/Week03/subsets.py @@ -0,0 +1,24 @@ +""" +78. Subsets +https://leetcode.com/problems/subsets/ +""" +from typing import List + +class Solution: + def subsets(self, nums: List[int]) -> List[List[int]]: + res = [] + n = len(nums) + + def backtrack(start = 0, track = []): + res.append(track[:]) + + for i in range(start, n): + track.append(nums[i]) + backtrack(i + 1, track) + track.pop() + + backtrack() + return res + +solution = Solution() +print(solution.subsets([1,2,3])) \ No newline at end of file From f85212e1509e070f5cd624858f51be3d5df37be2 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 28 Jun 2020 22:43:16 -0400 Subject: [PATCH 20/48] the rest of the week3 homework --- Week03/NOTE.md | 20 +++++++----- Week03/construct_binary_tree.py | 0 Week03/generate_parenthesis.py | 48 +++++++++++++++++++++++++++++ Week03/lowest_common_ancestor.py | 0 Week03/permutations.py | 52 +++++++++++++++++++++++++++++--- 5 files changed, 108 insertions(+), 12 deletions(-) create mode 100644 Week03/construct_binary_tree.py create mode 100644 Week03/generate_parenthesis.py create mode 100644 Week03/lowest_common_ancestor.py diff --git a/Week03/NOTE.md b/Week03/NOTE.md index 9e62e4be1..5d6dfe6e5 100644 --- a/Week03/NOTE.md +++ b/Week03/NOTE.md @@ -13,7 +13,7 @@ def recursion(level, param1, param2, ...): return # process logic in current level - process(level, data...) + process(level, data, ...) # drill down recursion(level + 1, p1, ...) @@ -37,14 +37,17 @@ def divide_conquer(problem, param1, param2, ...): 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, …) + result = process_result(subresult1, subresult2, subresult3, ...) # revert the current level states ``` +- Sometimes we need to return multiple results (tuple) +- Sometimes we need global variables to easily update the final result + Backtrack Template ```py result = [] @@ -56,11 +59,12 @@ def backtrack(path = [], choices): for choice in choices: if exclusive condition: # get rid of the illegal choices continue - path.append() # Make the choice + path.append(choice) # Make the choice backtrack(path, choices) # enter the next decision tree - path.pop() # Remove the choice (since the choice it's already made) + path.pop() # Remove the choice (since it's already made) ``` -- Time complexity: O(N * 2^N) -- Space complexity: O(N) -- Backtrack is a decision tree, updating the result is actually a preorder recursion +- Time complexity for backtrack algorithm is at least O(N!) +- Backtrack is a decision tree, updating the result is actually a preorder and/or postorder recursion +- Sometimes we don't need to explicitly maintain the choice list, we derive it using other parameters (e.g. index) +- Sometimes path can be a string instead of an array, and we use `path += 'choice'` and `path = path[:-1]` to make and remove choice \ No newline at end of file diff --git a/Week03/construct_binary_tree.py b/Week03/construct_binary_tree.py new file mode 100644 index 000000000..e69de29bb diff --git a/Week03/generate_parenthesis.py b/Week03/generate_parenthesis.py new file mode 100644 index 000000000..ab80debee --- /dev/null +++ b/Week03/generate_parenthesis.py @@ -0,0 +1,48 @@ +""" +22. Generate Parentheses +https://leetcode.com/problems/generate-parentheses/ +""" +from typing import List + +class Solution: + def generateParenthesis1(self, n: int) -> List[str]: + res = [] + + def generate(left = 0, right = 0, s = ''): + if left == n and right == n: + res.append(s) + return + + # process and drill down + if left < n: + generate(left + 1, right, s + '(') + if left > right: + generate(left, right + 1, s + ')') + + generate() + return res + + def generateParenthesis2(self, n: int) -> List[str]: + res = [] + + def backtrack(left = n, right = n, path = ''): + if left < 0 or right < 0: return + if right < left: return + if left == 0 and right == 0: + res.append(path) + return + + path = path + '(' + backtrack(left - 1, right, path) + path = path[:-1] + + path = path + ')' + backtrack(left, right - 1, path) + path = path[:-1] + + backtrack() + return res + +solution = Solution() +print(solution.generateParenthesis1(3)) +print(solution.generateParenthesis2(3)) \ No newline at end of file diff --git a/Week03/lowest_common_ancestor.py b/Week03/lowest_common_ancestor.py new file mode 100644 index 000000000..e69de29bb diff --git a/Week03/permutations.py b/Week03/permutations.py index fd5c76f0a..57ca17031 100644 --- a/Week03/permutations.py +++ b/Week03/permutations.py @@ -6,9 +6,10 @@ https://leetcode.com/problems/permutations-ii/ """ from typing import List +from collections import Counter class Solution: - def permute(self, nums: List[int]) -> List[List[int]]: + def permute1(self, nums: List[int]) -> List[List[int]]: res = [] n = len(nums) @@ -27,10 +28,31 @@ def backtrack(path = []): backtrack() return res - def permuteUnique(self, nums: List[int]) -> List[List[int]]: + def permute2(self, nums: List[int]) -> List[List[int]]: + res = [] + n = len(nums) + + # [0, first - 1] = filled numbers + # [first, n - 1] = numbers to be filled + # swap nums[first] and nums[i] before backtrack to maintain the dynamic array + def backtrack(first = 0): + if first == n: + res.append(nums[:]) + return + + for i in range(first, n): + nums[first], nums[i] = nums[i], nums[first] + backtrack(first + 1) + nums[first], nums[i] = nums[i], nums[first] + + backtrack() + return res + + def permuteUnique1(self, nums: List[int]) -> List[List[int]]: res = [] n = len(nums) used = [False] * n + nums.sort() def backtrack(path = []): if len(path) == n: @@ -50,6 +72,28 @@ def backtrack(path = []): backtrack() return res + def permuteUnique2(self, nums: List[int]) -> List[List[int]]: + res = [] + n = len(nums) + + def backtrack(path = [], unused = Counter(nums)): + if len(path) == n: + res.append(path[:]) + return + + for x in unused: + if unused[x] > 0: + path.append(x) + unused[x] -= 1 + backtrack(path) + path.pop() + unused[x] += 1 + + backtrack() + return res + solution = Solution() -print(solution.permute([1,2,3])) -print(solution.permuteUnique([1,1,2])) \ No newline at end of file +print(solution.permute1([1,2,3])) +print(solution.permute2([1,2,3])) +print(solution.permuteUnique1([1,1,2])) +print(solution.permuteUnique2([3,3,0,3])) \ No newline at end of file From 1000914192450da20dd0c8e55ea9cd70ab492eee Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Mon, 29 Jun 2020 02:16:05 -0400 Subject: [PATCH 21/48] 236 and 105 --- Week03/binary_tree.py | 98 ++++++++++++++++++++++++++++++++ Week03/construct_binary_tree.py | 41 +++++++++++++ Week03/lowest_common_ancestor.py | 86 ++++++++++++++++++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 Week03/binary_tree.py diff --git a/Week03/binary_tree.py b/Week03/binary_tree.py new file mode 100644 index 000000000..c24d41d5a --- /dev/null +++ b/Week03/binary_tree.py @@ -0,0 +1,98 @@ +""" +Binary Tree Data Structure +""" +class BinaryTreeNode: + def __init__(self, val=None, left=None, right=None): + self.val = val + self.left = None + self.right = None + +def create_binary_tree(iterable = ()): + """ Given in iterable, create a binary tree in level order + Example: create_tree([0,1,2,3,4,5,6,7,8,9]) + + ____0__ + / \ + __1__ 2 + / \ / \ + 3 4 5 6 + / \ / + 7 8 9 + """ + def insert(node, i): + if i < len(iterable) and iterable[i] is not None: + node = BinaryTreeNode(iterable[i]) + node.left = insert(node.left, 2 * i + 1) + node.right = insert(node.right, 2 * i + 2) + return node + return insert(None, 0) + +def cherry_pick(root, val): + if root is None or root.val == val: + return root + return cherry_pick(root.left, val) or cherry_pick(root.right, val) + +def print_binary_tree(root, index=False): + """ Pretty-print the binary tree. + Inspired by https://pypi.org/project/binarytree/ + """ + def build_tree_string(root, current, index=False, delimiter='-'): + if root is None: + return [], 0, 0, 0 + + line1 = [] + line2 = [] + if index: + node_repr = '{}{}{}'.format(current, delimiter, root.val) + else: + node_repr = str(root.val) + + new_root_width = gap_size = len(node_repr) + + # Get the left and right sub-boxes, their widths, and root repr positions + l_box, l_box_width, l_root_start, l_root_end = \ + build_tree_string(root.left, 2 * current + 1, index, delimiter) + r_box, r_box_width, r_root_start, r_root_end = \ + build_tree_string(root.right, 2 * current + 2, index, delimiter) + + # Draw the branch connecting the current root node to the left sub-box + # Pad the line with whitespaces where necessary + if l_box_width > 0: + l_root = (l_root_start + l_root_end) // 2 + 1 + line1.append(' ' * (l_root + 1)) + line1.append('_' * (l_box_width - l_root)) + line2.append(' ' * l_root + '/') + line2.append(' ' * (l_box_width - l_root)) + new_root_start = l_box_width + 1 + gap_size += 1 + else: + new_root_start = 0 + + # Draw the representation of the current root node + line1.append(node_repr) + line2.append(' ' * new_root_width) + + # Draw the branch connecting the current root node to the right sub-box + # Pad the line with whitespaces where necessary + if r_box_width > 0: + r_root = (r_root_start + r_root_end) // 2 + line1.append('_' * r_root) + line1.append(' ' * (r_box_width - r_root + 1)) + line2.append(' ' * r_root + '\\') + line2.append(' ' * (r_box_width - r_root)) + gap_size += 1 + new_root_end = new_root_start + new_root_width - 1 + + # Combine the left and right sub-boxes with the branches drawn above + gap = ' ' * gap_size + new_box = [''.join(line1), ''.join(line2)] + for i in range(max(len(l_box), len(r_box))): + l_line = l_box[i] if i < len(l_box) else ' ' * l_box_width + r_line = r_box[i] if i < len(r_box) else ' ' * r_box_width + new_box.append(l_line + gap + r_line) + + # Return the new box, its width and its root repr positions + return new_box, len(new_box[0]), new_root_start, new_root_end + + lines = build_tree_string(root, 0, index)[0] + print('\n' + '\n'.join((line.rstrip() for line in lines))) diff --git a/Week03/construct_binary_tree.py b/Week03/construct_binary_tree.py index e69de29bb..f4e92cc9b 100644 --- a/Week03/construct_binary_tree.py +++ b/Week03/construct_binary_tree.py @@ -0,0 +1,41 @@ +""" +105. Construct Binary Tree from Preorder and Inorder Traversal +https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ +""" +from typing import List +from binary_tree import BinaryTreeNode, print_binary_tree + +class Solution: + def buildTree(self, preorder: List[int], inorder: List[int]) -> BinaryTreeNode: + if not preorder or not inorder or len(preorder) != len(inorder): + return None + + def build(preorder_left, preorder_right, inorder_left, inorder_right): + # base + if preorder_left > preorder_right: + return None + + # process + preorder_root = preorder_left + inorder_root = index[preorder[preorder_root]] + root = BinaryTreeNode(preorder[preorder_root]) + + # drill down + size_left_subtree = inorder_root - inorder_left + root.left = build(preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root + 1) + root.right = build(preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right) + + # return + return root + + # preorder = [root, [left subtree], [right subtree]] + # inorder = [[left subtree], root, [right subtree]] + n = len(preorder) + index = { elem: i for i, elem in enumerate(inorder) } + return build(0, n-1, 0, n-1) + +solution = Solution() +preorder = [3,9,20,15,7] +inorder = [9,3,15,20,7] +ans = solution.buildTree(preorder, inorder) +print_binary_tree(ans) diff --git a/Week03/lowest_common_ancestor.py b/Week03/lowest_common_ancestor.py index e69de29bb..4dbca0eba 100644 --- a/Week03/lowest_common_ancestor.py +++ b/Week03/lowest_common_ancestor.py @@ -0,0 +1,86 @@ +""" +236. Lowest Common Ancestor of a Binary Tree +https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/ +""" +from binary_tree import BinaryTreeNode, create_binary_tree, print_binary_tree, cherry_pick + +class Solution: + def lowestCommonAncestor1(self, root: BinaryTreeNode, p: BinaryTreeNode, q: BinaryTreeNode) -> BinaryTreeNode: + if root is None: + return None + + def helper(root: BinaryTreeNode, p: BinaryTreeNode, q: BinaryTreeNode) -> BinaryTreeNode: + if root is None: + return None + + p_in_left = nodesIndex[p.val] <= nodesIndex[root.val] + p_in_right = nodesIndex[p.val] >= nodesIndex[root.val] + + q_in_left = nodesIndex[q.val] <= nodesIndex[root.val] + q_in_right = nodesIndex[q.val] >= nodesIndex[root.val] + + if (p_in_left and q_in_right) or (p_in_right and q_in_left): + # found LCA + return root + + if p_in_left and q_in_left: + # LCA must in the left subtree, keep finding + return helper(root.left, p, q) + + if p_in_right and q_in_right: + # LCA must in the right subtree, keep finding + return helper(root.right, p, q) + + return None + + def inorder(root: BinaryTreeNode): + if root is None: + return [] + return inorder(root.left) + [root.val] + inorder(root.right) + + nodesInorder = inorder(root) + nodesIndex = {} # based on assumption that all nodes values are unique + for i, elem in enumerate(nodesInorder): + nodesIndex[elem] = i + return helper(root, p, q) + + + def lowestCommonAncestor2(self, root: BinaryTreeNode, p: BinaryTreeNode, q: BinaryTreeNode) -> BinaryTreeNode: + # terminator + if root is None: + return None + if root == p or root == q: + return root + + # drill down + lca_left = self.lowestCommonAncestor2(root.left, p, q) + lca_right = self.lowestCommonAncestor2(root.right, p, q) + + # process + if lca_left is None: + return lca_right + if lca_right is None: + return lca_left + if lca_left and lca_right: + return root + + # return + return None + +solution = Solution() +tree = create_binary_tree([3, 5, 1, 6, 2, 0, 8, None, None, 7, 4]) +print_binary_tree(tree) + +p = cherry_pick(tree, 5) +q = cherry_pick(tree, 1) +ans = solution.lowestCommonAncestor1(tree, p, q) +print('lowestCommonAncestor1 of', '(', p.val, ') and (', q.val, ') is (', ans.val, ')') +ans = solution.lowestCommonAncestor2(tree, p, q) +print('lowestCommonAncestor2 of', '(', p.val, ') and (', q.val, ') is (', ans.val, ')') + +p = cherry_pick(tree, 5) +q = cherry_pick(tree, 4) +ans = solution.lowestCommonAncestor1(tree, p, q) +print('lowestCommonAncestor1 of', '(', p.val, ') and (', q.val, ') is (', ans.val, ')') +ans = solution.lowestCommonAncestor2(tree, p, q) +print('lowestCommonAncestor2 of', '(', p.val, ') and (', q.val, ') is (', ans.val, ')') From c07327e6405e61d582ff8fef41c7950e9cb0ad18 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 5 Jul 2020 19:18:38 -0400 Subject: [PATCH 22/48] week04 --- Week02/binary-tree-traversal.py | 12 +- Week03/NOTE.md | 49 +++--- Week04/NOTE.md | 256 +++++++++++++++++++++++++++++++- Week04/test.py | 150 +++++++++++++++++++ 4 files changed, 438 insertions(+), 29 deletions(-) create mode 100644 Week04/test.py diff --git a/Week02/binary-tree-traversal.py b/Week02/binary-tree-traversal.py index 9a45e88f7..7e63a9eb5 100644 --- a/Week02/binary-tree-traversal.py +++ b/Week02/binary-tree-traversal.py @@ -119,16 +119,16 @@ def levelOrder1(self, root: BinaryTreeNode) -> List[List[int]]: """ Solution #1: Recursively """ - def traverse(node, level): + def traverse(root, level): if not root: return if len(res) == level: res.append([]) - res[level].append(node.val) - if node.left: - traverse(node.left, level+1) - if node.right: - traverse(node.right, level+1) + res[level].append(root.val) + if root.left: + traverse(root.left, level+1) + if root.right: + traverse(root.right, level+1) res = [] traverse(root, 0) diff --git a/Week03/NOTE.md b/Week03/NOTE.md index 5d6dfe6e5..49859700e 100644 --- a/Week03/NOTE.md +++ b/Week03/NOTE.md @@ -1,16 +1,15 @@ Learning Notes Week 03 ====================== -Recursion ---------- - Recursion Template +------------------ + ```py def recursion(level, param1, param2, ...): # recursion terminator if level > MAX_LEVEL: - process_result - return + process_result + return # process logic in current level process(level, data, ...) @@ -21,34 +20,38 @@ def recursion(level, param1, param2, ...): # reverse the current level status if needed ``` -Divide and Conquer Template +Divide & Conquer Template +------------------------- + ```py def divide_conquer(problem, param1, param2, ...): - # recursion terminator - if problem is None: - process_result - return + # recursion terminator + if problem is None: + process_result + return - # prepare data (key is to how to split the problem) - data = prepare_data(problem) - subproblems = split_problem(problem, data) + # prepare data (key is to how to split the problem) + 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, ...) - ... + # 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, ...) + # process and generate the final result + result = process_result(subresult1, subresult2, subresult3, ...) - # revert the current level states + # revert the current level states ``` - Sometimes we need to return multiple results (tuple) - Sometimes we need global variables to easily update the final result Backtrack Template +------------------ + ```py result = [] def backtrack(path = [], choices): @@ -57,8 +60,10 @@ def backtrack(path = [], choices): return for choice in choices: - if exclusive condition: # get rid of the illegal choices + # get rid of the illegal choices + if exclusive condition: continue + path.append(choice) # Make the choice backtrack(path, choices) # enter the next decision tree path.pop() # Remove the choice (since it's already made) diff --git a/Week04/NOTE.md b/Week04/NOTE.md index 50de30414..28ab6f5b9 100644 --- a/Week04/NOTE.md +++ b/Week04/NOTE.md @@ -1 +1,255 @@ -学习笔记 \ No newline at end of file +Learning Notes Week 04 +====================== + +DFS Template +------------ + +```py +visited = set() + +def dfs(node): + # terminator + if not node: + return + + # already visited + if node in visited: + return + + # process current node + process(node.val) + + # add to visited + visited.add(node) + + # process children (drill down) + if node.children: + for child in node.children: + if child not in visited: + dfs(child, visited) +``` + +DFS Application +--------------- + +Island Problem +```py +def island(self, grid: List[List[int]]): + # length of row and column + m, n = len(grid), len(grid[0]) + + def dfs(r, c): + # base case: grid[r][c] is out of bound + if not inArea(r, c): + return + + # current node is not an island, or it's already visited + if grid[r][c] != 1: + return + + # mark as visited + grid[r][c] = 2 + + # visit neighbor nodes + dfs(r-1, c) + dfs(r+1, c) + dfs(r, c-1) + dfs(r, c+1) + + def inArea(r, c): + return 0 <= r < m and 0 <= c < n +``` + +Backtrack +```py +result = [] + +def backtrack(path = [], choices): + if end condition: + result.add(path[:]) # have to make a new copy + return + + for choice in choices: + # get rid of the illegal choices + if exclusive condition: + continue + + path.append(choice) # Make the choice + backtrack(path, choices) # enter the next decision tree + path.pop() # Remove the choice (since it's already made) +``` + +BFS Template +------------ + +```py +def bfs(root): + visited = set() + queue = collections.deque([root]) + + # Loop until queue is empty + while queue: + # get current node from queue + node = queue.popleft() + # process current node + process(node.val) + # add to visited + visited.add(node) + # process children + if node.children: + for child in node.children: + if child not in visited: + queue.append(child) +``` + +BFS Application +--------------- + +Level Order +```py +def bfs(root): + res = [] + visited = set() + queue = collections.deque([root]) + + # Loop until queue is empty + while queue: + # process all nodes from the current level + level_nodes = [] + for _ in range(len(queue)): + # get current node from queue + node = queue.popleft() + # process current node + level_nodes.append(node.val) + # add to visited + visited.add(node) + # process children + if node.children: + for child in node.children: + if child not in visited: + queue.append(child) + res.append(level_nodes) + + return res +``` + +Shorted Path +```py +def bfs(start, target): # any two nodes, doesn't have to start from the root + step = 0 + visited = set() + queue = collections.deque([start]) + + # Loop until queue is empty + while queue: + # spread the search from the current level + for _ in range(len(queue)): + # get current node from queue + node = queue.popleft() + # see if we reach the target + if node is target: + return step + # add to visited + visited.add(node) + # process children + if node.children: + for child in node.children: + if child not in visited: + queue.append(child) + step += 1 + + return 0 +``` + +Binary Search +------------- + +[left, right] +```py +def binary_search(nums, target) + left, right = 0, len(nums) - 1 + while left <= right: + mid = left + (right - left) // 2 + if nums[mid] < target: + left = mid + 1 + elif nums[mid] > target: + right = mid - 1 + elif nums[mid] == target: + return mid + return -1 +``` + +[left, right) +```py +def left_bound(nums, target): + left, right = 0, len(nums) - 1 + while left < right: + mid = left + (right - left) // 2 + if nums[mid] < target: + left = mid + 1 + elif nums[mid] > target: + right = mid - 1 + elif nums[mid] == target: + # don't return, lock down the left boundary + right = mid - 1 + + # check if left is out of boundary + if left >= nums.length or nums[left] != target + return -1 + return left +} +``` + +(left, right] +```py +def right_bound(nums, target): + left, right = 0, len(nums) - 1 + while left < right: + mid = left + (right - left) // 2 + if nums[mid] < target: + left = mid + 1 + elif nums[mid] > target: + right = mid - 1 + elif nums[mid] == target: + # don't return, lock down the right boundary + left = mid + 1 + + # check if right is out of boundary + if right < 0 or nums[right] != target + return -1 + return right +} +``` + +```py +def binary_search(l, r): + """ + Returns the smallest number m in range [l, r) such that g(m) is true. + Returns r if not found. + + Time Complexity: O(log(r - l) * (f(m) + g(m))) + Space Complexity: O(1) + """ + while l < r: + m = l + (r - l) // 2 + if f(m): # optional: if somehow we can determine m is the answer, return it + return m + if g(m): + r = m # new range [l, m) + else: + l = m + 1 # new range [m+1, r) + return l # or not found + +def binary_search(l, r): + """ + Returns the smallest number m in range [l, r] such that g(m) is true. + Returns r+1 if not found. + """ + while l <= r: + m = l + (r - l) // 2 + if g(m): + r = m - 1 # new range [l, m-1] + else: + l = m + 1 # new range [m+1, r] + return l +``` \ No newline at end of file diff --git a/Week04/test.py b/Week04/test.py new file mode 100644 index 000000000..6eef217fa --- /dev/null +++ b/Week04/test.py @@ -0,0 +1,150 @@ +import collections + +class TreeNode: + def __init__(self, val=None, children=None): + self.val = val + self.children = children + +def DFS1(root: TreeNode): + """ + Recursively + """ + def _dfs(node, visited = set()): + # terminator + if not node: + return + # already visited + if node in visited: + return + # process current node + res.append(node.val) + # add to visited + visited.add(node) + # process children (drill down) + if node.children: + for child in node.children: + if child not in visited: + _dfs(child, visited) + res = [] + _dfs(root) + return res + +def DFS2(root: TreeNode): + """ + Iteratively + """ + if not root: + return [] + res = [] + visited = set() + stack = [root] + # Loop until stack is empty + while stack: + # get current node from stack + node = stack.pop() + # already visited + if node in visited: + continue + # process current node + res.append(node.val) + # add to visited + visited.add(node) + # process children + if node.children: + for child in node.children[::-1]: + if child not in visited: + stack.append(child) + return res + +def BFS1(root: TreeNode): + """ + Recursively + """ + def _bfs(node, level = 0, visited = set()): + # terminator + if not node: + return + # already visited + if node in visited: + return + # process all nodes from the current level + if len(res) == level: + res.append([]) + res[level].append(node.val) + # add to visited + visited.add(node) + # process children (drill down) + if node.children: + for child in node.children: + if child not in visited: + _bfs(child, level + 1, visited) + res = [] + _bfs(root) + return [node for level in res for node in level] # flatten + +def BFS2(root: TreeNode): + """ + Iteratively + """ + if not root: + return [] + res = [] + visited = set() + queue = collections.deque([root]) + # Loop until queue is empty + while queue: + n = len(queue) + level_nodes = [] + # process all nodes from the current level + for _ in range(n): + # get current node from queue + node = queue.popleft() + # process current node + level_nodes.append(node.val) + # add to visited + visited.add(node) + # process children + if node.children: + for child in node.children: + if child not in visited: + queue.append(child) + res.append(level_nodes) + return [node for level in res for node in level] # flatten + +tree = TreeNode(1, [TreeNode(3, [TreeNode(5), TreeNode(6)]), TreeNode(2), TreeNode(4)]) +""" + ___1___ + / | \ + __3__ 2 4 + / \ +5 6 +""" +print(DFS1(tree)) +print(DFS2(tree)) +print(BFS1(tree)) +print(BFS2(tree)) + +def ShortestPathDFS(root, target): + if not root: + return -1 + step = 0 + visited = set() + stack = [root] + while stack: + node = stack.pop() + if node in visited: + continue + if node is target: + return step + if node.children: + for child in node.children: + if child not in visited: + stack.append(child) + visited.add(child) + step += 1 + return step + +target = TreeNode(6) +tree = TreeNode(1, [TreeNode(3, [TreeNode(5), target]), TreeNode(2), TreeNode(4)]) +print(ShortestPathDFS(tree, target)) +print(ShortestPathBFS(tree, target)) \ No newline at end of file From d9a99f6664c0701019457dc5aa5318794372bd21 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 5 Jul 2020 22:55:32 -0400 Subject: [PATCH 23/48] week 04 homeworks --- Week04/NOTE.md | 7 +- Week04/{test.py => bfs_dfs.py} | 25 ----- Week04/lsland_problems.py | 176 +++++++++++++++++++++++++++++++++ Week04/search_2d_matrix.py | 111 +++++++++++++++++++++ Week04/word_ladder.py | 41 ++++++++ 5 files changed, 334 insertions(+), 26 deletions(-) rename Week04/{test.py => bfs_dfs.py} (82%) create mode 100644 Week04/lsland_problems.py create mode 100644 Week04/search_2d_matrix.py create mode 100644 Week04/word_ladder.py diff --git a/Week04/NOTE.md b/Week04/NOTE.md index 28ab6f5b9..e6db5b79f 100644 --- a/Week04/NOTE.md +++ b/Week04/NOTE.md @@ -60,7 +60,7 @@ def island(self, grid: List[List[int]]): return 0 <= r < m and 0 <= c < n ``` -Backtrack +Backtrack (see Week03 NOTE) ```py result = [] @@ -164,6 +164,10 @@ def bfs(start, target): # any two nodes, doesn't have to start from the root Binary Search ------------- +- Monotonically increasing/decreasing +- Bounded (have upper and lower bound) +- Index accessible + [left, right] ```py def binary_search(nums, target) @@ -221,6 +225,7 @@ def right_bound(nums, target): } ``` +Target Function g(m) ```py def binary_search(l, r): """ diff --git a/Week04/test.py b/Week04/bfs_dfs.py similarity index 82% rename from Week04/test.py rename to Week04/bfs_dfs.py index 6eef217fa..fef528273 100644 --- a/Week04/test.py +++ b/Week04/bfs_dfs.py @@ -123,28 +123,3 @@ def BFS2(root: TreeNode): print(DFS2(tree)) print(BFS1(tree)) print(BFS2(tree)) - -def ShortestPathDFS(root, target): - if not root: - return -1 - step = 0 - visited = set() - stack = [root] - while stack: - node = stack.pop() - if node in visited: - continue - if node is target: - return step - if node.children: - for child in node.children: - if child not in visited: - stack.append(child) - visited.add(child) - step += 1 - return step - -target = TreeNode(6) -tree = TreeNode(1, [TreeNode(3, [TreeNode(5), target]), TreeNode(2), TreeNode(4)]) -print(ShortestPathDFS(tree, target)) -print(ShortestPathBFS(tree, target)) \ No newline at end of file diff --git a/Week04/lsland_problems.py b/Week04/lsland_problems.py new file mode 100644 index 000000000..84b3afb8c --- /dev/null +++ b/Week04/lsland_problems.py @@ -0,0 +1,176 @@ +""" +200. Number of Islands +https://leetcode.com/problems/number-of-islands/ + +695. Max Area of Island +https://leetcode.com/problems/max-area-of-island/ + +463. Island Perimeter +https://leetcode.com/problems/island-perimeter/ + +827. Making A Large Island +https://leetcode.com/problems/making-a-large-island/ +""" +from typing import List + +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + if not grid: + return 0 + + m, n = len(grid), len(grid[0]) + + def countIsland(r, c): + if 0 <= r < m and 0 <= c < n and grid[r][c] == '1': + grid[r][c] = '0' + countIsland(r+1, c) + countIsland(r-1, c) + countIsland(r, c+1) + countIsland(r, c-1) + + count = 0 + for r in range(m): + for c in range(n): + if grid[r][c] == '1': + count += 1 + countIsland(r, c) + + return count + + def maxAreaOfIsland(self, grid: List[List[int]]) -> int: + def area(grid, r, c): + if not inArea(grid, r, c): + return 0 + if grid[r][c] != 1: # ocean or visited + return 0 + grid[r][c] = -1 # mark as visited + return 1 \ + + area(grid, r, c-1) \ + + area(grid, r+1, c) \ + + area(grid, r, c+1) \ + + area(grid, r-1, c) + + def inArea(grid, r, c): + return 0 <= r < len(grid) and 0 <= c < len(grid[0]) + + maxArea = 0 + for r in range(len(grid)): + for c in range(len(grid[0])): + if grid[r][c] == 1: + islandArea = area(grid, r, c) + maxArea = max(maxArea, islandArea) + + return maxArea + + def islandPerimeter(self, grid: List[List[int]]) -> int: + m, n = len(grid), len(grid[0]) + + def perimeter(r, c): + if not (0 <= r < m and 0 <= c < n): # boundary + return 1 + if grid[r][c] == 0: # ocean + return 1 + if grid[r][c] != 1: # not land or visited + return 0 + grid[r][c] = -1 + return perimeter(r+1, c) \ + + perimeter(r-1, c) \ + + perimeter(r, c+1) \ + + perimeter(r, c-1) + + for r in range(m): + for c in range(n): + if grid[r][c] == 1: + return perimeter(r, c) + + return 0 + + def largestIsland(self, grid: List[List[int]]) -> int: + m, n = len(grid), len(grid[0]) + + def area(r, c, index): + if 0 <= r < m and 0 <= c < n and grid[r][c] == 1: + grid[r][c] = index + return 1 \ + + area(r+1, c, index) \ + + area(r-1, c, index) \ + + area(r, c+1, index) \ + + area(r, c-1, index) + return 0 + + areas = {} + index = 2 + for r in range(m): + for c in range(n): + if grid[r][c] == 1: + areas[index] = area(r, c, index) + index += 1 + + def connect(r, c): + if 0 <= r < m and 0 <= c < n and grid[r][c] > 1: + return grid[r][c] + return -1 + + res = max(areas.values() or [0]) + for r in range(m): + for c in range(n): + if grid[r][c] == 0: + surroundings = set([ + connect(r+1, c), + connect(r-1, c), + connect(r, c+1), + connect(r, c-1) + ]) + res = max(res, sum(areas[index] for index in surroundings if index > 0) + 1) + + return res + +solution = Solution() + +grid = [ + ['1', '1', '1', '1', '0'], + ['1', '1', '0', '1', '0'], + ['1', '1', '0', '0', '0'], + ['0', '0', '0', '0', '0'] +] +print('numIslands:', solution.numIslands(grid)) # 1 + +grid = [ + ['1', '1', '0', '0', '0'], + ['1', '1', '0', '0', '0'], + ['0', '0', '1', '0', '0'], + ['0', '0', '0', '1', '1'] +] +print('numIslands:', solution.numIslands(grid)) # 3 + +grid = [ + [0,0,1,0,0,0,0,1,0,0,0,0,0], + [0,0,0,0,0,0,0,1,1,1,0,0,0], + [0,1,1,0,1,0,0,0,0,0,0,0,0], + [0,1,0,0,1,1,0,0,1,0,1,0,0], + [0,1,0,0,1,1,0,0,1,1,1,0,0], + [0,0,0,0,0,0,0,0,0,0,1,0,0], + [0,0,0,0,0,0,0,1,1,1,0,0,0], + [0,0,0,0,0,0,0,1,1,0,0,0,0] +] +print('maxAreaOfIsland:', solution.maxAreaOfIsland(grid)) # 6 + +grid = [[0,0,0,0,0,0,0,0]] +print('maxAreaOfIsland:', solution.maxAreaOfIsland(grid)) # 0 + +grid = [ + [0,1,0,0], + [1,1,1,0], + [0,1,0,0], + [1,1,0,0] +] +print('islandPerimeter:', solution.islandPerimeter(grid)) # 16 + +grid = [[1, 0], [0, 1]] +print('largestIsland', solution.largestIsland(grid)) # 3 + +grid = [[1, 1], [1, 0]] +print('largestIsland', solution.largestIsland(grid)) # 4 + +grid = [[1, 1], [1, 1]] +print('largestIsland', solution.largestIsland(grid)) # 4 diff --git a/Week04/search_2d_matrix.py b/Week04/search_2d_matrix.py new file mode 100644 index 000000000..5608f9837 --- /dev/null +++ b/Week04/search_2d_matrix.py @@ -0,0 +1,111 @@ +""" +74. Search a 2D Matrix +https://leetcode.com/problems/making-a-large-island/ +""" +from typing import List + +class Solution: + def searchMatrix1(self, matrix: List[List[int]], target: int) -> bool: + """ + 1. 2D Binary Search + Treat the (m x n) 2D Matrix as sorted 1D array (length = m x n) + For any element in the array we have: x = matrix[m // n][m % n] + """ + m = len(matrix) + if m == 0: + return False + + n = len(matrix[0]) + if n == 0: + return False + + l, r = 0, m * n - 1 + while l <= r: + m = (l + r) // 2 + x = matrix[m // n][m % n] + if x < target: + l = m + 1 + elif x > target: + r = m - 1 + elif x == target: + return True + + return False + + def searchMatrix2(self, matrix: List[List[int]], target: int) -> bool: + """ + 2. Diagonal Binary Search + """ + m = len(matrix) + if m == 0: + return False + + n = len(matrix[0]) + if n == 0: + return False + + # Start from the bottom-left corner to up-right corner + x, y = m-1, 0 + while x >= 0 and y < n: + if matrix[x][y] > target: + x -= 1 # move up + elif matrix[x][y] < target: + y += 1 # move right + else: + return True + + return False + + def searchMatrix3(self, matrix: List[List[int]], target: int) -> bool: + """ + 3. Twice Binary Search + """ + m = len(matrix) + if m == 0: + return False + + n = len(matrix[0]) + if n == 0: + return False + + l, r = 0, m - 1 + while l <= r: + mid = (l + r) // 2 + if matrix[mid][-1] < target: + l = mid + 1 + elif matrix[mid][0] > target: + r = mid - 1 + else: + break + + row = matrix[mid] + + l, r = 0, n - 1 + while l <= r: + mid = (l + r) // 2 + if row[mid] < target: + l = mid + 1 + elif row[mid] > target: + r = mid - 1 + elif row[mid] == target: + return True + + return False + +solution = Solution() + +matrix = [ + [1, 3, 5, 7], + [10, 11, 16, 20], + [23, 30, 34, 50] +] + +target = 3 # found +print('searchMatrix1:', solution.searchMatrix1(matrix, target)) +print('searchMatrix2:', solution.searchMatrix1(matrix, target)) +print('searchMatrix3:', solution.searchMatrix1(matrix, target)) + +target = 13 # not found +print('searchMatrix1:', solution.searchMatrix1(matrix, target)) +print('searchMatrix2:', solution.searchMatrix1(matrix, target)) +print('searchMatrix3:', solution.searchMatrix1(matrix, target)) diff --git a/Week04/word_ladder.py b/Week04/word_ladder.py new file mode 100644 index 000000000..a718f86e9 --- /dev/null +++ b/Week04/word_ladder.py @@ -0,0 +1,41 @@ +""" +127. Word Ladder +https://leetcode.com/problems/word-ladder/description/ +""" +from typing import List +import collections +import string + +class Solution: + def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: + wordList = set(wordList) + step = 1 + queue = collections.deque([beginWord]) + while queue: + for _ in range(len(queue)): + word = queue.popleft() + if word == endWord: + return step + for i in range(len(word)): + for c in string.ascii_lowercase: + if c == word[i]: + continue + new_word = word[:i] + c + word[i+1:] + if new_word in wordList: + queue.append(new_word) + wordList.remove(new_word) + step += 1 + return 0 + + +solution = Solution() + +beginWord = "hit" +endWord = "cog" +wordList = ["hot","dot","dog","lot","log","cog"] +print(solution.ladderLength(beginWord, endWord, wordList)) # 5 + +beginWord = "hit" +endWord = "cog" +wordList = ["hot","dot","dog","lot","log"] +print(solution.ladderLength(beginWord, endWord, wordList)) # 0 From 3cb7770baad139793e2f7d69fb13c1803770799f Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Fri, 17 Jul 2020 23:11:33 -0400 Subject: [PATCH 24/48] mid term summay --- Week01/NOTE.md | 267 +++++++++++++++++++++++++++++++++++++++++-------- Week02/NOTE.md | 83 ++++++++++----- Week03/NOTE.md | 17 +++- Week04/NOTE.md | 70 +++++++------ 4 files changed, 335 insertions(+), 102 deletions(-) diff --git a/Week01/NOTE.md b/Week01/NOTE.md index 5b45348fc..8df7890ec 100644 --- a/Week01/NOTE.md +++ b/Week01/NOTE.md @@ -45,24 +45,32 @@ a.count(x) # total number of occurrences of x in s a.reverse() # reverses the items of s in place a.copy() # creates a shallow copy of s (same as s[:]) a.index(x[, start[, end]]) # index of the first occurrence of x in s -a.sort(key=None, reverse=False) +a.sort(key=None, reverse=False) # Sort the items of the list in place ``` String Operations ```py -string.digits # the string '0123456789' -string.hexdigits # the string '0123456789abcdefABCDEF' -string.octdigits # the string '01234567' -ord(c) # the unicode code representation of the char -ord(c) - ord('a') # the position of the char in 26 letters -chr(i) # string representation of the char unicode code s.strip([chars]) # return a copy of the string with the leading and trailing characters removed. s.startswith(prefix) # return True if string starts with the prefix, False otherwise. s.endswith(prefix) # return True if string starts with the prefix, False otherwise. s.slipt(delimiter) # return a list of the words of the string s. s.lower() # return a copy of the string with all the lowercase characters s.upper() # return a copy of the string with all the uppercase characters -'Hello {name}'.format(name='World') +ord(c) # the unicode code representation of the char +ord(c) - ord('a') # the position of the char in 26 letters +chr(i) # string representation of the char unicode code +``` + +String Constants +```py +import string + +string.digits # the string '0123456789' +string.hexdigits # the string '0123456789abcdefABCDEF' +string.octdigits # the string '01234567' +string.ascii_lowercase # the uppercase letters 'abcdefghijklmnopqrstuvwxyz' +string.ascii_letters # The lowercase letters 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +string.letters # The concatenation of the ascii_lowercase and ascii_uppercase ``` Coding Techniques @@ -80,6 +88,9 @@ vec = [-4, -2, 0, 2, 4] vec = [[1,2,3], [4,5,6], [7,8,9]] [num for elem in vec for num in elem] +# String formatter +'Hello {name}'.format(name='World') + # Get sliding windows of size k in an array of nums nums, k = [1,2,3,4,5,6], 3 n = len(nums) @@ -94,10 +105,29 @@ for i in range(n): print(res) # [5,6,7,1,2,3,4] # Useful functions -functools.reduce(func, iter, [initial_value]) -itertools.groupby(iterable[, key]) +map(lambda x: x * x, [1, 2, 3, 4, 5]) # [1, 4, 9, 16, 25] +map(lambda x, y: x + y, [1, 2, 3], [4, 5, 6]) # [5, 7, 9] +filter(lambda x: x % 2 != 0, [1, 2, 3, 4, 5, 6]) # [1, 3, 5] +any((False, False, True)) # True +all((False, True, True)) # False +sum([1, 2, 3, 4, 5]) # 15 +functools.reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) # calculates ((((1+2)+3)+4)+5) = 15 ``` +Leetcode Problems +- [1. Two Sum](https://leetcode.com/problems/two-sum/) +- [70. Climbing Stairs](https://leetcode.com/problems/climbing-stairs/) +- [66. Plus One](https://leetcode.com/problems/plus-one/) +- [283. Move Zeroes](https://leetcode.com/problems/move-zeroes/) +- [26. Remove Duplicates from Sorted Array ](https://leetcode.com/problems/remove-duplicates-from-sorted-array/) +- [88. Merge Sorted Array](https://leetcode.com/problems/merge-sorted-array/) +- [167. Two Sum II - Input array is sorted](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/) +- [15. 3Sum](https://leetcode.com/problems/3sum/) +- [344. Reverse String](https://leetcode.com/problems/reverse-string/) +- [189. Rotate Array](https://leetcode.com/problems/rotate-array/) +- [704. Binary Search](https://leetcode.com/problems/binary-search/) +- [11. Container With Most Water](https://leetcode.com/problems/container-with-most-water/) + Linked Lists ------------ @@ -122,7 +152,6 @@ Doubly linked list: `L -> x <- 2 <-> 3 <-> 5 <-> 4 -> x` | Insertion | O(1) | | Deletion | O(1) | -LinkedList Node ```py class ListNode: def __init__(self, val = 0, next = None): @@ -130,9 +159,8 @@ class ListNode: self.next = next ``` -Coding Techniques +Two Pointers Template ```py -# Linked List iteration framework (Double Pointers) prev, curr = None, head while curr: # do something with prev and curr @@ -144,28 +172,67 @@ while curr: prev = curr curr = curr.next -def reverseList(self, head: ListNode) -> ListNode: - prev, curr = None, head - while curr: - cnext = curr.next - if prev is None: - curr.next = None - else: - curr.next = prev - prev = curr - curr = cnext - return prev - -# Linked List recursion framework (Think Backwards) -def reverseList(self, head: ListNode) -> ListNode: - if not head or not head.next: # end condition +return prev +``` + +Dummy Head (Sentry) Template +```py +dummy = ListNode(None) +dummy.next = head + +prev, curr = dummy, head +while curr: + # prev will never be None + prev.next = curr.next + # move prev and curr + prev = curr + curr = curr.next + +return dummy.next +``` + +Fast Slow Template +```py +fast = slow = head +while fast and fast.next: + # fast move 2 steps + fast = fast.next.next + # slow move 1 step + slow = slow.next + +# when num of the list is odd, slow is the mid +# when num of the list is even, slow is the first node after mid +return slow +``` + +Linked List Recursion +```py +def traverse(head): + # base case + if head.next: return head - p = self.reverseList(head.next) # rest of the nodes are already reversed - head.next.next = head # start to reverse the first two nodes - head.next = None - return p # return head + # do something before (pre-order) + node = traverse(head.next) + # do something after (post-order) + return node ``` +Leetcode Problems +- [206. Reverse Linked List](https://leetcode.com/problems/reverse-linked-list/) +- [203. Remove Linked List Elements](https://leetcode.com/problems/remove-linked-list-elements/) +- [445. Add Two Numbers II](https://leetcode.com/problems/add-two-numbers-ii/) +- [92. Reverse Linked List II](https://leetcode.com/problems/reverse-linked-list-ii/) +- [21. Merge Two Sorted Lists](https://leetcode.com/problems/merge-two-sorted-lists/) +- [24. Swap Nodes in Pairs](https://leetcode.com/problems/swap-nodes-in-pairs/) +- [83. Remove Duplicates from Sorted List](https://leetcode.com/problems/remove-duplicates-from-sorted-list/) +- [876. Middle of the Linked List](https://leetcode.com/problems/middle-of-the-linked-list/) +- [19. Remove Nth Node From End of List](https://leetcode.com/problems/remove-nth-node-from-end-of-list/) +- [141. Linked List Cycle](https://leetcode.com/problems/linked-list-cycle/) +- [142. Linked List Cycle II](https://leetcode.com/problems/linked-list-cycle-ii/) +- [25. Reverse Nodes in k-Group](https://leetcode.com/problems/reverse-nodes-in-k-group/) +- [148. Sort List](https://leetcode.com/problems/sort-list/) +- [92. Reverse Linked List II](https://leetcode.com/problems/reverse-linked-list-ii/) + Stacks and Queues ----------------- @@ -178,23 +245,141 @@ Stacks and Queues | Insertion | O(1) | | Deletion | O(1) | -Stack Operations (List data types represent the implementation of stacks) +Stack Operations ```py +stack = [] # create a stack (nothing but a list) stack.append(x) # push stack.pop() # pop -stack[-1] # peek +stack[-1] # peek (top of the stack) ``` -Queue Operations (collection.deque - class represent the implementation of double ended queues) +Queue Operations ```py -q.append(x) # add x to the right side of the deque -q.appendleft(x) # add x to the left side of the deque -q.pop() # remove and return an element from the right side of the deque -q.popleft() # remove and return an element from the left side of the deque -q[0] # peek left side of the deque -q[-1] # peek right side of the deque +from collections import deque + +queue = deque() # create a double ended queue +queue.append(x) # add x to the right side of the deque +queue.appendleft(x) # add x to the left side of the deque +queue.pop() # remove and return an element from the right side of the deque +queue.popleft() # remove and return an element from the left side of the deque +queue[0] # peek left side of the deque +queue[-1] # peek right side of the deque ``` +Sliding Window Template +```py +from collections import Counter + +def slidingWindow(s, t): + window = Counter() + target = Counter(t) + + len_s, len_t = len(s), len(t) + left = right = 0 + valid = 0 + + while right < len_s: + # c is the element to be inserted into the window + c = s[right] + # expand the current window + right += 1 + # If c is our target, we are 1 step closer to the answer + if c in target: + window[c] += 1 + if window[c] == target[c]: + valid += 1 + + while # when we found a valid window + if valid == len(target): + # check the answer or update the result + + # d is the element to be removed from the window + d = s[left] + # shrink the current window + left += 1 + # if d is our target, shrinking might cause the window to be invalid + if d in target: + if window[d] == target[d]: + valid -= 1 + window[d] -= 1 +``` + +Mono stack +```py +n = len(nums) +stack = [] +leftMax = [-1] * n +for i in range(n): # push into stack + while stack and stack[-1] <= nums[i]: # compare with stack top + # pop out numbers smaller than me + stack.pop() + # now the top is the first element bigger than me + leftMax[i] = stack[-1] if stack else -1 + # push myself in stack for the next round + stack.append(nums[i]) +return leftMax + +# Variation 1: push to stack backwards to get the rightMax +rightMax = [-1] * n +for i in range(n-1, -1, -1): + ... + +# Variation 2: find min rather than max +leftMin = [-1] * n +for i in range(n): + while stack and nums[i] <= stack[-1]: + ... + +# Variation 3: push index to stack instead of numbers +while stack and nums[stack[-1]] <= nums[i]: + stack.pop() +... +stack.append(i) + +# Variation 4: find leftMax and rightMax in one pass +for i in range(n): + while stack and nums[stack[-1]] < nums[i]: + currIndex = stack.pop() + if not stack: + break + leftMax = stack[-1] + rightMax = i + print(leftMax, currIndex, rightMax) + stack.append(i) +``` + +Mono Queue +```py +class monoQueue: + def __init__(self): + self.queue = deque() + + def push(self, x): + while self.queue and self.queue[-1] < x: + self.queue.pop() + self.queue.append(x) + + def pop(self, x): + if self.queue and self.queue[0] == x: + self.queue.popleft() + + def max(self): + return self.queue[0] +``` + +Leetcode Problems +- [20. Valid Parentheses](https://leetcode.com/problems/valid-parentheses/) +- [155. Min Stack](https://leetcode.com/problems/min-stack/) +- [84. Largest Rectangle in Histogram](https://leetcode.com/problems/largest-rectangle-in-histogram/) +- [239. Sliding Window Maximum](https://leetcode.com/problems/sliding-window-maximum/) +- [76. Minimum Window Substring](https://leetcode.com/problems/minimum-window-substring/) +- [567. Permutation in String](https://leetcode.com/problems/permutation-in-string/) +- [438. Find All Anagrams in a String](https://leetcode.com/problems/find-all-anagrams-in-a-string/) +- [3. Longest Substring Without Repeating Characters](https://leetcode.com/problems/longest-substring-without-repeating-characters/) +- [239. Sliding Window Maximum](https://leetcode.com/problems/sliding-window-maximum/) +- [42. Trapping Rain Water](https://leetcode.com/problems/next-greater-element-ii/) +- + Skip List --------- diff --git a/Week02/NOTE.md b/Week02/NOTE.md index 5cf2dc37f..79348bcb0 100644 --- a/Week02/NOTE.md +++ b/Week02/NOTE.md @@ -32,6 +32,20 @@ d.vals() # Return a new view of the dictionary’s vals. d.values() # Return a new view of the dictionary’s values. ``` +Useful functions +```py +from collections import Counter + +cnt = Counter() # A Counter is a dict subclass for counting hashable objects. +for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']: + cnt[word] += 1 +print(cnt) # Counter({'blue': 3, 'red': 2, 'green': 1}) +``` + +Leetcode Problems +- [242. Valid Anagram](https://leetcode.com/problems/valid-anagram/description/) +- [49. Group Anagrams](https://leetcode.com/problems/group-anagrams/) + Binary Trees ------------ @@ -146,6 +160,15 @@ def levelorder(root): queue.append(node.right) ``` +LeetCode Problems +- [144. Binary Tree Preorder Traversal](https://leetcode.com/problems/binary-tree-preorder-traversal/) +- [94. Binary Tree Inorder Traversal](https://leetcode.com/problems/binary-tree-inorder-traversal/) +- [145. Binary Tree Postorder Traversal](https://leetcode.com/problems/binary-tree-postorder-traversal/) +- [102. Binary Tree Level Order Traversal](https://leetcode.com/problems/binary-tree-level-order-traversal/) +- [589. N-ary Tree Preorder Traversal](https://leetcode.com/problems/n-ary-tree-preorder-traversal/) +- [590. N-ary Tree Postorder Traversal](https://leetcode.com/problems/n-ary-tree-postorder-traversal/) +- [429. N-ary Tree Level Order Traversal](https://leetcode.com/problems/n-ary-tree-level-order-traversal/) + Binary Search Tree (BST) > A BST is a rooted binary tree whose internal nodes each store a val greater than all the vals in the node's left subtree and less than those in its right subtree. @@ -159,6 +182,17 @@ Binary Search Tree (BST) BST Operations +BST Template +```py +def BST(root, target): + if root.val == target: + # found it, do something + if val < root.val: + BST(root.left, val) # find in left tree + if val > root.val: + BST(root.right, val) # find in right tree +``` + Search ```py def search(root, val): @@ -218,44 +252,45 @@ Delete ``` ```py -def deleteNode(self, root: TreeNode, key: int) -> TreeNode: +def deleteNode(self, root: TreeNode, val: int) -> TreeNode: if root is None: return None - if key < root.val: - root.left = self.deleteNode(root.left, key) - elif key > root.val: - root.right = self.deleteNode(root.right, key) + if val < root.val: + root.left = self.deleteNode(root.left, val) + elif val > root.val: + root.right = self.deleteNode(root.right, val) else: - if root.left is None: - # has only right child - child = root.right - root = None - return child - elif root.right is None: - # has only left child - child = root.left - root = None - return child + if root.left is None: # has only right child + return root.right + if root.right is None: # has only left child + return root.left # has two children - child = self.minChild(root.right) - root.val = child.val - root.right = self.deleteNode(root.right , child.val) + minNode = self.getMin(root.right) + root.val = minNode.val + root.right = self.deleteNode(root.right, minNode.val) return root -def minChild(self, node: TreeNode) -> TreeNode: - curr = node - while curr.left is not None: - curr = curr.left - return curr +def getMin(self, node: TreeNode) -> TreeNode: + # left most child is the smallest + while node.left is not None: + node = node.left + return node ``` +LeetCode Problems +- [100. Same Tree](https://leetcode.com/problems/same-tree/) +- [700. Search in a Binary Search Tree](https://leetcode.com/problems/search-in-a-binary-search-tree/) +- [701. Insert into a Binary Search Tree](https://leetcode.com/problems/insert-into-a-binary-search-tree/) +- [450. Delete Node in a BST](https://leetcode.com/problems/delete-node-in-a-bst/) +- [98. Validate Binary Search Tree](https://leetcode.com/problems/validate-binary-search-tree/) + Heap ---- > A heap is a complete binary tree, and is represent by array. The children of the node at index i are at indices 2i + 1 and 2i + 2. Given element in a heap at position `i`: -- parent position: `(i-1) >> 1` +- parent position: `(i-1) >> 1` or `i // 2` - left child position: `2*i + 1` - right child position: `2*i + 2` diff --git a/Week03/NOTE.md b/Week03/NOTE.md index 49859700e..85249e380 100644 --- a/Week03/NOTE.md +++ b/Week03/NOTE.md @@ -49,6 +49,11 @@ def divide_conquer(problem, param1, param2, ...): - Sometimes we need to return multiple results (tuple) - Sometimes we need global variables to easily update the final result +Leetcode Problems +- [105. Construct Binary Tree from Preorder and Inorder Traversal](https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/) +- [236. Lowest Common Ancestor of a Binary Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/) +- [169. Majority Element](https://leetcode.com/problems/majority-element/) + Backtrack Template ------------------ @@ -72,4 +77,14 @@ def backtrack(path = [], choices): - Time complexity for backtrack algorithm is at least O(N!) - Backtrack is a decision tree, updating the result is actually a preorder and/or postorder recursion - Sometimes we don't need to explicitly maintain the choice list, we derive it using other parameters (e.g. index) -- Sometimes path can be a string instead of an array, and we use `path += 'choice'` and `path = path[:-1]` to make and remove choice \ No newline at end of file +- Sometimes path can be a string instead of an array, and we use `path += 'choice'` and `path = path[:-1]` to make and remove choice + +Leetcode Problems +- [22. Generate Parentheses](https://leetcode.com/problems/generate-parentheses/) +- [78. Subsets](https://leetcode.com/problems/subsets/) +- [46. Permutations](https://leetcode.com/problems/permutations/) +- [47. Permutations II](https://leetcode.com/problems/permutations-ii/) +- [77. Combinations](https://leetcode.com/problems/combinations/) +- [17. Letter Combinations of a Phone Number](https://leetcode.com/problems/letter-combinations-of-a-phone-number/) +- [51. N-Queens](https://leetcode.com/problems/n-queens/) +- [37. Sudoku Solver](https://leetcode.com/problems/sudoku-solver/) \ No newline at end of file diff --git a/Week04/NOTE.md b/Week04/NOTE.md index e6db5b79f..61681e947 100644 --- a/Week04/NOTE.md +++ b/Week04/NOTE.md @@ -61,23 +61,12 @@ def island(self, grid: List[List[int]]): ``` Backtrack (see Week03 NOTE) -```py -result = [] - -def backtrack(path = [], choices): - if end condition: - result.add(path[:]) # have to make a new copy - return - for choice in choices: - # get rid of the illegal choices - if exclusive condition: - continue - - path.append(choice) # Make the choice - backtrack(path, choices) # enter the next decision tree - path.pop() # Remove the choice (since it's already made) -``` +Leetcode Problems +- [200. Number of Islands](https://leetcode.com/problems/number-of-islands/) +- [695. Max Area of Island](https://leetcode.com/problems/max-area-of-island/) +- [463. Island Perimeter](https://leetcode.com/problems/island-perimeter/) +- [827. Making A Large Island](https://leetcode.com/problems/making-a-large-island/) BFS Template ------------ @@ -161,6 +150,20 @@ def bfs(start, target): # any two nodes, doesn't have to start from the root return 0 ``` +Leetcode Problems +- [22. Generate Parentheses](https://leetcode.com/problems/generate-parentheses/) +- [111. Minimum Depth of Binary Tree](https://leetcode.com/problems/minimum-depth-of-binary-tree/) +- [752. Open the Lock](https://leetcode.com/problems/open-the-lock/) +- [127. Word Ladder](https://leetcode.com/problems/word-ladder/) +- [126. Word Ladder II](https://leetcode.com/problems/word-ladder-ii/) +- [433. Minimum Genetic Mutation](https://leetcode.com/problems/minimum-genetic-mutation/) +- [515. Find Largest Value in Each Tree Row](https://leetcode.com/problems/find-largest-value-in-each-tree-row/) +- [529. Minesweeper](https://leetcode.com/problems/minesweeper/) + +Greedy Algorithm +---------------- +[TODO] + Binary Search ------------- @@ -173,7 +176,7 @@ Binary Search def binary_search(nums, target) left, right = 0, len(nums) - 1 while left <= right: - mid = left + (right - left) // 2 + mid = (left + right) // 2 if nums[mid] < target: left = mid + 1 elif nums[mid] > target: @@ -188,7 +191,7 @@ def binary_search(nums, target) def left_bound(nums, target): left, right = 0, len(nums) - 1 while left < right: - mid = left + (right - left) // 2 + mid = (left + right) // 2 if nums[mid] < target: left = mid + 1 elif nums[mid] > target: @@ -229,32 +232,27 @@ Target Function g(m) ```py def binary_search(l, r): """ - Returns the smallest number m in range [l, r) such that g(m) is true. - Returns r if not found. + Returns the smallest number m in range [l, r] such that g(m) is true. + Returns r+1 if not found. Time Complexity: O(log(r - l) * (f(m) + g(m))) Space Complexity: O(1) """ - while l < r: + while l <= r: m = l + (r - l) // 2 if f(m): # optional: if somehow we can determine m is the answer, return it return m - if g(m): - r = m # new range [l, m) - else: - l = m + 1 # new range [m+1, r) - return l # or not found - -def binary_search(l, r): - """ - Returns the smallest number m in range [l, r] such that g(m) is true. - Returns r+1 if not found. - """ - while l <= r: - m = l + (r - l) // 2 if g(m): r = m - 1 # new range [l, m-1] else: l = m + 1 # new range [m+1, r] - return l -``` \ No newline at end of file + return l # or not found +``` + +Leetcode Problems +- [704. Binary Search](https://leetcode.com/problems/binary-search/) +- [33. Search in Rotated Sorted Array](https://leetcode.com/problems/search-in-rotated-sorted-array/) +- [34. Find First and Last Position of Element in Sorted Array](https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/) +- [69. Sqrt(x)](https://leetcode.com/problems/sqrtx/) +- [74. Search a 2D Matrix](https://leetcode.com/problems/search-a-2d-matrix/) +- [367. Valid Perfect Square](https://leetcode.com/problems/valid-perfect-square/) \ No newline at end of file From 74d7155387525c80587a87d2ae694ff82d34d8ea Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 19 Jul 2020 23:21:41 -0400 Subject: [PATCH 25/48] week 06 homework and notes --- Week06/NOTE.md | 136 +++++++++++++++++++++++++- Week06/buy-sells-stocks.py | 193 +++++++++++++++++++++++++++++++++++++ Week06/house-robber.py | 56 +++++++++++ Week06/sub-array.py | 33 +++++++ Week06/sub-sequence.py | 42 ++++++++ Week06/unique-paths.py | 27 ++++++ 6 files changed, 486 insertions(+), 1 deletion(-) create mode 100644 Week06/buy-sells-stocks.py create mode 100644 Week06/house-robber.py create mode 100644 Week06/sub-array.py create mode 100644 Week06/sub-sequence.py create mode 100644 Week06/unique-paths.py diff --git a/Week06/NOTE.md b/Week06/NOTE.md index 50de30414..2c24a386a 100644 --- a/Week06/NOTE.md +++ b/Week06/NOTE.md @@ -1 +1,135 @@ -学习笔记 \ No newline at end of file +Learning Notes Week 04 +====================== + +Dynamic Programming +------------------- +> **Optimal Substructure**: the solution to a given optimization problem can be obtained by the combination of optimal solutions to its sub-problems. Such optimal substructures are usually described by means of "_recursion_". + +> **Overlapping Subproblems**: the space of sub-problems must be small, that is, any recursive algorithm solving the problem should solve the same sub-problems over and over, rather than generating new sub-problems. If a problem can be solved by combining optimal solutions to non-overlapping sub-problems, the strategy is called _"divide and conquer"_ instead. + +This can be achieved in either of two ways: +1. **Top-down**: Recursion + Memo +2. **Bottom-up**: Iteration + DP Table (states) + +Fibonacci sequence +------------------ + +Top-down +``` + _________________f(5)________________ + / \ + _______f(4)______ _______f(3)_ + / \ / \ + _______f(3)_ __f(2)_ __f(2)_ f(1) + / \ / \ / \ + __f(2)_ f(1) f(1) f(1) f(1) f(1) + / \ +f(1) f(1) +``` +Recursion +```py +def fib(N): + if N == 0: return 0 + if N == 1: return 1 + return fib(N-1) + fib(N-2) +``` +Space Optimization (Memo) +```py +memo = {} +def fib(N): + if N == 0: return 0 + if N == 1: return 1 + + if memo.get(N): + return memo[N] + + memo[N] = fib(N-1) + fib(N-2) + + return memo[N] +``` + +Bottom-up +``` +f(0) f(1) f(2) f(3) f(4) f(5) + 1 2 3 5 8 13 +---------------------------> +``` +Dynamic Programming +```py +def fib(N): + if N == 0: return 0 + if N == 1: return 1 + + dp = [0] * (N+1) + dp[0] = 0 + dp[1] = 1 + + for i in range(2, N+1): + dp[i] = dp[i-1] + dp[i-2] + + return dp[N] +``` +Space Optimization (Reduce States) +```py +def fib(self): + if N == 0: return 0 + if N == 1: return 1 + + dp_i = 0 + dp_i_1 = 0 + dp_i_2 = 1 + + for i in range(N): + dp_i = dp_i_1 + dp_i_2 + dp_i_2 = dp_i_1 + dp_i_1 = dp_i + + return dp_i +``` + +DP Framework +------------ +```py +# initialize base case +dp[0][0][...] = base +# status transfer +for status_1 in all_values_in_status_1: + for status_2 in all_values_in_status_2: + for ... + dp[status_1][status_2][...] = choose(choice_1,choice_2...) +``` + +Leetcode Problems +----------------- + +Basics +- [509. Fibonacci Number](https://leetcode.com/problems/fibonacci-number/) +- [120. Triangle](https://leetcode.com/problems/triangle/) + +House Robber +- [198. House Robber](https://leetcode.com/problems/house-robber/) +- [213. House Robber II](https://leetcode.com/problems/house-robber-ii/) +- [337. House Robber III](https://leetcode.com/problems/house-robber-iii/) + +Unique Paths +- [62. Unique Paths](https://leetcode.com/problems/unique-paths/) +- [63. Unique Paths II](https://leetcode.com/problems/unique-paths-ii/) +- [980. Unique Paths III](https://leetcode.com/problems/unique-paths-iii/) + +Sub Array / Sub Subsequence +- [53. Maximum Subarray](https://leetcode.com/problems/maximum-subarray/) +- [152. Maximum Product Subarray](https://leetcode.com/problems/maximum-product-subarray/description/) +- [718. Maximum Length of Repeated Subarray](https://leetcode.com/problems/maximum-length-of-repeated-subarray/) +- [416. Partition Equal Subset Sum](https://leetcode.com/problems/partition-equal-subset-sum/) +- [300. Longest Increasing Subsequence](https://leetcode.com/problems/longest-increasing-subsequence/) +- [516. Longest Palindromic Subsequence](https://leetcode.com/problems/longest-palindromic-subsequence/) +- [1143. Longest Common Subsequence](https://leetcode.com/problems/longest-common-subsequence/) +- [1035. Uncrossed Lines](https://leetcode.com/problems/uncrossed-lines/) + +Buy and Sell Stocks +- [121. Best Time to Buy and Sell Stock](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/) +- [122. Best Time to Buy and Sell Stock II](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/) +- [123. Best Time to Buy and Sell Stock III](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/) +- [188. Best Time to Buy and Sell Stock IV](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/) +- [309. Best Time to Buy and Sell Stock with Cooldown](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/) +- [714. Best Time to Buy and Sell Stock with Transaction Fee](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/) diff --git a/Week06/buy-sells-stocks.py b/Week06/buy-sells-stocks.py new file mode 100644 index 000000000..400361dad --- /dev/null +++ b/Week06/buy-sells-stocks.py @@ -0,0 +1,193 @@ +""" +121. Best Time to Buy and Sell Stock +https://leetcode.com/problems/best-time-to-buy-and-sell-stock/ + +122. Best Time to Buy and Sell Stock II +https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/ + +123. Best Time to Buy and Sell Stock III +https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/ + +188. Best Time to Buy and Sell Stock IV +https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/ + +309. Best Time to Buy and Sell Stock with Cooldown +https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/ + +714. Best Time to Buy and Sell Stock with Transaction Fee +https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/ +""" +from typing import List +import math + +class Solution: + def maxProfit1(self, prices: List[int]) -> int: + """ + dp[i][0] = max ( + dp[i-1][0] + dp[i-1][1] + prices[i] + ) + dp[i][1] = max ( + dp[i-1][1] + 0 - prices[i] + ) + """ + n = len(prices) + if n == 0: + return 0 + + dp = [[0 for _ in range(2)] for _ in range(n)] + dp[0][0] = 0 + dp[0][1] = -prices[0] + + for i in range(1, n): + dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]) + dp[i][1] = max(dp[i-1][1], -prices[i]) + + return dp[n-1][0] + + def maxProfit2(self, prices: List[int]) -> int: + """ + 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] + ) + """ + n = len(prices) + if n == 0: + return 0 + + dp = [[0 for _ in range(2)] for _ in range(n)] + dp[0][0] = 0 + dp[0][1] = -prices[0] + + for i in range(1, n): + 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]) + + return dp[n-1][0] + + def maxProfit3(self, prices: List[int]) -> int: + """ + 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] + ) + """ + n = len(prices) + if n == 0: + return 0 + + k = 2 + dp = [[[0 for _ in range(2)] for _ in range(k+1)] for _ in range(n)] + dp[0][1][0] = 0 + dp[0][1][1] = -prices[0] + dp[0][2][0] = 0 + dp[0][2][1] = -prices[0] + + for i in range(1, n): + for j in range(1, k+1): + dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1] + prices[i]) + dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i]) + + return dp[n-1][k][0] + + def maxProfit4(self, k: int, prices: List[int]) -> int: + """ + 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] + ) + """ + n = len(prices) + if n == 0: + return 0 + + if k > n/2: + res = 0 + for i, j in zip(prices[1:], prices[:-1]): + res += max(0, i-j) + return res + + dp = [[[-math.inf]*2 for _ in range(k+1)] for _ in range(n)] + dp[0][0][0] = 0 + dp[0][1][1] = -prices[0] + + for i in range(1, n): + for j in range(k+1): + dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1] + prices[i]) + if k > 0: + dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i]) + + res = max(dp[n-1][j][0] for j in range(k+1)) + return res + + def maxProfit5(self, prices: List[int]) -> int: + """ + dp[i][0] = max( + dp[i-1][0] + dp[i-1][1] + price[i] + ) + dp[i][1] = max( + dp[i-1][1] + dp[i-2][0] - price[i] + ) + """ + n = len(prices) + if n == 0: + return 0 + + dp = [[0 for _ in range(2)] for _ in range(n)] + dp[0][0] = 0 + dp[0][1] = -prices[0] + + for i in range(1, n): + 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]) + + return dp[n-1][0] + + def maxProfit6(self, prices: List[int], fee: int) -> int: + """ + 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 + ) + """ + n = len(prices) + if n == 0: + return 0 + + dp = [[0 for _ in range(2)] for _ in range(n)] + dp[0][0] = 0 + dp[0][1] = -prices[0] - fee + + for i in range(1, n): + 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) + + return dp[n-1][0] + +solution = Solution() +print(solution.maxProfit1([7,1,5,3,6,4])) # 5 +print(solution.maxProfit2([7,1,5,3,6,4])) # 7 +print(solution.maxProfit3([3,3,5,0,0,3,1,4])) #6 +# todo: maxProfit4 test case +print(solution.maxProfit5([1,2,3,0,2])) # 3 +print(solution.maxProfit6([1,3,2,8,4,9], 2)) # 8 diff --git a/Week06/house-robber.py b/Week06/house-robber.py new file mode 100644 index 000000000..156df1836 --- /dev/null +++ b/Week06/house-robber.py @@ -0,0 +1,56 @@ +""" +198. House Robber +https://leetcode.com/problems/house-robber/ + +213. House Robber II +https://leetcode.com/problems/house-robber-ii/ + +337. House Robber III +https://leetcode.com/problems/house-robber-iii/ +""" +from typing import List + +class Solution: + def rob1(self, nums: List[int]) -> int: + n = len(nums) + if n == 0: return 0 + if n == 1: return nums[0] + + k = 0 # dp[k] + k_1 = 0 # dp[k-1] + k_2 = 0 # dp[k-2] + + for i in range(n): + k = max(k_1, k_2 + nums[i]) + k_2 = k_1 + k_1 = k + + return k + + def rob2(self, nums: List[int]) -> int: + n = len(nums) + if n == 0: return 0 + if n == 1: return nums[0] + return max(self.rob1(nums[1:]), self.rob1(nums[:-1])) + + def rob3(self, root: TreeNode, memo = {}) -> int: + def dp(root): + if not root: + return 0, 0 + + rob_left, not_rob_left = dp(root.left) + rob_right, not_rob_right = dp(root.right) + + rob = root.val + not_rob_left + not_rob_right + not_rob = max(rob_left, not_rob_left) + max(rob_right, not_rob_right) + + return rob, not_rob + + rob, not_rob = dp(root) + return max(rob, not_rob) + + +solution = Solution() +print(solution.rob1([1,2,3,1])) # 4 +print(solution.rob2([2,3,2])) # 3 +# todo: rob3 test case \ No newline at end of file diff --git a/Week06/sub-array.py b/Week06/sub-array.py new file mode 100644 index 000000000..6df979b12 --- /dev/null +++ b/Week06/sub-array.py @@ -0,0 +1,33 @@ +""" +53. Maximum Subarray +https://leetcode.com/problems/maximum-subarray/ + +152. Maximum Product Subarray +https://leetcode.com/problems/maximum-product-subarray/ + +718. Maximum Length of Repeated Subarray +https://leetcode.com/problems/maximum-length-of-repeated-subarray/ +""" +from typing import List + +class Solution: + def findLength(self, A: List[int], B: List[int]) -> int: + """ + f(0, *) = 0 + f(*, 0) = 0 + f(i,j) = f(i-1, j-1) + 1, if A[i] == B[j] + """ + m, n = len(A), len(B) + dp = [[0 for _ in range(n+1)] for _ in range(m+1)] + res = 0 + + for i in range(1, m+1): + for j in range(1, n+1): + if A[i-1] == B[j-1]: + dp[i][j] = dp[i-1][j-1] + 1 + res = max(res, dp[i][j]) + + return res + +solution = Solution() +print(solution.findLength([1,2,3,2,1], [3,2,1,4,7])) # 3 \ No newline at end of file diff --git a/Week06/sub-sequence.py b/Week06/sub-sequence.py new file mode 100644 index 000000000..5056d1033 --- /dev/null +++ b/Week06/sub-sequence.py @@ -0,0 +1,42 @@ +""" +300. Longest Increasing Subsequence +https://leetcode.com/problems/longest-increasing-subsequence/) + +516. Longest Palindromic Subsequence +https://leetcode.com/problems/longest-palindromic-subsequence/ + +1143. Longest Common Subsequence +https://leetcode.com/problems/longest-common-subsequence/ + +1035. Uncrossed Lines +https://leetcode.com/problems/uncrossed-lines/ +""" +class Solution: + def longestCommonSubsequence(self, s: str, t: str) -> int: + """ + f(0, *) = 0 + f(*, 0) = 0 + f(i, j) = { + f(i-1, j-1) + 1 , when s[i-1] == t[j-1] + max(f(i-1, j), f(i, j-1)) , when s[i-1] != t[j-1] + } + """ + if not s or not t: + return 0 + + m, n = len(s), len(t) + 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 s[i-1] == t[j-1]: + 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] + +solution = Solution() +print(solution.longestCommonSubsequence('abcde', 'ace')) # 3 +print(solution.longestCommonSubsequence('abc', 'abc')) # 3 +print(solution.longestCommonSubsequence('abc', 'def')) # 0 diff --git a/Week06/unique-paths.py b/Week06/unique-paths.py new file mode 100644 index 000000000..6714ecea7 --- /dev/null +++ b/Week06/unique-paths.py @@ -0,0 +1,27 @@ +""" +62. Unique Paths +https://leetcode.com/problems/unique-paths/ + +63. Unique Paths II +https://leetcode.com/problems/unique-paths-ii/ + +980. Unique Paths III +https://leetcode.com/problems/unique-paths-iii/ +""" +class Solution: + def uniquePaths(self, m: int, n: int) -> int: + """ + f(0, *) = 1 + f(*, 0) = 1 + f(i, j) = f(i, j-1) + f(i-1, j) + """ + dp = [[1]*n] + [[1] + [0]*(n-1) for _ in range(m-1)] + + for i in range(1, m): + for j in range(1, n): + dp[i][j] = dp[i-1][j] + dp[i][j-1] + + return dp[-1][-1] + +solution = Solution() +print(solution.uniquePaths(7, 2)) # 28 \ No newline at end of file From a6a3685a5159500b8c4639288fba9f7d0aba3420 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 26 Jul 2020 22:24:00 -0400 Subject: [PATCH 26/48] week 07 notes and homework --- Week06/NOTE.md | 2 +- Week07/NOTE.md | 227 ++++++++++++++++++++++++++++- Week07/disjoint_set.py | 82 +++++++++++ Week07/friend-circles.py | 53 +++++++ Week07/minimum_genetic_mutation.py | 48 ++++++ Week07/similarity.py | 65 +++++++++ Week07/sudoku.py | 111 ++++++++++++++ Week07/trie.py | 52 +++++++ Week07/word_ladder.py | 52 +++++++ 9 files changed, 690 insertions(+), 2 deletions(-) create mode 100644 Week07/disjoint_set.py create mode 100644 Week07/friend-circles.py create mode 100644 Week07/minimum_genetic_mutation.py create mode 100644 Week07/similarity.py create mode 100644 Week07/sudoku.py create mode 100644 Week07/trie.py create mode 100644 Week07/word_ladder.py diff --git a/Week06/NOTE.md b/Week06/NOTE.md index 2c24a386a..3925b0b16 100644 --- a/Week06/NOTE.md +++ b/Week06/NOTE.md @@ -1,4 +1,4 @@ -Learning Notes Week 04 +Learning Notes Week 06 ====================== Dynamic Programming diff --git a/Week07/NOTE.md b/Week07/NOTE.md index 50de30414..7a3f61941 100644 --- a/Week07/NOTE.md +++ b/Week07/NOTE.md @@ -1 +1,226 @@ -学习笔记 \ No newline at end of file +Learning Notes Week 07 +====================== + +Trie +---- +> A trie is a tree-like data structure whose nodes store the letters of an alphabet. By structuring the nodes in a particular way, words and strings can be retrieved from the structure by traversing down a branch path of the tree. + +```py +def buildTrie(words): + trie = {} + for word in words: + node = trie + for char in word: + node = node.setdefault(char, {}) + node['#'] = True + return trie +``` + +``` +trie = buildTrie(["app", "apple", "bar", "ball"]) + + () + / \ + a b + | | + p a + | / \ + p r l +/ \ | | +# l # l + | | + e # + | + # +``` + +Leetcode Problems +- [208. Implement Trie (Prefix Tree)](https://leetcode.com/problems/implement-trie-prefix-tree/) +- [212. Word Search II](https://leetcode.com/problems/word-search-ii/) + +Disjoint-set (Union-find set) +------------ +> A disjoint-set data structure is a data structure that tracks a set of elements partitioned into a number of disjoint (non-overlapping) subsets. It provides near-constant-time operations to add new sets, to merge existing sets, and to determine whether elements are in the same set. + +```py +ds = DisjointSet(5) + +(0) (1) (2) (3) (4) + +ds.union(1, 2) +ds.union(3, 4) + +(0) (1) (3) + | | + (2) (4) +``` + +Leetcode Problems +- [547. Friend Circles](https://leetcode.com/problems/friend-circles/) +- [130. Surrounded Regions](https://leetcode.com/problems/surrounded-regions/) +- [200. Number of Islands](https://leetcode.com/problems/number-of-islands/) + +Advanced Search +--------------- + +Backtrack DFS with Pruning +```py +# Generate Parenthesis +def dfs(left = 0, right = 0, path = ''): + if left == n and right == n: + res.append(path) + return + + if right > left: # Pruning + return + + if left < n: + dfs(left + 1, right, path + '(') + + if right < n: + dfs(left, right + 1, path + ')') +``` + +Bidirectional BFS +```py +sourceQueue = collections.deque([source]) +targetQueue = collections.deque([target]) +visited = set([source]) +step = 0 + +while sourceQueue and targetQueue: + step += 1 + + # choose the smaller queue to spread + if len(sourceQueue) > len(targetQueue): + sourceQueue, targetQueue = targetQueue, sourceQueue + + for _ in range(len(sourceQueue)): + node = sourceQueue.popleft() + + for child in node.children: + # source and target meet + if child in targetQueue: + return step + 1 + + if not child in visited: + sourceQueue.append(child) + visited.add(child) + +return 0 # not found +``` + +A-Star (A*) Search +```py +def AstarSearch(graph, start, end): + pq = collections.priority_queue() # priority —> heuristic function + pq.append([start]) + visited.add(start) + while pq: + node = pq.pop() # can we add more intelligence 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) +``` + +Leetcode Problems +- [70. Climbing Stairs](https://leetcode.com/problems/climbing-stairs/) +- [22. Generate Parentheses](https://leetcode.com/problems/generate-parentheses/) +- [51. N-Queens](https://leetcode.com/problems/n-queens/) +- [36. Valid Sudoku](https://leetcode.com/problems/valid-sudoku/) +- [37. Sudoku Solver](https://leetcode.com/problems/sudoku-solver/) +- [1091. Shortest Path in Binary Matrix](https://leetcode.com/problems/shortest-path-in-binary-matrix/) +- [773. Sliding Puzzle](https://leetcode.com/problems/sliding-puzzle/) + +Self-balancing Binary Search Tree +-------------- + +> A self-balancing (or height-balanced) BST is any node-based BST that automatically keeps its height (maximal number of levels below the root) small in the face of arbitrary item insertions and deletions. + +AVL Tree + +> In an AVL tree, the heights of the two child subtrees of any node differ by at most one; if at any time they differ by more than one, re-balancing is done to restore this property. Lookup, insertion, and deletion all take O(log n) time in both the average and worst cases. + +```py +BalanceFactor(node) = Height(RightSubtree(node)) - Height(LeftSubtree(node)) +BalanceFactor(node) = {-1, 0, 1} +``` + +4 Types of Rotations +``` +Left-Left Case, right rotation: + + A (-2) B (0) + / / \ + B (-1) C (0) A (0) + / +C (0) + +Right Right Case, left rotation: + +A (2) B (0) + \ / \ + B (1) A (0) C (0) + \ + C (0) + +Left-Right Case, right rotation -> left rotation: + + A (-2) A (-2) C (0) + / / / \ +B (1) C (-1) B (0) A (0) + \ / + C (0) B (0) + +Right-Left Case, left rotation -> right rotation: + +A (-2) A (-2) C (0) + \ \ / \ + B (-1) C (-1) A (0) B (0) + / \ +C (0) B (0) +``` + +With Subtree +``` + right rotation + ------------------> + Y X + / \ / \ + X T3 T1 Y + / \ / \ + T1 T2 T2 T3 + <------------------ + left rotation + + Case 1 Case 4 + Z Y | Y Z + / \ / \ | / \ / \ + Y T4 X Z | Z X T1 Y + / \ / \ / \ | / \ / \ / \ + X T3 T1 T2 T3 T4 | T1 T2 T3 T4 T2 X + / \ | / \ + T1 T2 | T3 T4 + + Case 2 Case 3 + Z Z X | Y Z Z + / \ / \ / \ | / \ / \ / \ + Y T4 X T4 Y Z | Z X T1 Y T1 Y + / \ / \ / \ / \ | / \ / \ / \ / \ + T1 X Y T3 T1 T2 T3 T4 | T1 T2 T3 T4 T2 X X T4 + / \ / \ | / \ / \ + T1 T2 T1 T2 | T3 T4 T2 T3 +``` + +Red-Black Tree + +> In a red–black tree, each node stores an extra bit representing color, used to ensure that the tree remains approximately balanced during insertions and deletions. + +Properties +- Each node is either red or black. +- The root is black. This rule is sometimes omitted. Since the root can always be changed from red to black, but not necessarily vice versa, this rule has little effect on analysis. +- All leaves (NIL) are black. +- If a node is red, then both its children are black. +- Every path from a given node to any of its descendant NIL nodes goes through the same number of black nodes. \ No newline at end of file diff --git a/Week07/disjoint_set.py b/Week07/disjoint_set.py new file mode 100644 index 000000000..973638c1c --- /dev/null +++ b/Week07/disjoint_set.py @@ -0,0 +1,82 @@ +""" +Disjoint-set Data Structure +""" +class DisjointSet: + def __init__(self, size): + self.parents = [i for i in range(size)] + + def find(self, x): + """ + Worse Case: O(N) + """ + parents = self.parents + while x != parents[x]: + x = parents[x] + return x + + def union(self, x, y): + """ + Worse Case: O(N) + """ + parent_x, parent_y = self.find(x), self.find(y) + if parent_x != parent_y: + self.parents[parent_x] = parent_y + + def connected(self, x, y): + return self.find(x) == self.find(y) + +ds = DisjointSet(5) +print(ds.find(1)) # 1 +print(ds.connected(1, 2)) # False +ds.union(1, 2) +print(ds.find(1)) # 2 +print(ds.find(2)) # 2 +print(ds.connected(1, 2)) # True + +""" +Disjoint-set Data Structure Optimized +""" +class DisjointSetOptimized: + def __init__(self, size): + self.count = size + self.parents = [i for i in range(size)] + self.weights = [1 for _ in range(size)] + + def find(self, x): + """ + Close to O(1) + """ + parents = self.parents + while x != parents[x]: + # Compression: reduce the path to the root + parents[x] = parents[parents[x]] + x = parents[x] + return x + + def union(self, x, y): + """ + Average Case: O(logN) + """ + parent_x, parent_y = self.find(x), self.find(y) + if parent_x != parent_y: + weights = self.weights + parents = self.parents + # balancing: connect "lighter" tree onto the "heavier" tree + if weights[parent_x] > weights[parent_y]: + parents[parent_y] = parent_x + weights[parent_x] += weights[parent_y] + else: + parents[parent_x] = parent_y + weights[parent_y] += weights[parent_x] + self.count -= 1 + + def connected(self, x, y): + return self.find(x) == self.find(y) + +ds = DisjointSetOptimized(5) +print(ds.find(1)) # 1 +print(ds.connected(1, 2)) # False +ds.union(1, 2) +print(ds.find(1)) # 2 +print(ds.find(2)) # 2 +print(ds.connected(1, 2)) # True \ No newline at end of file diff --git a/Week07/friend-circles.py b/Week07/friend-circles.py new file mode 100644 index 000000000..318da60a2 --- /dev/null +++ b/Week07/friend-circles.py @@ -0,0 +1,53 @@ +""" +547. Friend Circles [Medium] +https://leetcode.com/problems/friend-circles/ +""" +from typing import List + +class DisjointSet: + def __init__(self, size): + self.count = size + self.parents = [i for i in range(size)] + self.weights = [1 for _ in range(size)] + + def find(self, x): + parents = self.parents + while x != parents[x]: + # Compression: reduce the path to the root + parents[x] = parents[parents[x]] + x = parents[x] + return x + + def union(self, x, y): + parent_x, parent_y = self.find(x), self.find(y) + if parent_x != parent_y: + weights = self.weights + parents = self.parents + # balancing: connect "lighter" tree onto the "heavier" tree + if weights[parent_x] > weights[parent_y]: + parents[parent_y] = parent_x + weights[parent_x] += weights[parent_y] + else: + parents[parent_x] = parent_y + weights[parent_y] += weights[parent_x] + self.count -= 1 + + def connected(self, x, y): + return self.find(x) == self.find(y) + +class Solution: + def findCircleNum(self, M: List[List[int]]) -> int: + N = len(M) + ds = DisjointSet(N) + for i in range(N): + for j in range(N): + if M[i][j] == 1 and i != j: + ds.union(i, j) + + return ds.count + +solution = Solution() +ans = solution.findCircleNum([[1,1,0], [1,1,0], [0,0,1]]) +print(ans) # 2 +ans = solution.findCircleNum([[1,1,0], [1,1,1], [0,1,1]]) +print(ans) # 1 \ No newline at end of file diff --git a/Week07/minimum_genetic_mutation.py b/Week07/minimum_genetic_mutation.py new file mode 100644 index 000000000..a0c6d1e52 --- /dev/null +++ b/Week07/minimum_genetic_mutation.py @@ -0,0 +1,48 @@ +""" +433. Minimum Genetic Mutation +https://leetcode.com/problems/minimum-genetic-mutation/ +""" +from typing import List +from collections import deque + +class Solution: + def minMutation(self, start: str, end: str, bank: List[str]) -> int: + bank = set(bank) + if end not in bank: + return -1 + + step = 0 + queue = deque([start]) + while queue: + n = len(queue) + for _ in range(n): + gene = queue.popleft() + if gene == end: + return step + mutations = self.mutate(gene) + for mutation in mutations: + if mutation in bank: + queue.append(mutation) + bank.remove(mutation) + step += 1 + + return -1 + + def mutate(self, gene: str) -> List[str]: + mutation_map = { + "A": "CGT", + "C": "AGT", + "G": "CAT", + "T": "CGA", + } + res = [] + for i, s in enumerate(gene): + for c in mutation_map[s]: + mutation = gene[:i] + c + gene[i+1:] + res.append(mutation) + return res + +solution = Solution() +print(solution.minMutation("AACCGGTT", "AACCGGTA", ["AACCGGTA"])) # 1 +print(solution.minMutation("AACCGGTT", "AAACGGTA", ["AACCGGTA", "AACCGCTA", "AAACGGTA"])) # 2 +print(solution.minMutation("AAAAACCC", "AACCCCCC", ["AAAACCCC", "AAACCCCC", "AACCCCCC"])) # 3 \ No newline at end of file diff --git a/Week07/similarity.py b/Week07/similarity.py new file mode 100644 index 000000000..a6d45c620 --- /dev/null +++ b/Week07/similarity.py @@ -0,0 +1,65 @@ +""" +Five Most Popular Similarity Measures in Python + +https://dataaspirant.com/five-most-popular-similarity-measures-implementation-in-python/ +""" +from math import * +from decimal import Decimal + +class Similarity(): + """ + Five similarity measures function + """ + def euclidean_distance(self, x, y): + """ + return euclidean distance between two lists + """ + return sqrt(sum(pow(a-b, 2) for a, b in zip(x, y))) + + def manhattan_distance(self, x, y): + """ + return manhattan distance between two lists + """ + return sum(abs(a-b) for a, b in zip(x,y)) + + def minkowski_distance(self, x, y, p_value): + """ + return minkowski distance between two lists + """ + return self.nth_root(sum(pow(abs(a-b), p_value) for a,b in zip(x, y)), p_value) + + def nth_root(self, value, n_root): + """ + returns the n_root of an value + """ + root_value = 1/float(n_root) + return round (Decimal(value) ** Decimal(root_value),3) + + def cosine_similarity(self, x, y): + """ + return cosine similarity between two lists + """ + numerator = sum(a*b for a,b in zip(x,y)) + denominator = self.square_rooted(x)*self.square_rooted(y) + return round(numerator/float(denominator),3) + + def square_rooted(self, x): + """ + return 3 rounded square rooted value + """ + return round(sqrt(sum([a*a for a in x])),3) + + def jaccard_similarity(self, x, y): + """ + returns the jaccard similarity between two lists + """ + intersection_cardinality = len(set.intersection(*[set(x), set(y)])) + union_cardinality = len(set.union(*[set(x), set(y)])) + return intersection_cardinality/float(union_cardinality) + +measures = Similarity() +print(measures.euclidean_distance([0,3,4,5], [7,6,3,-1])) +print(measures.manhattan_distance([10,20,10], [10,20,20])) +print(measures.minkowski_distance([0,3,4,5], [7,6,3,-1], 3)) +print(measures.cosine_similarity([3, 45, 7, 2], [2, 54, 13, 15])) +print(measures.jaccard_similarity([0,1,2,5,6], [0,2,3,5,7,9])) \ No newline at end of file diff --git a/Week07/sudoku.py b/Week07/sudoku.py new file mode 100644 index 000000000..245e490b2 --- /dev/null +++ b/Week07/sudoku.py @@ -0,0 +1,111 @@ +""" +36. Valid Sudoku +https://leetcode.com/problems/valid-sudoku/ + +37. Sudoku Solver +https://leetcode.com/problems/sudoku-solver/ +""" +from typing import List +from collections import Counter + +class Solution: + def isValidSudoku(self, board: List[List[str]]) -> bool: + row = [Counter() for _ in range(9)] + col = [Counter() for _ in range(9)] + box = [Counter() for _ in range(9)] + + for i in range(9): + for j in range(9): + num = board[i][j] + if num != '.': + num = int(num) + k = (i // 3) * 3 + (j // 3) # box index + + row[i][num] += 1 + col[j][num] += 1 + box[k][num] += 1 + + if row[i][num] > 1 or col[j][num] > 1 or box[k][num] > 1: + return False + return True + + def solveSudoku(self, board: List[List[str]]) -> None: + """ + Do not return anything, modify board in-place instead. + """ + nums = ['1','2','3','4','5','6','7','8','9'] + + def backtrack(row = 0, col = 0): + if col == 9: + return backtrack(row + 1, 0) # next row + + if row == 9: + return True # end of board + + if board[row][col] != '.': + return backtrack(row, col + 1) # next number + + for num in nums: + if not isValid(row, col, num): + continue + + board[row][col] = num + if backtrack(row, col + 1): + return True # found an solution + board[row][col] = '.' + + return False + + def isValid(r, c, n) -> bool: + for i in range(9): + if board[r][i] == n: + return False + if board[i][c] == n: + return False + if board[(r//3)*3 + i//3][(c//3)*3 + i%3] == n: + return False + return True + + backtrack() + + +solution = Solution() + +board1 = [ + ["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"] +] + +board2 = [ + ["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"] +] + +print(solution.isValidSudoku(board1)) # True +print(solution.isValidSudoku(board2)) # True + +solution.solveSudoku(board1) +print(board1) +# ['5', '3', '4', '6', '7', '8', '9', '1', '2'], +# ['6', '7', '2', '1', '9', '5', '3', '4', '8'], +# ['1', '9', '8', '3', '4', '2', '5', '6', '7'], +# ['8', '5', '9', '7', '6', '1', '4', '2', '3'], +# ['4', '2', '6', '8', '5', '3', '7', '9', '1'], +# ['7', '1', '3', '9', '2', '4', '8', '5', '6'], +# ['9', '6', '1', '5', '3', '7', '2', '8', '4'], +# ['2', '8', '7', '4', '1', '9', '6', '3', '5'], +# ['3', '4', '5', '2', '8', '6', '1', '7', '9'], \ No newline at end of file diff --git a/Week07/trie.py b/Week07/trie.py new file mode 100644 index 000000000..811cde90a --- /dev/null +++ b/Week07/trie.py @@ -0,0 +1,52 @@ +""" +Trie Data Structure +""" +import collections + +class Trie: + def __init__(self): + self.root = {} + + def insert(self, word: str) -> None: + """ + Inserts a word into the trie. + """ + root = self.root + for letter in word: + root = root.setdefault(letter, {}) + root['#'] = True + + def search(self, word: str) -> bool: + """ + Returns if the word is in the trie. + """ + root = self.root + for letter in word: + root = root.get(letter) + if root is None: + return False + return '#' in root + + def startsWith(self, prefix: str) -> bool: + """ + Returns if there is any word in the trie that starts with the given prefix. + """ + root = self.root + for letter in prefix: + root = root.get(letter) + if root is None: + return False + return True + + def __str__(self): + root = self.root + return str(root) + +trie = Trie() +trie.insert('apple') +print(trie) +print(trie.search('apple')) # True +print(trie.search('app')) # False +print(trie.startsWith('app')) # True +trie.insert("app") +print(trie.search("app")) # True \ No newline at end of file diff --git a/Week07/word_ladder.py b/Week07/word_ladder.py new file mode 100644 index 000000000..923d31013 --- /dev/null +++ b/Week07/word_ladder.py @@ -0,0 +1,52 @@ +""" +127. Word Ladder +https://leetcode.com/problems/word-ladder/description/ +""" +from typing import List +import collections +import string + +class Solution: + def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: + """ + Bidirectional BFS + """ + wordList = set(wordList) + if not wordList or endWord not in wordList: + return 0 + + beginQueue = collections.deque([beginWord]) + endQueue = collections.deque([endWord]) + visited = set([beginWord]) + step = 0 + + while beginQueue and endQueue: + step += 1 + if len(beginQueue) > len(endQueue): + beginQueue, endQueue = endQueue, beginQueue + for _ in range(len(beginQueue)): + word = beginQueue.popleft() + for i in range(len(word)): + for c in string.ascii_lowercase: + if c == word[i]: + continue + newword = word[:i] + c + word[i+1:] + if newword in endQueue: + return step + 1 + if not newword in visited and newword in wordList: + beginQueue.append(newword) + visited.add(newword) + return 0 + + +solution = Solution() + +beginWord = "hit" +endWord = "cog" +wordList = ["hot","dot","dog","lot","log","cog"] +print(solution.ladderLength(beginWord, endWord, wordList)) # 5 + +beginWord = "hit" +endWord = "cog" +wordList = ["hot","dot","dog","lot","log"] +print(solution.ladderLength(beginWord, endWord, wordList)) # 0 From 5b79dcb30dd5f1a1d5539b01bf050a51e63a5f69 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 2 Aug 2020 01:16:07 -0400 Subject: [PATCH 27/48] week 8 homework and notes --- Week08/LRU_cache.py | 108 +++++++++++++++++++++++ Week08/NOTE.md | 156 ++++++++++++++++++++++++++++++++- Week08/bloom_filter.py | 29 ++++++ Week08/hamming_weight.py | 28 ++++++ Week08/power_of_two.py | 14 +++ Week08/reverse_bits.py | 27 ++++++ Week08/sort.py | 185 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 546 insertions(+), 1 deletion(-) create mode 100644 Week08/LRU_cache.py create mode 100644 Week08/bloom_filter.py create mode 100644 Week08/hamming_weight.py create mode 100644 Week08/power_of_two.py create mode 100644 Week08/reverse_bits.py create mode 100644 Week08/sort.py diff --git a/Week08/LRU_cache.py b/Week08/LRU_cache.py new file mode 100644 index 000000000..a70328006 --- /dev/null +++ b/Week08/LRU_cache.py @@ -0,0 +1,108 @@ +""" +146. LRU Cache +https://leetcode.com/problems/lru-cache/ +""" +import collections + +class LRUCache: + def __init__(self, capacity): + self.dic = collections.OrderedDict() + self.remain = capacity + + def get(self, key): + if key not in self.dic: + return -1 + value = self.dic.pop(key) + self.dic[key] = value # key is now the latest one + return value + + 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 + +class DLinkedNode: + def __init__(self, key=0, value=0): + self.key = key + self.value = value + self.prev = None + self.next = None + +class LRUCache2: + def __init__(self, capacity: int): + self.cache = {} + self.head = DLinkedNode() + self.tail = DLinkedNode() + self.head.next = self.tail + self.tail.prev = self.head + self.capacity = capacity + self.size = 0 + + def get(self, key: int) -> int: + if key not in self.cache: + return -1 + # if key exists, locate node in cache, then move to head + node = self.cache[key] # key is now least recently used + self.moveToHead(node) + return node.value + + def put(self, key: int, value: int) -> None: + if key not in self.cache: + node = DLinkedNode(key, value) + self.cache[key] = node + self.addToHead(node) + self.size += 1 + if self.size > self.capacity: + removed = self.removeTail() + self.cache.pop(removed.key) + self.size -= 1 + else: + node = self.cache[key] + node.value = value + self.moveToHead(node) + + def addToHead(self, node): + node.prev = self.head + node.next = self.head.next + self.head.next.prev = node + self.head.next = node + + def removeNode(self, node): + node.prev.next = node.next + node.next.prev = node.prev + + def moveToHead(self, node): + self.removeNode(node) + self.addToHead(node) + + def removeTail(self): + node = self.tail.prev + self.removeNode(node) + return node + +cache = LRUCache(2) +print(cache.put(1, 1)) +print(cache.put(2, 2)) +print(cache.get(1)) # returns 1 +print(cache.put(3, 3)) # evicts key 2 +print(cache.get(2)) # returns -1 (not found) +print(cache.put(4, 4)) # evicts key 1 +print(cache.get(1)) # returns -1 (not found) +print(cache.get(3)) # returns 3 +print(cache.get(4)) # returns 4 + +cache2 = LRUCache2(2) +print(cache2.put(1, 1)) +print(cache2.put(2, 2)) +print(cache2.get(1)) # returns 1 +print(cache2.put(3, 3)) # evicts key 2 +print(cache2.get(2)) # returns -1 (not found) +print(cache2.put(4, 4)) # evicts key 1 +print(cache2.get(1)) # returns -1 (not found) +print(cache2.get(3)) # returns 3 +print(cache2.get(4)) # returns 4 \ No newline at end of file diff --git a/Week08/NOTE.md b/Week08/NOTE.md index 50de30414..b7d833b0c 100644 --- a/Week08/NOTE.md +++ b/Week08/NOTE.md @@ -1 +1,155 @@ -学习笔记 \ No newline at end of file +Learning Notes Week 08 +====================== + +Primitives +---------- + +Twos Complement +```py +-8 4 2 1 +[1 0 0 0] -8 +[1 0 0 1] -7 +[1 0 1 0] -6 +[1 0 1 1] -5 +[1 1 0 0] -4 +[1 1 0 1] -3 +[1 1 1 0] -2 +[1 1 1 1] -1 +[0 0 0 0] 0 +[0 0 0 1] 1 +[0 0 1 0] 2 +[0 0 1 1] 3 +[0 1 0 0] 4 +[0 1 0 1] 5 +[0 1 1 0] 6 +[0 1 1 1] 7 +``` + +How to Convert Negative Number +```py + [0 1 0 1] 5 +Invert [1 0 1 0] +Plus 1 [1 0 1 0] -5 + + [0 0 0 1 0 0 0 0] 16 +Invert [1 1 1 0 1 1 1 1] +Plus 1 [1 1 1 1 0 0 0 0] -16 +``` + +Bit-wise operators +```py +6 & 4 # 0110 & 0100 = 0100 (4) AND +1 | 2 # 0001 | 0010 = 0011 (3) OR +15 ^ 1 # 00001111 ^ 00000001 = 00001110 (14) XOR +8 >> 1 # 00001000 >> 1 = 00000100 (4) x >> y = x // 2^y +1 << 10 # 000000000001 << 10 = 010000000000 (1024) x << y = x * 2^y +-16 >> 2 # 11110000 >> 2 = 11111100 (-4) negative right shifting +-16 << 2 # 11110000 << 2 = 11000000 (-64) negative left shifting +~0 # ~0000 = 1111 (-1) ~x = -x - 1 +``` + +Bit Operation Tricks +```py +x & 1 == 1 # Odd number, same as x % 2 == 1 +x & 1 == 0 # Even number, same as x % 2 == 0 +x >> 1 # Same as x / 2 +x & 1 # Extract the last bit +(x >> k) & 1 # Extract the Kth bit +x |= 1 # Set the last bit +x |= (1 << k) # Set the Kth bit +x ^= 1 # Flip the last bit +x ^= (1 << k) # Flip the Kth bit +x & (x - 1) # Drop the lowest set bit of x +x & ~(x - 1) # Extract the lowest set bit of x +x & (-x) # Keep the lowest set bit and sets all the other bits to 0 +``` + +Leetcode Problems +- [191. Number of 1 Bits](https://leetcode.com/problems/number-of-1-bits/) +- [231. Power of Two](https://leetcode.com/problems/power-of-two/) +- [190. Reverse Bits](https://leetcode.com/problems/reverse-bits/) +- [338. Counting Bits](https://leetcode.com/problems/counting-bits/) + +Bloom Filter +------------ + +> A Bloom filter is a space-efficient probabilistic data structure that is used to test whether an element is a member of a set. The price for efficiency is that it is probabilistic in nature that means, there might be some False Positive results. + +``` +INITIAL BITS +0 0 0 0 0 0 0 0 0 0 + +INSERT X {0, 3, 7} +(1) 0 0 (1) 0 0 0 (1) 0 0 + +INSERT Y {2, 3, 4} +1 0 (1) (1) (1) 0 0 1 0 0 + +SEARCH Z {1, 4, 7} +1 (0) 1 1 (1) 0 0 (1) 0 0 <== Probably Present + +SEARCH W {1, 5, 6} +1 (0) 1 1 1 (0) (0) 1 0 0 <== Definitely NOT Present +``` + +Properties +- Unlike a standard hash table, a Bloom filter of a fixed size can represent a set with an arbitrarily large number of elements. +- Bloom filters never generate false negative result +- Adding an element never fails. However, the false positive rate increases steadily as elements are added until all bits in the filter are set to 1, at which point all queries yield a positive result. +- Deleting elements from filter is not possible because, if we delete a single element by clearing bits at indices generated by k hash functions, it might cause deletion of few other elements. + +LRU Cache +--------- + +> Discards the least recently used items first. This algorithm requires keeping track of what was used when, which is expensive if one wants to make sure the algorithm always discards the least recently used item. + +``` + A B C D E F C G +-------------------------------------------- +[ ] [A] [B] [C] [D] [E] [F] [C] [G] +[ ] [ ] [A] [B] [C] [D] [E] [F] [C] +[ ] [ ] [ ] [A] [B] [C] [D] [E] [F] +[ ] [ ] [ ] [ ] [A] [B] [C] [D] [E] +[ ] [ ] [ ] [ ] [ ] [A] [B] [B] [D] +-------------------------------------------- + [A] [B] +``` + +Properties +- Native Implementation: Hash Table + Doubly LinkedList +- Python API Implementation: `collections.OrderedDict()` +- O(1) lookup +- O(1) update + +- [146. LRU Cache](https://leetcode.com/problems/lru-cache/) + +Sorting +---------- + +Sorting Algorithms +- Comparison Sorts + - Simple Sorts + - Bubble Sort + - Selection Sort + - Insertion Sort + - Efficient Sorts + - Merge Sort + - Quick Sort + - Heap Sort +- Non-comparison Sorts + - Distribution Sorts + - Counting Sort + - Bucket Sort + - Radix Sort + +| Sorting | Average | Worst | Best | Space | Stability | +| :------------- | :-------- | :-------- | :-------- | :-------- | :-------- | +| Bubble Sort | O(N^2) | O(N^2) | O(N) | O(1) | YES | +| Selection Sort | O(N^2) | O(N^2) | O(N^2) | O(1) | NO | +| Insertion Sort | O(N^2) | O(N^2) | O(N) | O(1) | YES | +| Merge Sort | O(N*logN) | O(N*logN) | O(N*logN) | O(N) | YES | +| Quick Sort | O(N*logN) | O(N^2) | O(N*logN) | O(N*logN) | NO | +| Heap Sort | O(N*logN) | O(N*logN) | O(N*logN) | O(1) | YES | +| Counting Sort | O(N+k) | O(N+k) | O(N+k) | O(N+k) | YES | +| Bucket Sort | O(N+k) | O(N^2) | O(N) | O(N+k) | YES | +| Radix Sort | O(N*k) | O(N*k) | O(N*k) | O(N+k) | YES | \ No newline at end of file diff --git a/Week08/bloom_filter.py b/Week08/bloom_filter.py new file mode 100644 index 000000000..36e25d554 --- /dev/null +++ b/Week08/bloom_filter.py @@ -0,0 +1,29 @@ +""" +Bloom Filter +""" +from bitarray import bitarray +import mmh3 + +class BloomFilter: + def __init__(self, size, hashes): + self.size = size + self.hashes = hashes + self.bit_array = bitarray(size) + self.bit_array.setall(0) + + def add(self, s): + for seed in range(self.hashes): + result = mmh3.hash(s, seed) % self.size + self.bit_array[result] = 1 + + def lookup(self, s): + for seed in range(self.hashes): + result = mmh3.hash(s, seed) % self.size + if self.bit_array[result] == 0: + return 'Nope' + return 'Probably' + +bf = BloomFilter(500000, 7) +bf.add("bloom") +print(bf.lookup("bloom")) +print(bf.lookup("filter")) \ No newline at end of file diff --git a/Week08/hamming_weight.py b/Week08/hamming_weight.py new file mode 100644 index 000000000..473cf9722 --- /dev/null +++ b/Week08/hamming_weight.py @@ -0,0 +1,28 @@ +""" +191. Number of 1 Bits +https://leetcode.com/problems/number-of-1-bits/ +""" +class Solution: + def hammingWeight1(self, n: int) -> int: + bits = 0 + while n != 0: + bits += 1 + n &= (n - 1) # drop lowest set bit + return bits + + def hammingWeight2(self, n: int) -> int: + bits = 0 + mask = 1 + for i in range(32): + if (n & mask) != 0: + bits += 1 + mask <<= 1 + return bits + +solution = Solution() +print(solution.hammingWeight1(0b00000000000000000000000000001011)) # 3 +print(solution.hammingWeight1(0b00000000000000000000000010000000)) # 1 +print(solution.hammingWeight1(0b11111111111111111111111111111101)) # 31 +print(solution.hammingWeight2(0b00000000000000000000000000001011)) # 3 +print(solution.hammingWeight2(0b00000000000000000000000010000000)) # 1 +print(solution.hammingWeight2(0b11111111111111111111111111111101)) # 31 \ No newline at end of file diff --git a/Week08/power_of_two.py b/Week08/power_of_two.py new file mode 100644 index 000000000..137e25ae8 --- /dev/null +++ b/Week08/power_of_two.py @@ -0,0 +1,14 @@ +""" +231. Power of Two +https://leetcode.com/problems/power-of-two/ +""" +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + if n == 0: + return False + return n & (-n) == n + +solution = Solution() +print(solution.isPowerOfTwo(1)) # True +print(solution.isPowerOfTwo(16)) # True +print(solution.isPowerOfTwo(218)) # False \ No newline at end of file diff --git a/Week08/reverse_bits.py b/Week08/reverse_bits.py new file mode 100644 index 000000000..f1c5740e7 --- /dev/null +++ b/Week08/reverse_bits.py @@ -0,0 +1,27 @@ +""" +190. Reverse Bits +https://leetcode.com/problems/reverse-bits/ +""" +class Solution: + def reverseBits1(self, x: int) -> int: + result = 0 + power = 31 + while x: + result += (x & 1) << power + x >>= 1 + power -= 1 + return result + + def reverseBits2(self, n: int) -> int: + n = (n >> 16) | (n << 16) + n = ((n & 0xff00ff00) >> 8) | ((n & 0x00ff00ff) << 8) + n = ((n & 0xf0f0f0f0) >> 4) | ((n & 0x0f0f0f0f) << 4) + n = ((n & 0xcccccccc) >> 2) | ((n & 0x33333333) << 2) + n = ((n & 0xaaaaaaaa) >> 1) | ((n & 0x55555555) << 1) + return n + +solution = Solution() +print(bin(solution.reverseBits1(0b00000010100101000001111010011111))) +print(bin(solution.reverseBits1(0b11111111111111111111111111111101))) +print(bin(solution.reverseBits2(0b00000010100101000001111010011111))) +print(bin(solution.reverseBits2(0b11111111111111111111111111111101))) \ No newline at end of file diff --git a/Week08/sort.py b/Week08/sort.py new file mode 100644 index 000000000..222f22e0c --- /dev/null +++ b/Week08/sort.py @@ -0,0 +1,185 @@ +""" +Sorting Algorithms +""" +class Sort: + def BubbleSort(self, arr): + n = len(arr) + for i in range(n-1): + for j in range(n-1-i): + if arr[j] > arr[j+1]: + arr[j], arr[j+1] = arr[j+1], arr[j] + return arr + + def SelectionSort(self, arr): + n = len(arr) + for i in range(n-1): + min_j = i + for j in range(i+1, n): + if arr[j] < arr[min_j]: + min_j = j + arr[i], arr[min_j] = arr[min_j], arr[i] + return arr + + def InsertionSort(self, arr): + n = len(arr) + for i in range(1, n): + j = i + while j > 0 and arr[j-1] > arr[j]: + arr[j-1], arr[j] = arr[j], arr[j-1] + j -= 1 + return arr + + def MergeSort(self, arr): + n = len(arr) + if n < 2: return arr + mid = n // 2 + leftSorted = self.MergeSort(arr[:mid]) + rightSorted = self.MergeSort(arr[mid:]) + return self.merge(leftSorted, rightSorted) + + def merge(self, left, right): + res = [] + while left and right: + if left[0] <= right[0]: + res.append(left.pop(0)) + else: + res.append(right.pop(0)) + while left: res.append(left.pop(0)) + while right: res.append(right.pop(0)) + return res + + def QuickSort(self, arr): + def quickSort(arr, left, right): + if left < right: + p = self.partition(arr, left, right) + quickSort(arr, left, p - 1) + quickSort(arr, p + 1, right) + + quickSort(arr, 0, len(arr) - 1) + return arr + + def partition(self, arr, left, right): + pivot = arr[left] + i, j = left + 1, right + while i <= j: + while i <= j and arr[i] <= pivot: i += 1 + while i <= j and arr[j] >= pivot: j -= 1 + if i <= j: + arr[i], arr[j] = arr[j], arr[i] + arr[left], arr[j] = arr[j], arr[left] + return j + + def HeapSort(self, arr): + n = len(arr) + + # Build max heap. All nodes after n//2 - 1 are leaf-nodes and they + # don't need to be heapified. + for i in range(n//2 - 1, -1, -1): + self.heapify(arr, n, i) + + for i in range(n-1, 0, -1): + arr[i], arr[0] = arr[0], arr[i] + self.heapify(arr, i, 0) + + return arr + + def heapify(self, arr, n, i): + # Find largest among root, left child and right child + largest = i + left = 2 * i + 1 + right = 2 * i + 2 + + if left < n and arr[left] > arr[largest]: + largest = left + + if right < n and arr[right] > arr[largest]: + largest = right + + # Swap and continue if root is not largest + if largest != i: + arr[largest], arr[i] = arr[i], arr[largest] + self.heapify(arr, n, largest) + + def CountingSort(self, arr): + n = len(arr) + res = [0] * n + # Store the count of each elements in count array + max_elem = max(arr) + count = [0] * (max_elem + 1) + for i in range(n): + count[arr[i]] += 1 + # Store the cumulative count + for i in range(1, max_elem + 1): + count[i] += count[i-1] + # Find the index of each element of the original array in count array + # place the elements in output array + i = n - 1 + while i >= 0: + res[count[arr[i]] - 1] = arr[i] + count[arr[i]] -= 1 + i -= 1 + # Copy the sorted elements into original array + arr[:] = res[:] + return arr + + def RadixSort(self, arr): + # Get maximum element + max_elem = max(arr) + # Apply counting sort to sort elements based on place value. + place = 1 + while max_elem // place > 0: + self.couting(arr, place) + place *= 10 + return arr + + def couting(self, arr, place): + n = len(arr) + res = [0] * n + max_elem = max(arr) + count = [0] * (max_elem + 1) + # Calculate count of elements + for i in range(n): + index = arr[i] // place + count[index % 10] += 1 + # Calculate cumulative count + for i in range(1, max_elem + 1): + count[i] += count[i-1] + # Place the elements in sorted order + i = n - 1 + while i >= 0: + index = arr[i] // place + res[count[index % 10] - 1] = arr[i] + count[index % 10] -= 1 + i -= 1 + arr[:] = res[:] + + def BucketSort(self, arr): + n = len(arr) + # Create empty buckets + bucket = [[] for i in range(n)] + # Insert elements into their respective buckets + for x in arr: + index = int(10 * x) + bucket[index].append(x) + # Sort the elements of each bucket + for i in range(n): + bucket[i] = sorted(bucket[i]) + # Get the sorted elements + k = 0 + for i in range(n): + for j in range(len(bucket[i])): + arr[k] = bucket[i][j] + k += 1 + return arr + +arr = [3, 48, 15, 42, 26, 50, 27, 4, 34, 19, 2, 13, 36, 5, 47, 17] +sort = Sort() +print('Bubble Sort ', sort.BubbleSort(arr[:])) +print('Selection Sort', sort.SelectionSort(arr[:])) +print('Insertion Sort', sort.InsertionSort(arr[:])) +print('Merge Sort ', sort.MergeSort(arr[:])) +print('Quick Sort ', sort.QuickSort(arr[:])) +print('Heap Sort ', sort.HeapSort(arr[:])) +print('Counting Sort ', sort.CountingSort(arr[:])) +print('Radix Sort ', sort.RadixSort(arr[:])) +print('Bucket Sort ', [int(x*100) for x in sort.BucketSort([x/100 for x in arr])]) \ No newline at end of file From 5669d05776b7b5ac6f538f81b58a81370635aa1a Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Thu, 6 Aug 2020 22:14:45 -0400 Subject: [PATCH 28/48] more stuff on DP --- Week06/{sub-sequence.py => LCS.py} | 9 -- Week06/NOTE.md | 51 +++---- Week06/buy-sells-stocks.py | 183 ++++++++++++------------ Week06/{sub-array.py => find-length.py} | 18 +-- Week06/house-robber.py | 98 +++++++++++-- Week06/triangle.py | 41 ++++++ Week06/unique-paths.py | 56 ++++++-- 7 files changed, 294 insertions(+), 162 deletions(-) rename Week06/{sub-sequence.py => LCS.py} (78%) rename Week06/{sub-array.py => find-length.py} (67%) create mode 100644 Week06/triangle.py diff --git a/Week06/sub-sequence.py b/Week06/LCS.py similarity index 78% rename from Week06/sub-sequence.py rename to Week06/LCS.py index 5056d1033..177f8aaf0 100644 --- a/Week06/sub-sequence.py +++ b/Week06/LCS.py @@ -1,15 +1,6 @@ """ -300. Longest Increasing Subsequence -https://leetcode.com/problems/longest-increasing-subsequence/) - -516. Longest Palindromic Subsequence -https://leetcode.com/problems/longest-palindromic-subsequence/ - 1143. Longest Common Subsequence https://leetcode.com/problems/longest-common-subsequence/ - -1035. Uncrossed Lines -https://leetcode.com/problems/uncrossed-lines/ """ class Solution: def longestCommonSubsequence(self, s: str, t: str) -> int: diff --git a/Week06/NOTE.md b/Week06/NOTE.md index 3925b0b16..ca72e5d5c 100644 --- a/Week06/NOTE.md +++ b/Week06/NOTE.md @@ -26,25 +26,21 @@ Top-down / \ f(1) f(1) ``` + Recursion ```py def fib(N): - if N == 0: return 0 - if N == 1: return 1 + if N < 2: return N return fib(N-1) + fib(N-2) ``` + Space Optimization (Memo) ```py memo = {} def fib(N): - if N == 0: return 0 - if N == 1: return 1 - - if memo.get(N): - return memo[N] - - memo[N] = fib(N-1) + fib(N-2) - + if N < 2: return N + if N in memo: + memo[N] = fib(N-1) + fib(N-2) return memo[N] ``` @@ -54,37 +50,35 @@ f(0) f(1) f(2) f(3) f(4) f(5) 1 2 3 5 8 13 ---------------------------> ``` + Dynamic Programming ```py def fib(N): - if N == 0: return 0 - if N == 1: return 1 - + """ + dp(N) = { + N, N < 2 + dp(N-1) + dp(N-2), N >= 2 + } + """ + if N < 2: return N dp = [0] * (N+1) dp[0] = 0 dp[1] = 1 - for i in range(2, N+1): dp[i] = dp[i-1] + dp[i-2] - - return dp[N] + return dp[-1] ``` + Space Optimization (Reduce States) ```py def fib(self): - if N == 0: return 0 - if N == 1: return 1 - - dp_i = 0 - dp_i_1 = 0 - dp_i_2 = 1 - + if N < 2: return N + f, f1, f2 = 0, 0, 1 for i in range(N): - dp_i = dp_i_1 + dp_i_2 - dp_i_2 = dp_i_1 - dp_i_1 = dp_i - - return dp_i + f = f1 + f2 + f2 = f1 + f1 = f + return f ``` DP Framework @@ -104,6 +98,7 @@ Leetcode Problems Basics - [509. Fibonacci Number](https://leetcode.com/problems/fibonacci-number/) +- [70. Climbing Stairs](https://leetcode.com/problems/climbing-stairs/) - [120. Triangle](https://leetcode.com/problems/triangle/) House Robber diff --git a/Week06/buy-sells-stocks.py b/Week06/buy-sells-stocks.py index 400361dad..9603eef5c 100644 --- a/Week06/buy-sells-stocks.py +++ b/Week06/buy-sells-stocks.py @@ -1,21 +1,21 @@ """ -121. Best Time to Buy and Sell Stock +121. Best Time to Buy and Sell Stock https://leetcode.com/problems/best-time-to-buy-and-sell-stock/ -122. Best Time to Buy and Sell Stock II +122. Best Time to Buy and Sell Stock II https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/ -123. Best Time to Buy and Sell Stock III -https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/ - -188. Best Time to Buy and Sell Stock IV -https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/ - -309. Best Time to Buy and Sell Stock with Cooldown +309. Best Time to Buy and Sell Stock with Cooldown https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/ -714. Best Time to Buy and Sell Stock with Transaction Fee +714. Best Time to Buy and Sell Stock with Transaction Fee https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/ + +123. Best Time to Buy and Sell Stock III +https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/ + +188. Best Time to Buy and Sell Stock IV +https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/ """ from typing import List import math @@ -33,10 +33,9 @@ def maxProfit1(self, prices: List[int]) -> int: ) """ n = len(prices) - if n == 0: - return 0 + if n <= 1: return 0 - dp = [[0 for _ in range(2)] for _ in range(n)] + dp = [[None, None] for _ in range(n)] dp[0][0] = 0 dp[0][1] = -prices[0] @@ -44,7 +43,7 @@ def maxProfit1(self, prices: List[int]) -> int: dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]) dp[i][1] = max(dp[i-1][1], -prices[i]) - return dp[n-1][0] + return dp[-1][0] def maxProfit2(self, prices: List[int]) -> int: """ @@ -58,10 +57,9 @@ def maxProfit2(self, prices: List[int]) -> int: ) """ n = len(prices) - if n == 0: - return 0 + if n <= 1: return 0 - dp = [[0 for _ in range(2)] for _ in range(n)] + dp = [[None, None] for _ in range(n)] dp[0][0] = 0 dp[0][1] = -prices[0] @@ -69,72 +67,9 @@ def maxProfit2(self, prices: List[int]) -> int: 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]) - return dp[n-1][0] + return dp[-1][0] def maxProfit3(self, prices: List[int]) -> int: - """ - 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] - ) - """ - n = len(prices) - if n == 0: - return 0 - - k = 2 - dp = [[[0 for _ in range(2)] for _ in range(k+1)] for _ in range(n)] - dp[0][1][0] = 0 - dp[0][1][1] = -prices[0] - dp[0][2][0] = 0 - dp[0][2][1] = -prices[0] - - for i in range(1, n): - for j in range(1, k+1): - dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1] + prices[i]) - dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i]) - - return dp[n-1][k][0] - - def maxProfit4(self, k: int, prices: List[int]) -> int: - """ - 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] - ) - """ - n = len(prices) - if n == 0: - return 0 - - if k > n/2: - res = 0 - for i, j in zip(prices[1:], prices[:-1]): - res += max(0, i-j) - return res - - dp = [[[-math.inf]*2 for _ in range(k+1)] for _ in range(n)] - dp[0][0][0] = 0 - dp[0][1][1] = -prices[0] - - for i in range(1, n): - for j in range(k+1): - dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1] + prices[i]) - if k > 0: - dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i]) - - res = max(dp[n-1][j][0] for j in range(k+1)) - return res - - def maxProfit5(self, prices: List[int]) -> int: """ dp[i][0] = max( dp[i-1][0] @@ -146,8 +81,7 @@ def maxProfit5(self, prices: List[int]) -> int: ) """ n = len(prices) - if n == 0: - return 0 + if n <= 1: return 0 dp = [[0 for _ in range(2)] for _ in range(n)] dp[0][0] = 0 @@ -159,7 +93,7 @@ def maxProfit5(self, prices: List[int]) -> int: return dp[n-1][0] - def maxProfit6(self, prices: List[int], fee: int) -> int: + def maxProfit4(self, prices: List[int], fee: int) -> int: """ dp[i][0] = max( dp[i-1][0] @@ -171,8 +105,7 @@ def maxProfit6(self, prices: List[int], fee: int) -> int: ) """ n = len(prices) - if n == 0: - return 0 + if n <= 1: return 0 dp = [[0 for _ in range(2)] for _ in range(n)] dp[0][0] = 0 @@ -184,10 +117,76 @@ def maxProfit6(self, prices: List[int], fee: int) -> int: return dp[n-1][0] + def maxProfit5(self, prices: List[int]) -> int: + """ + 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] + ) + """ + n = len(prices) + if n <= 1: return 0 + + dp = [[[None, None] for _ in range(3)] for _ in range(n)] # (n, k+1, 2) + for i in range(n): + dp[i][0][0] = 0 + dp[i][0][1] = -math.inf + for j in range(1, 3): + dp[0][j][0] = 0 + dp[0][j][1] = -prices[0] + + for i in range(1, n): + for j in range(1, 3): + dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1] + prices[i]) + dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i]) + + return dp[-1][-1][0] + + def maxProfit6(self, k: int, prices: List[int]) -> int: + """ + 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] + ) + """ + n = len(prices) + if n <= 1: return 0 + + if k > n // 2: # back to unlimited situation (maxProfit3) + profit = 0 + for i in range(1, n): + if prices[i] > prices[i - 1]: + profit += prices[i] - prices[i - 1] + return profit + + dp = [[[None, None] for _ in range(k+1)] for _ in range(n)] # (n, k+1, 2) + for i in range(n): + dp[i][0][0] = 0 + dp[i][0][1] = -math.inf + for j in range(1, k+1): + dp[0][j][0] = 0 + dp[0][j][1] = -prices[0] + + for i in range(1, n): + for j in range(1, k+1): + dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1] + prices[i]) + dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i]) + + return dp[-1][-1][0] + + solution = Solution() -print(solution.maxProfit1([7,1,5,3,6,4])) # 5 -print(solution.maxProfit2([7,1,5,3,6,4])) # 7 -print(solution.maxProfit3([3,3,5,0,0,3,1,4])) #6 -# todo: maxProfit4 test case -print(solution.maxProfit5([1,2,3,0,2])) # 3 -print(solution.maxProfit6([1,3,2,8,4,9], 2)) # 8 +print("Maximum 1 Transaction:", solution.maxProfit1([7,1,5,3,6,4])) # 5 +print("Unlimited Transactions:", solution.maxProfit2([7,1,5,3,6,4])) # 7 +print("Unlimited Transactions with Cooldown:", solution.maxProfit3([1,2,3,0,2])) # 3 +print("Unlimited Transactions with Fee:", solution.maxProfit4([1,3,2,8,4,9], 2)) # 8 +print("Maximum 2 Transactions:", solution.maxProfit5([3,3,5,0,0,3,1,4])) # 6 +print("Maximum k Transactions:", solution.maxProfit6(2, [3,2,6,5,0,3])) # 7 \ No newline at end of file diff --git a/Week06/sub-array.py b/Week06/find-length.py similarity index 67% rename from Week06/sub-array.py rename to Week06/find-length.py index 6df979b12..fd747f022 100644 --- a/Week06/sub-array.py +++ b/Week06/find-length.py @@ -1,11 +1,5 @@ """ -53. Maximum Subarray -https://leetcode.com/problems/maximum-subarray/ - -152. Maximum Product Subarray -https://leetcode.com/problems/maximum-product-subarray/ - -718. Maximum Length of Repeated Subarray +718. Maximum Length of Repeated Subarray https://leetcode.com/problems/maximum-length-of-repeated-subarray/ """ from typing import List @@ -13,20 +7,20 @@ class Solution: def findLength(self, A: List[int], B: List[int]) -> int: """ - f(0, *) = 0 - f(*, 0) = 0 - f(i,j) = f(i-1, j-1) + 1, if A[i] == B[j] + status: { + dp[i][j] = 0, i = 0 + dp[i][j] = 0, j = 0 + dp[i][j] = dp[i-1][j-1] + 1, if A[i] == B[j] + } """ m, n = len(A), len(B) dp = [[0 for _ in range(n+1)] for _ in range(m+1)] res = 0 - for i in range(1, m+1): for j in range(1, n+1): if A[i-1] == B[j-1]: dp[i][j] = dp[i-1][j-1] + 1 res = max(res, dp[i][j]) - return res solution = Solution() diff --git a/Week06/house-robber.py b/Week06/house-robber.py index 156df1836..cb7559eca 100644 --- a/Week06/house-robber.py +++ b/Week06/house-robber.py @@ -2,30 +2,102 @@ 198. House Robber https://leetcode.com/problems/house-robber/ -213. House Robber II +213. House Robber II https://leetcode.com/problems/house-robber-ii/ -337. House Robber III +337. House Robber III https://leetcode.com/problems/house-robber-iii/ """ from typing import List +class TreeNode: + def __init__(self, val=None, left=None, right=None): + self.val = val + self.left = None + self.right = None + +def createTree(iterable = ()): + def insert(node, i): + if i < len(iterable) and iterable[i] is not None: + node = TreeNode(iterable[i]) + node.left = insert(node.left, 2 * i + 1) + node.right = insert(node.right, 2 * i + 2) + return node + return insert(None, 0) + class Solution: def rob1(self, nums: List[int]) -> int: + """ + choices = { + 0: not rob + 1: rob + } + + status = { + dp[i][0] = max(dp[i-1][0], dp[i-1][1]) + dp[i][1] = dp[i-1][0] + nums[i] + } + + base case = { + dp[0][0] = 0 + dp[0][1] = nums[0] + } + + answer = max(dp[-1][0], dp[-1][1]) + """ + n = len(nums) + if n == 0: return 0 + if n == 1: return nums[0] + + dp = [[0, 0] for _ in range(n)] + dp[0][0] = 0 + dp[0][1] = nums[0] + + for i in range(1, n): + 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][0], dp[-1][1]) + + def rob1(self, nums: List[int]) -> int: + """ + dp[i] = { + dp[0] = nums[0], n = 0 + dp[1] = max(nums[0], nums[1]), n = 1 + dp[i] = max(dp[i-1], dp[i-2] + nums[i]), n >= 2 + } + """ + n = len(nums) + if n == 0: return 0 + if n == 1: return nums[0] + + dp = [0 for _ in range(n)] + dp[0] = nums[0] + dp[1] = max(nums[0], nums[1]) + + for i in range(2, n): + dp[i] = max(dp[i-1], dp[i-2] + nums[i]) + + return dp[-1] + + def rob1(self, nums: List[int]) -> int: + """ + Space Optimization + """ n = len(nums) if n == 0: return 0 if n == 1: return nums[0] - k = 0 # dp[k] - k_1 = 0 # dp[k-1] - k_2 = 0 # dp[k-2] + f = 0 # dp[i] + f1 = 0 # dp[i-1] + f2 = 0 # dp[i-2] for i in range(n): - k = max(k_1, k_2 + nums[i]) - k_2 = k_1 - k_1 = k + f = max(f1, f2 + nums[i]) + f2 = f1 + f1 = f - return k + return f def rob2(self, nums: List[int]) -> int: n = len(nums) @@ -43,7 +115,6 @@ def dp(root): rob = root.val + not_rob_left + not_rob_right not_rob = max(rob_left, not_rob_left) + max(rob_right, not_rob_right) - return rob, not_rob rob, not_rob = dp(root) @@ -53,4 +124,9 @@ def dp(root): solution = Solution() print(solution.rob1([1,2,3,1])) # 4 print(solution.rob2([2,3,2])) # 3 -# todo: rob3 test case \ No newline at end of file + +tree = createTree([3,2,3,None,3,None,1]) +print(solution.rob3(tree)) # 7 + +tree = createTree([3,4,5,1,3,None,1]) +print(solution.rob3(tree)) # 9 \ No newline at end of file diff --git a/Week06/triangle.py b/Week06/triangle.py new file mode 100644 index 000000000..15f730587 --- /dev/null +++ b/Week06/triangle.py @@ -0,0 +1,41 @@ +""" +120. Triangle +https://leetcode.com/problems/triangle/ +""" +from typing import List + +class Solution: + def minimumTotal(self, triangle: List[List[int]]) -> int: + """ + 1. state definition: + dp[i][j] is the min total from (i,j) to bottom + + 2. state transform: + dp[i][j] = min(dp[i+1][j], dp[i+1][j+1]) + triangle[i][j] + """ + m = len(triangle) + dp = [[0 for _ in range(m+1)] for _ in range(m+1)] + for i in range(m-1, -1, -1): + for j in range(i+1): + dp[i][j] = min(dp[i+1][j], dp[i+1][j+1]) + triangle[i][j] + return dp[0][0] + + def minimumTotal(self, triangle: List[List[int]]) -> int: + """ + dp[j] = min(dp[j], dp[j+1]) + triangle[i][j] + """ + m = len(triangle) + dp = [0 for _ in range(m+1)] + for i in range(m-1, -1, -1): + for j in range(i+1): + dp[j] = min(dp[j], dp[j+1]) + triangle[i][j] + return dp[0] + +solution = Solution() +ans = solution.minimumTotal([ + [2], + [3,4], + [6,5,7], + [4,1,8,3] +]) +print(ans) # 2 + 3 + 5 = 11 \ No newline at end of file diff --git a/Week06/unique-paths.py b/Week06/unique-paths.py index 6714ecea7..055504175 100644 --- a/Week06/unique-paths.py +++ b/Week06/unique-paths.py @@ -1,19 +1,17 @@ """ 62. Unique Paths https://leetcode.com/problems/unique-paths/ - -63. Unique Paths II -https://leetcode.com/problems/unique-paths-ii/ - -980. Unique Paths III -https://leetcode.com/problems/unique-paths-iii/ """ +from typing import List + class Solution: def uniquePaths(self, m: int, n: int) -> int: """ - f(0, *) = 1 - f(*, 0) = 1 - f(i, j) = f(i, j-1) + f(i-1, j) + status = { + dp[i][j] = 1, i = 0 + dp[i][j] = 1, j = 0 + dp[i][j] = dp[i-1][j] + dp[i][j-1], i >= 1, j >= 1 + } """ dp = [[1]*n] + [[1] + [0]*(n-1) for _ in range(m-1)] @@ -23,5 +21,43 @@ def uniquePaths(self, m: int, n: int) -> int: return dp[-1][-1] + def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int: + """ + status = { + dp[i][j] = 0, i = 0, obstacleGrid[i][j] == 0 + dp[i][j] = 1, j = 0, obstacleGrid[i][j] == 1 + dp[i][j] = 0, i = 0, obstacleGrid[i][j] == 0 + dp[i][j] = 1, j = 0, obstacleGrid[i][j] == 1 + dp[i][j] = dp[i-1][j] + dp[i][j-1], i >= 1, j >= 1 + } + """ + if obstacleGrid[0][0] == 1: + return 0 + + m, n = len(obstacleGrid), len(obstacleGrid[0]) + dp = [[0 for _ in range(n)] for _ in range(m)] + dp[0][0] = 1 + + for i in range(1, m): + if obstacleGrid[i][0] == 1: break + else: dp[i][0] = dp[i-1][0] + + for j in range(1, n): + if obstacleGrid[0][j] == 1: break + else: dp[0][j] = dp[0][j-1] + + for i in range(1, m): + for j in range(1, n): + if obstacleGrid[i][j] == 0: + dp[i][j] = dp[i-1][j] + dp[i][j-1] + + return dp[-1][-1] + solution = Solution() -print(solution.uniquePaths(7, 2)) # 28 \ No newline at end of file +print(solution.uniquePaths(3,2)) # 3 +print(solution.uniquePaths(7,3)) # 28 +print(solution.uniquePathsWithObstacles([ + [0,0,0], + [0,1,0], + [0,0,0] +])) # 2 \ No newline at end of file From d3641f57fd65d339a43961912dae9f1d770b63c5 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 9 Aug 2020 22:44:16 -0400 Subject: [PATCH 29/48] week 9 homework and notes --- Week01/NOTE.md | 25 ----------- Week09/NOTE.md | 71 +++++++++++++++++++++++++++++++- Week09/first-unique-character.py | 18 ++++++++ Week09/jewels-and-stones.py | 14 +++++++ Week09/length-of-last-word.py | 20 +++++++++ Week09/longest-common-prefix.py | 21 ++++++++++ Week09/string-to-integer-atoi.py | 56 +++++++++++++++++++++++++ Week09/to-lower-case.py | 21 ++++++++++ 8 files changed, 220 insertions(+), 26 deletions(-) create mode 100644 Week09/first-unique-character.py create mode 100644 Week09/jewels-and-stones.py create mode 100644 Week09/length-of-last-word.py create mode 100644 Week09/longest-common-prefix.py create mode 100644 Week09/string-to-integer-atoi.py create mode 100644 Week09/to-lower-case.py diff --git a/Week01/NOTE.md b/Week01/NOTE.md index 8df7890ec..f0b04e364 100644 --- a/Week01/NOTE.md +++ b/Week01/NOTE.md @@ -48,31 +48,6 @@ a.index(x[, start[, end]]) # index of the first occurrence of x in s a.sort(key=None, reverse=False) # Sort the items of the list in place ``` -String Operations -```py -s.strip([chars]) # return a copy of the string with the leading and trailing characters removed. -s.startswith(prefix) # return True if string starts with the prefix, False otherwise. -s.endswith(prefix) # return True if string starts with the prefix, False otherwise. -s.slipt(delimiter) # return a list of the words of the string s. -s.lower() # return a copy of the string with all the lowercase characters -s.upper() # return a copy of the string with all the uppercase characters -ord(c) # the unicode code representation of the char -ord(c) - ord('a') # the position of the char in 26 letters -chr(i) # string representation of the char unicode code -``` - -String Constants -```py -import string - -string.digits # the string '0123456789' -string.hexdigits # the string '0123456789abcdefABCDEF' -string.octdigits # the string '01234567' -string.ascii_lowercase # the uppercase letters 'abcdefghijklmnopqrstuvwxyz' -string.ascii_letters # The lowercase letters 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' -string.letters # The concatenation of the ascii_lowercase and ascii_uppercase -``` - Coding Techniques ```py # List comprehension diff --git a/Week09/NOTE.md b/Week09/NOTE.md index 50de30414..b3200a385 100644 --- a/Week09/NOTE.md +++ b/Week09/NOTE.md @@ -1 +1,70 @@ -学习笔记 \ No newline at end of file +Learning Notes Week 09 +====================== + +String +------ + +String Operations +```py +s.strip([chars]) # return a copy of the string with the leading and trailing characters removed. +s.startswith(prefix) # return True if string starts with the prefix, False otherwise. +s.endswith(prefix) # return True if string starts with the prefix, False otherwise. +s.slipt(delimiter) # return a list of the words of the string s. +s.lower() # return a copy of the string with all the lowercase characters +s.upper() # return a copy of the string with all the uppercase characters +ord(c) # the unicode code representation of the char +ord(c) - ord('a') # the position of the char in 26 letters +chr(i) # string representation of the char unicode code +``` + +String Constants +```py +import string + +string.digits # the string '0123456789' +string.hexdigits # the string '0123456789abcdefABCDEF' +string.octdigits # the string '01234567' +string.ascii_lowercase # the uppercase letters 'abcdefghijklmnopqrstuvwxyz' +string.ascii_letters # The lowercase letters 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +string.letters # The concatenation of the ascii_lowercase and ascii_uppercase +``` + +String Basics +- [709. To Lower Case](https://leetcode-cn.com/problems/to-lower-case/) +- [58. Length of Last Word](https://leetcode.com/problems/length-of-last-word/) +- [771. Jewels and Stones](https://leetcode.com/problems/jewels-and-stones/) +- [387. First Unique Character in a String](https://leetcode.com/problems/first-unique-character-in-a-string/) +- [8. String to Integer (atoi)](https://leetcode.com/problems/string-to-integer-atoi/) + +String Operations +- [14. Longest Common Prefix](https://leetcode.com/problems/longest-common-prefix/) +- [344. Reverse String](https://leetcode.com/problems/reverse-string/) +- [541. Reverse String II](https://leetcode.com/problems/reverse-string-ii/) +- [151. Reverse Words in a String](https://leetcode.com/problems/reverse-words-in-a-string/) +- [557. Reverse Words in a String III](https://leetcode.com/problems/reverse-words-in-a-string-iii/) +- [917. Reverse Only Letters](https://leetcode.com/problems/reverse-only-letters/) + +Palindrome +- [125. Valid Palindrome](https://leetcode.com/problems/valid-palindrome/) +- [680. Valid Palindrome II](https://leetcode.com/problems/valid-palindrome-ii/) +- [5. Longest Palindromic Substring](https://leetcode.com/problems/longest-palindromic-substring/) + +Anagram +- [242. Valid Anagram](https://leetcode.com/problems/valid-anagram/) +- [49. Group Anagrams](https://leetcode.com/problems/group-anagrams/) +- [438. Find All Anagrams in a String](https://leetcode.com/problems/find-all-anagrams-in-a-string/) + +Advanced String Problems +- [10. Regular Expression Matching](https://leetcode.com/problems/regular-expression-matching/) +- [44. Wildcard Matching](https://leetcode.com/problems/wildcard-matching/) +- [115. Distinct Subsequences](https://leetcode.com/problems/distinct-subsequences/) + +String-searching algorithms +----------- + +| algorithm | Preprocessing Time | Matching Time | Space | +| ---------- | :----------------: | :-----------: | :---: | +| Naive | None | O(mn) | None | +| Rabin-Karp | O(m) | O(n+m) | O(1) | +| KMP | O(m) | O(n) | O(m) | +| Boyer-Moore | O(m+k) | O(mn) | O(k) | \ No newline at end of file diff --git a/Week09/first-unique-character.py b/Week09/first-unique-character.py new file mode 100644 index 000000000..b41d1692f --- /dev/null +++ b/Week09/first-unique-character.py @@ -0,0 +1,18 @@ +""" +387. First Unique Character in a String +https://leetcode.com/problems/first-unique-character-in-a-string/ +""" +from collections import Counter + +class Solution: + def firstUniqChar(self, s: str) -> int: + if not s: return -1 + cnt = Counter(s) + for i, c in enumerate(s): + if cnt[c] == 1: + return i + return -1 + +solution = Solution() +print(solution.firstUniqChar("leetcode")) +print(solution.firstUniqChar("loveleetcode")) \ No newline at end of file diff --git a/Week09/jewels-and-stones.py b/Week09/jewels-and-stones.py new file mode 100644 index 000000000..2df0d0628 --- /dev/null +++ b/Week09/jewels-and-stones.py @@ -0,0 +1,14 @@ +""" +771. Jewels and Stones +https://leetcode.com/problems/jewels-and-stones/ +""" +from collections import Counter + +class Solution: + def numJewelsInStones(self, J: str, S: str) -> int: + if not J or not S: return 0 + return sum([1 for s in S if s in Counter(J)]) + +solution = Solution() +print(solution.numJewelsInStones("aA", "aAAbbbb")) +print(solution.numJewelsInStones("z", "ZZ")) \ No newline at end of file diff --git a/Week09/length-of-last-word.py b/Week09/length-of-last-word.py new file mode 100644 index 000000000..df7d62e15 --- /dev/null +++ b/Week09/length-of-last-word.py @@ -0,0 +1,20 @@ +""" +58. Length of Last Word +https://leetcode.com/problems/length-of-last-word/ +""" +class Solution: + def lengthOfLastWord(self, s: str) -> int: + if not s: return 0 + count = 0 + i = len(s) - 1 + while i >= 0 and s[i] == ' ': + i -= 1 + while i >= 0 and s[i] != ' ': + count += 1 + i -= 1 + return count + +solution = Solution() +print(solution.lengthOfLastWord("Hello World")) +print(solution.lengthOfLastWord("")) +print(solution.lengthOfLastWord(" test ")) \ No newline at end of file diff --git a/Week09/longest-common-prefix.py b/Week09/longest-common-prefix.py new file mode 100644 index 000000000..c4e46fdfa --- /dev/null +++ b/Week09/longest-common-prefix.py @@ -0,0 +1,21 @@ +""" +14. Longest Common Prefix +https://leetcode.com/problems/longest-common-prefix/ +""" +from typing import List + +class Solution: + def longestCommonPrefix(self, strs: List[str]) -> str: + if not strs: return '' + m, n = len(strs[0]), len(strs) + for i in range(m): + c = strs[0][i] + if any(i == len(strs[j]) or strs[j][i] != c for j in range(1, n)): + return strs[0][:i] + return strs[0] + +solution = Solution() +print(solution.longestCommonPrefix(["flower","flow","flight"])) # "fl" +print(solution.longestCommonPrefix(["dog","racecar","car"])) # "" +print(solution.longestCommonPrefix(["a"])) # "a" +print(solution.longestCommonPrefix(["aa", "a"])) # "a" \ No newline at end of file diff --git a/Week09/string-to-integer-atoi.py b/Week09/string-to-integer-atoi.py new file mode 100644 index 000000000..936ab4116 --- /dev/null +++ b/Week09/string-to-integer-atoi.py @@ -0,0 +1,56 @@ +""" +8. String to Integer (atoi) +https://leetcode.com/problems/string-to-integer-atoi/ +""" +import string + +class Solution: + def myAtoi(self, str: str) -> int: + if not str: + return 0 + + INT_MIN = -2**31 + INT_MAX = 2**31 - 1 + + n = len(str) + i = 0 + while i < n and str[i] == ' ': + i += 1 + if i >= n: return 0 + + sign = 1 + if str[i] == '+': + i += 1 + elif str[i] == '-': + sign = -1 + i += 1 + if i >= n: return 0 + + start = i + while i < n and str[i] != ' ': + if str[i] not in string.digits: + break + i += 1 + + k = 0 + res = 0 + for j in range(i - 1, start - 1, -1): + res += (ord(str[j]) - ord('0')) * (10**k) + k += 1 + + res = res * sign + if INT_MIN <= res <= INT_MAX: + return res + return INT_MAX if res > 0 else INT_MIN + +solution = Solution() +print(solution.myAtoi("42")) +print(solution.myAtoi(" -42")) +print(solution.myAtoi("4193 with words")) +print(solution.myAtoi("words and 987")) +print(solution.myAtoi("-91283472332")) +print(solution.myAtoi("")) +print(solution.myAtoi(" ")) +print(solution.myAtoi("+")) +print(solution.myAtoi(" -0012a99")) +print(solution.myAtoi(" 3.1415926")) \ No newline at end of file diff --git a/Week09/to-lower-case.py b/Week09/to-lower-case.py new file mode 100644 index 000000000..f7f9822fb --- /dev/null +++ b/Week09/to-lower-case.py @@ -0,0 +1,21 @@ +""" +709. To Lower Case +https://leetcode-cn.com/problems/to-lower-case/ +""" +import string + +class Solution: + def toLowerCase(self, str: str) -> str: + if not str: return str + res = [''] * len(str) + for i, c in enumerate(str): + if c in string.ascii_uppercase: + res[i] = chr(ord(c) + 32) + else: + res[i] = c + return ''.join(res) + +solution = Solution() +print(solution.toLowerCase("Hello")) +print(solution.toLowerCase("here")) +print(solution.toLowerCase("LOVELY")) \ No newline at end of file From a61e056d4ee65eec52a651427497d14e4dfd9f9c Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Mon, 10 Aug 2020 20:19:08 -0400 Subject: [PATCH 30/48] Update NOTE.md --- Week06/NOTE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Week06/NOTE.md b/Week06/NOTE.md index ca72e5d5c..c8cc22176 100644 --- a/Week06/NOTE.md +++ b/Week06/NOTE.md @@ -39,7 +39,7 @@ Space Optimization (Memo) memo = {} def fib(N): if N < 2: return N - if N in memo: + if N not in memo: memo[N] = fib(N-1) + fib(N-2) return memo[N] ``` From 0902206d13d204599141b2f0b40825e89ca1a5f0 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Fri, 14 Aug 2020 22:26:33 -0400 Subject: [PATCH 31/48] Final review --- Week01/NOTE.md | 70 +++++++++++++++++++++++++++++++++++++++++------ Week02/NOTE.md | 74 ++++++++++++++++++++++++++------------------------ Week03/NOTE.md | 4 +-- Week04/NOTE.md | 36 ++++++++++++++++-------- Week06/NOTE.md | 3 ++ Week08/NOTE.md | 1 + 6 files changed, 131 insertions(+), 57 deletions(-) diff --git a/Week01/NOTE.md b/Week01/NOTE.md index f0b04e364..ba6d731d0 100644 --- a/Week01/NOTE.md +++ b/Week01/NOTE.md @@ -6,7 +6,7 @@ Array > Array is a contiguous block of memory. It is usually used to represent sequences. -- Arrays are lists, lists are mutable sequences, tuples are immutable sequences +- Arrays are lists, lists are **mutable** sequences, tuples are **immutable** sequences - Lists are dynamically-resized, there is no upper bound - Values can be deleted and inserted at arbitrary locations @@ -73,11 +73,11 @@ windows = [nums[i:i+k] for i in range(n-k+1)] print(windows) # [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]] # Rotate array by k times (right shift) -nums = [1,2,3,4,5,6,7] +nums, k = [1,2,3,4,5,6,7], 3 n = len(nums) for i in range(n): res[(i + k) % n] = nums[i] -print(res) # [5,6,7,1,2,3,4] +print(res) # [5,6,7,1,2,3,4] # Useful functions map(lambda x: x * x, [1, 2, 3, 4, 5]) # [1, 4, 9, 16, 25] @@ -108,13 +108,13 @@ Linked Lists > A list implements an ordered collection of values, which may include repetitions. -Singly linked list: `L -> 2 -> 3 -> 5 -> 4 -> EOL` +Singly linked list: `2 -> 3 -> 5 -> 4 -> x` - 2 is linked list head - 4 is linked list tail - 2's next is 3, 3's next is 5 and 5's next is 4, and 4's next is None - L sometimes is used as a "dummy" head -Doubly linked list: `L -> x <- 2 <-> 3 <-> 5 <-> 4 -> x` +Doubly linked list: `x <- 2 <-> 3 <-> 5 <-> 4 -> x` - 2's prev is None, 2's next is 3 - 3's prev is 2 , 3's next is 5 - 4's prev is 3 , 5's next is 4 @@ -264,9 +264,9 @@ def slidingWindow(s, t): if window[c] == target[c]: valid += 1 - while # when we found a valid window + while ... # when we found a valid window if valid == len(target): - # check the answer or update the result + ... # check the answer or update the result # d is the element to be removed from the window d = s[left] @@ -283,6 +283,7 @@ Mono stack ```py n = len(nums) stack = [] + leftMax = [-1] * n for i in range(n): # push into stack while stack and stack[-1] <= nums[i]: # compare with stack top @@ -353,9 +354,60 @@ Leetcode Problems - [3. Longest Substring Without Repeating Characters](https://leetcode.com/problems/longest-substring-without-repeating-characters/) - [239. Sliding Window Maximum](https://leetcode.com/problems/sliding-window-maximum/) - [42. Trapping Rain Water](https://leetcode.com/problems/next-greater-element-ii/) -- Skip List --------- -[TODO] \ No newline at end of file +> The skip list is a probabilisitc data structure that is built upon the general idea of a linked list. The skip list uses probability to build subsequent layers of linked lists upon an original linked list. Each additional layer of links contains fewer elements, but no new elements. + +``` + 1 10 + o---> o---------------------------------------------------------> o Top level + 1 3 2 5 + o---> o---------------> o---------> o---------------------------> o Level 3 + 1 2 1 2 3 2 + o---> o---------> o---> o---------> o---------------> o---------> o Level 2 + 1 1 1 1 1 1 1 1 1 1 1 + o---> o---> o---> o---> o---> o---> o---> o---> o---> o---> o---> o Bottom level +Head 1st 2nd 3rd 4th 5th 6th 7th 8th 9th 10th NIL + Node Node Node Node Node Node Node Node Node Node +``` + +| Operation | Time Complexity | +| ---------- | :-------------: | +| Access | O(log n) | +| Search | O(log n) | +| Insertion | O(log n) | +| Deletion | O(log n) | + +```py +def search(key): + p = topLeftNode() + while p.below: # Scan down + p = p.below + while key >= p.next: # Scan forward + p = p.next + return p + +def insert(key): + p, q = search(key), None + i = 1 + while CoinFlip() != 'Tails': + i = i + 1 # Height of tower for new element + if i >= h: + h = h + 1 + createNewLevel() # Creates new linked list level + while p.above is None: + p = p.prev # Scan backwards until you can go up + p = p.above + q = insertAfter(key, p) # Insert our key after position p + n = n + 1 + return q + +def delete(key): + # Search for all positions p_0, ..., p_i where key exists + if none are found: + return + # Delete all positions p_0, ..., p_i + # Remove all empty layers of skip list +``` \ No newline at end of file diff --git a/Week02/NOTE.md b/Week02/NOTE.md index 79348bcb0..a2b296deb 100644 --- a/Week02/NOTE.md +++ b/Week02/NOTE.md @@ -6,10 +6,10 @@ Hash Table > A hash table is a data structure used to store vals, optionally, with corresponding values. -- A mapping object maps hashable values to arbitrary objects. Mappings are mutable objects. The only standard mapping type in python is Dictionary. +- A mapping object maps hashable values to arbitrary objects. Mappings are **mutable** objects. The only standard mapping type in python is `Dictionary`. - A dictionary’s vals are almost arbitrary values. Values that are not hashable (like lists, dictionaries or other mutable types) may not be used as vals. - Dictionaries can be created by placing a comma-separated list of val: value pairs within braces or by the dict() constructor. -- A set is an unordered collection with no duplicate elements. Curly braces or the set() function can be used to create sets. (empty set must use set()) +- A `set` is an unordered collection with no duplicate elements. Curly braces or the set() function can be used to create sets. (empty set must use set()) | Operation | Time Complexity | | ---------- | :-------------: | @@ -27,9 +27,9 @@ val in d # Return True if d has a val val, else False. val not in d # Equivalent to not val in d. d.clear() # Remove all items from the dictionary. d.get(val[, default]) # Return the value for val if val is in the dictionary, else default. -d.items() # Return a new view of the dictionary’s items ((val, value) pairs). -d.vals() # Return a new view of the dictionary’s vals. +d.keys() # Return a new view of the dictionary’s keys. d.values() # Return a new view of the dictionary’s values. +d.items() # Return a new view of the dictionary’s items ((val, value) pairs). ``` Useful functions @@ -83,7 +83,7 @@ Complete Binary Tree > A binary tree in which every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible. - A complete binary tree has `2^k` nodes at every depth `k < n` and between `2^n` and `2^n+1 - 1` nodes altogether. -- It can be efficiently implemented as an array, where a node at index `i` has children at indexes `2i` and `2i+1` and a parent at index `i/2`, with 1-based indexing. +- It can be efficiently implemented as an array, where a node at index `i` has children at indexes `2i` and `2i+1` and a parent at index `i/2`, with 1-based indexing (`2i+1` and `2i+2` for 0-based indexing) ``` __A__ @@ -141,9 +141,9 @@ DFS Post-order Traversal: `left -> right -> root` ```py def postorder(self, root): if root: - self.postorder(left) - self.postorder(right) - self.visit(root) + postorder(left) + postorder(right) + visit(root) ``` BFS Level Order Traversal: `top -> bottom, left -> right` @@ -175,12 +175,10 @@ Binary Search Tree (BST) | Operation | Time Complexity | | ---------- | :-------------: | -| Access | O(log(n)) | -| Search | O(log(n)) | -| Insertion | O(log(n)) | -| Deletion | O(log(n)) | - -BST Operations +| Access | O(log n) | +| Search | O(log n) | +| Insertion | O(log n) | +| Deletion | O(log n) | BST Template ```py @@ -230,44 +228,50 @@ def insert(root, val): Delete ``` 1) Node to be deleted is leaf: Simply remove from the tree. - 50 50 - / \ Delete(20) / \ - 30 70 ---------> 30 70 - / \ / \ \ / \ -20 40 60 80 40 60 80 + + 50 50 + / \ Delete(20) / \ + 30 70 ---------> 30 70 + / \ / \ \ / \ +20 40 60 80 40 60 80 2) Node to be deleted has only one child: Copy the child to the node and delete the child. - 50 50 - / \ Delete(30) / \ - 30 70 ---------> 40 70 - \ / \ / \ - 40 60 80 60 80 + + 50 50 + / \ Delete(30) / \ + 30 70 ---------> 40 70 + \ / \ / \ + 40 60 80 60 80 3) Node to be deleted has two children: Find inorder successor of the node. Copy contents of the inorder successor to the node and delete the inorder successor. - 50 60 - / \ Delete(50) / \ - 40 70 ---------> 40 70 - / \ \ - 60 80 80 + + 50 60 + / \ Delete(30) / \ + 30 70 ---------> 40 70 + \ / \ \ + 40 60 80 80 ``` ```py def deleteNode(self, root: TreeNode, val: int) -> TreeNode: - if root is None: - return None + if root is None: return root + if val < root.val: root.left = self.deleteNode(root.left, val) elif val > root.val: root.right = self.deleteNode(root.right, val) else: - if root.left is None: # has only right child + # has one child + if root.left is None: # only right child return root.right - if root.right is None: # has only left child + if root.right is None: # only left child return root.left + # has two children minNode = self.getMin(root.right) root.val = minNode.val root.right = self.deleteNode(root.right, minNode.val) + return root def getMin(self, node: TreeNode) -> TreeNode: @@ -287,7 +291,7 @@ LeetCode Problems Heap ---- -> A heap is a complete binary tree, and is represent by array. The children of the node at index i are at indices 2i + 1 and 2i + 2. +> A heap is a complete binary tree, and is represent by array. The children of the node at index i are at indices 2i+1 and 2i+2. Given element in a heap at position `i`: - parent position: `(i-1) >> 1` or `i // 2` @@ -355,7 +359,7 @@ Graph Types of graphs: - Undirected Graph: nodes are connected by edges that are all bidirectional. -- Directed: nodes are connected by directed edges – they only go in one direction. +- Directed Graph: nodes are connected by directed edges – they only go in one direction. Graph Adjacency List Representation - The size of the array is equal to the number of nodes. diff --git a/Week03/NOTE.md b/Week03/NOTE.md index 85249e380..a799dd523 100644 --- a/Week03/NOTE.md +++ b/Week03/NOTE.md @@ -61,7 +61,7 @@ Backtrack Template result = [] def backtrack(path = [], choices): if end condition: - result.add(path[:]) # have to make a new copy + result.add(path[:]) # param pass by reference return for choice in choices: @@ -75,7 +75,7 @@ def backtrack(path = [], choices): ``` - Time complexity for backtrack algorithm is at least O(N!) -- Backtrack is a decision tree, updating the result is actually a preorder and/or postorder recursion +- Backtrack is a decision tree, updating the result is actually a preorder and/or postorder recursion (DFS) - Sometimes we don't need to explicitly maintain the choice list, we derive it using other parameters (e.g. index) - Sometimes path can be a string instead of an array, and we use `path += 'choice'` and `path = path[:-1]` to make and remove choice diff --git a/Week04/NOTE.md b/Week04/NOTE.md index 61681e947..28bba4a25 100644 --- a/Week04/NOTE.md +++ b/Week04/NOTE.md @@ -8,7 +8,7 @@ DFS Template visited = set() def dfs(node): - # terminator + # terminator (base case) if not node: return @@ -23,10 +23,8 @@ def dfs(node): visited.add(node) # process children (drill down) - if node.children: - for child in node.children: - if child not in visited: - dfs(child, visited) + for child in node.children: + dfs(child, visited) ``` DFS Application @@ -60,7 +58,9 @@ def island(self, grid: List[List[int]]): return 0 <= r < m and 0 <= c < n ``` -Backtrack (see Week03 NOTE) +Backtrack + +See [Week03 NOTE](../Week03/NOTE.md). Leetcode Problems - [200. Number of Islands](https://leetcode.com/problems/number-of-islands/) @@ -80,15 +80,20 @@ def bfs(root): while queue: # get current node from queue node = queue.popleft() + + # already visited + if node in visited: + continue + # process current node process(node.val) + # add to visited visited.add(node) + # process children - if node.children: - for child in node.children: - if child not in visited: - queue.append(child) + for child in node.children: + queue.append(child) ``` BFS Application @@ -162,7 +167,16 @@ Leetcode Problems Greedy Algorithm ---------------- -[TODO] + +> A greedy algorithm is any algorithm that follows the problem-solving heuristic of making the locally optimal choice at each stage. + +Leetcode Problems +- [860. Lemonade Change](https://leetcode.com/problems/lemonade-change/) +- [455. Assign Cookies](https://leetcode.com/problems/assign-cookies/) +- [874. Walking Robot Simulation](https://leetcode.com/problems/walking-robot-simulation) +- [322. Coin Change](https://leetcode.com/problems/coin-change/) +- [55. Jump Game](https://leetcode.com/problems/jump-game/) +- [45. Jump Game II](https://leetcode.com/problems/jump-game-ii/) Binary Search ------------- diff --git a/Week06/NOTE.md b/Week06/NOTE.md index ca72e5d5c..0391bed1f 100644 --- a/Week06/NOTE.md +++ b/Week06/NOTE.md @@ -86,6 +86,7 @@ DP Framework ```py # initialize base case dp[0][0][...] = base + # status transfer for status_1 in all_values_in_status_1: for status_2 in all_values_in_status_2: @@ -100,6 +101,8 @@ Basics - [509. Fibonacci Number](https://leetcode.com/problems/fibonacci-number/) - [70. Climbing Stairs](https://leetcode.com/problems/climbing-stairs/) - [120. Triangle](https://leetcode.com/problems/triangle/) +- [322. Coin Change](https://leetcode.com/problems/coin-change/) +- [518. Coin Change 2](https://leetcode.com/problems/coin-change-2/) House Robber - [198. House Robber](https://leetcode.com/problems/house-robber/) diff --git a/Week08/NOTE.md b/Week08/NOTE.md index b7d833b0c..5246b9737 100644 --- a/Week08/NOTE.md +++ b/Week08/NOTE.md @@ -121,6 +121,7 @@ Properties - O(1) lookup - O(1) update +Leetcode Problems - [146. LRU Cache](https://leetcode.com/problems/lru-cache/) Sorting From 8639a17126280bf3ab62e1b0bda13840dc278525 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Mon, 17 Aug 2020 23:20:40 -0400 Subject: [PATCH 32/48] red-black tree --- Week07/NOTE.md | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/Week07/NOTE.md b/Week07/NOTE.md index 7a3f61941..7d800f246 100644 --- a/Week07/NOTE.md +++ b/Week07/NOTE.md @@ -219,8 +219,39 @@ Red-Black Tree > In a red–black tree, each node stores an extra bit representing color, used to ensure that the tree remains approximately balanced during insertions and deletions. Properties -- Each node is either red or black. -- The root is black. This rule is sometimes omitted. Since the root can always be changed from red to black, but not necessarily vice versa, this rule has little effect on analysis. -- All leaves (NIL) are black. -- If a node is red, then both its children are black. -- Every path from a given node to any of its descendant NIL nodes goes through the same number of black nodes. \ No newline at end of file +1. Every node is either red or black. +2. The root is black. +3. All leaves (NIL) are black. +4. Red nodes have only black children (no two red nodes are connected) +5. Every path from a given node to any of its descendant NIL nodes goes through the same number of black nodes. + +Operations +1. Change Color +2. Rotate Left +3. Rotate Right + +``` + G G - Grand Parent + / \ + P U P - Parent, U - Uncle + / \ +S N S - Sibling, N - Current +``` + +RED-BLACK repair procedure Operations: +- All newly inserted nodes are considered as **RED** by default +- Case 1: current node N is root + - set current node N to **BLACK** +- Case 2: current node N's parent node P is **BLACK** + - Do Nothing since tree is still valid +- Case 3: current node N's parent node P is **RED** and it's Uncle node U is also **RED** + - set parent node P to **BLACK** + - set Uncle node U to **BLACK** + - set Grand parent node G to **RED** + - rerun on the RED-BLACK repair procedure G +- Case 4: current node N's parent node P is **RED** but it's uncle node U is **BLACK** + - Current node N is right sub-tree, rotate left parent node P + - current node N is left sub-tree + - Set parent node P to **BLACK** + - Set grand parent node G to **RED** + - Rotate right on grand parent node G \ No newline at end of file From d8c0a7275feaa7052431a13d3ef64d68df9ee527 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Wed, 19 Aug 2020 01:10:55 -0400 Subject: [PATCH 33/48] array revisit --- Week01/NOTE.md | 53 ++++++++++++++++++++++++++++++++++++-------------- Week02/NOTE.md | 1 + Week04/NOTE.md | 1 + 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/Week01/NOTE.md b/Week01/NOTE.md index ba6d731d0..8be4441a6 100644 --- a/Week01/NOTE.md +++ b/Week01/NOTE.md @@ -66,6 +66,41 @@ vec = [[1,2,3], [4,5,6], [7,8,9]] # String formatter 'Hello {name}'.format(name='World') +# Useful functions +map(lambda x: x * x, [1, 2, 3, 4, 5]) # [1, 4, 9, 16, 25] +map(lambda x, y: x + y, [1, 2, 3], [4, 5, 6]) # [5, 7, 9] +filter(lambda x: x % 2 != 0, [1, 2, 3, 4, 5, 6]) # [1, 3, 5] +any((False, False, True)) # True +all((False, True, True)) # False +sum([1, 2, 3, 4, 5]) # 15 +functools.reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) # calculates ((((1+2)+3)+4)+5) = 15 +``` + +```py +# total combinations of two numbers (pairs) in an array (brute force) +nums = [1,2,3,4] +n = len(nums) +for i in range(n-1): + for j in range(i+1, n): + print((nums[i], nums[j])) # [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)] + +# traverse backwards +nums = [1,2,3,4,5] +n = len(nums) +for i in range(n-1, -1, -1): + print(nums[i]) # [5,4,3,2,1] + +# Two Pointers +nums = [1,2,3,4,5,6,7,8,9] +n = len(nums) +i, j = 0, n-1 +while i <= j: + print(nums[i], nums[j]) # [(1, 9), (2, 8), (3, 7), (4, 6), (5, 5)] + while i <= j and nums[i+1] == nums[i]: i += 1 # skip duplicates + while i <= j and nums[j-1] == nums[j]: j -= 1 # skip duplicates + i += 1 + j -= 1 + # Get sliding windows of size k in an array of nums nums, k = [1,2,3,4,5,6], 3 n = len(nums) @@ -76,31 +111,19 @@ print(windows) # [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]] nums, k = [1,2,3,4,5,6,7], 3 n = len(nums) for i in range(n): - res[(i + k) % n] = nums[i] + res[(i+k)%n] = nums[i] print(res) # [5,6,7,1,2,3,4] - -# Useful functions -map(lambda x: x * x, [1, 2, 3, 4, 5]) # [1, 4, 9, 16, 25] -map(lambda x, y: x + y, [1, 2, 3], [4, 5, 6]) # [5, 7, 9] -filter(lambda x: x % 2 != 0, [1, 2, 3, 4, 5, 6]) # [1, 3, 5] -any((False, False, True)) # True -all((False, True, True)) # False -sum([1, 2, 3, 4, 5]) # 15 -functools.reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) # calculates ((((1+2)+3)+4)+5) = 15 ``` Leetcode Problems - [1. Two Sum](https://leetcode.com/problems/two-sum/) -- [70. Climbing Stairs](https://leetcode.com/problems/climbing-stairs/) - [66. Plus One](https://leetcode.com/problems/plus-one/) - [283. Move Zeroes](https://leetcode.com/problems/move-zeroes/) - [26. Remove Duplicates from Sorted Array ](https://leetcode.com/problems/remove-duplicates-from-sorted-array/) - [88. Merge Sorted Array](https://leetcode.com/problems/merge-sorted-array/) -- [167. Two Sum II - Input array is sorted](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/) -- [15. 3Sum](https://leetcode.com/problems/3sum/) -- [344. Reverse String](https://leetcode.com/problems/reverse-string/) - [189. Rotate Array](https://leetcode.com/problems/rotate-array/) -- [704. Binary Search](https://leetcode.com/problems/binary-search/) +- [344. Reverse String](https://leetcode.com/problems/reverse-string/) +- [15. 3Sum](https://leetcode.com/problems/3sum/) - [11. Container With Most Water](https://leetcode.com/problems/container-with-most-water/) Linked Lists diff --git a/Week02/NOTE.md b/Week02/NOTE.md index a2b296deb..a69dd0a72 100644 --- a/Week02/NOTE.md +++ b/Week02/NOTE.md @@ -43,6 +43,7 @@ print(cnt) # Counter({'blue': 3, 'red': 2, 'green': 1}) ``` Leetcode Problems +- [1. Two Sum](https://leetcode.com/problems/two-sum/) - [242. Valid Anagram](https://leetcode.com/problems/valid-anagram/description/) - [49. Group Anagrams](https://leetcode.com/problems/group-anagrams/) diff --git a/Week04/NOTE.md b/Week04/NOTE.md index 28bba4a25..f302cdd4f 100644 --- a/Week04/NOTE.md +++ b/Week04/NOTE.md @@ -265,6 +265,7 @@ def binary_search(l, r): Leetcode Problems - [704. Binary Search](https://leetcode.com/problems/binary-search/) +- [167. Two Sum II - Input array is sorted](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/) - [33. Search in Rotated Sorted Array](https://leetcode.com/problems/search-in-rotated-sorted-array/) - [34. Find First and Last Position of Element in Sorted Array](https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/) - [69. Sqrt(x)](https://leetcode.com/problems/sqrtx/) From d15bc071082689486e19103073650d3c0597b3cc Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 23 Aug 2020 00:57:56 -0400 Subject: [PATCH 34/48] summary --- Week01/NOTE.md | 10 +++++----- Week03/NOTE.md | 2 ++ summary.md | 28 ++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 summary.md diff --git a/Week01/NOTE.md b/Week01/NOTE.md index 8be4441a6..641d0a682 100644 --- a/Week01/NOTE.md +++ b/Week01/NOTE.md @@ -216,20 +216,19 @@ def traverse(head): ``` Leetcode Problems -- [206. Reverse Linked List](https://leetcode.com/problems/reverse-linked-list/) - [203. Remove Linked List Elements](https://leetcode.com/problems/remove-linked-list-elements/) -- [445. Add Two Numbers II](https://leetcode.com/problems/add-two-numbers-ii/) +- [206. Reverse Linked List](https://leetcode.com/problems/reverse-linked-list/) - [92. Reverse Linked List II](https://leetcode.com/problems/reverse-linked-list-ii/) +- [445. Add Two Numbers II](https://leetcode.com/problems/add-two-numbers-ii/) - [21. Merge Two Sorted Lists](https://leetcode.com/problems/merge-two-sorted-lists/) - [24. Swap Nodes in Pairs](https://leetcode.com/problems/swap-nodes-in-pairs/) -- [83. Remove Duplicates from Sorted List](https://leetcode.com/problems/remove-duplicates-from-sorted-list/) - [876. Middle of the Linked List](https://leetcode.com/problems/middle-of-the-linked-list/) +- [83. Remove Duplicates from Sorted List](https://leetcode.com/problems/remove-duplicates-from-sorted-list/) - [19. Remove Nth Node From End of List](https://leetcode.com/problems/remove-nth-node-from-end-of-list/) - [141. Linked List Cycle](https://leetcode.com/problems/linked-list-cycle/) - [142. Linked List Cycle II](https://leetcode.com/problems/linked-list-cycle-ii/) -- [25. Reverse Nodes in k-Group](https://leetcode.com/problems/reverse-nodes-in-k-group/) - [148. Sort List](https://leetcode.com/problems/sort-list/) -- [92. Reverse Linked List II](https://leetcode.com/problems/reverse-linked-list-ii/) +- [25. Reverse Nodes in k-Group](https://leetcode.com/problems/reverse-nodes-in-k-group/) Stacks and Queues ----------------- @@ -368,6 +367,7 @@ class monoQueue: Leetcode Problems - [20. Valid Parentheses](https://leetcode.com/problems/valid-parentheses/) +- [445. Add Two Numbers II](https://leetcode.com/problems/add-two-numbers-ii/) - [155. Min Stack](https://leetcode.com/problems/min-stack/) - [84. Largest Rectangle in Histogram](https://leetcode.com/problems/largest-rectangle-in-histogram/) - [239. Sliding Window Maximum](https://leetcode.com/problems/sliding-window-maximum/) diff --git a/Week03/NOTE.md b/Week03/NOTE.md index a799dd523..728d9fa0e 100644 --- a/Week03/NOTE.md +++ b/Week03/NOTE.md @@ -50,6 +50,8 @@ def divide_conquer(problem, param1, param2, ...): - Sometimes we need global variables to easily update the final result Leetcode Problems +- [21. Merge Two Sorted Lists](https://leetcode.com/problems/merge-two-sorted-lists/) +- [206. Reverse Linked List](https://leetcode.com/problems/reverse-linked-list/) - [105. Construct Binary Tree from Preorder and Inorder Traversal](https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/) - [236. Lowest Common Ancestor of a Binary Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/) - [169. Majority Element](https://leetcode.com/problems/majority-element/) diff --git a/summary.md b/summary.md new file mode 100644 index 000000000..a922aca3e --- /dev/null +++ b/summary.md @@ -0,0 +1,28 @@ +Data Structure and Algorithms +- Array +- LinkedList +- Stacks and Queues +- Skip List +- Hash Table +- Binary Trees +- Binary Search Trees (BST) +- Heap +- Graph +- Recursion +- Divide & Conquer +- Backtrack +- DFS +- BFS +- Greedy +- Binary Search +- Dynamic Programming +- Trie +- Disjoint-set +- Advanced Search +- Self-balancing BST (AVL, Red-Black, B-tree, B+tree) +- Primitives +- Bloom Filter +- LRU Cache +- Sorting (Bubble, Selection, Insertion, Merge, Quick, Heap, Couting, Bucket, Radix) +- String +- String-searching algorithms (Naive, Rabin-Karp, KMP, Boyer-Moore) From 852ee1a5c08f4a9c41d6f65dc6df613279b5da0c Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Thu, 3 Sep 2020 01:36:47 -0400 Subject: [PATCH 35/48] review 1 --- README.md | 56 ++++++++++++------------ Week01/NOTE.md | 111 +++++++++++++++++++----------------------------- Week01/stack.py | 12 ++++++ Week09/NOTE.md | 46 ++++++++++++++++++++ summary.md | 28 ------------ 5 files changed, 130 insertions(+), 123 deletions(-) create mode 100644 Week01/stack.py delete mode 100644 summary.md diff --git a/README.md b/README.md index c2f794952..c94798952 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,28 @@ -# 极客大学「算法训练营-第10期」作业提交仓库 - - -## 讲师课件下载地址 - -请大家通过该链接查看讲师课件并进行下载,链接:https://pan.baidu.com/s/1GOuXJfnlERQs8bI8HNwPrQ 密码:zlua - - -## 仓库目录结构说明 - -1. `week01/` 代表第一周作业提交目录,以此类推。 -2. 请在对应周的目录下新建或修改自己的代码作业。 -2. 每周均有一个 `REDAME.md` 文档,你可以将自己当周的学习心得以及做题过程中的思考记录在该文档中。 - -## 作业提交规则 - -1. 先将本仓库 Fork 到自己 GitHub 账号下。 -2. 将 Fork 后的仓库 Clone 到本地,然后在本地仓库中对应周的目录下新建或修改自己的代码作业,当周的学习总结写在对应周的README.md文件里。 -3. 在本地仓库完成作业后,push 到自己的 GitHub 远程仓库。 -4. 最后将远程仓库中当周的作业链接,按格式贴到班级仓库对应学习周的issue下面。 -5. 提交issue请务必按照规定格式进行提交,否则作业统计工具将抓取不到你的作业提交记录。 - -详细的作业提交流程可以查阅:https://shimo.im/docs/m5rtM8K8rNsjw5jk/ - - -## 注意事项 - - 如果对 Git 和 GitHub 不太了解,请参考 [Git 官方文档](https://git-scm.com/book/zh/v2) 或者极客时间的[《玩转 Git 三剑客》](https://time.geekbang.org/course/intro/145)视频课程。 +Data Structure and Algorithms +- Array +- LinkedList +- Stacks and Queues +- Skip List +- Hash Table +- Binary Trees +- Binary Search Trees (BST) +- Heap +- Graph +- Recursion +- Divide & Conquer +- Backtrack +- DFS +- BFS +- Greedy +- Binary Search +- Dynamic Programming +- Trie +- Disjoint-set +- Advanced Search +- Self-balancing BST (AVL, Red-Black, B-tree, B+tree) +- Primitives +- Bloom Filter +- LRU Cache +- Sorting (Bubble, Selection, Insertion, Merge, Quick, Heap, Couting, Bucket, Radix) +- String +- String-searching algorithms (Naive, Rabin-Karp, KMP, Boyer-Moore) \ No newline at end of file diff --git a/Week01/NOTE.md b/Week01/NOTE.md index 641d0a682..31853773d 100644 --- a/Week01/NOTE.md +++ b/Week01/NOTE.md @@ -207,7 +207,7 @@ Linked List Recursion ```py def traverse(head): # base case - if head.next: + if not head: return head # do something before (pre-order) node = traverse(head.next) @@ -263,86 +263,66 @@ queue[0] # peek left side of the deque queue[-1] # peek right side of the deque ``` -Sliding Window Template -```py -from collections import Counter - -def slidingWindow(s, t): - window = Counter() - target = Counter(t) - - len_s, len_t = len(s), len(t) - left = right = 0 - valid = 0 - - while right < len_s: - # c is the element to be inserted into the window - c = s[right] - # expand the current window - right += 1 - # If c is our target, we are 1 step closer to the answer - if c in target: - window[c] += 1 - if window[c] == target[c]: - valid += 1 - - while ... # when we found a valid window - if valid == len(target): - ... # check the answer or update the result - - # d is the element to be removed from the window - d = s[left] - # shrink the current window - left += 1 - # if d is our target, shrinking might cause the window to be invalid - if d in target: - if window[d] == target[d]: - valid -= 1 - window[d] -= 1 -``` - Mono stack ```py n = len(nums) stack = [] -leftMax = [-1] * n +prevGreaterElement = [-1] * n for i in range(n): # push into stack while stack and stack[-1] <= nums[i]: # compare with stack top - # pop out numbers smaller than me - stack.pop() - # now the top is the first element bigger than me - leftMax[i] = stack[-1] if stack else -1 + stack.pop() # pop out numbers smaller than me + # now the top is the first element larger than me + prevGreaterElement[i] = stack[-1] if stack else -1 # push myself in stack for the next round stack.append(nums[i]) -return leftMax +print(prevGreaterElement) # Variation 1: push to stack backwards to get the rightMax -rightMax = [-1] * n +nextGreaterElement = [-1] * n for i in range(n-1, -1, -1): - ... + while stack and stack[-1] <= nums[i]: + stack.pop() + nextGreaterElement[i] = stack[-1] if stack else -1 + stack.append(nums[i]) +print(nextGreaterElement) -# Variation 2: find min rather than max -leftMin = [-1] * n +# Variation 2: find min rather than max (change the compare part) +prevSmallerElement = [-1] * n for i in range(n): - while stack and nums[i] <= stack[-1]: - ... + while stack and stack[-1] > nums[i]: + stack.pop() + prevSmallerElement[i] = stack[-1] if stack else -1 + stack.append(nums[i]) +print(prevSmallerElement) # Variation 3: push index to stack instead of numbers -while stack and nums[stack[-1]] <= nums[i]: - stack.pop() -... -stack.append(i) +prevGreaterIndex = [-1] * n +for i in range(n): + while stack and nums[stack[-1]] <= nums[i]: + stack.pop() + prevGreaterIndex[i] = stack[-1] if stack else -1 + stack.append(i) +print(prevGreaterIndex) + +# Mono Increasing Stack +for i in range(n): + while stack and nums[stack[-1]] > nums[i]: + curr = stack.pop() + if not stack: + ... + left, right = stack[-1], i + print(left, curr, right) + stack.append(i) -# Variation 4: find leftMax and rightMax in one pass +# Mono Decreasing Stack for i in range(n): while stack and nums[stack[-1]] < nums[i]: - currIndex = stack.pop() + curr = stack.pop() if not stack: - break - leftMax = stack[-1] - rightMax = i - print(leftMax, currIndex, rightMax) + ... + left, right = stack[-1], i + print(left, curr, right) stack.append(i) ``` @@ -369,14 +349,11 @@ Leetcode Problems - [20. Valid Parentheses](https://leetcode.com/problems/valid-parentheses/) - [445. Add Two Numbers II](https://leetcode.com/problems/add-two-numbers-ii/) - [155. Min Stack](https://leetcode.com/problems/min-stack/) +- [496. Next Greater Element I](https://leetcode.com/problems/next-greater-element-i/) +- [503. Next Greater Element II](https://leetcode.com/problems/next-greater-element-ii/) +- [42. Trapping Rain Water](https://leetcode.com/problems/trapping-rain-water/) - [84. Largest Rectangle in Histogram](https://leetcode.com/problems/largest-rectangle-in-histogram/) - [239. Sliding Window Maximum](https://leetcode.com/problems/sliding-window-maximum/) -- [76. Minimum Window Substring](https://leetcode.com/problems/minimum-window-substring/) -- [567. Permutation in String](https://leetcode.com/problems/permutation-in-string/) -- [438. Find All Anagrams in a String](https://leetcode.com/problems/find-all-anagrams-in-a-string/) -- [3. Longest Substring Without Repeating Characters](https://leetcode.com/problems/longest-substring-without-repeating-characters/) -- [239. Sliding Window Maximum](https://leetcode.com/problems/sliding-window-maximum/) -- [42. Trapping Rain Water](https://leetcode.com/problems/next-greater-element-ii/) Skip List --------- diff --git a/Week01/stack.py b/Week01/stack.py new file mode 100644 index 000000000..282d2e108 --- /dev/null +++ b/Week01/stack.py @@ -0,0 +1,12 @@ +nums = [3,2,1,0,1,2,3] +n = len(nums) + +stack = [] +for i in range(n): + while stack and nums[stack[-1]] <= nums[i]: + curr = stack.pop() + if not stack: + break + left, right = stack[-1], i + print(left, curr, right) + stack.append(i) \ No newline at end of file diff --git a/Week09/NOTE.md b/Week09/NOTE.md index b3200a385..7c2865c4b 100644 --- a/Week09/NOTE.md +++ b/Week09/NOTE.md @@ -29,6 +29,46 @@ string.ascii_letters # The lowercase letters 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' string.letters # The concatenation of the ascii_lowercase and ascii_uppercase ``` +Sliding Window Template +```py +from collections import Counter + +def slidingWindow(s, t): + window = Counter() + target = Counter(t) + + valid = 0 + left = right = 0 + while right < len(s): + # c is the element to be inserted into the window + c = s[right] + # if we insert it, is the window still valid? + if c in target: + window[c] += 1 + if window[c] == target[c]: + valid += 1 + # expand the current window + right += 1 + + print(s[left: right]) + + # when we found a valid window + while right - left >= len(t): + # check the answer or update the result + if valid == len(target): + ... + + # d is the element to be removed from the window + d = s[left] + # if we remove it, is the window still valid? + if d in target: + if window[d] == target[d]: + valid -= 1 + window[d] -= 1 + # shrink the current window + left += 1 +``` + String Basics - [709. To Lower Case](https://leetcode-cn.com/problems/to-lower-case/) - [58. Length of Last Word](https://leetcode.com/problems/length-of-last-word/) @@ -54,6 +94,12 @@ Anagram - [49. Group Anagrams](https://leetcode.com/problems/group-anagrams/) - [438. Find All Anagrams in a String](https://leetcode.com/problems/find-all-anagrams-in-a-string/) +Sliding Window +- [3. Longest Substring Without Repeating Characters](https://leetcode.com/problems/longest-substring-without-repeating-characters/) +- [76. Minimum Window Substring](https://leetcode.com/problems/minimum-window-substring/) +- [438. Find All Anagrams in a String](https://leetcode.com/problems/find-all-anagrams-in-a-string/) +- [567. Permutation in String](https://leetcode.com/problems/permutation-in-string/) + Advanced String Problems - [10. Regular Expression Matching](https://leetcode.com/problems/regular-expression-matching/) - [44. Wildcard Matching](https://leetcode.com/problems/wildcard-matching/) diff --git a/summary.md b/summary.md deleted file mode 100644 index a922aca3e..000000000 --- a/summary.md +++ /dev/null @@ -1,28 +0,0 @@ -Data Structure and Algorithms -- Array -- LinkedList -- Stacks and Queues -- Skip List -- Hash Table -- Binary Trees -- Binary Search Trees (BST) -- Heap -- Graph -- Recursion -- Divide & Conquer -- Backtrack -- DFS -- BFS -- Greedy -- Binary Search -- Dynamic Programming -- Trie -- Disjoint-set -- Advanced Search -- Self-balancing BST (AVL, Red-Black, B-tree, B+tree) -- Primitives -- Bloom Filter -- LRU Cache -- Sorting (Bubble, Selection, Insertion, Merge, Quick, Heap, Couting, Bucket, Radix) -- String -- String-searching algorithms (Naive, Rabin-Karp, KMP, Boyer-Moore) From 6c372feb89b0eaa623880c5560b004818d5734a0 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 6 Sep 2020 23:30:28 -0400 Subject: [PATCH 36/48] Update NOTE.md --- Week02/NOTE.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Week02/NOTE.md b/Week02/NOTE.md index a69dd0a72..920735a35 100644 --- a/Week02/NOTE.md +++ b/Week02/NOTE.md @@ -244,13 +244,14 @@ Delete \ / \ / \ 40 60 80 60 80 -3) Node to be deleted has two children: Find inorder successor of the node. Copy contents of the inorder successor to the node and delete the inorder successor. +3) Node to be deleted has two children: Find inorder successor of the node. + Copy contents of the inorder successor to the node and delete the inorder successor. 50 60 - / \ Delete(30) / \ - 30 70 ---------> 40 70 - \ / \ \ - 40 60 80 80 + / \ Delete(50) / \ + 40 70 ---------> 40 70 + / \ \ + 60 80 80 ``` ```py @@ -383,4 +384,4 @@ Graph Adjacency Matrix Representation | **1** | 1 | 0 | 1 | 1 | 0 | | **2** | 0 | 1 | 0 | 1 | 1 | | **3** | 1 | 1 | 1 | 0 | 0 | -| **4** | 0 | 0 | 1 | 0 | 0 | \ No newline at end of file +| **4** | 0 | 0 | 1 | 0 | 0 | From 900c420396791928978a21a9c1290e8d3cdfcef1 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sat, 19 Sep 2020 08:53:35 -0400 Subject: [PATCH 37/48] traversal --- Week01/stack.py | 12 -------- Week02/NOTE.md | 3 +- Week02/binary-tree-traversal.py | 54 +++++++++++++++++++++++++++++++-- Week02/n-ary_tree_traversal.py | 17 +++++------ 4 files changed, 59 insertions(+), 27 deletions(-) delete mode 100644 Week01/stack.py diff --git a/Week01/stack.py b/Week01/stack.py deleted file mode 100644 index 282d2e108..000000000 --- a/Week01/stack.py +++ /dev/null @@ -1,12 +0,0 @@ -nums = [3,2,1,0,1,2,3] -n = len(nums) - -stack = [] -for i in range(n): - while stack and nums[stack[-1]] <= nums[i]: - curr = stack.pop() - if not stack: - break - left, right = stack[-1], i - print(left, curr, right) - stack.append(i) \ No newline at end of file diff --git a/Week02/NOTE.md b/Week02/NOTE.md index a69dd0a72..7f3b85fef 100644 --- a/Week02/NOTE.md +++ b/Week02/NOTE.md @@ -7,8 +7,7 @@ Hash Table > A hash table is a data structure used to store vals, optionally, with corresponding values. - A mapping object maps hashable values to arbitrary objects. Mappings are **mutable** objects. The only standard mapping type in python is `Dictionary`. -- A dictionary’s vals are almost arbitrary values. Values that are not hashable (like lists, dictionaries or other mutable types) may not be used as vals. -- Dictionaries can be created by placing a comma-separated list of val: value pairs within braces or by the dict() constructor. +- A `dict` vals are almost arbitrary values. Values that are not hashable (like lists, dictionaries or other mutable types) may not be used as vals. - A `set` is an unordered collection with no duplicate elements. Curly braces or the set() function can be used to create sets. (empty set must use set()) | Operation | Time Complexity | diff --git a/Week02/binary-tree-traversal.py b/Week02/binary-tree-traversal.py index 7e63a9eb5..5c5faf466 100644 --- a/Week02/binary-tree-traversal.py +++ b/Week02/binary-tree-traversal.py @@ -34,6 +34,21 @@ def preorderTraversal2(self, root: BinaryTreeNode) -> List[int]: """ Solution #2: Iteratively """ + if not root: return [] + res = [] + stack = [root] + while stack: + node = stack.pop() + if node is not None: + res.append(node.val) + stack.append(node.right) + stack.append(node.left) + return res + + def preorderTraversal3(self, root: BinaryTreeNode) -> List[int]: + """ + Solution #3: White/Grey Tagging + """ WHITE, GREY = 0, 1 res = [] stack = [(WHITE, root)] @@ -63,10 +78,28 @@ def traverse(root): traverse(root) return res - def inorderTraversal2(self, root: BinaryTreeNode) -> List[int]: + def inorderTraversal2(self, root: TreeNode) -> List[int]: """ Solution #2: Iteratively """ + if not root: return [] + res = [] + stack = [] + while True: + while root: + stack.append(root) + root = root.left + if not stack: + return res + node = stack.pop() + res.append(node.val) + root = node.right + return res + + def inorderTraversal3(self, root: BinaryTreeNode) -> List[int]: + """ + Solution #3: White/Grey Tagging + """ WHITE, GREY = 0, 1 res = [] stack = [(WHITE, root)] @@ -96,9 +129,24 @@ def traverse(root): traverse(root) return res - def postorderTraversal2(self, root: BinaryTreeNode) -> List[int]: + def postorderTraversal2(self, root: TreeNode) -> List[int]: + """ + Solution #2: Iteratively + """ + if not root: return root + res = [] + stack = [root] + while stack: + node = stack.pop() + if node is not None: + res.append(node.val) + stack.append(node.left) + stack.append(node.right) + return res[::-1] + + def postorderTraversal3(self, root: BinaryTreeNode) -> List[int]: """ - Solution #1: Iteratively + Solution #3: White/Grey Tagging """ WHITE, GREY = 0, 1 res = [] diff --git a/Week02/n-ary_tree_traversal.py b/Week02/n-ary_tree_traversal.py index a17c50508..896ac4736 100644 --- a/Week02/n-ary_tree_traversal.py +++ b/Week02/n-ary_tree_traversal.py @@ -22,8 +22,7 @@ def preorder1(self, root: TreeNode) -> List[int]: Solution #1: Recursively """ def traverse(root): - if not root: - return + if not root: return res.append(root.val) if root.children is not None: for child in root.children: @@ -37,18 +36,16 @@ def preorder2(self, root: TreeNode) -> List[int]: """ Solution #2: Iteratively """ - if not root: - return [] + if not root: return [] res = [] stack = [root] while stack: - root = stack.pop() - if root.children is not None: - # from right to left append child - for child in root.children[::-1]: + node = stack.pop() + if not node: continue + res.append(node.val) + if node.children: + for child in node.children[::-1]: stack.append(child) - if root is not None: - res.append(root.val) return res def postorder1(self, root: TreeNode) -> List[int]: From d51fa06d0aa4003ab9fddf502a8b20a456142440 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Fri, 23 Jul 2021 16:40:11 -0400 Subject: [PATCH 38/48] small change to arrays --- Week01/NOTE.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Week01/NOTE.md b/Week01/NOTE.md index 31853773d..89ee781ee 100644 --- a/Week01/NOTE.md +++ b/Week01/NOTE.md @@ -67,9 +67,9 @@ vec = [[1,2,3], [4,5,6], [7,8,9]] 'Hello {name}'.format(name='World') # Useful functions +filter(lambda x: x % 2 != 0, [1, 2, 3, 4, 5, 6]) # [1, 3, 5] map(lambda x: x * x, [1, 2, 3, 4, 5]) # [1, 4, 9, 16, 25] map(lambda x, y: x + y, [1, 2, 3], [4, 5, 6]) # [5, 7, 9] -filter(lambda x: x % 2 != 0, [1, 2, 3, 4, 5, 6]) # [1, 3, 5] any((False, False, True)) # True all((False, True, True)) # False sum([1, 2, 3, 4, 5]) # 15 @@ -110,16 +110,17 @@ print(windows) # [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]] # Rotate array by k times (right shift) nums, k = [1,2,3,4,5,6,7], 3 n = len(nums) +res = [0] * n for i in range(n): res[(i+k)%n] = nums[i] print(res) # [5,6,7,1,2,3,4] ``` Leetcode Problems -- [1. Two Sum](https://leetcode.com/problems/two-sum/) -- [66. Plus One](https://leetcode.com/problems/plus-one/) - [283. Move Zeroes](https://leetcode.com/problems/move-zeroes/) - [26. Remove Duplicates from Sorted Array ](https://leetcode.com/problems/remove-duplicates-from-sorted-array/) +- [66. Plus One](https://leetcode.com/problems/plus-one/) +- [1. Two Sum](https://leetcode.com/problems/two-sum/) - [88. Merge Sorted Array](https://leetcode.com/problems/merge-sorted-array/) - [189. Rotate Array](https://leetcode.com/problems/rotate-array/) - [344. Reverse String](https://leetcode.com/problems/reverse-string/) From 64530bf8cd70f438ad0b8bd2bf439d8553524bbe Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Mon, 26 Jul 2021 11:26:14 -0400 Subject: [PATCH 39/48] more on week 01 notes --- Week01/NOTE.md | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/Week01/NOTE.md b/Week01/NOTE.md index 89ee781ee..4e1d0838d 100644 --- a/Week01/NOTE.md +++ b/Week01/NOTE.md @@ -181,8 +181,8 @@ dummy.next = head prev, curr = dummy, head while curr: - # prev will never be None - prev.next = curr.next + # prev will never be None here + # move prev and curr prev = curr curr = curr.next @@ -194,9 +194,9 @@ Fast Slow Template ```py fast = slow = head while fast and fast.next: - # fast move 2 steps + # fast move 2 steps at a time fast = fast.next.next - # slow move 1 step + # slow move 1 step at a time slow = slow.next # when num of the list is odd, slow is the mid @@ -305,25 +305,25 @@ for i in range(n): prevGreaterIndex[i] = stack[-1] if stack else -1 stack.append(i) print(prevGreaterIndex) +``` -# Mono Increasing Stack +Mono Increasing Stack Template +```py for i in range(n): while stack and nums[stack[-1]] > nums[i]: - curr = stack.pop() - if not stack: - ... - left, right = stack[-1], i - print(left, curr, right) + curr = stack.pop() # current index + left = stack[-1] # prev smallest index + right = i # next smallest index stack.append(i) +``` -# Mono Decreasing Stack +Mono Decreasing Stack Template +```py for i in range(n): while stack and nums[stack[-1]] < nums[i]: - curr = stack.pop() - if not stack: - ... - left, right = stack[-1], i - print(left, curr, right) + curr = stack.pop() # current index + left = stack[-1] # prev largest index + right = i # next largest index stack.append(i) ``` @@ -348,7 +348,6 @@ class monoQueue: Leetcode Problems - [20. Valid Parentheses](https://leetcode.com/problems/valid-parentheses/) -- [445. Add Two Numbers II](https://leetcode.com/problems/add-two-numbers-ii/) - [155. Min Stack](https://leetcode.com/problems/min-stack/) - [496. Next Greater Element I](https://leetcode.com/problems/next-greater-element-i/) - [503. Next Greater Element II](https://leetcode.com/problems/next-greater-element-ii/) From e99388a8c4b4ec429c04b0b43aaddaa3986e1270 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Tue, 27 Jul 2021 11:33:55 -0400 Subject: [PATCH 40/48] small changes for week02 --- Week02/binary-tree-traversal.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Week02/binary-tree-traversal.py b/Week02/binary-tree-traversal.py index 5c5faf466..d3a4be64b 100644 --- a/Week02/binary-tree-traversal.py +++ b/Week02/binary-tree-traversal.py @@ -78,7 +78,7 @@ def traverse(root): traverse(root) return res - def inorderTraversal2(self, root: TreeNode) -> List[int]: + def inorderTraversal2(self, root: BinaryTreeNode) -> List[int]: """ Solution #2: Iteratively """ @@ -129,7 +129,7 @@ def traverse(root): traverse(root) return res - def postorderTraversal2(self, root: TreeNode) -> List[int]: + def postorderTraversal2(self, root: BinaryTreeNode) -> List[int]: """ Solution #2: Iteratively """ From 1d1aee1632b3bf3215895a0dd2493233222ec59f Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Fri, 30 Jul 2021 11:49:49 -0400 Subject: [PATCH 41/48] change structure --- {Week01 => 01}/NOTE.md | 4 +- {Week01 => 01}/linked_list.py | 0 {Week01 => 01}/merge-sorted-array.py | 0 {Week01 => 01}/merge-two-sorted-lists.py | 0 {Week01 => 01}/move-zeroes.py | 0 {Week01 => 01}/plus-one.py | 0 {Week01 => 01}/remove-duplicates.py | 0 {Week01 => 01}/rotate-array.py | 0 {Week01 => 01}/two-sum.py | 0 {Week02 => 02}/NOTE.md | 61 +----- {Week02 => 02}/binary-tree-traversal.py | 0 {Week02 => 02}/binary_heap.py | 0 {Week02 => 02}/binary_search_tree.py | 0 {Week02 => 02}/binary_tree.py | 0 {Week02 => 02}/group_anagrams.py | 0 {Week02 => 02}/n-ary_tree_traversal.py | 0 {Week02 => 02}/valid_anagram.py | 0 {Week03 => 03}/NOTE.md | 114 ++++++++++- {Week03 => 03}/binary_tree.py | 0 {Week03 => 03}/combinations.py | 0 {Week03 => 03}/construct_binary_tree.py | 0 {Week03 => 03}/generate_parenthesis.py | 0 {Week03 => 03}/lowest_common_ancestor.py | 0 {Week03 => 03}/permutations.py | 0 {Week03 => 03}/subsets.py | 0 {Week04 => 04}/NOTE.md | 209 +++++++++------------ {Week04 => 04}/bfs_dfs.py | 0 {Week04 => 04}/lsland_problems.py | 0 {Week04 => 04}/search_2d_matrix.py | 0 {Week04 => 04}/word_ladder.py | 0 05/NOTE.md | 131 +++++++++++++ {Week08 => 05}/sort.py | 0 {Week06 => 06}/LCS.py | 0 {Week06 => 06}/NOTE.md | 20 +- {Week06 => 06}/buy-sells-stocks.py | 0 {Week06 => 06}/find-length.py | 0 {Week06 => 06}/house-robber.py | 0 {Week06 => 06}/triangle.py | 0 {Week06 => 06}/unique-paths.py | 0 {Week07 => 07}/NOTE.md | 83 +------- {Week07 => 07}/disjoint_set.py | 0 {Week07 => 07}/friend-circles.py | 0 {Week07 => 07}/minimum_genetic_mutation.py | 0 {Week07 => 07}/similarity.py | 0 {Week07 => 07}/sudoku.py | 0 {Week07 => 07}/trie.py | 0 {Week07 => 07}/word_ladder.py | 0 {Week08 => 08}/LRU_cache.py | 0 {Week08 => 08}/NOTE.md | 34 +--- {Week08 => 08}/bloom_filter.py | 0 {Week08 => 08}/hamming_weight.py | 0 {Week08 => 08}/power_of_two.py | 0 {Week08 => 08}/reverse_bits.py | 0 {Week09 => 09}/NOTE.md | 3 +- {Week09 => 09}/first-unique-character.py | 0 {Week09 => 09}/jewels-and-stones.py | 0 {Week09 => 09}/length-of-last-word.py | 0 {Week09 => 09}/longest-common-prefix.py | 0 {Week09 => 09}/string-to-integer-atoi.py | 0 {Week09 => 09}/to-lower-case.py | 0 README.md | 44 +++-- 61 files changed, 388 insertions(+), 315 deletions(-) rename {Week01 => 01}/NOTE.md (99%) rename {Week01 => 01}/linked_list.py (100%) rename {Week01 => 01}/merge-sorted-array.py (100%) rename {Week01 => 01}/merge-two-sorted-lists.py (100%) rename {Week01 => 01}/move-zeroes.py (100%) rename {Week01 => 01}/plus-one.py (100%) rename {Week01 => 01}/remove-duplicates.py (100%) rename {Week01 => 01}/rotate-array.py (100%) rename {Week01 => 01}/two-sum.py (100%) rename {Week02 => 02}/NOTE.md (89%) rename {Week02 => 02}/binary-tree-traversal.py (100%) rename {Week02 => 02}/binary_heap.py (100%) rename {Week02 => 02}/binary_search_tree.py (100%) rename {Week02 => 02}/binary_tree.py (100%) rename {Week02 => 02}/group_anagrams.py (100%) rename {Week02 => 02}/n-ary_tree_traversal.py (100%) rename {Week02 => 02}/valid_anagram.py (100%) rename {Week03 => 03}/NOTE.md (53%) rename {Week03 => 03}/binary_tree.py (100%) rename {Week03 => 03}/combinations.py (100%) rename {Week03 => 03}/construct_binary_tree.py (100%) rename {Week03 => 03}/generate_parenthesis.py (100%) rename {Week03 => 03}/lowest_common_ancestor.py (100%) rename {Week03 => 03}/permutations.py (100%) rename {Week03 => 03}/subsets.py (100%) rename {Week04 => 04}/NOTE.md (53%) rename {Week04 => 04}/bfs_dfs.py (100%) rename {Week04 => 04}/lsland_problems.py (100%) rename {Week04 => 04}/search_2d_matrix.py (100%) rename {Week04 => 04}/word_ladder.py (100%) create mode 100644 05/NOTE.md rename {Week08 => 05}/sort.py (100%) rename {Week06 => 06}/LCS.py (100%) rename {Week06 => 06}/NOTE.md (86%) rename {Week06 => 06}/buy-sells-stocks.py (100%) rename {Week06 => 06}/find-length.py (100%) rename {Week06 => 06}/house-robber.py (100%) rename {Week06 => 06}/triangle.py (100%) rename {Week06 => 06}/unique-paths.py (100%) rename {Week07 => 07}/NOTE.md (72%) rename {Week07 => 07}/disjoint_set.py (100%) rename {Week07 => 07}/friend-circles.py (100%) rename {Week07 => 07}/minimum_genetic_mutation.py (100%) rename {Week07 => 07}/similarity.py (100%) rename {Week07 => 07}/sudoku.py (100%) rename {Week07 => 07}/trie.py (100%) rename {Week07 => 07}/word_ladder.py (100%) rename {Week08 => 08}/LRU_cache.py (100%) rename {Week08 => 08}/NOTE.md (76%) rename {Week08 => 08}/bloom_filter.py (100%) rename {Week08 => 08}/hamming_weight.py (100%) rename {Week08 => 08}/power_of_two.py (100%) rename {Week08 => 08}/reverse_bits.py (100%) rename {Week09 => 09}/NOTE.md (99%) rename {Week09 => 09}/first-unique-character.py (100%) rename {Week09 => 09}/jewels-and-stones.py (100%) rename {Week09 => 09}/length-of-last-word.py (100%) rename {Week09 => 09}/longest-common-prefix.py (100%) rename {Week09 => 09}/string-to-integer-atoi.py (100%) rename {Week09 => 09}/to-lower-case.py (100%) diff --git a/Week01/NOTE.md b/01/NOTE.md similarity index 99% rename from Week01/NOTE.md rename to 01/NOTE.md index 4e1d0838d..50994745a 100644 --- a/Week01/NOTE.md +++ b/01/NOTE.md @@ -1,5 +1,4 @@ -Learning Notes Week 01 -====================== +# 01 Array ----- @@ -76,6 +75,7 @@ sum([1, 2, 3, 4, 5]) # 15 functools.reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) # calculates ((((1+2)+3)+4)+5) = 15 ``` +Common Patterns ```py # total combinations of two numbers (pairs) in an array (brute force) nums = [1,2,3,4] diff --git a/Week01/linked_list.py b/01/linked_list.py similarity index 100% rename from Week01/linked_list.py rename to 01/linked_list.py diff --git a/Week01/merge-sorted-array.py b/01/merge-sorted-array.py similarity index 100% rename from Week01/merge-sorted-array.py rename to 01/merge-sorted-array.py diff --git a/Week01/merge-two-sorted-lists.py b/01/merge-two-sorted-lists.py similarity index 100% rename from Week01/merge-two-sorted-lists.py rename to 01/merge-two-sorted-lists.py diff --git a/Week01/move-zeroes.py b/01/move-zeroes.py similarity index 100% rename from Week01/move-zeroes.py rename to 01/move-zeroes.py diff --git a/Week01/plus-one.py b/01/plus-one.py similarity index 100% rename from Week01/plus-one.py rename to 01/plus-one.py diff --git a/Week01/remove-duplicates.py b/01/remove-duplicates.py similarity index 100% rename from Week01/remove-duplicates.py rename to 01/remove-duplicates.py diff --git a/Week01/rotate-array.py b/01/rotate-array.py similarity index 100% rename from Week01/rotate-array.py rename to 01/rotate-array.py diff --git a/Week01/two-sum.py b/01/two-sum.py similarity index 100% rename from Week01/two-sum.py rename to 01/two-sum.py diff --git a/Week02/NOTE.md b/02/NOTE.md similarity index 89% rename from Week02/NOTE.md rename to 02/NOTE.md index eb1399057..421ab2e7c 100644 --- a/Week02/NOTE.md +++ b/02/NOTE.md @@ -1,5 +1,4 @@ -Learning Notes Week 02 -====================== +# 02 Hash Table ---------- @@ -46,8 +45,8 @@ Leetcode Problems - [242. Valid Anagram](https://leetcode.com/problems/valid-anagram/description/) - [49. Group Anagrams](https://leetcode.com/problems/group-anagrams/) -Binary Trees ------------- +Binary Tree +----------- > A binary tree is either empty, or a root node together with a left binary tree and a right binary tree. @@ -119,7 +118,7 @@ class BinaryTreeNode: Binary Tree Traversal -DFS Pre-order Traversal: `root -> left -> right` +Pre-order Traversal: `root -> left -> right` ```py def preorder(self, root): if root: @@ -128,7 +127,7 @@ def preorder(self, root): preorder(right) ``` -DFS In-order Traversal: `left -> root -> right` +In-order Traversal: `left -> root -> right` ```py def inorder(self, root): if root: @@ -137,7 +136,7 @@ def inorder(self, root): inorder(right) ``` -DFS Post-order Traversal: `left -> right -> root` +Post-order Traversal: `left -> right -> root` ```py def postorder(self, root): if root: @@ -146,7 +145,7 @@ def postorder(self, root): visit(root) ``` -BFS Level Order Traversal: `top -> bottom, left -> right` +Level Order Traversal: `top -> bottom, left -> right` ```py def levelorder(root): queue = collections.deque() @@ -170,6 +169,7 @@ LeetCode Problems - [429. N-ary Tree Level Order Traversal](https://leetcode.com/problems/n-ary-tree-level-order-traversal/) Binary Search Tree (BST) +------------------------ > A BST is a rooted binary tree whose internal nodes each store a val greater than all the vals in the node's left subtree and less than those in its right subtree. @@ -340,47 +340,4 @@ heapq.heappushpop(h, a) # pushes a on the heap and then pops and returns the sma heapq.heapreplace(h, e) # pops and returns smallest item, and adds new item; the heap size is unchanged heapq.nlargest(n, L) # Find the n largest elements in a dataset. heapq.nsmallest(n, L) # Find the n smallest elements in a dataset. -``` - -Graph ------ - -> A graph is a finite set of vertices and connected by a set of edges. - -``` -(1:M)---(0:E) - | \ | - | \ | - | \ | -(2:B)---(3:L) - \ - \ - (4:P) -``` - -Types of graphs: -- Undirected Graph: nodes are connected by edges that are all bidirectional. -- Directed Graph: nodes are connected by directed edges – they only go in one direction. - -Graph Adjacency List Representation -- The size of the array is equal to the number of nodes. -- A single index, array[i] represents the list of nodes adjacent to the ith node. -``` -0 -> 1 -> 3# -1 -> 0 -> 2 -> 3# -2 -> 1 -> 3 -> 4# -3 -> 0 -> 1 -> 2# -4 -> 2# -``` - -Graph Adjacency Matrix Representation -- An Adjacency Matrix is a 2D array of size V x V where V is the number of nodes in a graph. -- A slot matrix[i][j] = 1 indicates that there is an edge from node i to node j. - -| | 0 | 1 | 2 | 3 | 4 | -|-------|---|---|---|---|---| -| **0** | 0 | 1 | 0 | 1 | 0 | -| **1** | 1 | 0 | 1 | 1 | 0 | -| **2** | 0 | 1 | 0 | 1 | 1 | -| **3** | 1 | 1 | 1 | 0 | 0 | -| **4** | 0 | 0 | 1 | 0 | 0 | +``` \ No newline at end of file diff --git a/Week02/binary-tree-traversal.py b/02/binary-tree-traversal.py similarity index 100% rename from Week02/binary-tree-traversal.py rename to 02/binary-tree-traversal.py diff --git a/Week02/binary_heap.py b/02/binary_heap.py similarity index 100% rename from Week02/binary_heap.py rename to 02/binary_heap.py diff --git a/Week02/binary_search_tree.py b/02/binary_search_tree.py similarity index 100% rename from Week02/binary_search_tree.py rename to 02/binary_search_tree.py diff --git a/Week02/binary_tree.py b/02/binary_tree.py similarity index 100% rename from Week02/binary_tree.py rename to 02/binary_tree.py diff --git a/Week02/group_anagrams.py b/02/group_anagrams.py similarity index 100% rename from Week02/group_anagrams.py rename to 02/group_anagrams.py diff --git a/Week02/n-ary_tree_traversal.py b/02/n-ary_tree_traversal.py similarity index 100% rename from Week02/n-ary_tree_traversal.py rename to 02/n-ary_tree_traversal.py diff --git a/Week02/valid_anagram.py b/02/valid_anagram.py similarity index 100% rename from Week02/valid_anagram.py rename to 02/valid_anagram.py diff --git a/Week03/NOTE.md b/03/NOTE.md similarity index 53% rename from Week03/NOTE.md rename to 03/NOTE.md index 728d9fa0e..6c823621a 100644 --- a/Week03/NOTE.md +++ b/03/NOTE.md @@ -1,8 +1,45 @@ -Learning Notes Week 03 -====================== +# 03 -Recursion Template ------------------- +Recursion +--------- + +> A recursive function is defined in terms of base cases and recursive steps. + +- In a base case, we compute the result immediately given the inputs to the function call. +- In a recursive step, we compute the result with the help of one or more recursive calls to this same function, but with the inputs somehow reduced in size or complexity, closer to a base case. + +To calculate Product of **n**: +``` +n! = n x (n-1) x ... x 2 x 1 +``` + +Recurrence relation +``` +n! = { + 1 if n = 0 + (n-1)! x n if n > 0 +} +``` + +Iterative Implementation +```py +def factorial(n: int) -> int: + fact = 1 + for i in range(n): + fact = fact * i + } + return fact +``` + +Iterative Implementation +```py +def factorial(n: int) -> int: + if n = 0: + return 1 + return n * factorial(n-1) +``` + +Generic Recursive Template ```py def recursion(level, param1, param2, ...): @@ -20,8 +57,12 @@ def recursion(level, param1, param2, ...): # reverse the current level status if needed ``` -Divide & Conquer Template -------------------------- +Divide & Conquer +---------------- + +- Divide the problem into a number of subproblems that are smaller instances of the same problem. +- Conquer the subproblems by solving them recursively. If they are small enough, solve the subproblems as base cases. +- Combine the solutions to the subproblems into the solution for the original problem. ```py def divide_conquer(problem, param1, param2, ...): @@ -56,16 +97,19 @@ Leetcode Problems - [236. Lowest Common Ancestor of a Binary Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/) - [169. Majority Element](https://leetcode.com/problems/majority-element/) -Backtrack Template ------------------- +Backtrack +--------- + +> Backtracking can be defined as a general algorithmic technique that considers searching every possible combination in order to solve a computational problem. ```py result = [] -def backtrack(path = [], choices): +def backtrack(path): if end condition: result.add(path[:]) # param pass by reference return + # Get the choice list for choice in choices: # get rid of the illegal choices if exclusive condition: @@ -81,6 +125,53 @@ def backtrack(path = [], choices): - Sometimes we don't need to explicitly maintain the choice list, we derive it using other parameters (e.g. index) - Sometimes path can be a string instead of an array, and we use `path += 'choice'` and `path = path[:-1]` to make and remove choice +Backtrack DFS with Pruning +```py +# Generate Parenthesis +def backtrack(left = 0, right = 0, path = ''): + if left == n and right == n: + res.append(path) + return + + if right > left: # Pruning + return + + if left < n: + backtrack(left + 1, right, path + '(') + + if right < n: + backtrack(left, right + 1, path + ')') +``` + +Bidirectional BFS +```py +sourceQueue = collections.deque([source]) +targetQueue = collections.deque([target]) +visited = set([source]) +step = 0 + +while sourceQueue and targetQueue: + step += 1 + + # choose the smaller queue to spread + if len(sourceQueue) > len(targetQueue): + sourceQueue, targetQueue = targetQueue, sourceQueue + + for _ in range(len(sourceQueue)): + node = sourceQueue.popleft() + + for child in node.children: + # source and target meet + if child in targetQueue: + return step + 1 + + if not child in visited: + sourceQueue.append(child) + visited.add(child) + +return 0 # not found +``` + Leetcode Problems - [22. Generate Parentheses](https://leetcode.com/problems/generate-parentheses/) - [78. Subsets](https://leetcode.com/problems/subsets/) @@ -89,4 +180,7 @@ Leetcode Problems - [77. Combinations](https://leetcode.com/problems/combinations/) - [17. Letter Combinations of a Phone Number](https://leetcode.com/problems/letter-combinations-of-a-phone-number/) - [51. N-Queens](https://leetcode.com/problems/n-queens/) -- [37. Sudoku Solver](https://leetcode.com/problems/sudoku-solver/) \ No newline at end of file +- [36. Valid Sudoku](https://leetcode.com/problems/valid-sudoku/) +- [37. Sudoku Solver](https://leetcode.com/problems/sudoku-solver/) +- [1091. Shortest Path in Binary Matrix](https://leetcode.com/problems/shortest-path-in-binary-matrix/) +- [773. Sliding Puzzle](https://leetcode.com/problems/sliding-puzzle/) \ No newline at end of file diff --git a/Week03/binary_tree.py b/03/binary_tree.py similarity index 100% rename from Week03/binary_tree.py rename to 03/binary_tree.py diff --git a/Week03/combinations.py b/03/combinations.py similarity index 100% rename from Week03/combinations.py rename to 03/combinations.py diff --git a/Week03/construct_binary_tree.py b/03/construct_binary_tree.py similarity index 100% rename from Week03/construct_binary_tree.py rename to 03/construct_binary_tree.py diff --git a/Week03/generate_parenthesis.py b/03/generate_parenthesis.py similarity index 100% rename from Week03/generate_parenthesis.py rename to 03/generate_parenthesis.py diff --git a/Week03/lowest_common_ancestor.py b/03/lowest_common_ancestor.py similarity index 100% rename from Week03/lowest_common_ancestor.py rename to 03/lowest_common_ancestor.py diff --git a/Week03/permutations.py b/03/permutations.py similarity index 100% rename from Week03/permutations.py rename to 03/permutations.py diff --git a/Week03/subsets.py b/03/subsets.py similarity index 100% rename from Week03/subsets.py rename to 03/subsets.py diff --git a/Week04/NOTE.md b/04/NOTE.md similarity index 53% rename from Week04/NOTE.md rename to 04/NOTE.md index f302cdd4f..a1ab93349 100644 --- a/Week04/NOTE.md +++ b/04/NOTE.md @@ -1,8 +1,73 @@ -Learning Notes Week 04 -====================== +# 04 -DFS Template ------------- +Graph +----- + +> A graph is a finite set of vertices and connected by a set of edges. + +``` +(1:M)---(0:E) + | \ | + | \ | + | \ | +(2:B)---(3:L) + \ + \ + (4:P) +``` + +Types of graphs: +- Undirected Graph: nodes are connected by edges that are all bidirectional. +- Directed Graph: nodes are connected by directed edges – they only go in one direction. + +Graph Adjacency List Representation +- The size of the array is equal to the number of nodes. +- A single index, array[i] represents the list of nodes adjacent to the ith node. +``` +0 -> 1 -> 3# +1 -> 0 -> 2 -> 3# +2 -> 1 -> 3 -> 4# +3 -> 0 -> 1 -> 2# +4 -> 2# +``` + +Graph Adjacency Matrix Representation +- An Adjacency Matrix is a 2D array of size V x V where V is the number of nodes in a graph. +- A slot matrix[i][j] = 1 indicates that there is an edge from node i to node j. + +| | 0 | 1 | 2 | 3 | 4 | +|-------|---|---|---|---|---| +| **0** | 0 | 1 | 0 | 1 | 0 | +| **1** | 1 | 0 | 1 | 1 | 0 | +| **2** | 0 | 1 | 0 | 1 | 1 | +| **3** | 1 | 1 | 1 | 0 | 0 | +| **4** | 0 | 0 | 1 | 0 | 0 | + +Graph Python Implementation +```py +# A -> B +# A -> C +# B -> C +# B -> D +# C -> D +# D -> C +# E -> F +# F -> C + +graph = { + 'A': ['B', 'C'], + 'B': ['C', 'D'], + 'C': ['D'], + 'D': ['C'], + 'E': ['F'], + 'F': ['C'] +} +``` + +Depth-first Search (DFS) +------------------------ + +> Depth-first search is an algorithm for traversing or searching tree or graph data structures. The algorithm starts at the root node and explores as far as possible along each branch before backtracking. ```py visited = set() @@ -27,9 +92,6 @@ def dfs(node): dfs(child, visited) ``` -DFS Application ---------------- - Island Problem ```py def island(self, grid: List[List[int]]): @@ -58,18 +120,16 @@ def island(self, grid: List[List[int]]): return 0 <= r < m and 0 <= c < n ``` -Backtrack - -See [Week03 NOTE](../Week03/NOTE.md). - Leetcode Problems - [200. Number of Islands](https://leetcode.com/problems/number-of-islands/) - [695. Max Area of Island](https://leetcode.com/problems/max-area-of-island/) - [463. Island Perimeter](https://leetcode.com/problems/island-perimeter/) - [827. Making A Large Island](https://leetcode.com/problems/making-a-large-island/) -BFS Template ------------- +Breadth-first Search (BFS) +-------------------------- + +> Breadth-first search is an algorithm for searching a tree data structure for a node that satisfies a given property. It starts at the tree root and explores all nodes at the present depth prior to moving on to the nodes at the next depth level. ```py def bfs(root): @@ -96,9 +156,6 @@ def bfs(root): queue.append(child) ``` -BFS Application ---------------- - Level Order ```py def bfs(root): @@ -127,7 +184,7 @@ def bfs(root): return res ``` -Shorted Path +Shortest Path ```py def bfs(start, target): # any two nodes, doesn't have to start from the root step = 0 @@ -165,109 +222,21 @@ Leetcode Problems - [515. Find Largest Value in Each Tree Row](https://leetcode.com/problems/find-largest-value-in-each-tree-row/) - [529. Minesweeper](https://leetcode.com/problems/minesweeper/) -Greedy Algorithm ----------------- - -> A greedy algorithm is any algorithm that follows the problem-solving heuristic of making the locally optimal choice at each stage. - -Leetcode Problems -- [860. Lemonade Change](https://leetcode.com/problems/lemonade-change/) -- [455. Assign Cookies](https://leetcode.com/problems/assign-cookies/) -- [874. Walking Robot Simulation](https://leetcode.com/problems/walking-robot-simulation) -- [322. Coin Change](https://leetcode.com/problems/coin-change/) -- [55. Jump Game](https://leetcode.com/problems/jump-game/) -- [45. Jump Game II](https://leetcode.com/problems/jump-game-ii/) - -Binary Search -------------- +A-Star (A*) Search +------------------ -- Monotonically increasing/decreasing -- Bounded (have upper and lower bound) -- Index accessible +> A-star is a graph traversal and path search algorithm, which is often used in computer science due to its completeness, optimality, and optimal efficiency. One major practical drawback is its O space complexity, as it stores all generated nodes in memory. -[left, right] ```py -def binary_search(nums, target) - left, right = 0, len(nums) - 1 - while left <= right: - mid = (left + right) // 2 - if nums[mid] < target: - left = mid + 1 - elif nums[mid] > target: - right = mid - 1 - elif nums[mid] == target: - return mid - return -1 -``` - -[left, right) -```py -def left_bound(nums, target): - left, right = 0, len(nums) - 1 - while left < right: - mid = (left + right) // 2 - if nums[mid] < target: - left = mid + 1 - elif nums[mid] > target: - right = mid - 1 - elif nums[mid] == target: - # don't return, lock down the left boundary - right = mid - 1 - - # check if left is out of boundary - if left >= nums.length or nums[left] != target - return -1 - return left -} -``` - -(left, right] -```py -def right_bound(nums, target): - left, right = 0, len(nums) - 1 - while left < right: - mid = left + (right - left) // 2 - if nums[mid] < target: - left = mid + 1 - elif nums[mid] > target: - right = mid - 1 - elif nums[mid] == target: - # don't return, lock down the right boundary - left = mid + 1 - - # check if right is out of boundary - if right < 0 or nums[right] != target - return -1 - return right -} -``` - -Target Function g(m) -```py -def binary_search(l, r): - """ - Returns the smallest number m in range [l, r] such that g(m) is true. - Returns r+1 if not found. - - Time Complexity: O(log(r - l) * (f(m) + g(m))) - Space Complexity: O(1) - """ - while l <= r: - m = l + (r - l) // 2 - if f(m): # optional: if somehow we can determine m is the answer, return it - return m - if g(m): - r = m - 1 # new range [l, m-1] - else: - l = m + 1 # new range [m+1, r] - return l # or not found -``` - -Leetcode Problems -- [704. Binary Search](https://leetcode.com/problems/binary-search/) -- [167. Two Sum II - Input array is sorted](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/) -- [33. Search in Rotated Sorted Array](https://leetcode.com/problems/search-in-rotated-sorted-array/) -- [34. Find First and Last Position of Element in Sorted Array](https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/) -- [69. Sqrt(x)](https://leetcode.com/problems/sqrtx/) -- [74. Search a 2D Matrix](https://leetcode.com/problems/search-a-2d-matrix/) -- [367. Valid Perfect Square](https://leetcode.com/problems/valid-perfect-square/) \ No newline at end of file +def AstarSearch(graph, start, end): + pq = collections.priority_queue() # priority —> heuristic function + pq.append([start]) + visited.add(start) + while pq: + node = pq.pop() # can we add more intelligence 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) +``` \ No newline at end of file diff --git a/Week04/bfs_dfs.py b/04/bfs_dfs.py similarity index 100% rename from Week04/bfs_dfs.py rename to 04/bfs_dfs.py diff --git a/Week04/lsland_problems.py b/04/lsland_problems.py similarity index 100% rename from Week04/lsland_problems.py rename to 04/lsland_problems.py diff --git a/Week04/search_2d_matrix.py b/04/search_2d_matrix.py similarity index 100% rename from Week04/search_2d_matrix.py rename to 04/search_2d_matrix.py diff --git a/Week04/word_ladder.py b/04/word_ladder.py similarity index 100% rename from Week04/word_ladder.py rename to 04/word_ladder.py diff --git a/05/NOTE.md b/05/NOTE.md new file mode 100644 index 000000000..f63f447ce --- /dev/null +++ b/05/NOTE.md @@ -0,0 +1,131 @@ +# 05 + +Binary Search +------------- + +- Monotonically increasing/decreasing +- Bounded (have upper and lower bound) +- Index accessible + +[left, right] +```py +def binary_search(nums, target) + left, right = 0, len(nums) - 1 + while left <= right: + mid = (left + right) // 2 + if nums[mid] < target: + left = mid + 1 + elif nums[mid] > target: + right = mid - 1 + elif nums[mid] == target: + return mid + return -1 +``` + +[left, right) +```py +def left_bound(nums, target): + left, right = 0, len(nums) - 1 + while left < right: + mid = (left + right) // 2 + if nums[mid] < target: + left = mid + 1 + elif nums[mid] > target: + right = mid - 1 + elif nums[mid] == target: + # don't return, lock down the left boundary + right = mid - 1 + + # check if left is out of boundary + if left >= nums.length or nums[left] != target + return -1 + return left +} +``` + +(left, right] +```py +def right_bound(nums, target): + left, right = 0, len(nums) - 1 + while left < right: + mid = left + (right - left) // 2 + if nums[mid] < target: + left = mid + 1 + elif nums[mid] > target: + right = mid - 1 + elif nums[mid] == target: + # don't return, lock down the right boundary + left = mid + 1 + + # check if right is out of boundary + if right < 0 or nums[right] != target + return -1 + return right +} +``` + +Target Function g(m) +```py +def binary_search(l, r): + """ + Returns the smallest number m in range [l, r] such that g(m) is true. + Returns r+1 if not found. + + Time Complexity: O(log(r - l) * (f(m) + g(m))) + Space Complexity: O(1) + """ + while l <= r: + m = l + (r - l) // 2 + if f(m): # optional: if somehow we can determine m is the answer, return it + return m + if g(m): + r = m - 1 # new range [l, m-1] + else: + l = m + 1 # new range [m+1, r] + return l # or not found +``` + +Leetcode Problems +- [704. Binary Search](https://leetcode.com/problems/binary-search/) +- [167. Two Sum II - Input array is sorted](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/) +- [33. Search in Rotated Sorted Array](https://leetcode.com/problems/search-in-rotated-sorted-array/) +- [34. Find First and Last Position of Element in Sorted Array](https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/) +- [69. Sqrt(x)](https://leetcode.com/problems/sqrtx/) +- [74. Search a 2D Matrix](https://leetcode.com/problems/search-a-2d-matrix/) +- [367. Valid Perfect Square](https://leetcode.com/problems/valid-perfect-square/) + +Sorting +------- + +Simple Sorts (Comparison) +- Bubble Sort +- Selection Sort +- Insertion Sort + +| Sorting | Average | Worst | Best | Space | Stability | +| :------------- | :-------- | :-------- | :-------- | :-------- | :-------- | +| Bubble Sort | O(N^2) | O(N^2) | O(N) | O(1) | YES | +| Selection Sort | O(N^2) | O(N^2) | O(N^2) | O(1) | NO | +| Insertion Sort | O(N^2) | O(N^2) | O(N) | O(1) | YES | + +Efficient Sorts (Comparison) +- Merge Sort +- Quick Sort +- Heap Sort + +| Sorting | Average | Worst | Best | Space | Stability | +| :------------- | :-------- | :-------- | :-------- | :-------- | :-------- | +| Merge Sort | O(N*logN) | O(N*logN) | O(N*logN) | O(N) | YES | +| Quick Sort | O(N*logN) | O(N^2) | O(N*logN) | O(N*logN) | NO | +| Heap Sort | O(N*logN) | O(N*logN) | O(N*logN) | O(1) | YES | + +Distribution Sorts (Non-comparison) +- Counting Sort +- Bucket Sort +- Radix Sort + +| Sorting | Average | Worst | Best | Space | Stability | +| :------------- | :-------- | :-------- | :-------- | :-------- | :-------- | +| Counting Sort | O(N+k) | O(N+k) | O(N+k) | O(N+k) | YES | +| Bucket Sort | O(N+k) | O(N^2) | O(N) | O(N+k) | YES | +| Radix Sort | O(N*k) | O(N*k) | O(N*k) | O(N+k) | YES | \ No newline at end of file diff --git a/Week08/sort.py b/05/sort.py similarity index 100% rename from Week08/sort.py rename to 05/sort.py diff --git a/Week06/LCS.py b/06/LCS.py similarity index 100% rename from Week06/LCS.py rename to 06/LCS.py diff --git a/Week06/NOTE.md b/06/NOTE.md similarity index 86% rename from Week06/NOTE.md rename to 06/NOTE.md index 1f39ada64..d3c2c47dc 100644 --- a/Week06/NOTE.md +++ b/06/NOTE.md @@ -1,11 +1,10 @@ -Learning Notes Week 06 -====================== +# NOTE 06 Dynamic Programming ------------------- -> **Optimal Substructure**: the solution to a given optimization problem can be obtained by the combination of optimal solutions to its sub-problems. Such optimal substructures are usually described by means of "_recursion_". +- **Optimal Substructure**: the solution to a given optimization problem can be obtained by the combination of optimal solutions to its sub-problems. Such optimal substructures are usually described by means of "_recursion_". -> **Overlapping Subproblems**: the space of sub-problems must be small, that is, any recursive algorithm solving the problem should solve the same sub-problems over and over, rather than generating new sub-problems. If a problem can be solved by combining optimal solutions to non-overlapping sub-problems, the strategy is called _"divide and conquer"_ instead. +- **Overlapping Subproblems**: the space of sub-problems must be small, that is, any recursive algorithm solving the problem should solve the same sub-problems over and over, rather than generating new sub-problems. If a problem can be solved by combining optimal solutions to non-overlapping sub-problems, the strategy is called _"divide and conquer"_ instead. This can be achieved in either of two ways: 1. **Top-down**: Recursion + Memo @@ -131,3 +130,16 @@ Buy and Sell Stocks - [188. Best Time to Buy and Sell Stock IV](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/) - [309. Best Time to Buy and Sell Stock with Cooldown](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/) - [714. Best Time to Buy and Sell Stock with Transaction Fee](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/) + +Greedy Algorithm +---------------- + +> A greedy algorithm is any algorithm that follows the problem-solving heuristic of making the locally optimal choice at each stage. + +Leetcode Problems +- [860. Lemonade Change](https://leetcode.com/problems/lemonade-change/) +- [455. Assign Cookies](https://leetcode.com/problems/assign-cookies/) +- [874. Walking Robot Simulation](https://leetcode.com/problems/walking-robot-simulation) +- [322. Coin Change](https://leetcode.com/problems/coin-change/) +- [55. Jump Game](https://leetcode.com/problems/jump-game/) +- [45. Jump Game II](https://leetcode.com/problems/jump-game-ii/) \ No newline at end of file diff --git a/Week06/buy-sells-stocks.py b/06/buy-sells-stocks.py similarity index 100% rename from Week06/buy-sells-stocks.py rename to 06/buy-sells-stocks.py diff --git a/Week06/find-length.py b/06/find-length.py similarity index 100% rename from Week06/find-length.py rename to 06/find-length.py diff --git a/Week06/house-robber.py b/06/house-robber.py similarity index 100% rename from Week06/house-robber.py rename to 06/house-robber.py diff --git a/Week06/triangle.py b/06/triangle.py similarity index 100% rename from Week06/triangle.py rename to 06/triangle.py diff --git a/Week06/unique-paths.py b/06/unique-paths.py similarity index 100% rename from Week06/unique-paths.py rename to 06/unique-paths.py diff --git a/Week07/NOTE.md b/07/NOTE.md similarity index 72% rename from Week07/NOTE.md rename to 07/NOTE.md index 7d800f246..3f6bac8a3 100644 --- a/Week07/NOTE.md +++ b/07/NOTE.md @@ -1,5 +1,4 @@ -Learning Notes Week 07 -====================== +# 07 Trie ---- @@ -39,7 +38,7 @@ Leetcode Problems - [212. Word Search II](https://leetcode.com/problems/word-search-ii/) Disjoint-set (Union-find set) ------------- +----------------------------- > A disjoint-set data structure is a data structure that tracks a set of elements partitioned into a number of disjoint (non-overlapping) subsets. It provides near-constant-time operations to add new sets, to merge existing sets, and to determine whether elements are in the same set. ```py @@ -60,82 +59,8 @@ Leetcode Problems - [130. Surrounded Regions](https://leetcode.com/problems/surrounded-regions/) - [200. Number of Islands](https://leetcode.com/problems/number-of-islands/) -Advanced Search ---------------- - -Backtrack DFS with Pruning -```py -# Generate Parenthesis -def dfs(left = 0, right = 0, path = ''): - if left == n and right == n: - res.append(path) - return - - if right > left: # Pruning - return - - if left < n: - dfs(left + 1, right, path + '(') - - if right < n: - dfs(left, right + 1, path + ')') -``` - -Bidirectional BFS -```py -sourceQueue = collections.deque([source]) -targetQueue = collections.deque([target]) -visited = set([source]) -step = 0 - -while sourceQueue and targetQueue: - step += 1 - - # choose the smaller queue to spread - if len(sourceQueue) > len(targetQueue): - sourceQueue, targetQueue = targetQueue, sourceQueue - - for _ in range(len(sourceQueue)): - node = sourceQueue.popleft() - - for child in node.children: - # source and target meet - if child in targetQueue: - return step + 1 - - if not child in visited: - sourceQueue.append(child) - visited.add(child) - -return 0 # not found -``` - -A-Star (A*) Search -```py -def AstarSearch(graph, start, end): - pq = collections.priority_queue() # priority —> heuristic function - pq.append([start]) - visited.add(start) - while pq: - node = pq.pop() # can we add more intelligence 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) -``` - -Leetcode Problems -- [70. Climbing Stairs](https://leetcode.com/problems/climbing-stairs/) -- [22. Generate Parentheses](https://leetcode.com/problems/generate-parentheses/) -- [51. N-Queens](https://leetcode.com/problems/n-queens/) -- [36. Valid Sudoku](https://leetcode.com/problems/valid-sudoku/) -- [37. Sudoku Solver](https://leetcode.com/problems/sudoku-solver/) -- [1091. Shortest Path in Binary Matrix](https://leetcode.com/problems/shortest-path-in-binary-matrix/) -- [773. Sliding Puzzle](https://leetcode.com/problems/sliding-puzzle/) - -Self-balancing Binary Search Tree --------------- +Self-balancing BST +------------------ > A self-balancing (or height-balanced) BST is any node-based BST that automatically keeps its height (maximal number of levels below the root) small in the face of arbitrary item insertions and deletions. diff --git a/Week07/disjoint_set.py b/07/disjoint_set.py similarity index 100% rename from Week07/disjoint_set.py rename to 07/disjoint_set.py diff --git a/Week07/friend-circles.py b/07/friend-circles.py similarity index 100% rename from Week07/friend-circles.py rename to 07/friend-circles.py diff --git a/Week07/minimum_genetic_mutation.py b/07/minimum_genetic_mutation.py similarity index 100% rename from Week07/minimum_genetic_mutation.py rename to 07/minimum_genetic_mutation.py diff --git a/Week07/similarity.py b/07/similarity.py similarity index 100% rename from Week07/similarity.py rename to 07/similarity.py diff --git a/Week07/sudoku.py b/07/sudoku.py similarity index 100% rename from Week07/sudoku.py rename to 07/sudoku.py diff --git a/Week07/trie.py b/07/trie.py similarity index 100% rename from Week07/trie.py rename to 07/trie.py diff --git a/Week07/word_ladder.py b/07/word_ladder.py similarity index 100% rename from Week07/word_ladder.py rename to 07/word_ladder.py diff --git a/Week08/LRU_cache.py b/08/LRU_cache.py similarity index 100% rename from Week08/LRU_cache.py rename to 08/LRU_cache.py diff --git a/Week08/NOTE.md b/08/NOTE.md similarity index 76% rename from Week08/NOTE.md rename to 08/NOTE.md index 5246b9737..eee568dd6 100644 --- a/Week08/NOTE.md +++ b/08/NOTE.md @@ -1,5 +1,4 @@ -Learning Notes Week 08 -====================== +# 08 Primitives ---------- @@ -123,34 +122,3 @@ Properties Leetcode Problems - [146. LRU Cache](https://leetcode.com/problems/lru-cache/) - -Sorting ----------- - -Sorting Algorithms -- Comparison Sorts - - Simple Sorts - - Bubble Sort - - Selection Sort - - Insertion Sort - - Efficient Sorts - - Merge Sort - - Quick Sort - - Heap Sort -- Non-comparison Sorts - - Distribution Sorts - - Counting Sort - - Bucket Sort - - Radix Sort - -| Sorting | Average | Worst | Best | Space | Stability | -| :------------- | :-------- | :-------- | :-------- | :-------- | :-------- | -| Bubble Sort | O(N^2) | O(N^2) | O(N) | O(1) | YES | -| Selection Sort | O(N^2) | O(N^2) | O(N^2) | O(1) | NO | -| Insertion Sort | O(N^2) | O(N^2) | O(N) | O(1) | YES | -| Merge Sort | O(N*logN) | O(N*logN) | O(N*logN) | O(N) | YES | -| Quick Sort | O(N*logN) | O(N^2) | O(N*logN) | O(N*logN) | NO | -| Heap Sort | O(N*logN) | O(N*logN) | O(N*logN) | O(1) | YES | -| Counting Sort | O(N+k) | O(N+k) | O(N+k) | O(N+k) | YES | -| Bucket Sort | O(N+k) | O(N^2) | O(N) | O(N+k) | YES | -| Radix Sort | O(N*k) | O(N*k) | O(N*k) | O(N+k) | YES | \ No newline at end of file diff --git a/Week08/bloom_filter.py b/08/bloom_filter.py similarity index 100% rename from Week08/bloom_filter.py rename to 08/bloom_filter.py diff --git a/Week08/hamming_weight.py b/08/hamming_weight.py similarity index 100% rename from Week08/hamming_weight.py rename to 08/hamming_weight.py diff --git a/Week08/power_of_two.py b/08/power_of_two.py similarity index 100% rename from Week08/power_of_two.py rename to 08/power_of_two.py diff --git a/Week08/reverse_bits.py b/08/reverse_bits.py similarity index 100% rename from Week08/reverse_bits.py rename to 08/reverse_bits.py diff --git a/Week09/NOTE.md b/09/NOTE.md similarity index 99% rename from Week09/NOTE.md rename to 09/NOTE.md index 7c2865c4b..d1ac8c314 100644 --- a/Week09/NOTE.md +++ b/09/NOTE.md @@ -1,5 +1,4 @@ -Learning Notes Week 09 -====================== +# 09 String ------ diff --git a/Week09/first-unique-character.py b/09/first-unique-character.py similarity index 100% rename from Week09/first-unique-character.py rename to 09/first-unique-character.py diff --git a/Week09/jewels-and-stones.py b/09/jewels-and-stones.py similarity index 100% rename from Week09/jewels-and-stones.py rename to 09/jewels-and-stones.py diff --git a/Week09/length-of-last-word.py b/09/length-of-last-word.py similarity index 100% rename from Week09/length-of-last-word.py rename to 09/length-of-last-word.py diff --git a/Week09/longest-common-prefix.py b/09/longest-common-prefix.py similarity index 100% rename from Week09/longest-common-prefix.py rename to 09/longest-common-prefix.py diff --git a/Week09/string-to-integer-atoi.py b/09/string-to-integer-atoi.py similarity index 100% rename from Week09/string-to-integer-atoi.py rename to 09/string-to-integer-atoi.py diff --git a/Week09/to-lower-case.py b/09/to-lower-case.py similarity index 100% rename from Week09/to-lower-case.py rename to 09/to-lower-case.py diff --git a/README.md b/README.md index c94798952..6f58b0b2c 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,46 @@ -Data Structure and Algorithms +# Data Structure and Algorithm Notes + +[NOTE 01](01/NOTE.md) - Array -- LinkedList -- Stacks and Queues +- Linked List +- Stack and Queue - Skip List + +[NOTE 02](02/NOTE.md) - Hash Table -- Binary Trees -- Binary Search Trees (BST) +- Binary Tree +- Binary Search Tree (BST) - Heap -- Graph + +[NOTE 03](03/NOTE.md) - Recursion -- Divide & Conquer +- Divide and Conquer - Backtrack -- DFS -- BFS -- Greedy + +[NOTE 04](04/NOTE.md) +- Graph +- Depth-first search (DFS) +- Breadth-first search (BFS) +- A-Star Search + +[NOTE 05](05/NOTE.md) - Binary Search +- Sorting + +[NOTE 06](06/NOTE.md) - Dynamic Programming +- Greedy + +[NOTE 07](07/NOTE.md) - Trie - Disjoint-set -- Advanced Search -- Self-balancing BST (AVL, Red-Black, B-tree, B+tree) +- Self-balancing BST (AVL tree, Red-Black tree, B- tree, B+ tree) + +[NOTE 08](08/NOTE.md) - Primitives - Bloom Filter - LRU Cache -- Sorting (Bubble, Selection, Insertion, Merge, Quick, Heap, Couting, Bucket, Radix) + +[NOTE 09](09/NOTE.md) - String - String-searching algorithms (Naive, Rabin-Karp, KMP, Boyer-Moore) \ No newline at end of file From 9acf433fe7082d8a1a0224e3d789f5cbd5c14ceb Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 1 Aug 2021 23:46:50 -0400 Subject: [PATCH 42/48] more clear --- 02/{binary_heap.py => heap.py} | 6 +-- 03/NOTE.md | 59 ++------------------ 04/NOTE.md | 58 ++++++++++++++------ 04/graph.py | 51 ++++++++++++++++++ {07 => 04}/minimum_genetic_mutation.py | 0 {07 => 04}/sudoku.py | 0 05/NOTE.md | 74 +++++++++++++------------- 07/word_ladder.py | 52 ------------------ 8 files changed, 138 insertions(+), 162 deletions(-) rename 02/{binary_heap.py => heap.py} (98%) create mode 100644 04/graph.py rename {07 => 04}/minimum_genetic_mutation.py (100%) rename {07 => 04}/sudoku.py (100%) delete mode 100644 07/word_ladder.py diff --git a/02/binary_heap.py b/02/heap.py similarity index 98% rename from 02/binary_heap.py rename to 02/heap.py index 21b4cee2c..ccca6f1c6 100644 --- a/02/binary_heap.py +++ b/02/heap.py @@ -1,7 +1,7 @@ """ -My Implementation of Max Heap +Max Heap Data Structure """ -class BinaryHeap: +class MaxHeap: def __init__(self, capacity = 10): self.heapsize = 0 self.heap = [-1] * capacity @@ -104,7 +104,7 @@ def printheap(self): line = line[:-1] + '\n' print(line) -max_heap = BinaryHeap(10) +max_heap = MaxHeap(10) max_heap.insert(10) max_heap.insert(4) max_heap.insert(9) diff --git a/03/NOTE.md b/03/NOTE.md index 6c823621a..f5f06fef9 100644 --- a/03/NOTE.md +++ b/03/NOTE.md @@ -40,7 +40,6 @@ def factorial(n: int) -> int: ``` Generic Recursive Template - ```py def recursion(level, param1, param2, ...): # recursion terminator @@ -109,6 +108,9 @@ def backtrack(path): result.add(path[:]) # param pass by reference return + if some condition: # Pruning if necessary + return + # Get the choice list for choice in choices: # get rid of the illegal choices @@ -122,56 +124,9 @@ def backtrack(path): - Time complexity for backtrack algorithm is at least O(N!) - Backtrack is a decision tree, updating the result is actually a preorder and/or postorder recursion (DFS) -- Sometimes we don't need to explicitly maintain the choice list, we derive it using other parameters (e.g. index) +- Sometimes we don't need to explicitly maintain the choice list, we **derive** it using other parameters (e.g. index) - Sometimes path can be a string instead of an array, and we use `path += 'choice'` and `path = path[:-1]` to make and remove choice -Backtrack DFS with Pruning -```py -# Generate Parenthesis -def backtrack(left = 0, right = 0, path = ''): - if left == n and right == n: - res.append(path) - return - - if right > left: # Pruning - return - - if left < n: - backtrack(left + 1, right, path + '(') - - if right < n: - backtrack(left, right + 1, path + ')') -``` - -Bidirectional BFS -```py -sourceQueue = collections.deque([source]) -targetQueue = collections.deque([target]) -visited = set([source]) -step = 0 - -while sourceQueue and targetQueue: - step += 1 - - # choose the smaller queue to spread - if len(sourceQueue) > len(targetQueue): - sourceQueue, targetQueue = targetQueue, sourceQueue - - for _ in range(len(sourceQueue)): - node = sourceQueue.popleft() - - for child in node.children: - # source and target meet - if child in targetQueue: - return step + 1 - - if not child in visited: - sourceQueue.append(child) - visited.add(child) - -return 0 # not found -``` - Leetcode Problems - [22. Generate Parentheses](https://leetcode.com/problems/generate-parentheses/) - [78. Subsets](https://leetcode.com/problems/subsets/) @@ -179,8 +134,4 @@ Leetcode Problems - [47. Permutations II](https://leetcode.com/problems/permutations-ii/) - [77. Combinations](https://leetcode.com/problems/combinations/) - [17. Letter Combinations of a Phone Number](https://leetcode.com/problems/letter-combinations-of-a-phone-number/) -- [51. N-Queens](https://leetcode.com/problems/n-queens/) -- [36. Valid Sudoku](https://leetcode.com/problems/valid-sudoku/) -- [37. Sudoku Solver](https://leetcode.com/problems/sudoku-solver/) -- [1091. Shortest Path in Binary Matrix](https://leetcode.com/problems/shortest-path-in-binary-matrix/) -- [773. Sliding Puzzle](https://leetcode.com/problems/sliding-puzzle/) \ No newline at end of file +- [51. N-Queens](https://leetcode.com/problems/n-queens/) \ No newline at end of file diff --git a/04/NOTE.md b/04/NOTE.md index a1ab93349..a923e64e3 100644 --- a/04/NOTE.md +++ b/04/NOTE.md @@ -45,22 +45,13 @@ Graph Adjacency Matrix Representation Graph Python Implementation ```py -# A -> B -# A -> C -# B -> C -# B -> D -# C -> D -# D -> C -# E -> F -# F -> C - graph = { - 'A': ['B', 'C'], - 'B': ['C', 'D'], - 'C': ['D'], - 'D': ['C'], - 'E': ['F'], - 'F': ['C'] + 'A': ['B', 'C'], # A -> B, A -> C + 'B': ['C', 'D'], # B -> C, B -> D + 'C': ['D'], # C -> D + 'D': ['C'], # D -> C + 'E': ['F'], # E -> F + 'F': ['C'] # F -> C } ``` @@ -125,6 +116,8 @@ Leetcode Problems - [695. Max Area of Island](https://leetcode.com/problems/max-area-of-island/) - [463. Island Perimeter](https://leetcode.com/problems/island-perimeter/) - [827. Making A Large Island](https://leetcode.com/problems/making-a-large-island/) +- [36. Valid Sudoku](https://leetcode.com/problems/valid-sudoku/) +- [37. Sudoku Solver](https://leetcode.com/problems/sudoku-solver/) Breadth-first Search (BFS) -------------------------- @@ -212,6 +205,35 @@ def bfs(start, target): # any two nodes, doesn't have to start from the root return 0 ``` +Bidirectional BFS +```py +sourceQueue = collections.deque([source]) +targetQueue = collections.deque([target]) +visited = set([source]) +step = 0 + +while sourceQueue and targetQueue: + step += 1 + + # choose the smaller queue to spread + if len(sourceQueue) > len(targetQueue): + sourceQueue, targetQueue = targetQueue, sourceQueue + + for _ in range(len(sourceQueue)): + node = sourceQueue.popleft() + + for child in node.children: + # source and target meet + if child in targetQueue: + return step + 1 + + if not child in visited: + sourceQueue.append(child) + visited.add(child) + +return 0 # not found +``` + Leetcode Problems - [22. Generate Parentheses](https://leetcode.com/problems/generate-parentheses/) - [111. Minimum Depth of Binary Tree](https://leetcode.com/problems/minimum-depth-of-binary-tree/) @@ -221,6 +243,7 @@ Leetcode Problems - [433. Minimum Genetic Mutation](https://leetcode.com/problems/minimum-genetic-mutation/) - [515. Find Largest Value in Each Tree Row](https://leetcode.com/problems/find-largest-value-in-each-tree-row/) - [529. Minesweeper](https://leetcode.com/problems/minesweeper/) +- [773. Sliding Puzzle](https://leetcode.com/problems/sliding-puzzle/) A-Star (A*) Search ------------------ @@ -239,4 +262,7 @@ def AstarSearch(graph, start, end): nodes = generate_related_nodes(node) unvisited = [node for node in nodes if node not in visited] pq.push(unvisited) -``` \ No newline at end of file +``` + +Leetcode Problems +- [1091. Shortest Path in Binary Matrix](https://leetcode.com/problems/shortest-path-in-binary-matrix/) \ No newline at end of file diff --git a/04/graph.py b/04/graph.py new file mode 100644 index 000000000..539a0b7f4 --- /dev/null +++ b/04/graph.py @@ -0,0 +1,51 @@ +""" +Graph Data Structure +""" +graph = { + 'A': ['B', 'C'], + 'B': ['C', 'D'], + 'C': ['D'], + 'D': ['C'], + 'E': ['F'], + 'F': ['C'] +} + +def find_path(graph, start, end, path = []): + path = path + [start] + + if start == end: + return path + + if start not in graph: + return None + + for node in graph[start]: + if node not in path: + newpath = find_path(graph, node, end, path) + if newpath: + return newpath + + return node + +def find_all_paths(graph, start, end, path = []): + path = path + [start] + + if start == end: + return [path] + + if start not in graph: + return [] + + paths = [] + for node in graph[start]: + if node not in path: + newpaths = find_all_paths(graph, node, end, path) + for newpath in newpaths: + paths.append(newpath) + + return paths + +print(find_path(graph, 'A', 'D')) +print(find_path(graph, 'E', 'D')) +print(find_all_paths(graph, 'A', 'D')) +print(find_all_paths(graph, 'E', 'D')) \ No newline at end of file diff --git a/07/minimum_genetic_mutation.py b/04/minimum_genetic_mutation.py similarity index 100% rename from 07/minimum_genetic_mutation.py rename to 04/minimum_genetic_mutation.py diff --git a/07/sudoku.py b/04/sudoku.py similarity index 100% rename from 07/sudoku.py rename to 04/sudoku.py diff --git a/05/NOTE.md b/05/NOTE.md index f63f447ce..59e99079a 100644 --- a/05/NOTE.md +++ b/05/NOTE.md @@ -1,5 +1,41 @@ # 05 +Sorting +------- + +Simple Sorts (Comparison) +- Bubble Sort +- Selection Sort +- Insertion Sort + +| Sorting | Average | Worst | Best | Space | Stability | +| :------------- | :-------- | :-------- | :-------- | :-------- | :-------- | +| Bubble Sort | O(N^2) | O(N^2) | O(N) | O(1) | YES | +| Selection Sort | O(N^2) | O(N^2) | O(N^2) | O(1) | NO | +| Insertion Sort | O(N^2) | O(N^2) | O(N) | O(1) | YES | + +Efficient Sorts (Comparison) +- Merge Sort +- Quick Sort +- Heap Sort + +| Sorting | Average | Worst | Best | Space | Stability | +| :------------- | :-------- | :-------- | :-------- | :-------- | :-------- | +| Merge Sort | O(N*logN) | O(N*logN) | O(N*logN) | O(N) | YES | +| Quick Sort | O(N*logN) | O(N^2) | O(N*logN) | O(N*logN) | NO | +| Heap Sort | O(N*logN) | O(N*logN) | O(N*logN) | O(1) | YES | + +Distribution Sorts (Non-comparison) +- Counting Sort +- Bucket Sort +- Radix Sort + +| Sorting | Average | Worst | Best | Space | Stability | +| :------------- | :-------- | :-------- | :-------- | :-------- | :-------- | +| Counting Sort | O(N+k) | O(N+k) | O(N+k) | O(N+k) | YES | +| Bucket Sort | O(N+k) | O(N^2) | O(N) | O(N+k) | YES | +| Radix Sort | O(N*k) | O(N*k) | O(N*k) | O(N+k) | YES | + Binary Search ------------- @@ -92,40 +128,4 @@ Leetcode Problems - [34. Find First and Last Position of Element in Sorted Array](https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/) - [69. Sqrt(x)](https://leetcode.com/problems/sqrtx/) - [74. Search a 2D Matrix](https://leetcode.com/problems/search-a-2d-matrix/) -- [367. Valid Perfect Square](https://leetcode.com/problems/valid-perfect-square/) - -Sorting -------- - -Simple Sorts (Comparison) -- Bubble Sort -- Selection Sort -- Insertion Sort - -| Sorting | Average | Worst | Best | Space | Stability | -| :------------- | :-------- | :-------- | :-------- | :-------- | :-------- | -| Bubble Sort | O(N^2) | O(N^2) | O(N) | O(1) | YES | -| Selection Sort | O(N^2) | O(N^2) | O(N^2) | O(1) | NO | -| Insertion Sort | O(N^2) | O(N^2) | O(N) | O(1) | YES | - -Efficient Sorts (Comparison) -- Merge Sort -- Quick Sort -- Heap Sort - -| Sorting | Average | Worst | Best | Space | Stability | -| :------------- | :-------- | :-------- | :-------- | :-------- | :-------- | -| Merge Sort | O(N*logN) | O(N*logN) | O(N*logN) | O(N) | YES | -| Quick Sort | O(N*logN) | O(N^2) | O(N*logN) | O(N*logN) | NO | -| Heap Sort | O(N*logN) | O(N*logN) | O(N*logN) | O(1) | YES | - -Distribution Sorts (Non-comparison) -- Counting Sort -- Bucket Sort -- Radix Sort - -| Sorting | Average | Worst | Best | Space | Stability | -| :------------- | :-------- | :-------- | :-------- | :-------- | :-------- | -| Counting Sort | O(N+k) | O(N+k) | O(N+k) | O(N+k) | YES | -| Bucket Sort | O(N+k) | O(N^2) | O(N) | O(N+k) | YES | -| Radix Sort | O(N*k) | O(N*k) | O(N*k) | O(N+k) | YES | \ No newline at end of file +- [367. Valid Perfect Square](https://leetcode.com/problems/valid-perfect-square/) \ No newline at end of file diff --git a/07/word_ladder.py b/07/word_ladder.py deleted file mode 100644 index 923d31013..000000000 --- a/07/word_ladder.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -127. Word Ladder -https://leetcode.com/problems/word-ladder/description/ -""" -from typing import List -import collections -import string - -class Solution: - def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: - """ - Bidirectional BFS - """ - wordList = set(wordList) - if not wordList or endWord not in wordList: - return 0 - - beginQueue = collections.deque([beginWord]) - endQueue = collections.deque([endWord]) - visited = set([beginWord]) - step = 0 - - while beginQueue and endQueue: - step += 1 - if len(beginQueue) > len(endQueue): - beginQueue, endQueue = endQueue, beginQueue - for _ in range(len(beginQueue)): - word = beginQueue.popleft() - for i in range(len(word)): - for c in string.ascii_lowercase: - if c == word[i]: - continue - newword = word[:i] + c + word[i+1:] - if newword in endQueue: - return step + 1 - if not newword in visited and newword in wordList: - beginQueue.append(newword) - visited.add(newword) - return 0 - - -solution = Solution() - -beginWord = "hit" -endWord = "cog" -wordList = ["hot","dot","dog","lot","log","cog"] -print(solution.ladderLength(beginWord, endWord, wordList)) # 5 - -beginWord = "hit" -endWord = "cog" -wordList = ["hot","dot","dog","lot","log"] -print(solution.ladderLength(beginWord, endWord, wordList)) # 0 From 32c442bf0bd1fd2199d1583aca10e6858a985ca0 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Mon, 2 Aug 2021 15:52:47 -0400 Subject: [PATCH 43/48] Update NOTE.md --- 04/NOTE.md | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/04/NOTE.md b/04/NOTE.md index a923e64e3..79dde216c 100644 --- a/04/NOTE.md +++ b/04/NOTE.md @@ -160,19 +160,27 @@ def bfs(root): while queue: # process all nodes from the current level level_nodes = [] + for _ in range(len(queue)): # get current node from queue node = queue.popleft() - # process current node + + # check if the node is already visited + if node in visited: + continue + + # process current node level_nodes.append(node.val) - # add to visited + + # add to visited visited.add(node) - # process children + + # process children if node.children: for child in node.children: - if child not in visited: - queue.append(child) - res.append(level_nodes) + queue.append(child) + + res.append(level_nodes) return res ``` @@ -190,16 +198,23 @@ def bfs(start, target): # any two nodes, doesn't have to start from the root for _ in range(len(queue)): # get current node from queue node = queue.popleft() - # see if we reach the target + + # check if the node is already visited + if node in visited: + continue + + # see if we reach the target if node is target: return step - # add to visited + + # add to visited visited.add(node) - # process children + + # process children if node.children: for child in node.children: - if child not in visited: - queue.append(child) + queue.append(child) + step += 1 return 0 @@ -265,4 +280,4 @@ def AstarSearch(graph, start, end): ``` Leetcode Problems -- [1091. Shortest Path in Binary Matrix](https://leetcode.com/problems/shortest-path-in-binary-matrix/) \ No newline at end of file +- [1091. Shortest Path in Binary Matrix](https://leetcode.com/problems/shortest-path-in-binary-matrix/) From 52c02fb829a702f4791de490025c7086c1241e62 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Mon, 2 Aug 2021 23:53:24 -0400 Subject: [PATCH 44/48] modify bfs template: --- 04/NOTE.md | 68 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/04/NOTE.md b/04/NOTE.md index 79dde216c..106efe4c7 100644 --- a/04/NOTE.md +++ b/04/NOTE.md @@ -160,27 +160,28 @@ def bfs(root): while queue: # process all nodes from the current level level_nodes = [] - + for _ in range(len(queue)): # get current node from queue node = queue.popleft() - - # check if the node is already visited - if node in visited: - continue - - # process current node - level_nodes.append(node.val) - - # add to visited - visited.add(node) - - # process children - if node.children: - for child in node.children: + + # check if the node is already visited + if node in visited: + continue + + # process current node + level_nodes.append(node.val) + + # add to visited + visited.add(node) + + # process children + if node.children: + for child in node.children: + if child not in visited: queue.append(child) - - res.append(level_nodes) + + res.append(level_nodes) return res ``` @@ -198,23 +199,24 @@ def bfs(start, target): # any two nodes, doesn't have to start from the root for _ in range(len(queue)): # get current node from queue node = queue.popleft() - - # check if the node is already visited - if node in visited: - continue - - # see if we reach the target - if node is target: - return step - - # add to visited - visited.add(node) - - # process children - if node.children: - for child in node.children: + + # check if the node is already visited + if node in visited: + continue + + # see if we reach the target + if node is target: + return step + + # add to visited + visited.add(node) + + # process children + if node.children: + for child in node.children: + if child not in visited: queue.append(child) - + step += 1 return 0 From 7c027612adce93c45de8c816032090dd7fa4f6af Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Fri, 13 Aug 2021 01:17:21 -0400 Subject: [PATCH 45/48] binary search --- 04/NOTE.md | 109 +++++++------ 04/word_ladder.py | 63 +++++++- 05/NOTE.md | 216 +++++++++++++++++++++----- {04 => 05}/search_2d_matrix.py | 0 05/sort.py | 272 ++++++++++++++++----------------- 5 files changed, 423 insertions(+), 237 deletions(-) rename {04 => 05}/search_2d_matrix.py (100%) diff --git a/04/NOTE.md b/04/NOTE.md index 106efe4c7..1c44259b7 100644 --- a/04/NOTE.md +++ b/04/NOTE.md @@ -126,8 +126,8 @@ Breadth-first Search (BFS) ```py def bfs(root): - visited = set() queue = collections.deque([root]) + visited = set() # Loop until queue is empty while queue: @@ -151,10 +151,10 @@ def bfs(root): Level Order ```py -def bfs(root): - res = [] - visited = set() +def levelOrder(root): queue = collections.deque([root]) + visited = set() + res = [] # Loop until queue is empty while queue: @@ -165,33 +165,33 @@ def bfs(root): # get current node from queue node = queue.popleft() - # check if the node is already visited - if node in visited: - continue + # check if the node is already visited + if node in visited: + continue - # process current node - level_nodes.append(node.val) + # process current node + level_nodes.append(node.val) - # add to visited - visited.add(node) + # add to visited + visited.add(node) - # process children - if node.children: - for child in node.children: - if child not in visited: - queue.append(child) + # process children + if node.children: + for child in node.children: + if child not in visited: + queue.append(child) - res.append(level_nodes) + res.append(level_nodes) return res ``` Shortest Path ```py -def bfs(start, target): # any two nodes, doesn't have to start from the root - step = 0 - visited = set() +def shortestPath(start, target): # any two nodes, doesn't have to start from the root queue = collections.deque([start]) + visited = set([start]) + step = 0 # Loop until queue is empty while queue: @@ -200,22 +200,16 @@ def bfs(start, target): # any two nodes, doesn't have to start from the root # get current node from queue node = queue.popleft() - # check if the node is already visited - if node in visited: - continue - - # see if we reach the target - if node is target: - return step + # see if we reach the target + if node is target: + return step - # add to visited - visited.add(node) - - # process children - if node.children: - for child in node.children: - if child not in visited: - queue.append(child) + # process children + if node.children: + for child in node.children: + if child not in visited: + queue.append(child) + visited.add(child) step += 1 @@ -224,41 +218,41 @@ def bfs(start, target): # any two nodes, doesn't have to start from the root Bidirectional BFS ```py -sourceQueue = collections.deque([source]) -targetQueue = collections.deque([target]) -visited = set([source]) -step = 0 +def biBfs(source, target): + sourceQueue = collections.deque([source]) + targetQueue = collections.deque([target]) + visited = set([source]) + step = 0 -while sourceQueue and targetQueue: - step += 1 + while sourceQueue and targetQueue: + # choose the smaller queue to spread + if len(sourceQueue) > len(targetQueue): + sourceQueue, targetQueue = targetQueue, sourceQueue - # choose the smaller queue to spread - if len(sourceQueue) > len(targetQueue): - sourceQueue, targetQueue = targetQueue, sourceQueue + for _ in range(len(sourceQueue)): + node = sourceQueue.popleft() - for _ in range(len(sourceQueue)): - node = sourceQueue.popleft() + for child in node.children: + # source and target meet + if child in targetQueue: + return step + 1 - for child in node.children: - # source and target meet - if child in targetQueue: - return step + 1 + if not child in visited: + sourceQueue.append(child) + visited.add(child) - if not child in visited: - sourceQueue.append(child) - visited.add(child) + step += 1 -return 0 # not found + return 0 # not found ``` Leetcode Problems -- [22. Generate Parentheses](https://leetcode.com/problems/generate-parentheses/) - [111. Minimum Depth of Binary Tree](https://leetcode.com/problems/minimum-depth-of-binary-tree/) -- [752. Open the Lock](https://leetcode.com/problems/open-the-lock/) +- [515. Find Largest Value in Each Tree Row](https://leetcode.com/problems/find-largest-value-in-each-tree-row/) - [127. Word Ladder](https://leetcode.com/problems/word-ladder/) - [126. Word Ladder II](https://leetcode.com/problems/word-ladder-ii/) +- [752. Open the Lock](https://leetcode.com/problems/open-the-lock/) - [433. Minimum Genetic Mutation](https://leetcode.com/problems/minimum-genetic-mutation/) -- [515. Find Largest Value in Each Tree Row](https://leetcode.com/problems/find-largest-value-in-each-tree-row/) - [529. Minesweeper](https://leetcode.com/problems/minesweeper/) - [773. Sliding Puzzle](https://leetcode.com/problems/sliding-puzzle/) @@ -282,4 +276,5 @@ def AstarSearch(graph, start, end): ``` Leetcode Problems +- [126. Word Ladder II](https://leetcode.com/problems/word-ladder-ii/) - [1091. Shortest Path in Binary Matrix](https://leetcode.com/problems/shortest-path-in-binary-matrix/) diff --git a/04/word_ladder.py b/04/word_ladder.py index a718f86e9..8c3e8220b 100644 --- a/04/word_ladder.py +++ b/04/word_ladder.py @@ -9,24 +9,71 @@ class Solution: def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: wordList = set(wordList) - step = 1 + if not wordList or endWord not in wordList: + return 0 + queue = collections.deque([beginWord]) + visited = set([beginWord]) + step = 1 + while queue: for _ in range(len(queue)): word = queue.popleft() + if word == endWord: return step + for i in range(len(word)): for c in string.ascii_lowercase: if c == word[i]: continue + new_word = word[:i] + c + word[i+1:] - if new_word in wordList: + if new_word not in visited and new_word in wordList: queue.append(new_word) - wordList.remove(new_word) + visited.add(new_word) step += 1 + return 0 + def ladderLength2(self, beginWord: str, endWord: str, wordList: List[str]) -> int: + wordList = set(wordList) + if not wordList or endWord not in wordList: + return 0 + + # Bidirectional BFS + beginQueue = collections.deque([beginWord]) + endQueue = collections.deque([endWord]) + visited = set([beginWord, endWord]) + step = 1 + + while beginQueue and endQueue: + # choose the smaller queue to spread + if len(beginQueue) > len(endQueue): + beginQueue, endQueue = endQueue, beginQueue + + # spread the search from the current level + for _ in range(len(beginQueue)): + word = beginQueue.popleft() + + # process each character in the current word + for i in range(len(word)): + for c in string.ascii_lowercase: # a-z + if c == word[i]: + continue + + # see if we reach the target + newWord = word[:i] + c + word[i+1:] + if newWord in endQueue: + return step + 1 + + # process children, if not visited + if newWord not in visited and newWord in wordList: + beginQueue.append(newWord) + visited.add(newWord) + step += 1 + + return 0 # not found solution = Solution() @@ -39,3 +86,13 @@ def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int endWord = "cog" wordList = ["hot","dot","dog","lot","log"] print(solution.ladderLength(beginWord, endWord, wordList)) # 0 + +beginWord = "hit" +endWord = "cog" +wordList = ["hot","dot","dog","lot","log","cog"] +print(solution.ladderLength2(beginWord, endWord, wordList)) # 5 + +beginWord = "hit" +endWord = "cog" +wordList = ["hot","dot","dog","lot","log"] +print(solution.ladderLength2(beginWord, endWord, wordList)) # 0 \ No newline at end of file diff --git a/05/NOTE.md b/05/NOTE.md index 59e99079a..dd6cb0c46 100644 --- a/05/NOTE.md +++ b/05/NOTE.md @@ -3,21 +3,95 @@ Sorting ------- -Simple Sorts (Comparison) -- Bubble Sort -- Selection Sort -- Insertion Sort +### Simple Sorts (Comparison based) + +- **Bubble Sort**: compares two adjacent elements and swaps them if they are not in the intended order. +``` +bubbleSort(array) + for i <- 1 to indexOfLastUnsortedElement-1 + if leftElement > rightElement + swap leftElement and rightElement +end bubbleSort +``` + +- **Insertion Sort**: places an unsorted element at its suitable place in each iteration. +``` +insertionSort(array) + mark first element as sorted + for each unsorted element X + 'extract' the element X + for j <- lastSortedIndex down to 0 + if current element j > X + move sorted element to the right by 1 + break loop and insert X here +end insertionSort +``` + +- **Selection Sort**: selects the smallest element from an unsorted list in each iteration and places that element at the beginning of the unsorted list. +``` +selectionSort(array, size) + repeat (size - 1) times + set the first unsorted element as the minimum + for each of the unsorted elements + if element < currentMinimum + set element as new minimum + swap minimum with first unsorted position +end selectionSort +``` | Sorting | Average | Worst | Best | Space | Stability | | :------------- | :-------- | :-------- | :-------- | :-------- | :-------- | | Bubble Sort | O(N^2) | O(N^2) | O(N) | O(1) | YES | -| Selection Sort | O(N^2) | O(N^2) | O(N^2) | O(1) | NO | | Insertion Sort | O(N^2) | O(N^2) | O(N) | O(1) | YES | +| Selection Sort | O(N^2) | O(N^2) | O(N^2) | O(1) | NO | + +Efficient Sorts (Comparison based) +- **Merge Sort**: the MergeSort function repeatedly divides the array into two halves until we reach a stage where we try to perform MergeSort on a subarray of size 1 (i.e. left == right). After that, the merge function comes into play and combines the sorted arrays into larger arrays until the whole array is merged. +``` +MergeSort(A, l, r): + if l > r + return + m = (l+r)/2 + mergeSort(A, l, m) + mergeSort(A, m+1, r) + merge(A, l, m, r) +``` + +- **Quick Sort**: An array is divided into sub-arrays by selecting a pivot element from the array. The pivot element should be positioned in such a way that elements less than pivot are kept on the left side and elements greater than pivot are on the right side of the pivot. The left and right sub-arrays are also divided using the same approach. This process continues until each subarray contains a single element. At this point, elements are already sorted. Finally, elements are combined to form a sorted array. +``` +quickSort(array, leftmostIndex, rightmostIndex) + if (leftmostIndex < rightmostIndex) + pivotIndex <- partition(array,leftmostIndex, rightmostIndex) + quickSort(array, leftmostIndex, pivotIndex - 1) + quickSort(array, pivotIndex, rightmostIndex) + +partition(array, leftmostIndex, rightmostIndex) + set rightmostIndex as pivotIndex + storeIndex <- leftmostIndex - 1 + for i <- leftmostIndex + 1 to rightmostIndex + if element[i] < pivotElement + swap element[i] and element[storeIndex] + storeIndex++ + swap pivotElement and element[storeIndex+1] +return storeIndex + 1 +``` + +- **Heap Sort**: Build a max-heap based on the array (heapify), the largest item is stored at the root node. Remove the root element and put at the end of the array (nth position) Put the last item of the tree (heap) at the vacant place. Reduce the size of the heap by 1. Heapify the root element again so that we have the highest element at root. The process is repeated until all the items of the list are sorted. +``` +heapify(array, index) + Root = array[index] + Largest = largest(root, left child, right child) + if(Root != Largest) + Swap(Root, Largest) + heapify(array, Largest) -Efficient Sorts (Comparison) -- Merge Sort -- Quick Sort -- Heap Sort +heapSort(array) + for index <- n//2-1 to 0 + heapify the index + for index <- n-1 to 0 + Swap(array[0], array[index]) + heapify(array, index, 0) +``` | Sorting | Average | Worst | Best | Space | Stability | | :------------- | :-------- | :-------- | :-------- | :-------- | :-------- | @@ -25,7 +99,7 @@ Efficient Sorts (Comparison) | Quick Sort | O(N*logN) | O(N^2) | O(N*logN) | O(N*logN) | NO | | Heap Sort | O(N*logN) | O(N*logN) | O(N*logN) | O(1) | YES | -Distribution Sorts (Non-comparison) +Distribution Sorts (Non-comparison based) - Counting Sort - Bucket Sort - Radix Sort @@ -36,68 +110,129 @@ Distribution Sorts (Non-comparison) | Bucket Sort | O(N+k) | O(N^2) | O(N) | O(N+k) | YES | | Radix Sort | O(N*k) | O(N*k) | O(N*k) | O(N+k) | YES | -Binary Search -------------- +Searching +--------- +### Linear Search + +Linear search is a sequential searching algorithm where we start from one end and check every element of the list until the desired element is found. It is the simplest searching algorithm. +``` +LinearSearch(array, key) + for each item in the array + if item == value + return its index +``` + +### Binary Search + +Binary Search is a searching algorithm for finding an element's position in a sorted array. Binary search can be implemented only when the array is: - Monotonically increasing/decreasing - Bounded (have upper and lower bound) - Index accessible -[left, right] +Iterative Method ```py def binary_search(nums, target) left, right = 0, len(nums) - 1 while left <= right: - mid = (left + right) // 2 + # use (left + right) // 2 might be out of bound + mid = left + (right - left) // 2 if nums[mid] < target: left = mid + 1 elif nums[mid] > target: right = mid - 1 - elif nums[mid] == target: + else: # found return mid return -1 ``` -[left, right) +Recursive Method +```py +def binary_search(nums, target): + def helper(left, right): + if left <= right: + # use (left + right) // 2 might be out of bound + mid = left + (right - left) // 2 + if nums[mid] < target: + return helper(mid + 1, right) + elif nums[mid] > target: + return helper(left, mid - 1) + else: # found + return mid + return -1 + return helper(0, len(nums) - 1) +``` + +Variation 1: Find the first match (array contains duplicates) ```py -def left_bound(nums, target): +def binary_search1(nums, target): left, right = 0, len(nums) - 1 - while left < right: - mid = (left + right) // 2 + while left <= right: + # use (left + right) // 2 might be out of bound + mid = left + (right - left) // 2 if nums[mid] < target: left = mid + 1 elif nums[mid] > target: right = mid - 1 - elif nums[mid] == target: - # don't return, lock down the left boundary - right = mid - 1 - - # check if left is out of boundary - if left >= nums.length or nums[left] != target - return -1 - return left -} + else: + if mid == 0 or a[mid - 1] != target: + return mid # the first match + else: + right = mid - 1 # keep searching + return -1 ``` -(left, right] +Variation 2: Find the last match (array contains duplicates) ```py -def right_bound(nums, target): +def binary_search2(nums, target): left, right = 0, len(nums) - 1 - while left < right: + while left <= right: + # use (left + right) // 2 might be out of bound mid = left + (right - left) // 2 if nums[mid] < target: left = mid + 1 elif nums[mid] > target: right = mid - 1 - elif nums[mid] == target: - # don't return, lock down the right boundary + else: + if mid == n - 1 or a[mid + 1] != target: + return mid # the first match + else: + left = mid + 1 # keep searching + return -1 +``` + +Variation 3: Find first number greater than target (array contains duplicates) +```py +def binary_search3(nums, target): + left, right = 0, len(nums) - 1 + while left <= right: + # use (left + right) // 2 might be out of bound + mid = left + (right - left) // 2 + if nums[mid] < target: left = mid + 1 + else: + if mid == 0 or a[mid - 1] < target: + return mid # the first match + else: + right = mid - 1 # keep searching + return -1 +``` - # check if right is out of boundary - if right < 0 or nums[right] != target - return -1 - return right -} +Variation 4: Find first number smaller than target (array contains duplicates) +```py +def binary_search4(nums, target): + left, right = 0, len(nums) - 1 + while left <= right: + # use (left + right) // 2 might be out of bound + mid = left + (right - left) // 2 + if nums[mid] > target: + right = mid - 1 + else: + if mid == n - 1 or a[mid + 1] > target: + return mid # the first match + else: + left = mid + 1 # keep searching + return -1 ``` Target Function g(m) @@ -121,6 +256,11 @@ def binary_search(l, r): return l # or not found ``` +| Searching | Average | Worst | Best | Space | +| :------------- | :------- | :------- | :------- | :-------- | +| Linear Search | O(n) | O(n) | O(n) | O(1) | +| Binary Search | O(log n) | O(log n) | O(1) | O(1) | + Leetcode Problems - [704. Binary Search](https://leetcode.com/problems/binary-search/) - [167. Two Sum II - Input array is sorted](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/) diff --git a/04/search_2d_matrix.py b/05/search_2d_matrix.py similarity index 100% rename from 04/search_2d_matrix.py rename to 05/search_2d_matrix.py diff --git a/05/sort.py b/05/sort.py index 222f22e0c..15108ce5b 100644 --- a/05/sort.py +++ b/05/sort.py @@ -2,163 +2,157 @@ Sorting Algorithms """ class Sort: - def BubbleSort(self, arr): - n = len(arr) + def BubbleSort(self, a): + n = len(a) for i in range(n-1): for j in range(n-1-i): - if arr[j] > arr[j+1]: - arr[j], arr[j+1] = arr[j+1], arr[j] - return arr + if a[j] > a[j+1]: + a[j], a[j+1] = a[j+1], a[j] + return a - def SelectionSort(self, arr): - n = len(arr) + def InsertionSort(self, a): + n = len(a) + for i in range(1, n): + key = a[i] + j = i - 1 + while j >= 0 and a[j] > key: + a[j+1] = a[j] + j -= 1 + a[j+1] = key + return a + + def SelectionSort(self, a): + n = len(a) for i in range(n-1): min_j = i for j in range(i+1, n): - if arr[j] < arr[min_j]: + if a[j] < a[min_j]: min_j = j - arr[i], arr[min_j] = arr[min_j], arr[i] - return arr - - def InsertionSort(self, arr): - n = len(arr) - for i in range(1, n): - j = i - while j > 0 and arr[j-1] > arr[j]: - arr[j-1], arr[j] = arr[j], arr[j-1] - j -= 1 - return arr - - def MergeSort(self, arr): - n = len(arr) - if n < 2: return arr + a[i], a[min_j] = a[min_j], a[i] + return a + + def MergeSort(self, a): + def merge(left, right): + res = [] + while left and right: + if left[0] <= right[0]: + res.append(left.pop(0)) + else: + res.append(right.pop(0)) + while left: res.append(left.pop(0)) + while right: res.append(right.pop(0)) + return res + + n = len(a) + if n < 2: return a mid = n // 2 - leftSorted = self.MergeSort(arr[:mid]) - rightSorted = self.MergeSort(arr[mid:]) - return self.merge(leftSorted, rightSorted) - - def merge(self, left, right): - res = [] - while left and right: - if left[0] <= right[0]: - res.append(left.pop(0)) - else: - res.append(right.pop(0)) - while left: res.append(left.pop(0)) - while right: res.append(right.pop(0)) - return res - - def QuickSort(self, arr): - def quickSort(arr, left, right): - if left < right: - p = self.partition(arr, left, right) - quickSort(arr, left, p - 1) - quickSort(arr, p + 1, right) - - quickSort(arr, 0, len(arr) - 1) - return arr - - def partition(self, arr, left, right): - pivot = arr[left] - i, j = left + 1, right - while i <= j: - while i <= j and arr[i] <= pivot: i += 1 - while i <= j and arr[j] >= pivot: j -= 1 - if i <= j: - arr[i], arr[j] = arr[j], arr[i] - arr[left], arr[j] = arr[j], arr[left] - return j - - def HeapSort(self, arr): - n = len(arr) - - # Build max heap. All nodes after n//2 - 1 are leaf-nodes and they - # don't need to be heapified. + leftSorted = self.MergeSort(a[:mid]) + rightSorted = self.MergeSort(a[mid:]) + return merge(leftSorted, rightSorted) + + def QuickSort(self, a): + def quickSort(a, l, r): + if l < r: + p = partition(a, l, r) + quickSort(a, r, p-1) + quickSort(a, p+1, r) + + def partition(a, left, right): + pivot = a[left] + i, j = left + 1, right + while i <= j: + while i <= j and a[i] <= pivot: i += 1 + while i <= j and a[j] >= pivot: j -= 1 + if i <= j: + a[i], a[j] = a[j], a[i] + a[left], a[j] = a[j], a[left] + return j + + quickSort(a, 0, len(a) - 1) + return a + + def HeapSort(self, a): + def heapify(a, n, i): + largest = i + l = 2 * i + 1 + r = 2 * i + 2 + + if l < n and a[l] > a[largest]: + largest = l + if r < n and a[r] > a[largest]: + largest = r + + if largest != i: + a[largest], a[i] = a[i], a[largest] + heapify(a, n, largest) + + n = len(a) for i in range(n//2 - 1, -1, -1): - self.heapify(arr, n, i) - + heapify(a, n, i) for i in range(n-1, 0, -1): - arr[i], arr[0] = arr[0], arr[i] - self.heapify(arr, i, 0) - - return arr - - def heapify(self, arr, n, i): - # Find largest among root, left child and right child - largest = i - left = 2 * i + 1 - right = 2 * i + 2 - - if left < n and arr[left] > arr[largest]: - largest = left + a[i], a[0] = a[0], a[i] + heapify(a, i, 0) + return a - if right < n and arr[right] > arr[largest]: - largest = right - - # Swap and continue if root is not largest - if largest != i: - arr[largest], arr[i] = arr[i], arr[largest] - self.heapify(arr, n, largest) - - def CountingSort(self, arr): - n = len(arr) + def CountingSort(self, a): + n = len(a) res = [0] * n - # Store the count of each elements in count array - max_elem = max(arr) + # Store the count of each elements in count aay + max_elem = max(a) count = [0] * (max_elem + 1) for i in range(n): - count[arr[i]] += 1 + count[a[i]] += 1 # Store the cumulative count for i in range(1, max_elem + 1): count[i] += count[i-1] - # Find the index of each element of the original array in count array - # place the elements in output array + # Find the index of each element of the original aay in count aay + # place the elements in output aay i = n - 1 while i >= 0: - res[count[arr[i]] - 1] = arr[i] - count[arr[i]] -= 1 + res[count[a[i]] - 1] = a[i] + count[a[i]] -= 1 i -= 1 - # Copy the sorted elements into original array - arr[:] = res[:] - return arr + # Copy the sorted elements into original aay + a[:] = res[:] + return a + + def RadixSort(self, a): + def doCount(a, place): + n = len(a) + res = [0] * n + max_elem = max(a) + count = [0] * (max_elem + 1) + # Calculate count of elements + for i in range(n): + index = a[i] // place + count[index % 10] += 1 + # Calculate cumulative count + for i in range(1, max_elem + 1): + count[i] += count[i-1] + # Place the elements in sorted order + i = n - 1 + while i >= 0: + index = a[i] // place + res[count[index % 10] - 1] = a[i] + count[index % 10] -= 1 + i -= 1 + a[:] = res[:] - def RadixSort(self, arr): # Get maximum element - max_elem = max(arr) + max_elem = max(a) # Apply counting sort to sort elements based on place value. place = 1 while max_elem // place > 0: - self.couting(arr, place) + doCount(a, place) place *= 10 - return arr - - def couting(self, arr, place): - n = len(arr) - res = [0] * n - max_elem = max(arr) - count = [0] * (max_elem + 1) - # Calculate count of elements - for i in range(n): - index = arr[i] // place - count[index % 10] += 1 - # Calculate cumulative count - for i in range(1, max_elem + 1): - count[i] += count[i-1] - # Place the elements in sorted order - i = n - 1 - while i >= 0: - index = arr[i] // place - res[count[index % 10] - 1] = arr[i] - count[index % 10] -= 1 - i -= 1 - arr[:] = res[:] + return a - def BucketSort(self, arr): - n = len(arr) + def BucketSort(self, a): + n = len(a) # Create empty buckets bucket = [[] for i in range(n)] # Insert elements into their respective buckets - for x in arr: + for x in a: index = int(10 * x) bucket[index].append(x) # Sort the elements of each bucket @@ -168,18 +162,18 @@ def BucketSort(self, arr): k = 0 for i in range(n): for j in range(len(bucket[i])): - arr[k] = bucket[i][j] + a[k] = bucket[i][j] k += 1 - return arr + return a -arr = [3, 48, 15, 42, 26, 50, 27, 4, 34, 19, 2, 13, 36, 5, 47, 17] +a = [3, 48, 15, 42, 26, 50, 27, 4, 34, 19, 2, 13, 36, 5, 47, 17] sort = Sort() -print('Bubble Sort ', sort.BubbleSort(arr[:])) -print('Selection Sort', sort.SelectionSort(arr[:])) -print('Insertion Sort', sort.InsertionSort(arr[:])) -print('Merge Sort ', sort.MergeSort(arr[:])) -print('Quick Sort ', sort.QuickSort(arr[:])) -print('Heap Sort ', sort.HeapSort(arr[:])) -print('Counting Sort ', sort.CountingSort(arr[:])) -print('Radix Sort ', sort.RadixSort(arr[:])) -print('Bucket Sort ', [int(x*100) for x in sort.BucketSort([x/100 for x in arr])]) \ No newline at end of file +print('Bubble Sort ', sort.BubbleSort(a[:])) +print('Selection Sort', sort.SelectionSort(a[:])) +print('Insertion Sort', sort.InsertionSort(a[:])) +print('Merge Sort ', sort.MergeSort(a[:])) +print('Quick Sort ', sort.QuickSort(a[:])) +print('Heap Sort ', sort.HeapSort(a[:])) +print('Counting Sort ', sort.CountingSort(a[:])) +print('Radix Sort ', sort.RadixSort(a[:])) +print('Bucket Sort ', [int(x*100) for x in sort.BucketSort([x/100 for x in a])]) \ No newline at end of file From 42e7f627c5cbe82a37cb89576713a12765c44d13 Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Fri, 27 Aug 2021 21:24:43 -0400 Subject: [PATCH 46/48] 10: --- 10/NOTE.md | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 5 +++- 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 10/NOTE.md diff --git a/10/NOTE.md b/10/NOTE.md new file mode 100644 index 000000000..cf2c76986 --- /dev/null +++ b/10/NOTE.md @@ -0,0 +1,72 @@ +# System Design + +## 1. Requirement Clarifications + +- Ask Questions! Define System Scope +- Clarify what parts of system to focus on +- Functional Requirements + - From an end user perspective +- None Functional Requirements + - Highly Available + - Acceptable Latency (realtime?) + - Data Consistency + - Highly Reliable (no data lost) + +## 2. Capacity Estimation + +- User Estimates + - total Users (500M) + - daily active users (1M) +- Traffic/Bandwidth Estimates + - read/write ratio (100:1) + - number of *entities* generated per month (writes) + - transaction per second (TPS) +- Storage Estimates + - how long will the data store? (5 years? 10 years?) + - size of each stored object + - calculate total storage + +## 3. API Design + +- postData(api_key, user_id, data, data_location, user_location, timestamp, …) +- generateTimeline(user_id, current_time, user_location, ...) +- markFavorite(user_id, item_id, timestamp, ...) + +## 4. Define Data Model + +What database to use? NoSQL? Relational? + +- **User**: UserID (PK), Name, Email, DoB, CreationDate, LastLogin, etc. +- **Tweet**: TweetID (PK), Content, TweetLocation, NumberOfLikes, TimeStamp, etc. +- **UserFollow**: UserID1, UserID2 (combined PK) +- **FavoriteTweets**: UserID, TweetID (combined PK), TimeStamp + +## 5. High-Level Design + +``` + | App Server | + / | ... | --> cache --> Databases +client --> load Balancer - | App Server | + \ | ... | --> cache --> File Storage + | App Server | +``` + +## 6. Detailed Design + +Dig Deeper into two or three major components. + +- Data Partitioning (Sharding based on user_id? item_id? creation_date?) +- Data Flow (Pull? Push? or Hybrid?) +- Offline Generation Data (pre generate aggregate data for fast retrieval) +- Key Generation Service (pre generate keys instead of realtime hashing) +- Caching +- Load Balancer +- Fault Tolerance + +## 7. Identify and Resolve Bottleneck + +- Do we have Single point of failure? how to mitigate it? +- Do we have replicas of the data? +- Do we have Failover services available? +- How to handle "hot" users? +- How to monitoring the performance of our service? Do we get fail alerts? diff --git a/README.md b/README.md index 6f58b0b2c..dc07ac79a 100644 --- a/README.md +++ b/README.md @@ -43,4 +43,7 @@ [NOTE 09](09/NOTE.md) - String -- String-searching algorithms (Naive, Rabin-Karp, KMP, Boyer-Moore) \ No newline at end of file +- String-searching algorithms (Naive, Rabin-Karp, KMP, Boyer-Moore) + +[NOTE 10](10/NOTES.md) +- System Design \ No newline at end of file From a358b63b37dccadfed7c08a91cded1d5c904083e Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Sun, 29 Aug 2021 23:31:11 -0400 Subject: [PATCH 47/48] so far --- 01/NOTE.md | 61 +--------- 02/NOTE.md | 33 ++++-- 03/NOTE.md | 39 ++----- 04/NOTE.md | 32 +++--- 05/NOTE.md | 15 +-- 05/search_2d_matrix.py | 2 +- 05/sort.py | 249 ++++++++++++++++++++++++++--------------- 06/NOTE.md | 2 +- 07/NOTE.md | 61 +++++++++- 10/NOTE.md | 2 +- README.md | 8 +- 11 files changed, 290 insertions(+), 214 deletions(-) diff --git a/01/NOTE.md b/01/NOTE.md index 50994745a..95b924da3 100644 --- a/01/NOTE.md +++ b/01/NOTE.md @@ -312,8 +312,10 @@ Mono Increasing Stack Template for i in range(n): while stack and nums[stack[-1]] > nums[i]: curr = stack.pop() # current index + if not stack: break left = stack[-1] # prev smallest index right = i # next smallest index + # do something with curr, left and right... stack.append(i) ``` @@ -322,8 +324,10 @@ Mono Decreasing Stack Template for i in range(n): while stack and nums[stack[-1]] < nums[i]: curr = stack.pop() # current index + if not stack: break left = stack[-1] # prev largest index right = i # next largest index + # do something with curr, left and right... stack.append(i) ``` @@ -354,60 +358,3 @@ Leetcode Problems - [42. Trapping Rain Water](https://leetcode.com/problems/trapping-rain-water/) - [84. Largest Rectangle in Histogram](https://leetcode.com/problems/largest-rectangle-in-histogram/) - [239. Sliding Window Maximum](https://leetcode.com/problems/sliding-window-maximum/) - -Skip List ---------- - -> The skip list is a probabilisitc data structure that is built upon the general idea of a linked list. The skip list uses probability to build subsequent layers of linked lists upon an original linked list. Each additional layer of links contains fewer elements, but no new elements. - -``` - 1 10 - o---> o---------------------------------------------------------> o Top level - 1 3 2 5 - o---> o---------------> o---------> o---------------------------> o Level 3 - 1 2 1 2 3 2 - o---> o---------> o---> o---------> o---------------> o---------> o Level 2 - 1 1 1 1 1 1 1 1 1 1 1 - o---> o---> o---> o---> o---> o---> o---> o---> o---> o---> o---> o Bottom level -Head 1st 2nd 3rd 4th 5th 6th 7th 8th 9th 10th NIL - Node Node Node Node Node Node Node Node Node Node -``` - -| Operation | Time Complexity | -| ---------- | :-------------: | -| Access | O(log n) | -| Search | O(log n) | -| Insertion | O(log n) | -| Deletion | O(log n) | - -```py -def search(key): - p = topLeftNode() - while p.below: # Scan down - p = p.below - while key >= p.next: # Scan forward - p = p.next - return p - -def insert(key): - p, q = search(key), None - i = 1 - while CoinFlip() != 'Tails': - i = i + 1 # Height of tower for new element - if i >= h: - h = h + 1 - createNewLevel() # Creates new linked list level - while p.above is None: - p = p.prev # Scan backwards until you can go up - p = p.above - q = insertAfter(key, p) # Insert our key after position p - n = n + 1 - return q - -def delete(key): - # Search for all positions p_0, ..., p_i where key exists - if none are found: - return - # Delete all positions p_0, ..., p_i - # Remove all empty layers of skip list -``` \ No newline at end of file diff --git a/02/NOTE.md b/02/NOTE.md index 421ab2e7c..d7d79b307 100644 --- a/02/NOTE.md +++ b/02/NOTE.md @@ -32,11 +32,17 @@ d.items() # Return a new view of the dictionary’s items ((val, va Useful functions ```py -from collections import Counter +from collections import defaultdict, Counter -cnt = Counter() # A Counter is a dict subclass for counting hashable objects. -for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']: - cnt[word] += 1 +# A defaultdict is initialized with a function ("default factory") that takes no arguments and provides the default value for a nonexistent key. +dic = {} # build-in dictionary +dic['a'] # KeyError, no such key + +dic = defaultdict(int) # using int() as default factory +dic['a'] # defaultdict(, {'a': 0}) + +# A Counter is a dict subclass for counting hashable objects. +cnt = Counter(['red', 'blue', 'red', 'green', 'blue', 'blue']) print(cnt) # Counter({'blue': 3, 'red': 2, 'green': 1}) ``` @@ -184,11 +190,13 @@ BST Template ```py def BST(root, target): if root.val == target: - # found it, do something + # found it, do something... + if val < root.val: - BST(root.left, val) # find in left tree + BST(root.left, val) # find in left tree + if val > root.val: - BST(root.right, val) # find in right tree + BST(root.right, val) # find in right tree ``` Search @@ -197,9 +205,9 @@ def search(root, val): if root is None: return None if val < root.val: - return search(root.left, val) + return self.search(root.left, val) elif val > root.val: - return search(root.right, val) + return self.search(root.right, val) return root ``` @@ -219,9 +227,9 @@ def insert(root, val): if root is None: return BinaryTreeNode(val) if val < root.val: - root.left = insert(root.left, val) + root.left = self.insert(root.left, val) elif val > root.val: - root.right = insert(node.right, val) + root.right = self.insert(node.right, val) return root ``` @@ -255,7 +263,8 @@ Delete ```py def deleteNode(self, root: TreeNode, val: int) -> TreeNode: - if root is None: return root + if root is None: + return root if val < root.val: root.left = self.deleteNode(root.left, val) diff --git a/03/NOTE.md b/03/NOTE.md index f5f06fef9..685243f5f 100644 --- a/03/NOTE.md +++ b/03/NOTE.md @@ -6,21 +6,13 @@ Recursion > A recursive function is defined in terms of base cases and recursive steps. - In a base case, we compute the result immediately given the inputs to the function call. -- In a recursive step, we compute the result with the help of one or more recursive calls to this same function, but with the inputs somehow reduced in size or complexity, closer to a base case. +- In a recursive step, we compute the result with the help of one or more recursive calls to this same function, but with the inputs somehow **reduced** in size or complexity, closer to a base case. -To calculate Product of **n**: +To calculate Factorial of **n**: ``` n! = n x (n-1) x ... x 2 x 1 ``` -Recurrence relation -``` -n! = { - 1 if n = 0 - (n-1)! x n if n > 0 -} -``` - Iterative Implementation ```py def factorial(n: int) -> int: @@ -31,7 +23,15 @@ def factorial(n: int) -> int: return fact ``` -Iterative Implementation +Recurrence relation +``` +n! = { + 1 if n = 0 + (n-1)! x n if n > 0 +} +``` + +Recursive Implementation ```py def factorial(n: int) -> int: if n = 0: @@ -39,23 +39,6 @@ def factorial(n: int) -> int: return n * factorial(n-1) ``` -Generic Recursive Template -```py -def 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 -``` - Divide & Conquer ---------------- diff --git a/04/NOTE.md b/04/NOTE.md index 1c44259b7..5949efec4 100644 --- a/04/NOTE.md +++ b/04/NOTE.md @@ -64,7 +64,7 @@ Depth-first Search (DFS) visited = set() def dfs(node): - # terminator (base case) + # base case if not node: return @@ -78,9 +78,9 @@ def dfs(node): # add to visited visited.add(node) - # process children (drill down) + # process neighbors / children for child in node.children: - dfs(child, visited) + dfs(child) ``` Island Problem @@ -94,21 +94,27 @@ def island(self, grid: List[List[int]]): if not inArea(r, c): return - # current node is not an island, or it's already visited - if grid[r][c] != 1: + # current node is ocean, or it's already visited + if grid[r][c] == 0 or grid[r][c] == -1: return # mark as visited - grid[r][c] = 2 + grid[r][c] = -1 # visit neighbor nodes - dfs(r-1, c) - dfs(r+1, c) - dfs(r, c-1) - dfs(r, c+1) + dfs(r+1, c) # UP + dfs(r-1, c) # DOWN + dfs(r, c-1) # LEFT + dfs(r, c+1) # RIGHT def inArea(r, c): return 0 <= r < m and 0 <= c < n + + for r in range(m): + for c in range(n): + # start dfs for each element in grid + dfs(r, c) + ``` Leetcode Problems @@ -144,7 +150,7 @@ def bfs(root): # add to visited visited.add(node) - # process children + # process neighbors / children for child in node.children: queue.append(child) ``` @@ -213,7 +219,7 @@ def shortestPath(start, target): # any two nodes, doesn't have to start from the step += 1 - return 0 + return 0 # not found ``` Bidirectional BFS @@ -237,7 +243,7 @@ def biBfs(source, target): if child in targetQueue: return step + 1 - if not child in visited: + if child not in visited: sourceQueue.append(child) visited.add(child) diff --git a/05/NOTE.md b/05/NOTE.md index dd6cb0c46..2fba0e4d1 100644 --- a/05/NOTE.md +++ b/05/NOTE.md @@ -45,7 +45,8 @@ end selectionSort | Insertion Sort | O(N^2) | O(N^2) | O(N) | O(1) | YES | | Selection Sort | O(N^2) | O(N^2) | O(N^2) | O(1) | NO | -Efficient Sorts (Comparison based) +### Efficient Sorts (Comparison based) + - **Merge Sort**: the MergeSort function repeatedly divides the array into two halves until we reach a stage where we try to perform MergeSort on a subarray of size 1 (i.e. left == right). After that, the merge function comes into play and combines the sorted arrays into larger arrays until the whole array is merged. ``` MergeSort(A, l, r): @@ -99,7 +100,7 @@ heapSort(array) | Quick Sort | O(N*logN) | O(N^2) | O(N*logN) | O(N*logN) | NO | | Heap Sort | O(N*logN) | O(N*logN) | O(N*logN) | O(1) | YES | -Distribution Sorts (Non-comparison based) +### Distribution Sorts (Non-comparison based) - Counting Sort - Bucket Sort - Radix Sort @@ -115,7 +116,7 @@ Searching ### Linear Search -Linear search is a sequential searching algorithm where we start from one end and check every element of the list until the desired element is found. It is the simplest searching algorithm. +> Linear search is a sequential searching algorithm where we start from one end and check every element of the list until the desired element is found. It is the simplest searching algorithm. ``` LinearSearch(array, key) for each item in the array @@ -125,7 +126,7 @@ LinearSearch(array, key) ### Binary Search -Binary Search is a searching algorithm for finding an element's position in a sorted array. Binary search can be implemented only when the array is: +> Binary Search is a searching algorithm for finding an element's position in a sorted array. Binary search can be implemented only when the array is: - Monotonically increasing/decreasing - Bounded (have upper and lower bound) - Index accessible @@ -195,7 +196,7 @@ def binary_search2(nums, target): right = mid - 1 else: if mid == n - 1 or a[mid + 1] != target: - return mid # the first match + return mid # the last match else: left = mid + 1 # keep searching return -1 @@ -212,7 +213,7 @@ def binary_search3(nums, target): left = mid + 1 else: if mid == 0 or a[mid - 1] < target: - return mid # the first match + return mid # the first number greater than target else: right = mid - 1 # keep searching return -1 @@ -229,7 +230,7 @@ def binary_search4(nums, target): right = mid - 1 else: if mid == n - 1 or a[mid + 1] > target: - return mid # the first match + return mid # the first number smaller than target else: left = mid + 1 # keep searching return -1 diff --git a/05/search_2d_matrix.py b/05/search_2d_matrix.py index 5608f9837..e0f0ca67e 100644 --- a/05/search_2d_matrix.py +++ b/05/search_2d_matrix.py @@ -95,7 +95,7 @@ def searchMatrix3(self, matrix: List[List[int]], target: int) -> bool: solution = Solution() matrix = [ - [1, 3, 5, 7], + [ 1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 50] ] diff --git a/05/sort.py b/05/sort.py index 15108ce5b..86b2cf75f 100644 --- a/05/sort.py +++ b/05/sort.py @@ -2,36 +2,64 @@ Sorting Algorithms """ class Sort: - def BubbleSort(self, a): - n = len(a) + def BubbleSort(self, nums): + if nums is None: + return nums + + n = len(nums) + if n < 2: + return nums + for i in range(n-1): for j in range(n-1-i): - if a[j] > a[j+1]: - a[j], a[j+1] = a[j+1], a[j] - return a + if nums[j] > nums[j+1]: + nums[j], nums[j+1] = nums[j+1], nums[j] + + return nums + + def InsertionSort(self, nums): + if nums is None: + return nums + + n = len(nums) + if n < 2: + return nums - def InsertionSort(self, a): - n = len(a) for i in range(1, n): - key = a[i] + key = nums[i] j = i - 1 - while j >= 0 and a[j] > key: - a[j+1] = a[j] + while j >= 0 and nums[j] > key: + nums[j+1] = nums[j] j -= 1 - a[j+1] = key - return a + nums[j+1] = key + + return nums + + def SelectionSort(self, nums): + if nums is None: + return nums + + n = len(nums) + if n < 2: + return nums - def SelectionSort(self, a): - n = len(a) for i in range(n-1): min_j = i for j in range(i+1, n): - if a[j] < a[min_j]: + if nums[j] < nums[min_j]: min_j = j - a[i], a[min_j] = a[min_j], a[i] - return a + nums[i], nums[min_j] = nums[min_j], nums[i] + + return nums + + def MergeSort(self, nums): + if nums is None: + return nums + + n = len(nums) + if n < 2: + return nums - def MergeSort(self, a): def merge(left, right): res = [] while left and right: @@ -43,137 +71,182 @@ def merge(left, right): while right: res.append(right.pop(0)) return res - n = len(a) - if n < 2: return a mid = n // 2 - leftSorted = self.MergeSort(a[:mid]) - rightSorted = self.MergeSort(a[mid:]) + leftSorted = self.MergeSort(nums[:mid]) + rightSorted = self.MergeSort(nums[mid:]) return merge(leftSorted, rightSorted) - def QuickSort(self, a): - def quickSort(a, l, r): - if l < r: - p = partition(a, l, r) - quickSort(a, r, p-1) - quickSort(a, p+1, r) + def QuickSort(self, nums): + if nums is None: + return nums + + n = len(nums) + if n < 2: + return nums - def partition(a, left, right): - pivot = a[left] + def partition(left, right): + pivot = nums[left] i, j = left + 1, right while i <= j: - while i <= j and a[i] <= pivot: i += 1 - while i <= j and a[j] >= pivot: j -= 1 + while i <= j and nums[i] <= pivot: i += 1 + while i <= j and nums[j] >= pivot: j -= 1 if i <= j: - a[i], a[j] = a[j], a[i] - a[left], a[j] = a[j], a[left] + nums[i], nums[j] = nums[j], nums[i] + nums[left], nums[j] = nums[j], nums[left] return j - quickSort(a, 0, len(a) - 1) - return a + def quickSort(left, right): + if left < right: + p = partition(left, right) + quickSort(right, p-1) + quickSort(p+1, right) + + quickSort(0, n-1) + return nums + + def HeapSort(self, nums): + if nums is None: + return nums + + n = len(nums) + if n < 2: + return nums - def HeapSort(self, a): - def heapify(a, n, i): + def heapify(n, i): largest = i - l = 2 * i + 1 - r = 2 * i + 2 + left = 2 * i + 1 + right = 2 * i + 2 - if l < n and a[l] > a[largest]: - largest = l - if r < n and a[r] > a[largest]: - largest = r + if left < n and nums[left] > nums[largest]: + largest = left + if right < n and nums[right] > nums[largest]: + largest = right if largest != i: - a[largest], a[i] = a[i], a[largest] - heapify(a, n, largest) + nums[largest], nums[i] = nums[i], nums[largest] + heapify(n, largest) - n = len(a) for i in range(n//2 - 1, -1, -1): - heapify(a, n, i) + heapify(n, i) + for i in range(n-1, 0, -1): - a[i], a[0] = a[0], a[i] - heapify(a, i, 0) - return a + nums[i], nums[0] = nums[0], nums[i] + heapify(i, 0) + + return nums + + def CountingSort(self, nums): + if nums is None: + return nums + + n = len(nums) + if n < 2: + return nums - def CountingSort(self, a): - n = len(a) res = [0] * n - # Store the count of each elements in count aay - max_elem = max(a) + + # Store the count of each elements in count array + max_elem = max(nums) count = [0] * (max_elem + 1) for i in range(n): - count[a[i]] += 1 + count[nums[i]] += 1 + # Store the cumulative count for i in range(1, max_elem + 1): count[i] += count[i-1] - # Find the index of each element of the original aay in count aay - # place the elements in output aay + + # Find the index of each element of the original array in count array + # place the elements in output array i = n - 1 while i >= 0: - res[count[a[i]] - 1] = a[i] - count[a[i]] -= 1 + res[count[nums[i]] - 1] = nums[i] + count[nums[i]] -= 1 i -= 1 + # Copy the sorted elements into original aay - a[:] = res[:] - return a + nums[:] = res[:] + + return nums - def RadixSort(self, a): - def doCount(a, place): - n = len(a) + def RadixSort(self, nums): + if nums is None: + return nums + + n = len(nums) + if n < 2: + return nums + + def countSort(place): res = [0] * n - max_elem = max(a) count = [0] * (max_elem + 1) + # Calculate count of elements for i in range(n): - index = a[i] // place + index = nums[i] // place count[index % 10] += 1 + # Calculate cumulative count for i in range(1, max_elem + 1): count[i] += count[i-1] + # Place the elements in sorted order i = n - 1 while i >= 0: - index = a[i] // place - res[count[index % 10] - 1] = a[i] + index = nums[i] // place + res[count[index % 10] - 1] = nums[i] count[index % 10] -= 1 i -= 1 - a[:] = res[:] + + nums[:] = res[:] # Get maximum element - max_elem = max(a) + max_elem = max(nums) + # Apply counting sort to sort elements based on place value. place = 1 while max_elem // place > 0: - doCount(a, place) + countSort(place) place *= 10 - return a - def BucketSort(self, a): - n = len(a) + return nums + + def BucketSort(self, nums): + if nums is None: + return nums + + n = len(nums) + if n < 2: + return nums + # Create empty buckets bucket = [[] for i in range(n)] + # Insert elements into their respective buckets - for x in a: + for x in nums: index = int(10 * x) bucket[index].append(x) + # Sort the elements of each bucket for i in range(n): bucket[i] = sorted(bucket[i]) + # Get the sorted elements k = 0 for i in range(n): for j in range(len(bucket[i])): - a[k] = bucket[i][j] + nums[k] = bucket[i][j] k += 1 - return a -a = [3, 48, 15, 42, 26, 50, 27, 4, 34, 19, 2, 13, 36, 5, 47, 17] + return nums + +nums = [3, 48, 15, 42, 26, 50, 27, 4, 34, 19, 2, 13, 36, 5, 47, 17] sort = Sort() -print('Bubble Sort ', sort.BubbleSort(a[:])) -print('Selection Sort', sort.SelectionSort(a[:])) -print('Insertion Sort', sort.InsertionSort(a[:])) -print('Merge Sort ', sort.MergeSort(a[:])) -print('Quick Sort ', sort.QuickSort(a[:])) -print('Heap Sort ', sort.HeapSort(a[:])) -print('Counting Sort ', sort.CountingSort(a[:])) -print('Radix Sort ', sort.RadixSort(a[:])) -print('Bucket Sort ', [int(x*100) for x in sort.BucketSort([x/100 for x in a])]) \ No newline at end of file +print('Bubble Sort ', sort.BubbleSort(nums[:])) +print('Selection Sort', sort.SelectionSort(nums[:])) +print('Insertion Sort', sort.InsertionSort(nums[:])) +print('Merge Sort ', sort.MergeSort(nums[:])) +print('Quick Sort ', sort.QuickSort(nums[:])) +print('Heap Sort ', sort.HeapSort(nums[:])) +print('Counting Sort ', sort.CountingSort(nums[:])) +print('Radix Sort ', sort.RadixSort(nums[:])) +print('Bucket Sort ', [int(x*100) for x in sort.BucketSort([x/100 for x in nums])]) \ No newline at end of file diff --git a/06/NOTE.md b/06/NOTE.md index d3c2c47dc..6a764fe14 100644 --- a/06/NOTE.md +++ b/06/NOTE.md @@ -1,4 +1,4 @@ -# NOTE 06 +# 06 Dynamic Programming ------------------- diff --git a/07/NOTE.md b/07/NOTE.md index 3f6bac8a3..43a34d263 100644 --- a/07/NOTE.md +++ b/07/NOTE.md @@ -1,5 +1,62 @@ # 07 +Skip List +--------- + +> The skip list is a probabilisitc data structure that is built upon the general idea of a linked list. The skip list uses probability to build subsequent layers of linked lists upon an original linked list. Each additional layer of links contains fewer elements, but no new elements. + +``` + 1 10 + o---> o---------------------------------------------------------> o Top level + 1 3 2 5 + o---> o---------------> o---------> o---------------------------> o Level 3 + 1 2 1 2 3 2 + o---> o---------> o---> o---------> o---------------> o---------> o Level 2 + 1 1 1 1 1 1 1 1 1 1 1 + o---> o---> o---> o---> o---> o---> o---> o---> o---> o---> o---> o Bottom level +Head 1st 2nd 3rd 4th 5th 6th 7th 8th 9th 10th NIL + Node Node Node Node Node Node Node Node Node Node +``` + +| Operation | Time Complexity | +| ---------- | :-------------: | +| Access | O(log n) | +| Search | O(log n) | +| Insertion | O(log n) | +| Deletion | O(log n) | + +```py +def search(key): + p = topLeftNode() + while p.below: # Scan down + p = p.below + while key >= p.next: # Scan forward + p = p.next + return p + +def insert(key): + p, q = search(key), None + i = 1 + while CoinFlip() != 'Tails': + i = i + 1 # Height of tower for new element + if i >= h: + h = h + 1 + createNewLevel() # Creates new linked list level + while p.above is None: + p = p.prev # Scan backwards until you can go up + p = p.above + q = insertAfter(key, p) # Insert our key after position p + n = n + 1 + return q + +def delete(key): + # Search for all positions p_0, ..., p_i where key exists + if none are found: + return + # Delete all positions p_0, ..., p_i + # Remove all empty layers of skip list +``` + Trie ---- > A trie is a tree-like data structure whose nodes store the letters of an alphabet. By structuring the nodes in a particular way, words and strings can be retrieved from the structure by traversing down a branch path of the tree. @@ -64,7 +121,7 @@ Self-balancing BST > A self-balancing (or height-balanced) BST is any node-based BST that automatically keeps its height (maximal number of levels below the root) small in the face of arbitrary item insertions and deletions. -AVL Tree +### AVL Tree > In an AVL tree, the heights of the two child subtrees of any node differ by at most one; if at any time they differ by more than one, re-balancing is done to restore this property. Lookup, insertion, and deletion all take O(log n) time in both the average and worst cases. @@ -139,7 +196,7 @@ With Subtree T1 T2 T1 T2 | T3 T4 T2 T3 ``` -Red-Black Tree +### Red-Black Tree > In a red–black tree, each node stores an extra bit representing color, used to ensure that the tree remains approximately balanced during insertions and deletions. diff --git a/10/NOTE.md b/10/NOTE.md index cf2c76986..927344293 100644 --- a/10/NOTE.md +++ b/10/NOTE.md @@ -1,4 +1,4 @@ -# System Design +# 10 System Design ## 1. Requirement Clarifications diff --git a/README.md b/README.md index dc07ac79a..49d3e4bf2 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ - Array - Linked List - Stack and Queue -- Skip List [NOTE 02](02/NOTE.md) - Hash Table @@ -24,14 +23,15 @@ - A-Star Search [NOTE 05](05/NOTE.md) -- Binary Search -- Sorting +- Sorting Algorithms +- Searching Algorithms [NOTE 06](06/NOTE.md) - Dynamic Programming - Greedy [NOTE 07](07/NOTE.md) +- Skip List - Trie - Disjoint-set - Self-balancing BST (AVL tree, Red-Black tree, B- tree, B+ tree) @@ -43,7 +43,7 @@ [NOTE 09](09/NOTE.md) - String -- String-searching algorithms (Naive, Rabin-Karp, KMP, Boyer-Moore) +- String Searching algorithms (Naive, Rabin-Karp, KMP, Boyer-Moore) [NOTE 10](10/NOTES.md) - System Design \ No newline at end of file From 59c8b144f4245ed4a8b06a458954ca05c0c73aea Mon Sep 17 00:00:00 2001 From: Jason Ding Date: Mon, 30 Aug 2021 11:49:13 -0400 Subject: [PATCH 48/48] fix some link --- 09/NOTE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/09/NOTE.md b/09/NOTE.md index d1ac8c314..43dd3a717 100644 --- a/09/NOTE.md +++ b/09/NOTE.md @@ -69,7 +69,7 @@ def slidingWindow(s, t): ``` String Basics -- [709. To Lower Case](https://leetcode-cn.com/problems/to-lower-case/) +- [709. To Lower Case](https://leetcode.com/problems/to-lower-case/) - [58. Length of Last Word](https://leetcode.com/problems/length-of-last-word/) - [771. Jewels and Stones](https://leetcode.com/problems/jewels-and-stones/) - [387. First Unique Character in a String](https://leetcode.com/problems/first-unique-character-in-a-string/)