diff --git a/Week_02/leetcode264.py b/Week_02/leetcode264.py new file mode 100644 index 0000000..cd7c745 --- /dev/null +++ b/Week_02/leetcode264.py @@ -0,0 +1,13 @@ +class Solution: + def nthUglyNumber(self, n: int) -> int: + dp, a, b, c = [1], 0, 0, 0 + for i in range(1, n): + n2, n3, n5 = dp[a]*2, dp[b]*3, dp[c]*5 + dp.append(min(n2, n3, n5)) + if dp[-1] == n2: + a += 1 + if dp[-1] == n3: + b += 1 + if dp[-1] == n5: + c += 1 + return dp[-1] \ No newline at end of file diff --git a/Week_02/leetcode347.py b/Week_02/leetcode347.py new file mode 100644 index 0000000..a1a2c22 --- /dev/null +++ b/Week_02/leetcode347.py @@ -0,0 +1,17 @@ +class Solution: + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + # count = collections.Counter(nums) + # res = [(k, v) for k, v in count.items()] + # res.sort(key=lambda x: x[1], reverse=True) + # return [i[0] for i in res][:k] + + return [i[0] for i in collections.Counter(nums).most_common(k)] + + # frq = defaultdict(list) + # for key, cnt in Counter(nums).items(): + # frq[cnt].append(key) + # res = [] + # for times in range(len(nums), 0, -1): + # if times in frq: + # res.extend(frq[times]) + # if len(res) >= k: return res[:k] \ No newline at end of file diff --git a/Week_03/README.md b/Week_03/README.md index 50de304..6323a99 100644 --- a/Week_03/README.md +++ b/Week_03/README.md @@ -1 +1,30 @@ -学习笔记 \ No newline at end of file +学习笔记 +1. 树的面试题解法一般都是递归 + + 因为树本身就是递归定义的,有其重复性 + +2. 递归本质上类似于循环(底层汇编) + +3. 递归模板(一定要机械化记忆) + + 递归终止条件 + + 处理当前层逻辑 + + 下探到下一层 + + 清理当前层 + +4. 写递归的思维要点 + + 不要人肉进行递归(最大误区,可以直接按递归模板来) + + 找最近重复子问题 + + 数学归纳法思维 + +5. 分治和回溯是特殊的递归(找最近重复性) + + 动态规划(最优重复性) + +6. 回溯算法类似于DFS,关键细节在剪枝 \ No newline at end of file diff --git a/Week_03/leetcode47.py b/Week_03/leetcode47.py new file mode 100644 index 0000000..248ccc5 --- /dev/null +++ b/Week_03/leetcode47.py @@ -0,0 +1,14 @@ +class Solution: + def permuteUnique(self, nums: List[int]) -> List[List[int]]: + def dfs(nums, path): + if not nums: + res.append(path) + return + for i in range(len(nums)): + if i > 0 and nums[i] == nums[i-1]: + continue + dfs(nums[:i]+nums[i+1:], path+[nums[i]]) + nums.sort() + res = [] + dfs(nums, []) + return res \ No newline at end of file diff --git a/Week_03/leetcode77.py b/Week_03/leetcode77.py new file mode 100644 index 0000000..81258ac --- /dev/null +++ b/Week_03/leetcode77.py @@ -0,0 +1,16 @@ +class Solution: + def combine(self, n: int, k: int) -> List[List[int]]: + # 库函数 + # return list(combinations(range(1, n+1), k)) + + def dfs(num): + if len(com) == k: + res.append(com[:]) + return + for j in range(num, n+1): + com.append(j) + dfs(j+1) + com.pop() + res, com = [], [] + dfs(1) + return res \ No newline at end of file diff --git a/Week_04/README.md b/Week_04/README.md index 50de304..a4b0ed6 100644 --- a/Week_04/README.md +++ b/Week_04/README.md @@ -1 +1,15 @@ -学习笔记 \ No newline at end of file +学习笔记 + +1. 深度优先搜索和广度优先搜索都是暴力搜索,不含任何智能,本质上是对每个节点遍历一遍 + 牢记模板,多进行训练即可 + +2. 贪心算法:局部最优->全局最优(要能证明) + 可以解决一些最优化的问题 + +3. 二分查找的前提 + + 1)目标函数单调性 + 2)存在上下界 + 3)能够通过索引访问 + +4. 搜索遍历所有节点,贪心,动态规划都可以解决最优化问题 \ No newline at end of file diff --git a/Week_04/homework.py b/Week_04/homework.py new file mode 100644 index 0000000..beac93a --- /dev/null +++ b/Week_04/homework.py @@ -0,0 +1,22 @@ +# 题目:使用二分查找,寻找一个半有序数组 [4, 5, 6, 7, 0, 1, 2] 中间无序的地方 +def find_point(nums): + left, right = 0, len(nums) - 1 + while left < right: + # 已经有序,说明找到旋转点或者原数组没有旋转,可以退出循环 + if nums[left] < nums[right]: + break + mid = (left + right) // 2 + # 还在升序序列中,说明旋转点还在右边,改变 left + # 这里取等于是因为前面的计算 mid 会偏向 left, 即可能 mid = left,所以 left 变化时要加一 + if nums[mid] >= nums[left]: + left = mid + 1 + else: + right = mid + return left + + +if __name__ == "__main__": + print(find_point([4, 5, 6, 7, 0, 1, 2])) + print(find_point([7, 8, 0, 1, 2, 3, 4])) + print(find_point([1, 3])) + print(find_point([3, 1])) diff --git a/Week_04/leetcode102.py b/Week_04/leetcode102.py new file mode 100644 index 0000000..1187f3b --- /dev/null +++ b/Week_04/leetcode102.py @@ -0,0 +1,37 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + def levelOrder(self, root: TreeNode) -> List[List[int]]: + # BFS + # queue = deque([root]) + # res = [] + # while queue: + # n = len(queue) + # tmp = [] + # for _ in range(n): + # root = queue.popleft() + # if root: + # tmp.append(root.val) + # queue.append(root.left) + # queue.append(root.right) + # if tmp: + # res.append(tmp) + # return res + + # DFS + def dfs(level, root): + if not root: + return + if level == len(res): + res.append([]) + res[level].append(root.val) + dfs(level+1, root.left) + dfs(level+1, root.right) + res = [] + dfs(0, root) + return res \ No newline at end of file diff --git a/Week_04/leetcode126.py b/Week_04/leetcode126.py new file mode 100644 index 0000000..82ec444 --- /dev/null +++ b/Week_04/leetcode126.py @@ -0,0 +1,22 @@ +class Solution: + def findLadders(self, beginWord: str, endWord: str, wordList: List[str]) -> List[List[str]]: + word_set = set(wordList) + if endWord not in word_set: + return [] + word_dict = defaultdict(list) + for word in word_set: + for i in range(len(word)): + word_dict[word[:i] + '*' + word[i+1:]].append(word) + layer = {beginWord: [[beginWord]]} + while layer: + if endWord in layer: + return layer[endWord] + new_layer = defaultdict(list) + for word in layer: + for i in range(len(word)): + for new_word in word_dict[word[:i] + '*' + word[i+1:]]: + if new_word in word_set: + new_layer[new_word] += [j + [new_word] for j in layer[word]] + word_set -= set(new_layer.keys()) + layer = new_layer + return [] \ No newline at end of file diff --git a/Week_04/leetcode433.py b/Week_04/leetcode433.py new file mode 100644 index 0000000..36c5212 --- /dev/null +++ b/Week_04/leetcode433.py @@ -0,0 +1,53 @@ +class Solution: + def minMutation(self, start: str, end: str, bank: List[str]) -> int: + # BFS + # bank = set(bank) + # if end not in bank: + # return -1 + # change = { + # 'A': 'CGT', + # 'C': 'AGT', + # 'G': 'ACT', + # 'T': 'ACG' + # } + # queue = deque([(start, 0)]) + # while queue: + # start, step = queue.popleft() + # for i, v in enumerate(start): + # for c in change[v]: + # new_start = start[:i] + c + start[i+1:] + # if new_start == end: + # return step + 1 + # if new_start in bank: + # queue.append((new_start, step + 1)) + # bank.remove(new_start) + # return -1 + + # 双向BFS + bank = set(bank) + if end not in bank: + return -1 + change = { + 'A': 'CGT', + 'C': 'AGT', + 'G': 'ACT', + 'T': 'ACG' + } + start_set, end_set = {start}, {end} + step = 0 + while start_set: + step += 1 + new_set = set() + for start in start_set: + for i, v in enumerate(start): + for c in change[v]: + new_start = start[:i] + c + start[i+1:] + if new_start in end_set: + return step + if new_start in bank: + new_set.add(new_start) + bank.remove(new_start) + start_set = new_set + if len(end_set) < len(start_set): + start_set, end_set = end_set, start_set + return -1 \ No newline at end of file diff --git a/Week_04/leetcode45.py b/Week_04/leetcode45.py new file mode 100644 index 0000000..69a2237 --- /dev/null +++ b/Week_04/leetcode45.py @@ -0,0 +1,36 @@ +class Solution: + def jump(self, nums: List[int]) -> int: + # BFS + start, end, true_end = 0, 0, len(nums) - 1 + step = 0 + while end < true_end: + step += 1 + start, end = end + 1, max(i + nums[i] for i in range(start, end + 1)) + return step + + # if len(nums) < 2: + # return 0 + # queue = deque([0]) + # step, position, end = 0, 0, len(nums) - 1 + # while queue: + # step += 1 + # n = len(queue) + # for _ in range(n): + # i = queue.popleft() + # if i + nums[i] >= end: + # return step + # position = max(position, i + nums[i]) + + # queue.extend(range(i+1, position+1)) + + # if len(nums) < 2: + # return 0 + # step, cur_end, farthest = 0, 0, 0 + # end = len(nums) - 1 + # for i in range(end): + # farthest = max(farthest, i + nums[i]) + # if i == cur_end: + # step += 1 + # cur_end = farthest + # if cur_end >= end: + # return step \ No newline at end of file diff --git a/Week_04/leetcode515.py b/Week_04/leetcode515.py new file mode 100644 index 0000000..7da9481 --- /dev/null +++ b/Week_04/leetcode515.py @@ -0,0 +1,43 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def largestValues(self, root: TreeNode) -> List[int]: + # BFS + # if not root: + # return [] + # queue, res = deque([root]), [] + # while queue: + # num = -math.inf + # n = len(queue) + # for _ in range(n): + # node = queue.popleft() + # num = max(num, node.val) + # if node.left: + # queue.append(node.left) + # if node.right: + # queue.append(node.right) + # res.append(num) + # return res + queue, res = [root], [] + while any(queue): + res.append(max(node.val for node in queue)) + queue = [kid for node in queue for kid in (node.left, node.right) if kid] + return res + + # DFS + # def dfs(level, root): + # if not root: + # return + # if level == len(res): + # res.append(root.val) + # else: + # res[level] = max(res[level], root.val) + # dfs(level+1, root.left) + # dfs(level+1, root.right) + # res = [] + # dfs(0, root) + # return res \ No newline at end of file diff --git a/Week_04/leetcode69.py b/Week_04/leetcode69.py new file mode 100644 index 0000000..4c3262e --- /dev/null +++ b/Week_04/leetcode69.py @@ -0,0 +1,24 @@ +class Solution: + def mySqrt(self, x: int) -> int: + # 二分 + # left, right = 0, x + # res = 0 + # while left <= right: + # mid = (left + right) // 2 + # if mid * mid <= x: + # res = mid + # left = mid + 1 + # else: + # right = mid - 1 + # return res + + # 牛顿 + # r = x + # while r * r > x: + # r = (r + x // r) // 2 + # return r + + r = x + while r * r - x > 1e-6: + r = (r + x / r) / 2 + return int(r) \ No newline at end of file diff --git a/Week_05/README.md b/Week_05/README.md index 50de304..e69de29 100644 --- a/Week_05/README.md +++ b/Week_05/README.md @@ -1 +0,0 @@ -学习笔记 \ No newline at end of file diff --git a/Week_06/README.md b/Week_06/README.md index 50de304..93ad91c 100644 --- a/Week_06/README.md +++ b/Week_06/README.md @@ -1 +1,31 @@ -学习笔记 \ No newline at end of file +学习笔记 + +1. 分治 + 回溯 + 递归 + 动态规划(动态递推) + + 本质:将复杂问题分解为各种子问题,同时寻找重复性 + +2. 写递归程序方法论(本质:寻找重复性) + + - 人肉递归低效、很累,要抵制人肉递归 + + - 找到最近最简方法,将其拆解成可重复解决的子问题 + + - 数学归纳法思维 + +3. 动态规划(动态递推):分治 + 最优子结构 + + 关键点 + + - 动态规划和递归、分治没有根本上的差别(关键看有无最优子结构) + + - 共性:找到重复子问题 + + - 差异性:最优子结构,中途可以淘汰次优解 + + 解法: + + - 自顶向下:递归加记忆化搜索 + + - 自底向上:循环(找递推公式) + + 步骤:1、寻找最优子结构;2、状态数组定义;3、递推方程(DP方程、状态转移方程) \ No newline at end of file diff --git a/Week_06/leetcode1143.py b/Week_06/leetcode1143.py new file mode 100644 index 0000000..57d723c --- /dev/null +++ b/Week_06/leetcode1143.py @@ -0,0 +1,21 @@ +class Solution: + def longestCommonSubsequence(self, text1: str, text2: str) -> int: + # m, n = len(text1), len(text2) + # dp = [[0] * (n + 1) for _ in range(m + 1)] + # for i in range(1, m + 1): + # for j in range(1, n + 1): + # if text1[i - 1] == text2[j - 1]: + # dp[i][j] = dp[i - 1][j - 1] + 1 + # else: + # dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + # return dp[m][n] + + m, n = len(text1), len(text2) + dp = [0] * (n + 1) + for i in range(m): + pre = 0 + for j in range(n): + tmp = dp[j] + dp[j] = pre + 1 if text1[i] == text2[j] else max(dp[j], dp[j - 1]) + pre = tmp + return dp[-2] \ No newline at end of file diff --git a/Week_06/leetcode152.py b/Week_06/leetcode152.py new file mode 100644 index 0000000..39b226e --- /dev/null +++ b/Week_06/leetcode152.py @@ -0,0 +1,17 @@ +class Solution: + def maxProduct(self, nums: List[int]) -> int: + # 状态数组:dp[0,1][i] 包含nums[i]的子数组的最小,最大乘积 + # DP 方程 + # dp = [[0] * len(nums) for _ in range(2)] + # dp[0][0], dp[1][0] = nums[0], nums[0] + # for i in range(1, len(nums)): + # dp[0][i] = min(dp[0][i - 1] * nums[i], dp[1][i - 1] * nums[i], nums[i]) + # dp[1][i] = max(dp[0][i - 1] * nums[i], dp[1][i - 1] * nums[i], nums[i]) + # return max(dp[1]) + + # 状态压缩 + maxp, minp, res = nums[0], nums[0], nums[0] + for i in range(1, len(nums)): + minp, maxp = min(minp * nums[i], maxp * nums[i], nums[i]), max(minp * nums[i], maxp * nums[i], nums[i]) + res = max(res, maxp) + return res \ No newline at end of file diff --git a/Week_06/leetcode213.py b/Week_06/leetcode213.py new file mode 100644 index 0000000..bde11bd --- /dev/null +++ b/Week_06/leetcode213.py @@ -0,0 +1,8 @@ +class Solution: + def rob(self, nums: List[int]) -> int: + def helper(start, end): + a, b = 0, 0 + for i in range(start, end): + a, b = b, max(b, a + nums[i]) + return b + return max(helper(0, len(nums) - 1), helper(1, len(nums))) if len(nums) > 1 else nums[0] \ No newline at end of file diff --git a/Week_06/leetcode221.py b/Week_06/leetcode221.py new file mode 100644 index 0000000..9ee0ea7 --- /dev/null +++ b/Week_06/leetcode221.py @@ -0,0 +1,27 @@ +class Solution: + def maximalSquare(self, matrix: List[List[str]]) -> int: + # 求最大面积,找最大边长 + # 状态数组 dp[i][j] = min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1 + # 以(i, j)为右下角的正方形的边长 + # m, n = len(matrix), len(matrix[0]) + # dp = [[0 for _ in range(n + 1)] for _ in range(m + 1)] + # res = 0 + # for i in range(m): + # for j in range(n): + # if matrix[i][j] == '1': + # dp[i + 1][j + 1] = min(dp[i + 1][j], dp[i][j + 1], dp[i][j]) + 1 + # res = max(res, dp[i + 1][j + 1]) + # return res * res + + # 状态压缩 + m, n = len(matrix), len(matrix[0]) + dp = [0] * (n + 1) + res = 0 + for i in range(m): + pre = 0 + for j in range(n): + tmp = dp[j] + dp[j] = min(pre, dp[j], dp[j - 1]) + 1 if matrix[i][j] == '1' else 0 + pre = tmp + res = max(res, dp[j]) + return res * res \ No newline at end of file diff --git a/Week_06/leetcode312.py b/Week_06/leetcode312.py new file mode 100644 index 0000000..4e05a7c --- /dev/null +++ b/Week_06/leetcode312.py @@ -0,0 +1,12 @@ +class Solution: + def maxCoins(self, nums: List[int]) -> int: + nums.insert(0, 1) + nums.append(1) + n = len(nums) + dp = [[0 for _ in range(n)] for _ in range(n)] + for m in range(2, n): + for i in range(n - m): + j = i + m + for k in range(i + 1, j): + dp[i][j] = max(dp[i][j], dp[i][k] + dp[k][j] + nums[i] * nums[k] * nums[j]) + return dp[0][-1] \ No newline at end of file diff --git a/Week_06/leetcode32.py b/Week_06/leetcode32.py new file mode 100644 index 0000000..f47b642 --- /dev/null +++ b/Week_06/leetcode32.py @@ -0,0 +1,48 @@ +class Solution: + def longestValidParentheses(self, s: str) -> int: + # n = len(s) + # dp = [0] * n + # res = 0 + # for i in range(1, len(s)): + # if s[i] == ')': + # if s[i - 1] == '(': + # dp[i] = dp[i - 2] + 2 + # elif i - dp[i - 1] - 1 >= 0 and s[i - dp[i - 1] - 1] == '(': + # dp[i] = dp[i - 1] + 2 + dp[i - dp[i - 1] - 2] + # res = max(res, dp[i]) + # return res + + # stack, res = [-1], 0 + # for i, c in enumerate(s): + # if c == '(': + # stack.append(i) + # else: + # stack.pop() + # if not stack: + # stack.append(i) + # else: + # res = max(res, i - stack[-1]) + # return res + + res = 0 + left, right = 0, 0 + for c in s: + if c == '(': + left += 1 + else: + right += 1 + if right > left: + left, right = 0, 0 + elif right == left: + res = max(res, right * 2) + left, right = 0, 0 + for c in s[::-1]: + if c == ')': + right += 1 + else: + left += 1 + if left > right: + left, right = 0, 0 + elif left == right: + res = max(res, left * 2) + return res \ No newline at end of file diff --git a/Week_06/leetcode322.py b/Week_06/leetcode322.py new file mode 100644 index 0000000..8b6071e --- /dev/null +++ b/Week_06/leetcode322.py @@ -0,0 +1,28 @@ +class Solution: + def coinChange(self, coins: List[int], amount: int) -> int: + # DP + # dp = [0] + [amount + 1] * amount + # for i in range(1, amount + 1): + # for coin in coins: + # if i - coin >= 0: + # dp[i] = min(dp[i], dp[i - coin] + 1) + # return dp[amount] if dp[amount] != amount + 1 else -1 + + # dfs + def dfs(amount, i, count): + if amount == 0: + self.res = min(self.res, count) + return 1 + if i == len(coins): + return + k = amount // coins[i] + if count + k >= self.res: + return + while k >= 0: + if dfs(amount - k * coins[i], i + 1, count + k): + return + k -= 1 + self.res = amount + 1 + coins.sort(reverse=True) + dfs(amount, 0, 0) + return self.res if self.res != amount + 1 else -1 \ No newline at end of file diff --git a/Week_06/leetcode403.py b/Week_06/leetcode403.py new file mode 100644 index 0000000..a502c7c --- /dev/null +++ b/Week_06/leetcode403.py @@ -0,0 +1,43 @@ +class Solution: + def canCross(self, stones: List[int]) -> bool: + n = len(stones) + dp = [[False for _ in range(n + 1)] for _ in range(n)] + dp[0][0] = True + for i in range(1, n): + for j in range(i): + k = stones[i] - stones[j] + if k <= i: + dp[i][k] = dp[j][k - 1] or dp[j][k] or dp[j][k + 1] + return any(dp[-1]) + + # dp = dict() + # for stone in stones: + # dp[stone] = set() + # dp[0].add(0) + # for i, stone in enumerate(stones): + # for k in dp[stone]: + # for step in range(k - 1, k + 2): + # if step > 0 and stone + step in dp: + # dp[stone + step].add(step) + # return len(dp[stones[-1]]) != 0 + + + + # def helper(j, k): + # if dp[j][k] > -1: + # return dp[j][k] + # if j == n - 1: + # return 1 + # for i in range(j + 1, n): + # gap = stones[i] - stones[j] + # if k - 1 <= gap <= k + 1: + # if helper(i, gap) == 1: + # dp[i][gap] = 1 + # return 1 + # else: + # dp[i][gap] = 0 + # return 0 + + # n = len(stones) + # dp = [[-1 for _ in range(n)] for _ in range(n)] + # return helper(0, 0) == 1 diff --git a/Week_06/leetcode410.py b/Week_06/leetcode410.py new file mode 100644 index 0000000..c04b5f5 --- /dev/null +++ b/Week_06/leetcode410.py @@ -0,0 +1,31 @@ +class Solution: + def splitArray(self, nums: List[int], m: int) -> int: + # 二分 + # left, right = max(nums), sum(nums) + # while left < right: + # mid = (left + right) // 2 + # count, tmp = 0, 0 + # for i in nums: + # tmp += i + # if tmp > mid: + # tmp = i + # count += 1 + # count += 1 + # if count > m: + # left = mid + 1 + # else: + # right = mid + # return left + + # dp[i][j] 前 i 个数分成 j 段的最大值的最小 + n = len(nums) + dp = [[math.inf for _ in range(m + 1)] for _ in range(n + 1)] + dp[0][0] = 0 + sub = [0] + for i in nums: + sub.append(sub[-1] + i) + for i in range(1, n + 1): + for j in range(1, min(i, m) + 1): + for k in range(i): + dp[i][j] = min(dp[i][j], max(dp[k][j - 1], sub[i] - sub[k])) + return dp[n][m] \ No newline at end of file diff --git a/Week_06/leetcode53.py b/Week_06/leetcode53.py new file mode 100644 index 0000000..8441e40 --- /dev/null +++ b/Week_06/leetcode53.py @@ -0,0 +1,15 @@ +class Solution: + def maxSubArray(self, nums: List[int]) -> int: + # n = len(nums) + # dp = [0 for _ in range(n)] + # dp[0], res = nums[0], nums[0] + # for i in range(1, n): + # dp[i] = max(dp[i - 1], 0) + nums[i] + # res = max(dp[i], res) + # return res + + pre = res = nums[0] + for i in range(1, len(nums)): + pre = max(pre, 0) + nums[i] + res = max(pre, res) + return res \ No newline at end of file diff --git a/Week_06/leetcode552.py b/Week_06/leetcode552.py new file mode 100644 index 0000000..357a351 --- /dev/null +++ b/Week_06/leetcode552.py @@ -0,0 +1,45 @@ +class Solution: + def checkRecord(self, n: int) -> int: + # O(3^n) + # @lru_cache + # def dfs(level, hasA, L1, L2): + # if level == n: + # return 1 + # p, a, l = 0, 0, 0 + # p = dfs(level + 1, hasA, False, False) # P + # if not hasA: + # a = dfs(level + 1, True, False, False) # A + # if not (L1 and L2): + # l = dfs(level + 1, hasA, L2, True) # L + # return (p + a + l) % (10 ** 9 + 7) + # return dfs(0, False, False, False) + + # dp + # _mod = 10 ** 9 + 7 + # dp = [[[0 for _ in range(3)] for _ in range(2)] for _ in range(n)] + # dp[0][0][0], dp[0][0][1], dp[0][1][0] = 1, 1, 1 + # for i in range(1, n): + # # p + # dp[i][0][0] = (dp[i-1][0][0] + dp[i-1][0][1] + dp[i-1][0][2]) % _mod + # dp[i][1][0] = (dp[i-1][1][0] + dp[i-1][1][1] + dp[i-1][1][2]) % _mod + + # # l + # dp[i][0][1] = dp[i-1][0][0] + # dp[i][0][2] = dp[i-1][0][1] + # dp[i][1][1] = dp[i-1][1][0] + # dp[i][1][2] = dp[i-1][1][1] + + # # a + # dp[i][1][0] += (dp[i-1][0][0] + dp[i-1][0][1] + dp[i-1][0][2]) % _mod + # return (sum(dp[-1][0]) + sum(dp[-1][1])) % _mod + + _mod = 10 ** 9 + 7 + dp = [[0 for _ in range(3)] for _ in range(2)] + dp[0][0], dp[0][1], dp[1][0] = 1, 1, 1 + for i in range(1, n): + t00, t10 = dp[0][0], dp[1][0] + dp[1][0] = (sum(dp[0]) + sum(dp[1])) % _mod + dp[0][0] = (t00 + dp[0][1] + dp[0][2]) % _mod + dp[0][1], dp[0][2] = t00, dp[0][1] + dp[1][1], dp[1][2] = t10, dp[1][1] + return (sum(dp[0]) + sum(dp[1])) % _mod \ No newline at end of file diff --git a/Week_06/leetcode62.py b/Week_06/leetcode62.py new file mode 100644 index 0000000..d99cf35 --- /dev/null +++ b/Week_06/leetcode62.py @@ -0,0 +1,16 @@ +class Solution: + def uniquePaths(self, m: int, n: int) -> int: + # dp = [0 for _ in range(n)] + # for i in range(m): + # for j in range(n): + # if i == 0 or j == 0: + # dp[j] = 1 + # else: + # dp[j] += dp[j - 1] + # return dp[-1] + + dp = [1] * n + for i in range(1, m): + for j in range(1, n): + dp[j] += dp[j - 1] + return dp[-1] \ No newline at end of file diff --git a/Week_06/leetcode63.py b/Week_06/leetcode63.py new file mode 100644 index 0000000..dd670a3 --- /dev/null +++ b/Week_06/leetcode63.py @@ -0,0 +1,42 @@ +class Solution: + def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int: + # m, n = len(obstacleGrid), len(obstacleGrid[0]) + # if obstacleGrid[0][0] == 1: + # return 0 + # dp = [[0] * n for _ in range(m)] + # dp[0][0] = 1 + # for i in range(1, m): + # if obstacleGrid[i][0] == 1: + # dp[i][0] = 0 + # else: + # dp[i][0] = dp[i - 1][0] + # for j in range(1, n): + # if obstacleGrid[0][j] == 1: + # dp[0][j] = 0 + # else: + # dp[0][j] = dp[0][j - 1] + # for i in range(1, m): + # for j in range(1, n): + # if obstacleGrid[i][j] == 1: + # dp[i][j] = 0 + # else: + # dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + # return dp[-1][-1] + + # m, n = len(obstacleGrid), len(obstacleGrid[0]) + # dp = [0] * n + # dp[0] = 1 + # for i in range(m): + # for j in range(n): + # if obstacleGrid[i][j] == 1: + # dp[j] = 0 + # elif j >= 1: + # dp[j] += dp[j - 1] + # return dp[-1] + + m, n = len(obstacleGrid), len(obstacleGrid[0]) + dp = [1] + [0] * n + for i in range(m): + for j in range(n): + dp[j] = 0 if obstacleGrid[i][j] else dp[j] + dp[j - 1] + return dp[-2] \ No newline at end of file diff --git a/Week_06/leetcode64.py b/Week_06/leetcode64.py new file mode 100644 index 0000000..24e000c --- /dev/null +++ b/Week_06/leetcode64.py @@ -0,0 +1,9 @@ +class Solution: + def minPathSum(self, grid: List[List[int]]) -> int: + # 状态数组 dp[j] = min(dp[j], dp[j - 1]) + grid[i][j] + m, n = len(grid), len(grid[0]) + dp = [0] + [math.inf] * n + for i in range(m): + for j in range(n): + dp[j] = min(dp[j], dp[j - 1]) + grid[i][j] + return dp[-2] \ No newline at end of file diff --git a/Week_06/leetcode647.py b/Week_06/leetcode647.py new file mode 100644 index 0000000..a31fe3c --- /dev/null +++ b/Week_06/leetcode647.py @@ -0,0 +1,24 @@ +class Solution: + def countSubstrings(self, s: str) -> int: + # dp[i][j] (i, j)间是否是回文串 i <= j + # n = len(s) + # dp = [[0 for _ in range(n)] for _ in range(n)] + # count = 0 + # for j in range(n): + # for i in range(j + 1): + # if s[i] == s[j] and (j - i < 2 or dp[i + 1][j - 1]): + # dp[i][j] = True + # count += 1 + # return count + + n = len(s) + dp = [False for _ in range(n)] + count = 0 + for j in range(n): + for i in range(j + 1): + if s[i] == s[j] and (j - i < 2 or dp[i + 1]): + dp[i] = True + count += 1 + else: + dp[i] = False + return count \ No newline at end of file diff --git a/Week_06/leetcode91.py b/Week_06/leetcode91.py new file mode 100644 index 0000000..a525d10 --- /dev/null +++ b/Week_06/leetcode91.py @@ -0,0 +1,26 @@ +class Solution: + def numDecodings(self, s: str) -> int: + # 从后往前推 + # dp[i] = dp[i + 1] + dp[i + 2] + # if s[0] == '0': + # return 0 + # dp = [0] * (len(s) + 1) + # dp[-1], dp[-2] = 1, int(s[-1] != '0') + # for i in range(len(s) - 2, -1, -1): + # if s[i] == '0': + # continue + # if int(s[i: i + 2]) > 26: + # dp[i] = dp[i + 1] + # else: + # dp[i] = dp[i + 1] + dp[i + 2] + # return dp[0] + + pre, ans = 1, int(s[-1] != '0') + for i in range(len(s) - 2, -1, -1): + if s[i] == '0': + pre, ans = ans, 0 + elif int(s[i: i + 2]) > 26: + pre, ans = ans, ans + else: + pre, ans = ans, pre + ans + return ans \ No newline at end of file diff --git a/Week_07/README.md b/Week_07/README.md index 50de304..9ab0b8b 100644 --- a/Week_07/README.md +++ b/Week_07/README.md @@ -1 +1,112 @@ -学习笔记 \ No newline at end of file +学习笔记 + +1. 字典树(trie) + + - 数据结构 + + python 使用 dict 来动态实现 + + - 核心思想 + + 空间换时间 + + 利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的 + + - 基本性质 + + 节点本身不存完整单词 + + 从根节点到某一节点,路径上经过的字符串连接起来,为该节点对应的字符串 + + 每个节点的所有子节点路径代表的路径都不相同 + +2. 并查集 + + 适用场景:组团、配对问题、找连通分量 + + 基本操作:建群、合并群、查找群主 + +3. 初级搜索 -> 高级搜索 + + 优化方式:不重复(fabonacci)、剪枝(生成括号问题) + + 搜索方向:双向搜索(BFS)、启发式搜索(优先级搜索) + +4. 平衡二叉树 + + 当二叉搜索树退化为链表时,查询性能下降,需要左右子树节点平衡 + + 种类:2-3 tree;AVL tree;B-tree;Red-black tree;splay tree;treap + +5. AVL tree + + - balance factor(平衡因子)= {-1, 0, 1} + + 左子树的高度减去右子树的高度(有时相反) + + - 通过旋转操作进行平衡 + + 左旋: 右右子树 + + 右旋: 左左子树 + + 左右旋: 左右子树 + + 右左旋: 右左子树 + + - 不足 + + 节点需要存储额外信息,且调整次数频繁 + +6. 红黑树 + + 红黑树是一种近似平衡的二叉搜索树,它能够确保任何一个节点的左右子树的高度差小于两倍。 + + - 5个特点 + + 每个节点要么是红色,要么是黑色 + + 根节点是黑色 + + 每个叶子节点(空节点)是黑色 + + 不能有相邻接的两个红色节点 + + 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点 + + - 关键性质 + + 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长 + + - AVL 和红黑树对比 + + AVL 严格平衡,查找性能更好 + + 红黑树提供更快的插入、删除操作,平衡要求相对较松 + + AVL 节点需要存储额外信息(平衡因子,高度),内存空间消耗大,红黑树只需要一个bit来存红或黑 + +7. 课后作业 + + 7.1 单词搜索II 使用前缀树的时间复杂度为O(m * n * 4^k)(m,n 为board的行数和列数,k为单词平均长度),详细代码可查看作业 + + 7.2 双向 BFS 代码模板 + +```python + def BFS(graph, start, end): +     visited = set() + front = {start} + back = {end} + while front:  + for node in front: + visited.add(node) + process(node)  + nodes = generate_related_nodes(node)  + new_front.add(nodes) + front = new_front + if len(back) < len(front): + front, end = end, front + # other processing work +  ... +``` + diff --git a/Week_07/leetcode130.py b/Week_07/leetcode130.py new file mode 100644 index 0000000..8350fe5 --- /dev/null +++ b/Week_07/leetcode130.py @@ -0,0 +1,84 @@ +class Solution: + def solve(self, board: List[List[str]]) -> None: + """ + Do not return anything, modify board in-place instead. + """ + # BFS + if not board or not board[0]: + return + m, n = len(board), len(board[0]) + q = deque() + for i in range(m): + for j in range(n): + if i == 0 or i == m - 1 or j == 0 or j == n - 1 and board[i][j] == 'O': + q.append((i, j)) + while q: + x, y = q.popleft() + if 0 <= x < m and 0 <= y < n and board[x][y] == 'O': + board[x][y] = '#' + q.extend([(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)]) + for i in range(m): + for j in range(n): + if board[i][j] == 'O': + board[i][j] = 'X' + elif board[i][j] == '#': + board[i][j] = 'O' + + + # def dfs(x, y): + # board[x][y] = '#' + # for dx, dy in ((-1, 0), (0, 1), (1, 0), (0, -1)): + # i, j = x + dx, y + dy + # if 0 <= i < m and 0 <= j < n and board[i][j] == 'O': + # dfs(i, j) + # if not board or not board[0]: + # return + # m, n = len(board), len(board[0]) + # for i in range(m): + # if board[i][0] == 'O': + # dfs(i, 0) + # if board[i][n - 1] == 'O': + # dfs(i, n - 1) + # for i in range(n): + # if board[0][i] == 'O': + # dfs(0, i) + # if board[m - 1][i] == 'O': + # dfs(m - 1, i) + # for i in range(m): + # for j in range(n): + # if board[i][j] == 'O': + # board[i][j] = 'X' + # elif board[i][j] == '#': + # board[i][j] = 'O' + + # UnionFindSet + # if not board or not board[0]: + # return + # m, n = len(board), len(board[0]) + # p, dummy = UnionFindSet(m * n + 1), m * n + # for i in range(m): + # for j in range(n): + # if board[i][j] == 'O': + # if i == 0 or i == m - 1 or j == 0 or j == n - 1: + # p.union(i * n + j, dummy) + # else: + # for dx, dy in ((-1, 0), (0, 1), (1, 0), (0, -1)): + # x, y = i + dx, j + dy + # if board[x][y] == 'O': + # p.union(i * n + j, x * n + y) + # for i in range(m): + # for j in range(n): + # if p.find(i * n + j) != p.find(dummy): + # board[i][j] = 'X' + +class UnionFindSet: + def __init__(self, n): + self.p = [i for i in range(n)] + + def union(self, i, j): + self.p[self.find(j)] = self.find(i) + + def find(self, i): + if self.p[i] != i: + self.p[i] = self.find(self.p[i]) + return self.p[i] \ No newline at end of file diff --git a/Week_07/leetcode200.py b/Week_07/leetcode200.py new file mode 100644 index 0000000..cfc1e18 --- /dev/null +++ b/Week_07/leetcode200.py @@ -0,0 +1,49 @@ +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + # def dfs(i, j): + # grid[i][j] = '0' + # for k in range(4): + # x, y = i + dx[k], j + dy[k] + # if 0 <= x < m and 0 <= y < n and grid[x][y] == '1': + # dfs(x, y) + + + # dx = [-1, 0, 1, 0] + # dy = [0, 1, 0, -1] + # m, n = len(grid), len(grid[0]) + # res = 0 + # for i in range(m): + # for j in range(n): + # if grid[i][j] == '1': + # res += 1 + # dfs(i, j) + # return res + + m, n = len(grid), len(grid[0]) + p = defaultdict(int) + for i in range(m): + for j in range(n): + if grid[i][j] == '1': + p[i * n + j] = i * n + j + for i in range(m): + for j in range(n): + if grid[i][j] == '1': + for dx, dy in ((0, 1), (1, 0)): + x, y = i + dx, j + dy + if 0 <= x < m and 0 <= y < n and grid[x][y] == '1': + self.union(p, i * n + j, x * n + y) + res = len(set(self.find(p, i) for i in p)) + return res + + def union(self, p, i, j): + p1 = self.find(p, i) + p2 = self.find(p, j) + p[p2] = p1 + + def find(self, p, i): + root = i + while p[root] != root: + root = p[root] + while p[i] != i: + p[i], i = root, p[i] + return root \ No newline at end of file diff --git a/Week_07/leetcode208.py b/Week_07/leetcode208.py new file mode 100644 index 0000000..b717fdb --- /dev/null +++ b/Week_07/leetcode208.py @@ -0,0 +1,50 @@ +class Trie: + + def __init__(self): + """ + Initialize your data structure here. + """ + self.root = dict() + self.end = '#' + + + def insert(self, word: str) -> None: + """ + Inserts a word into the trie. + """ + node = self.root + for c in word: + node = node.setdefault(c, dict()) + node[self.end] = self.end + + + def search(self, word: str) -> bool: + """ + Returns if the word is in the trie. + """ + node = self.root + for c in word: + if c not in node: + return False + node = node[c] + return self.end in node + + + def startsWith(self, prefix: str) -> bool: + """ + Returns if there is any word in the trie that starts with the given prefix. + """ + node = self.root + for c in prefix: + if c not in node: + return False + node = node[c] + return True + + + +# Your Trie object will be instantiated and called as such: +# obj = Trie() +# obj.insert(word) +# param_2 = obj.search(word) +# param_3 = obj.startsWith(prefix) \ No newline at end of file diff --git a/Week_07/leetcode212.py b/Week_07/leetcode212.py new file mode 100644 index 0000000..50edfbd --- /dev/null +++ b/Week_07/leetcode212.py @@ -0,0 +1,29 @@ +class Solution: + def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: + def dfs(x, y, node): + c = board[x][y] + w = node[c].pop('#', False) + if w: + res.append(w) + if not node[c]: + node.pop(c) + return + board[x][y] = '#' + for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)): + i, j = x + dx, y + dy + if 0 <= i < m and 0 <= j < n and board[i][j] in node[c]: + dfs(i, j, node[c]) + board[x][y] = c + m, n = len(board), len(board[0]) + root = dict() + for w in words: + node = root + for c in w: + node = node.setdefault(c, {}) + node['#'] = w + res = [] + for i in range(m): + for j in range(n): + if board[i][j] in root: + dfs(i, j, root) + return res \ No newline at end of file diff --git a/Week_07/leetcode547.py b/Week_07/leetcode547.py new file mode 100644 index 0000000..0fa6f21 --- /dev/null +++ b/Week_07/leetcode547.py @@ -0,0 +1,52 @@ +class Solution: + def findCircleNum(self, isConnected: List[List[int]]) -> int: + # UnionFindSet + n = len(isConnected) + p = [i for i in range(n)] + for i in range(n): + for j in range(i): + if isConnected[i][j] == 1: + self.union(p, i, j) + return len(set(self.find(p, i) for i in range(n))) + + # DFS + # def dfs(i): + # visited.add(i) + # for j in range(n): + # if isConnected[i][j] and j not in visited: + # dfs(j) + # n = len(isConnected) + # visited = set() + # count = 0 + # for i in range(n): + # if i not in visited: + # dfs(i) + # count += 1 + # return count + + # BFS + # n = len(isConnected) + # visited = set() + # count = 0 + # for i in range(n): + # if i not in visited: + # count += 1 + # q = deque([i]) + # while q: + # k = q.popleft() + # visited.add(k) + # for j in range(n): + # if isConnected[k][j] and j not in visited: + # q.append(j) + # return count + + + def union(self, p, i, j): + p1 = self.find(p, i) + p2 = self.find(p, j) + p[p2] = p1 + + def find(self, p, i): + if p[i] != i: + p[i] = self.find(p, p[i]) + return p[i] \ No newline at end of file diff --git a/Week_08/README.md b/Week_08/README.md index 50de304..9dbcf87 100644 --- a/Week_08/README.md +++ b/Week_08/README.md @@ -1 +1,44 @@ -学习笔记 \ No newline at end of file +学习笔记 + +1. 位运算 + + 将x最右边的n位清零:x & (~0 << n) + + 获取x的第n位值(0或1):(x >> n) & 1 + + 获取x的第n位的幂值:x & (1 << n) + + 仅将第n位置为1:x | (1 << n) + + 仅将第n位置为0:x & (~(1 << n)) + + 将x最高位至第n位(含)清零:x & ((1 << n) - 1) + +2. 位运算实战要点 + + - 判断奇偶: + + x % 2 == 1 => x & 1 == 1 + + x % 2 == 0 => x & 1 == 0 + + - x / 2 => x >> 1 + - x = x & (x - 1) 清零最低位的1 + - x & -x 得到最低位的1 + - x & (~x) = 0 + +3. 布隆过滤器(Bloom Filter) + + 一个很长的二进制向量和一系列随机映射函数,,可以用于检索一个元素是否在一个集合中 + + 优点:空间效率和查询效率要远远超过一般的算法 + + 缺点:有一定的误识别率和删除困难 + +4. LRU cache + + 两个要素:大小,替换策略 + + hash table + DoubleLinkList + + O(1)查询、修改、更新 \ No newline at end of file diff --git a/Week_08/leetcode1122.py b/Week_08/leetcode1122.py new file mode 100644 index 0000000..958b43f --- /dev/null +++ b/Week_08/leetcode1122.py @@ -0,0 +1,13 @@ +class Solution: + def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]: + # 自定义比较函数 + # d = {v: i for i, v in enumerate(arr2)} + # return sorted(arr1, key=lambda v: d.get(v, len(arr2) + v)) + + d = Counter(arr1) + res = [] + for v in arr2: + res.extend([v] * d.pop(v)) + # res.extend(sorted(v for v, i in d.items() for _ in range(i))) + res.extend(v for v, n in sorted(d.items()) for _ in range(n)) + return res \ No newline at end of file diff --git a/Week_08/leetcode338.py b/Week_08/leetcode338.py new file mode 100644 index 0000000..9fa5fb8 --- /dev/null +++ b/Week_08/leetcode338.py @@ -0,0 +1,6 @@ +class Solution: + def countBits(self, num: int) -> List[int]: + res = [0] + for i in range(1, num + 1): + res.append(res[i >> 1] + (i & 1)) + return res \ No newline at end of file diff --git a/Week_09/README.md b/Week_09/README.md index 50de304..ea6e609 100644 --- a/Week_09/README.md +++ b/Week_09/README.md @@ -1 +1,9 @@ -学习笔记 \ No newline at end of file +学习笔记 + +1. 不同路径II 状态转移方程 + + dp[i][j] = 0 if obstacleGrid[i][j] else dp[i - 1][j] + dp[i][j - 1] + + dp[i][j] 表示从起始点走到点(i, j)的不同路径数,当(i, j)上有障碍物时,个数为0, + 否则,等于从左边过来的个数加上上面过来的个数 + diff --git a/Week_09/leetcode121.py b/Week_09/leetcode121.py new file mode 100644 index 0000000..df38016 --- /dev/null +++ b/Week_09/leetcode121.py @@ -0,0 +1,28 @@ +class Solution: + def maxProfit(self, prices: List[int]) -> int: + # cost, pro = math.inf, 0 + # for v in prices: + # cost = min(cost, v) + # pro = max(pro, v - cost) + # return pro + + # dp[k][0, 1] -> 第k次交易0(不持有),1(持有)的状态 + # dp = [[0, 0], [0, -math.inf]] + # for v in prices: + # dp[1][0] = max(dp[1][0], dp[1][1] + v) + # dp[1][1] = max(dp[1][1], dp[0][0] - v) + # return dp[1][0] + + # dp = [0, -math.inf] + # for v in prices: + # dp[0] = max(dp[0], dp[1] + v) + # dp[1] = max(dp[1], -v) + # return dp[0] + + # 第i天卖出股票的最大利润 + dp, res = 0, 0 + for i in range(1, len(prices)): + dp = max(dp, 0) + prices[i] - prices[i - 1] + res = max(res, dp) + return res + \ No newline at end of file diff --git a/Week_09/leetcode387.py b/Week_09/leetcode387.py new file mode 100644 index 0000000..314de6d --- /dev/null +++ b/Week_09/leetcode387.py @@ -0,0 +1,12 @@ +class Solution: + def firstUniqChar(self, s: str) -> int: + # for i, c in enumerate(s): + # if s.count(c) == 1: + # return i + # return -1 + + d = Counter(s) + for i, c in enumerate(s): + if d[c] == 1: + return i + return -1 diff --git "a/Week_\346\257\225\344\270\232\346\200\273\347\273\223/README.md" "b/Week_\346\257\225\344\270\232\346\200\273\347\273\223/README.md" index 50de304..d541206 100644 --- "a/Week_\346\257\225\344\270\232\346\200\273\347\273\223/README.md" +++ "b/Week_\346\257\225\344\270\232\346\200\273\347\273\223/README.md" @@ -1 +1,21 @@ -学习笔记 \ No newline at end of file +大学时的算法课学得迷迷糊糊的,后面没再用过后基本都忘却了,所以对数据结构和算法 + +还是很抗拒的,不知道要怎么去掌握它。 + +通过这门课程,了解到实用的学习方法(五毒神掌),并通过每一天的坚持练习(虽然春 + +节那几天还是断了),慢慢地也攻克下了一个个难点,虽然遇到那些难题时,还是会很抗 + +拒,但也知道要怎么去面对和解决它们了。 + +另外,五毒神掌真的是个好东西,感觉可以用到很多方面去,之前工作的时候,很多东西 + +都是需要用到的时候就去查,用完之后又都忘了,所以总感觉学不到什么东西,后面也可 + +以用五毒神掌试着践行看看。 + +最后,感谢这个课程的老师,班班还有一起打卡学习的同学们,已经很久没有这样花很长一 + +段时间坚持学习了。失败只有一种,就是半途而废。未来的路可能依旧不好走,但大概不会 + +再放弃了。 \ No newline at end of file