├── .gitignore ├── 240_搜索二维矩阵 II.py ├── 242_有效的字母异位词.py ├── 26_Remove Duplicates from Sorted Array.py ├── 283_移动零.py ├── 287_寻找重复数.py ├── 28_Implement strStr().py ├── 303_区域和检索 - 数组不可变.py ├── 32_最长有效括号.py ├── 349_两个数组的交集.py ├── 34_在排序数组中查找元素的第一个和最后一个位置.py ├── 350_两个数组的交集 II.py ├── 35_Search Insert Position.py ├── 378_有序矩阵中第K小的元素.py ├── 415_Add Strings.py ├── 435_Non-overlapping Intervals.py ├── 445_Add Two Numbers II.py ├── 455_Assign Cookies.py ├── 45_跳跃游戏 II.py ├── 461_汉明距离.py ├── 485_最大连续1的个数.py ├── 496_下一个更大元素 I.py ├── 49_字母异位词分组.py ├── 4_寻找两个正序数组的中位数.py ├── 509_斐波那契数.py ├── 53_Maximum Subarray.py ├── 680_Valid Palindrome II.py ├── 739_Daily Temperatures.py ├── 744_寻找比目标字母大的最小字母.py ├── 76_最小覆盖子串.py ├── 9_Palindrome Number.py ├── README.md ├── SQL └── 175_组合两个表.py ├── 二分法 ├── 1091_二进制矩阵中的最短路径.py ├── 1095_山脉数组中查找目标值.py ├── 11旋转数组的最小数字.py ├── 1385_两个数组间的距离值.py ├── 153_寻找旋转排序数组中的最小值.py ├── 154_寻找旋转排序数组中的最小值 II.py ├── 278_第一个错误的版本.py ├── 287_寻找重复数.py ├── 34_在排序数组中查找元素的第一个和最后一个位置.py ├── 35_Search Insert Position.py ├── 378_有序矩阵中第K小的元素.py ├── 540_有序数组中的单一元素.py ├── 69_Sqrt(x).py ├── 744_寻找比目标字母大的最小字母.py └── quick_sort.py ├── 位运算 ├── 136_Single Number.py ├── 1371_每个元音包含偶数次的最长子字符串.py ├── 137_只出现一次的数字 II.py ├── 168_Excel Sheet Column Title.py ├── 172_Factorial Trailing Zeroes.py ├── 190_颠倒二进制位.py ├── 191_位1的个数.py ├── 260_只出现一次的数字 III.py ├── 263_丑数.py ├── 268_缺失数字.py ├── 405_Convert a Number to Hexadecimal.py ├── 504_Base 7.py ├── 50_Pow(x, n).py ├── 645_错误的集合.py └── 7_Reverse Integer.py ├── 其他 ├── 13_Roman to Integer.py ├── 146_LRU缓存机制.py ├── 14_Longest Common Prefix.py ├── 204_Count Primes.py ├── 205_同构字符串.py ├── 38_Count and Say.py ├── 409_最长回文串.py ├── 454_四数相加 II.py ├── 55_跳跃游戏.py ├── 56.py ├── 58_Length of Last Word.py ├── 605_Can Place Flowers.py ├── 633_Sum of Square Numbers.py ├── 667_优美的排列 II.py ├── 66_Plus One.py ├── 67_Add Binary.py ├── 697_数组的度.py ├── 766_托普利茨矩阵.py ├── 769_最多能完成排序的块.py └── 93_复原IP地址.py ├── 列表 ├── 118_Pascal's Triangle.py ├── 119_Pascal's Triangle II.py ├── 167_Two Sum II - Input array is sorted.py ├── 225_Implement Stack using Queues.py ├── 232_Implement Queue using Stacks.py ├── 27_Remove Element.py ├── 54_螺旋矩阵.py ├── 565_数组嵌套.py ├── 566_重塑矩阵.py └── 88_Merge Sorted Array.py ├── 前缀和 ├── 1248_统计「优美子数组」.py ├── 1371_每个元音包含偶数次的最长子字符串.py ├── 1_两数之和.py ├── 560_和为K的子数组.py ├── 930_和相同的二元子数组.py ├── 992_K 个不同整数的子数组.py └── 前缀和扩展_和不为0的连续子数组个数.py ├── 动态规划 ├── 1143_最长公共子序列.py ├── 121_Best Time to Buy and Sell Stock.py ├── 122_Best Time to Buy and Sell Stock II.py ├── 123_买卖股票的最佳时机 III.py ├── 1277_统计全为1的正方形子矩阵.py ├── 152_乘积最大子数组.py ├── 188_买卖股票的最佳时机 IV.py ├── 198_打家劫舍.py ├── 213_打家劫舍 II.py ├── 221_最大正方形.py ├── 300_最长上升子序列.py ├── 309_最佳买卖股票时机含冷冻期.py ├── 337_打家劫舍 III.py ├── 376_摆动序列.py ├── 413_等差数列划分.py ├── 416_分割等和子集.py ├── 494_目标和.py ├── 583_两个字符串的删除操作.py ├── 5_最长回文子串.py ├── 62_不同路径.py ├── 63_不同路径 II.py ├── 646_最长数对链.py ├── 647_回文子串.py ├── 64_最小路径和.py ├── 650_只有两个键的键盘.py ├── 70_Climbing Stairs.py ├── 714_买卖股票的最佳时机含手续费.py ├── 72_编辑距离.py └── 983_最低票价.py ├── 单调栈 ├── 496_下一个更大元素 I.py ├── 503_Next Greater Element II.py └── 739_Daily Temperatures.py ├── 双指针 ├── 11_盛最多水的容器.py ├── 125_Valid Palindrome.py ├── 151_翻转字符串里的单词.py ├── 15_三数之和.py ├── 18_四数之和.py └── 392_判断子序列.py ├── 回溯 ├── 17_电话号码的字母组合.py ├── 216_组合总和 III.py ├── 257_二叉树的所有路径.py ├── 36_有效的数独.py ├── 37_解数独.py ├── 39_组合总和.py ├── 40_组合总和 II.py ├── 46_全排列.py ├── 47_全排列 II.py ├── 51_N皇后.py ├── 77_组合.py ├── 78_子集.py ├── 79_单词搜索.py └── 90_子集 II.py ├── 图(有向图判环+无向图判环_染色+连通量) ├── 207_course.py ├── 210_course2.py ├── 547_朋友圈.py ├── 684_并查集.py ├── 785_Is Graph Bipartite.py ├── 886_可能的二分法.py └── 图的学习顺序.txt ├── 图(遍历) ├── 130_Surrounded Regions.py ├── 131_分割回文串.py ├── 200_岛屿数量.py ├── 417_太平洋大西洋水流问题.py ├── 542_01矩阵.py ├── 695_岛屿的最大面积.py └── 994_腐烂的橘子.py ├── 堆(优先队列) ├── 215_数组中的第K个最大元素.py └── 692_前K个高频单词.py ├── 排序 └── 215_数组中的第K个最大元素.py ├── 栈 ├── 155_Min Stack.py ├── 20_Valid Parentheses.py └── 946_验证栈序列.py ├── 树 ├── 100_Same Tree.py ├── 101_Symmetric Tree.py ├── 102_Binary Tree Level Order Traversal.py ├── 103_二叉树的锯齿形层次遍历.py ├── 104_Maximum Depth of Binary Tree.py ├── 105_从前序与中序遍历序列构造二叉树.py ├── 106_从中序与后序遍历序列构造二叉树.py ├── 107_Binary Tree Level Order Traversal II.py ├── 108_Convert Sorted Array to Binary Search Tree.py ├── 110_Balanced Binary Tree.py ├── 111_Minimum Depth of Binary Tree.py ├── 112_Path Sum.py ├── 113_路径总和 II.py ├── 144_Binary Tree Preorder Traversal.py ├── 145_Binary Tree Postorder Traversal.py ├── 226_翻转二叉树.py ├── 236_二叉树的最近公共祖先.py ├── 257_二叉树的所有路径.py ├── 297_二叉树的序列化与反序列化.py ├── 572_另一课树的子树.py ├── 94_Binary Tree Inorder Traversal.py ├── 951_翻转等价二叉树.py ├── 98_验证二叉搜索树.py ├── bfs+dfs │ ├── 102_Binary Tree Level Order Traversal.py │ ├── 103_二叉树的锯齿形层次遍历.py │ ├── 107_Binary Tree Level Order Traversal II.py │ ├── 144_Binary Tree Preorder Traversal.py │ ├── 145_Binary Tree Postorder Traversal.py │ ├── 94_Binary Tree Inorder Traversal.py │ └── tree_readme.py └── 前中后遍历数组的搜索 │ ├── 105_从前序与中序遍历序列构造二叉树.py │ ├── 106_从中序与后序遍历序列构造二叉树.py │ └── 108_Convert Sorted Array to Binary Search Tree.py ├── 滑动窗口 ├── 1004_最大连续1的个数 III.py ├── 1248_统计「优美子数组」.py ├── 209_长度最小的子数组.py ├── 28_Implement strStr().py ├── 3_最长不重复子串.py ├── 567_字符串的排列.py ├── 904_水果成篮.py ├── 930_和相同的二元子数组.py ├── 992_K 个不同整数的子数组.py └── 滑动窗口.md ├── 股票题 ├── 121_Best Time to Buy and Sell Stock.py ├── 122_Best Time to Buy and Sell Stock II.py ├── 123_买卖股票的最佳时机 III.py ├── 188_买卖股票的最佳时机 IV.py ├── 309_最佳买卖股票时机含冷冻期.py └── 714_买卖股票的最佳时机含手续费.py └── 链表 ├── 141_Linked List Cycle.py ├── 160_Intersection of Two Linked Lists.py ├── 19_Remove Nth Node From End of List.py ├── 206_Reverse Linked List.py ├── 21_Merge Two Sorted Lists.py ├── 234_回文链表.py ├── 237_删除链表中的节点.py ├── 23_合并K个排序链表.py ├── 24_Swap Nodes in Pairs.py ├── 25_K 个一组翻转链表.py ├── 83_Remove Duplicates from Sorted List.py ├── 92_Reverse Linked List II.py ├── offer06_从尾到头打印链表.py ├── offer18_删除链表的节点.py ├── offer22_链表中倒数第k个节点.py └── 刷题顺序.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /240_搜索二维矩阵 II.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jun 16 18:19:28 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #从左下角开始查找 9 | class Solution: 10 | def searchMatrix(self, matrix, target): 11 | """ 12 | :type matrix: List[List[int]] 13 | :type target: int 14 | :rtype: bool 15 | """ 16 | 17 | row = len(matrix) - 1 18 | col = 0 19 | while row >= 0 and col <= len(matrix[0])-1: 20 | if matrix[row][col] > target: 21 | row -= 1 22 | elif matrix[row][col] < target: 23 | col += 1 24 | else: 25 | return True 26 | return False 27 | 28 | #从右上角开始查找,不过此时需要判断matrix是否存在,因为一开始要使用matrix[0],不存在的话会报错 29 | class Solution: 30 | def findNumberIn2DArray(self, matrix: List[List[int]], target: int) -> bool: 31 | if not matrix: 32 | return False 33 | row = 0 34 | col = len(matrix[0]) - 1 35 | while col >= 0 and row <= len(matrix) - 1: 36 | if matrix[row][col] > target: 37 | col -= 1 38 | elif matrix[row][col] < target: 39 | row += 1 40 | else: 41 | return True 42 | return False -------------------------------------------------------------------------------- /242_有效的字母异位词.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed May 13 18:30:26 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def isAnagram(self, s: str, t: str) -> bool: 11 | if len(s) != len(t): 12 | return False 13 | adict = {} 14 | for i in range(len(s)): 15 | if s[i] not in adict: 16 | adict[s[i]] = 1 17 | else: 18 | adict[s[i]] += 1 19 | for j in range(len(t)): 20 | if t[j] not in adict: 21 | return False 22 | else: 23 | if adict[t[j]] == 0: 24 | return False 25 | else: 26 | adict[t[j]] -= 1 27 | return True -------------------------------------------------------------------------------- /26_Remove Duplicates from Sorted Array.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Aug 27 11:13:11 2019 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def removeDuplicates(self, nums: List[int]) -> int: 10 | i = 0 11 | while i < len(nums)-1: 12 | if nums[i] == nums[i+1]: 13 | nums.pop(i) 14 | else: 15 | i += 1 16 | return len(nums) 17 | 18 | class Solution: 19 | def removeDuplicates(self, nums: List[int]) -> int: 20 | i = 0 21 | while i < len(nums)-1: 22 | if nums[i] == nums[i+1]: 23 | nums.pop(i+1) 24 | else: 25 | i += 1 26 | return len(nums) 27 | 28 | 29 | class Solution: 30 | def removeDuplicates(self, nums: List[int]) -> int: 31 | if not nums: 32 | return 0 33 | n = len(nums) 34 | count = 1 35 | j = 0 36 | for i in range(1,n): 37 | if nums[i-j] != nums[i-j-1]: 38 | count += 1 39 | else: 40 | nums.pop(i-j-1) 41 | j += 1 42 | return count 43 | 44 | class Solution: 45 | def removeDuplicates(self, nums: List[int]) -> int: 46 | if not nums: 47 | return 0 48 | 49 | count = 0 50 | #if we let count = 1,when len(nums)=1,nums[count] must be out of range 51 | for i in range(len(nums)): 52 | if nums[count] != nums[i]: 53 | count += 1 54 | nums[count] = nums[i] 55 | return count+1 -------------------------------------------------------------------------------- /283_移动零.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jun 16 11:48:30 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def moveZeroes(self, nums: List[int]) -> None: 11 | """ 12 | Do not return anything, modify nums in-place instead. 13 | """ 14 | slow = 0 15 | fast = 0 16 | while fast < len(nums): 17 | if nums[slow] == 0 and nums[fast] != 0: 18 | nums[slow], nums[fast] = nums[fast], nums[slow] 19 | #之所以加这个判断是因为当前位置如果是0但是同时fast也是0,那么我们需要按住不动,一直移动fast去找fast指向不为0的数 20 | #如果之后找到的数都是0,那么就意味着找完了,因此fast < len(nums)作为最后的while判断条件 21 | if nums[slow] != 0: 22 | slow += 1 23 | fast += 1 24 | -------------------------------------------------------------------------------- /287_寻找重复数.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Jun 20 10:32:12 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #典型根据抽屉原理,无须情况下也可使用 9 | #注意只有一个重复的整数 10 | ''' 11 | 我的经验是把定义区间成为左闭右闭区间,左右边界是无差别的,弄成左闭右开,反而增加了思考的复杂程度; 12 | 明确 int = left + ( right - left ) / 2 这里除以 2 是下取整; 13 | 明确 while(left <= right) 和 while(left < right) 这两种写法其实在思路上有本质差别, while(left <= right) 在循环体内部直接查找元素, 14 | 而 while(left < right) 在循环体内部一直在排除元素,第 2 种思路在解决复杂问题的时候,可以使得问题变得简单; 15 | 始终在思考下一轮搜索区间是什么,把它作为注释写到代码里面,就能帮助我们搞清楚边界是不是能取到,等于、+1 、-1 之类的细节; 16 | 思考清楚每一行代码背后的语义是什么,保证语义上清晰,也是写对代码,减少 bug 的一个非常有效的策略。 17 | ''' 18 | 19 | class Solution: 20 | def findDuplicate(self, nums: List[int]) -> int: 21 | size = len(nums) 22 | left = 1 23 | right = size - 1 24 | 25 | while left < right: 26 | mid = (right + left) // 2 27 | 28 | cnt = 0 29 | for num in nums: 30 | if num <= mid: 31 | cnt += 1 32 | # 根据抽屉原理,小于等于 4 的数的个数如果严格大于 4 个, 33 | # 此时重复元素一定出现在 [1, 4] 区间里 34 | 35 | if cnt > mid: 36 | # 重复的元素一定出现在 [left, mid] 区间里 37 | right = mid 38 | else: 39 | # if 分析正确了以后,else 搜索的区间就是 if 的反面 40 | # [mid + 1, right] 41 | left = mid + 1 42 | return left -------------------------------------------------------------------------------- /28_Implement strStr().py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Aug 28 10:46:00 2019 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0712 10 | 滑动窗口 11 | 这道题的升级版是567,只要needle中的元素全在窗口中就行,可以打乱重组,需要用hashmap去比较 12 | ''' 13 | 14 | class Solution: 15 | def strStr(self, haystack: str, needle: str) -> int: 16 | #定长滑动窗口 17 | start = 0 18 | for end in range(len(needle)-1,len(haystack)): 19 | if haystack[start:end+1] == needle: 20 | return start 21 | start += 1 22 | return -1 23 | 24 | #------------------------------------------------------------ 25 | class Solution: 26 | def strStr(self, haystack: str, needle: str) -> int: 27 | if needle == '': 28 | return 0 29 | if needle not in haystack: 30 | return -1 31 | else: 32 | return haystack.index(needle) 33 | 34 | 35 | class Solution: 36 | def strStr(self, haystack: str, needle: str) -> int: 37 | if not needle: 38 | return 0 39 | if needle in haystack: 40 | return haystack.index(needle) 41 | else: 42 | return -1 43 | 44 | 45 | -------------------------------------------------------------------------------- /303_区域和检索 - 数组不可变.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat May 9 13:36:58 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #注意class中变量要加self 9 | #错位存储dp中的值 10 | #i=1表示1之前的总和,而不是直接表示包括1的总和,这样会造成i=0时候出现问题 11 | #dp[i],表示第i(index)个数(不含自己)之前的和 12 | class NumArray: 13 | 14 | def __init__(self, nums: List[int]): 15 | self.dp = [0 for _ in range(len(nums)+1)] 16 | for i in range(1 , len(nums)+1): 17 | self.dp[i] = nums[i-1] + self.dp[i-1] 18 | 19 | def sumRange(self, i: int, j: int) -> int: 20 | return self.dp[j+1] - self.dp[i] 21 | 22 | 23 | class NumArray: 24 | 25 | def __init__(self, nums: List[int]): 26 | #i=1表示1之前的总和,而不是直接表示包括1的总和,这样会造成i=0时候出现问题 27 | self.dp = [0]*(len(nums)+1) 28 | for i in range(1,len(nums)+1): 29 | self.dp[i] = self.dp[i-1] + nums[i-1] 30 | 31 | def sumRange(self, i: int, j: int) -> int: 32 | return self.dp[j+1]-self.dp[i] 33 | -------------------------------------------------------------------------------- /32_最长有效括号.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jul 1 11:04:54 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def longestValidParentheses(self, s: str) -> int: 11 | maxans = 0 12 | dp = [0]*len(s) 13 | for i in range(1, len(s)): 14 | if s[i] == ")": 15 | if s[i - 1] == "(": 16 | dp[i] = (dp[i - 2] if i >= 2 else 0 ) + 2 17 | elif i - dp[i - 1] > 0 and s[i - dp[i - 1] - 1] == "(": 18 | dp[i] = dp[i - 1] + (dp[i - dp[i - 1] - 2] if i - dp[i - 1] >= 2 else 0) +2 19 | maxans = max(maxans, dp[i]) 20 | return maxans -------------------------------------------------------------------------------- /349_两个数组的交集.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jul 14 20:30:23 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #简单双指针 9 | class Solution: 10 | def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: 11 | 12 | if not nums1 or not nums2: 13 | return [] 14 | 15 | nums1Len = len(nums1) 16 | nums2Len = len(nums2) 17 | p1, p2 = 0, 0 18 | res = set() 19 | nums1, nums2 = sorted(nums1), sorted(nums2) 20 | while p1 < nums1Len and p2 < nums2Len: 21 | if nums1[p1] == nums2[p2]: 22 | res.add(nums1[p1]) 23 | p1 += 1 24 | p2 += 1 25 | elif nums1[p1] > nums2[p2]: 26 | p2 += 1 27 | else: 28 | p1 += 1 29 | return list(res) -------------------------------------------------------------------------------- /350_两个数组的交集 II.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jul 14 20:34:35 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]: 11 | if not nums1 or not nums2: 12 | return [] 13 | 14 | nums1Len = len(nums1) 15 | nums2Len = len(nums2) 16 | p1, p2 = 0, 0 17 | res = [] 18 | nums1, nums2 = sorted(nums1), sorted(nums2) 19 | while p1 < nums1Len and p2 < nums2Len: 20 | if nums1[p1] == nums2[p2]: 21 | res.append(nums1[p1]) 22 | p1 += 1 23 | p2 += 1 24 | elif nums1[p1] > nums2[p2]: 25 | p2 += 1 26 | else: 27 | p1 += 1 28 | return res -------------------------------------------------------------------------------- /378_有序矩阵中第K小的元素.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jun 17 11:22:48 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #此题在240的基础上采用二分查找 9 | #此时的二分查找实际上指定的left,right都是实际数值,而不是index,这一点要区分一下 10 | #这种方式加上240的方法实际上相当于将matrix按从小到大的顺序展平,count_num实现的操作相当于这个目的,只不过实际上没有这么做 11 | class Solution(object): 12 | def kthSmallest(self, matrix, k): 13 | """ 14 | :type matrix: List[List[int]] 15 | :type k: int 16 | :rtype: int 17 | """ 18 | 19 | # 计算小于等于目标值的元素个数,根据递增规则,从右上角开始查找 20 | def count_num(m, target): 21 | i = 0 22 | j = len(m) - 1 23 | ans = 0 24 | while i < len(m) and j >= 0: 25 | if m[i][j] <= target: 26 | ans += j + 1 27 | i += 1 28 | else: 29 | j -= 1 30 | return ans 31 | #思路:左上角元素最小,右下角元素最大,计算小于等于中间值的元素个数 32 | left = matrix[0][0] 33 | right = matrix[-1][-1] 34 | 35 | while left < right: 36 | mid = (left+right) // 2 37 | count = count_num(matrix,mid) 38 | if count < k: 39 | left = mid + 1 40 | #等于时候一定是在mid及mid之前,这块保证了为何left,right求中位数以后,最后出来的left,right一定式矩阵中有的数据 41 | else: 42 | right = mid 43 | return left 44 | 45 | 46 | 47 | #method 2 48 | left = matrix[0][0] 49 | right = matrix[-1][-1] 50 | # 二分法查找,注意等号的存在 51 | while left <= right: 52 | mid = (left + right) // 2 53 | count = count_num(matrix, mid) 54 | 55 | if count < k: 56 | 57 | left = mid + 1 58 | else: 59 | #两者相同时候,其实还是将r减小了1位,所以最后应该输出left, 60 | #此时不直接输出mid是因为mid表示的数值不一定在matrix中存在 61 | right = mid - 1 62 | #非常存,无论何时都要输出left 63 | return left 64 | 65 | -------------------------------------------------------------------------------- /415_Add Strings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Feb 27 20:55:35 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0705 10 | 写while循环的时候一定要手动更新while循环的条件,要不然很容易造成死循环 11 | ''' 12 | class Solution: 13 | def addStrings(self, num1: str, num2: str) -> str: 14 | res = '' 15 | carry = 0 16 | i = len(num1) - 1 17 | j = len(num2) - 1 18 | while i >= 0 or j >= 0 or carry: 19 | if i < 0: 20 | a = 0 21 | else: 22 | a = num1[i] 23 | if j < 0: 24 | b = 0 25 | else: 26 | b = num2[j] 27 | 28 | temp = int(a) + int(b) + carry 29 | carry = temp // 10 30 | res += str(temp % 10) 31 | i -= 1 32 | j -= 1 33 | #注意加完了以后要颠倒一下 34 | return res[::-1] -------------------------------------------------------------------------------- /435_Non-overlapping Intervals.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Feb 2 20:56:01 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #排列list中的某一项 9 | #前一项的末尾元素如果大于后一项的开头元素,那么必定发生重叠 10 | #在每次选择中,区间的结尾最为重要,选择的区间结尾越小,留给后面的区间的空间越大,那么后面能够选择的区间个数也就越大。 11 | #按区间的结尾进行排序 12 | class Solution: 13 | def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int: 14 | intervals = sorted(intervals, key=lambda x : x[1]) 15 | i = 1 16 | count = 0 17 | while 1 <= i < len(intervals): 18 | if intervals[i-1][-1] > intervals[i][0]: 19 | count += 1 20 | intervals.remove(intervals[i]) 21 | else: 22 | i += 1 23 | return count -------------------------------------------------------------------------------- /445_Add Two Numbers II.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Feb 16 15:36:38 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | ''' 10 | 0701:相当于基于stack的add binary 11 | ''' 12 | 13 | class Solution: 14 | def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode: 15 | pre = None 16 | carry = 0 17 | stack1 = [] 18 | stack2 = [] 19 | while l1: 20 | stack1.append(l1.val) 21 | l1 = l1.next 22 | while l2: 23 | stack2.append(l2.val) 24 | l2 = l2.next 25 | while stack1 or stack2 or carry: 26 | if stack1: 27 | temp1 = stack1.pop() 28 | else: 29 | temp1 = 0 30 | if stack2: 31 | temp2 = stack2.pop() 32 | else: 33 | temp2 = 0 34 | cur_val = temp1 + temp2 + carry 35 | cur_use = cur_val % 10 36 | carry = cur_val // 10 37 | cur_node = ListNode(cur_use) 38 | cur_node.next = pre 39 | pre = cur_node 40 | return pre 41 | 42 | #双栈问题 43 | class Solution: 44 | def addTwoNumbers(self, l1: ListNode, l2: ListNode): 45 | dummynode = ListNode(0) 46 | 47 | stack1, stack2 = [], [] 48 | carry = 0 49 | while l1 is not None: 50 | stack1.append(l1.val) 51 | l1 = l1.next 52 | while l2 is not None: 53 | stack2.append(l2.val) 54 | l2 = l2.next 55 | while stack1 or stack2 or carry: 56 | if stack1: 57 | val1 = stack1.pop() 58 | else: 59 | val1 = 0 60 | if stack2: 61 | val2 = stack2.pop() 62 | else: 63 | val2 = 0 64 | value = val1 + val2 + carry 65 | valnode = value % 10 66 | carry = value // 10 67 | node = ListNode(valnode) 68 | node.next = dummynode.next 69 | dummynode.next = node 70 | 71 | return dummynode.next 72 | 73 | -------------------------------------------------------------------------------- /455_Assign Cookies.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Feb 2 18:07:08 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def findContentChildren(self, g: List[int], s: List[int]) -> int: 10 | count = 0 11 | g = sorted(g) 12 | s = sorted(s) 13 | i, j = 0, 0 14 | while i < len(g) and j < len(s): 15 | if g[i] <= s[j]: 16 | count += 1 17 | i += 1 18 | j += 1 19 | return count -------------------------------------------------------------------------------- /45_跳跃游戏 II.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon May 4 15:11:38 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #每次找到可以跳到的最远位置,把指针指向最远位置,继续遍历list, 9 | #当前遍历到的值如果在上一个最远位置内且可以跳到更远的位置,那么说明第二次可以从这里开始跳(这也就意味着第一次已经跳完了),可以到达更远的位置 10 | class Solution: 11 | def jump(self, nums: List[int]) -> int: 12 | maxbound, end, step = 0, 0, 0 13 | for i in range(len(nums)-1): 14 | maxbound = max(maxbound, i + nums[i]) 15 | if i == end:#已经在上一次可能做的落脚点中找到这步的最远落脚点,可以跳了 16 | step += 1 17 | end = maxbound#更新最大落脚点 18 | return step -------------------------------------------------------------------------------- /461_汉明距离.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu May 14 09:11:52 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution(object): 10 | def hammingDistance(self, x, y): 11 | xor = x ^ y 12 | distance = 0 13 | while xor: 14 | # mask out the rest bits 15 | if xor % 2 == 1: 16 | distance += 1 17 | xor = xor >> 1 18 | #逻辑右移,即二进制形式向右移动一位,左边补零 19 | #xor = xor // 2 20 | return distance -------------------------------------------------------------------------------- /485_最大连续1的个数.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jun 16 18:01:44 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | #主要需要注意的问题在于,如果最后一个数恰好为1,那么其实最后一次循环的时候不可能进入else判断中,也就没办法把最后一步1的个数放入length中 10 | #因此需要特殊处理这个问题 11 | class Solution: 12 | def findMaxConsecutiveOnes(self, nums: List[int]) -> int: 13 | if not nums: 14 | return 0 15 | length = 0 16 | count = 0 17 | for i in range(len(nums)): 18 | if nums[i] == 1: 19 | count += 1 20 | length = max(length,count) 21 | else: 22 | length = max(length,count) 23 | count = 0 24 | 25 | return length 26 | 27 | 28 | class Solution: 29 | def findMaxConsecutiveOnes(self, nums: List[int]) -> int: 30 | if not nums: 31 | return 0 32 | length = 0 33 | count = 0 34 | for i in range(len(nums)): 35 | if nums[i] == 1: 36 | count += 1 37 | 38 | else: 39 | length = max(length,count) 40 | count = 0 41 | return max(length,count) -------------------------------------------------------------------------------- /496_下一个更大元素 I.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jul 1 13:37:52 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | ''' 10 | 0710 11 | ''' 12 | class Solution: 13 | def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]: 14 | stack = [] 15 | res = [-1 for _ in range(len(nums1))] 16 | for num in nums2: 17 | while stack and num > nums1[stack[-1]]: 18 | res[stack.pop()] = num 19 | if num in nums1: 20 | stack.append(nums1.index(num)) 21 | return res 22 | 23 | 24 | #503变体 25 | #遍历nums2,因为实际上答案的顺序是存在于nums2中的,将nums2中每次遍历到的值在nums1中找到对应的index压入栈中,之后跟739一样 26 | class Solution: 27 | def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]: 28 | stack = [] 29 | res = [-1 for _ in range(len(nums1))] 30 | for i in range(len(nums2)): 31 | while stack and nums1[stack[-1]] < nums2[i]: 32 | res[stack.pop()] = nums2[i] 33 | if nums2[i] in nums1: 34 | stack.append(nums1.index(nums2[i])) 35 | return res -------------------------------------------------------------------------------- /49_字母异位词分组.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jul 21 09:52:27 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def groupAnagrams(self, strs: List[str]) -> List[List[str]]: 11 | dict = {} 12 | for item in strs: 13 | key = tuple(sorted(item)) 14 | dict[key] = dict.get(key, []) + [item] 15 | return list(dict.values()) -------------------------------------------------------------------------------- /509_斐波那契数.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jun 23 09:27:21 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #注意当应用index的时候,一定要思考特殊情况的存在,检查是否可能Out of range 9 | class Solution: 10 | def fib(self, N: int) -> int: 11 | if N == 0 or N == 1: 12 | return N 13 | dp = [0 for _ in range(N+1)] 14 | dp[1] = 1 15 | for i in range(2,N+1): 16 | dp[i] = dp[i-1] + dp[i-2] 17 | return dp[-1] 18 | 19 | 20 | class Solution: 21 | def fib(self, N: int) -> int: 22 | if N == 0 or N == 1: 23 | return N 24 | onestep = 1 25 | twostep = 0 26 | for i in range(2,N+1): 27 | twostep, onestep = onestep, onestep + twostep 28 | return onestep 29 | 30 | 31 | class Solution: 32 | def fib(self, N: int) -> int: 33 | if N == 0 or N == 1: 34 | return N 35 | return self.fib(N-1) + self.fib(N-2) -------------------------------------------------------------------------------- /53_Maximum Subarray.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Aug 29 13:21:24 2019 4 | 5 | @author: leiya 6 | """ 7 | 8 | #0623:updated 9 | #V1 10 | class Solution: 11 | def maxSubArray(self, nums: List[int]) -> int: 12 | dp = nums[:] 13 | for i in range(1, len(nums)): 14 | if dp[i-1] < 0: 15 | dp[i] = nums[i] 16 | else: 17 | dp[i] = dp[i-1] + nums[i] 18 | return max(dp) 19 | #V1优化 20 | class Solution: 21 | def maxSubArray(self, nums: List[int]) -> int: 22 | dp = nums[:] 23 | for i in range(1, len(nums)): 24 | dp[i] = max(dp[i-1] + nums[i], nums[i]) 25 | return max(dp) 26 | 27 | #V1空间优化 28 | class Solution: 29 | def maxSubArray(self, nums: List[int]) -> int: 30 | for i in range(1, len(nums)): 31 | nums[i] = max(nums[i-1] + nums[i], nums[i]) 32 | return max(nums) 33 | 34 | 35 | #---------------------------------------------------------------------- 36 | 37 | class Solution: 38 | def maxSubArray(self, nums: List[int]) -> int: 39 | if max(nums) < 0: 40 | return max(nums) 41 | local_max,global_max =0,0 42 | for num in nums: 43 | local_max = max(0,local_max + num) 44 | global_max = max(global_max,local_max) 45 | return global_max 46 | 47 | class Solution: 48 | def maxSubArray(self, nums: List[int]) -> int: 49 | local_max = 0 50 | global_max = 0 51 | if max(nums) < 0: 52 | return max(nums) 53 | for i in range(len(nums)): 54 | local_max = max(nums[i]+local_max, 0) 55 | global_max = max(local_max, global_max) 56 | return global_max 57 | -------------------------------------------------------------------------------- /744_寻找比目标字母大的最小字母.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jun 21 11:42:48 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0702 10 | 注意判断末尾的特殊情况 11 | 二分法应用的时候特别要注意特殊情况,因为很有可能最后可能的答案根本就不再搜索的范围内,可能在list边界之外 12 | ''' 13 | class Solution: 14 | def nextGreatestLetter(self, letters: List[str], target: str) -> str: 15 | left = 0 16 | right = len(letters) - 1 17 | if letters[right] <= target: 18 | return letters[0] 19 | while left < right: 20 | mid = (left+right) // 2 21 | if letters[mid] <= target: 22 | left = mid + 1 23 | else: 24 | right = mid 25 | return letters[left] 26 | 27 | class Solution: 28 | def nextGreatestLetter(self, letters: List[str], target: str) -> str: 29 | left = 0 30 | right = len(letters) - 1 31 | #注意此处有等号 32 | if ord(target) >= ord(letters[right]): 33 | return letters[0] 34 | while left < right: 35 | mid = (left+right) // 2 36 | if ord(letters[mid]) <= ord(target): 37 | left = mid + 1 38 | else: 39 | right = mid 40 | return letters[left] 41 | -------------------------------------------------------------------------------- /9_Palindrome Number.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Aug 31 17:16:14 2019 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | updated: 0630 10 | 注意最后比较的是res和之前的值 11 | ''' 12 | 13 | class Solution: 14 | def isPalindrome(self, x: int) -> bool: 15 | if x < 0: 16 | return False 17 | res = 0 18 | newx = x 19 | while newx != 0: 20 | temp = newx % 10 21 | res = 10*res + temp 22 | newx = newx // 10 23 | return res == x 24 | 25 | 26 | class Solution: 27 | def isPalindrome(self, x: int) -> bool: 28 | if str(x) == str(x)[::-1]: 29 | return True 30 | else: 31 | return False 32 | 33 | class Solution: 34 | def isPalindrome(self, x: int) -> bool: 35 | if x < 0: 36 | return False 37 | raw = x 38 | result = 0 39 | while x > 0: 40 | a = x % 10 41 | result = result * 10 + a 42 | x //= 10 43 | if result == raw: 44 | return True 45 | else: 46 | return False -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Leetcode_in_python3 2 | This repo includes solutions in Python3 for the most popular and common questions in Leetcode. 3 | 4 | Some important questions have been recorded as vedio tutorials and uploaded into my Bilibili channel. 5 | 6 | The link is: https://space.bilibili.com/15965981 7 | 8 | Welcome to follow me or sumbit issues in this repo. 9 | 10 | ## Note: 11 | This repo will be updated everyday, which means new methods or comments will be added into older version files. 12 | 13 | ## Star History 14 | 15 | [![Star History Chart](https://api.star-history.com/svg?repos=yanglei-github/Leetcode_in_python3&type=Timeline)](https://star-history.com/#yanglei-github/Leetcode_in_python3&Timeline) 16 | 17 | -------------------------------------------------------------------------------- /SQL/175_组合两个表.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Sep 4 16:48:44 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | select P.FirstName, P.LastName, A.City, A.State 10 | from Person as P left outer join Address as A 11 | on P.PersonId = A.PersonId; -------------------------------------------------------------------------------- /二分法/1095_山脉数组中查找目标值.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jun 21 09:29:53 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | # """ 10 | # This is MountainArray's API interface. 11 | # You should not implement it, or speculate about its implementation 12 | # """ 13 | #class MountainArray: 14 | # def get(self, index: int) -> int: 15 | # def length(self) -> int: 16 | 17 | class Solution: 18 | def findInMountainArray(self, target: int, mountain_arr: 'MountainArray') -> int: 19 | size = mountain_arr.length() 20 | #mountaintop是山顶元素所在的index 21 | mountaintop = self.find_mountaintop(mountain_arr,0,size-1) 22 | res = self.find_from_sorted_arr(mountain_arr,0,mountaintop,target) 23 | if res != -1: 24 | return res 25 | return self.find_from_inversed_arr(mountain_arr,mountaintop+1,size-1,target) 26 | def find_mountaintop(self,mountain_arr,l,r): 27 | while l < r: 28 | mid = (l+r) // 2 29 | #上述情况每次取左中位数,一旦进入循环一定至少有两个元素 30 | #因此,左中位数一定有右边元素,数组下标不会越界 31 | if mountain_arr.get(mid) < mountain_arr.get(mid+1): 32 | #相当于使用这个判断删除掉所有不符合要求的条件 33 | l = mid + 1 34 | else: 35 | #left和right互补 36 | r = mid 37 | return l 38 | def find_from_sorted_arr(self,mountain_arr,l,r,target): 39 | while l < r: 40 | mid = (l+r) // 2 41 | if mountain_arr.get(mid) < target: 42 | l = mid + 1 43 | else: 44 | r = mid 45 | if mountain_arr.get(l) == target: 46 | return l 47 | else: 48 | return -1 49 | 50 | def find_from_inversed_arr(self,mountain_arr,l,r,target): 51 | while l < r: 52 | mid = (l+r) // 2 53 | if mountain_arr.get(mid) > target: 54 | l = mid + 1 55 | else: 56 | r = mid 57 | if mountain_arr.get(l) == target: 58 | return l 59 | else: 60 | return -1 61 | -------------------------------------------------------------------------------- /二分法/11旋转数组的最小数字.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jun 23 09:54:22 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #注意相同元素的处理 9 | class Solution: 10 | def minArray(self, numbers: List[int]) -> int: 11 | left = 0 12 | right = len(numbers) - 1 13 | while left < right: 14 | mid = (left+right) // 2 15 | if numbers[mid] > numbers[right]: 16 | left = mid + 1 17 | #去掉right还有 mid指向和原来right相同的值,所以本质上去掉right无关紧要 18 | elif numbers[mid] == numbers[right]: 19 | right -= 1 20 | else: 21 | right = mid 22 | return numbers[left] -------------------------------------------------------------------------------- /二分法/1385_两个数组间的距离值.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def findTheDistanceValue(self, arr1, arr2, d): 3 | res=0 4 | arr2.sort() 5 | for i in range(len(arr1)): 6 | left, right=0, len(arr2)-1 7 | flag = 0 8 | while left < right: 9 | mid=(right+left) // 2 10 | if arr2[mid] - arr1[i] > d: 11 | right=mid-1 12 | elif arr2[mid] - arr1[i] < -d: 13 | left = mid + 1 14 | else: 15 | flag = 1 16 | break 17 | if abs(arr2[right]-arr1[i]) > d and flag == 0: 18 | res+=1 19 | return res -------------------------------------------------------------------------------- /二分法/153_寻找旋转排序数组中的最小值.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jun 21 18:59:40 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #没有相同数字存在,这一点决定了else中nums[mid] == nums[right]的可能性永远不会发生,如果发生会造成[3,3,1,3],right会错过1 9 | #有想象力的二分法,二分法本质就在于如何信誓旦旦的排除掉那些不符合要求的元素(divide the search space into two and see which direction to go. 10 | #只不过这个过程是一半一半的找, 11 | #即通过改变left,right,不断的二分,重要点在于基于何种条件移动他们 12 | class Solution: 13 | def findMin(self, nums: List[int]) -> int: 14 | left = 0 15 | right = len(nums) - 1 16 | while left < right: 17 | mid = (left+right) // 2 18 | if nums[mid] > nums[right]: 19 | left = mid + 1 20 | else: 21 | right = mid 22 | return nums[left] 23 | 24 | -------------------------------------------------------------------------------- /二分法/154_寻找旋转排序数组中的最小值 II.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jun 23 09:59:40 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #有重复数字的情况 9 | #相当于在判断的时候多了一种情况,nums[mid] == nums[right],这个时候只需要向前移动right一步,保证下次循环可以继续下去即可 10 | class Solution: 11 | def findMin(self, nums: List[int]) -> int: 12 | left = 0 13 | right = len(nums) - 1 14 | while left < right: 15 | mid = (left+right) // 2 16 | if nums[mid] > nums[right]: 17 | left = mid + 1 18 | elif nums[mid] == nums[right]: 19 | right = right - 1 20 | else: 21 | right = mid 22 | 23 | return nums[left] -------------------------------------------------------------------------------- /二分法/278_第一个错误的版本.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jun 21 14:18:21 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | # The isBadVersion API is already defined for you. 10 | # @param version, an integer 11 | # @return a bool 12 | # def isBadVersion(version): 13 | 14 | class Solution: 15 | def firstBadVersion(self, n): 16 | """ 17 | :type n: int 18 | :rtype: int 19 | """ 20 | left = 1 21 | right = n 22 | while left < right: 23 | mid = (left+right) // 2 24 | if not isBadVersion(mid): 25 | left = mid + 1 26 | else: 27 | right = mid 28 | return left -------------------------------------------------------------------------------- /二分法/287_寻找重复数.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Jun 20 10:32:12 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #典型根据抽屉原理,无序情况下也可使用 9 | #注意只有一个重复的整数 10 | ''' 11 | 我的经验是把定义区间成为左闭右闭区间,左右边界是无差别的,弄成左闭右开,反而增加了思考的复杂程度; 12 | 明确 int = left + ( right - left ) / 2 这里除以 2 是下取整; 13 | 明确 while(left <= right) 和 while(left < right) 这两种写法其实在思路上有本质差别, while(left <= right) 在循环体内部直接查找元素, 14 | 而 while(left < right) 在循环体内部一直在排除元素,第 2 种思路在解决复杂问题的时候,可以使得问题变得简单; 15 | 始终在思考下一轮搜索区间是什么,把它作为注释写到代码里面,就能帮助我们搞清楚边界是不是能取到,等于、+1 、-1 之类的细节; 16 | 思考清楚每 17 | 一行代码背后的语义是什么,保证语义上清晰,也是写对代码,减少 bug 的一个非常有效的策略。 18 | ''' 19 | class Solution: 20 | def findDuplicate(self, nums: List[int]) -> int: 21 | left = 1 22 | right = len(nums) - 1 23 | while left < right: 24 | mid = (left + right) // 2 25 | count = 0 26 | for num in nums: 27 | if num <= mid: 28 | count += 1 29 | if count > mid: 30 | right = mid 31 | else: 32 | left = mid + 1 33 | return left 34 | 35 | class Solution: 36 | def findDuplicate(self, nums: List[int]) -> int: 37 | size = len(nums) 38 | left = 1 39 | right = size - 1 40 | 41 | while left < right: 42 | mid = (right + left) // 2 43 | 44 | cnt = 0 45 | for num in nums: 46 | if num <= mid: 47 | cnt += 1 48 | # 根据抽屉原理,小于等于 4 的数的个数如果严格大于 4 个, 49 | # 此时重复元素一定出现在 [1, 4] 区间里 50 | 51 | if cnt > mid: 52 | # 重复的元素一定出现在 [left, mid] 区间里 53 | right = mid 54 | else: 55 | # if 分析正确了以后,else 搜索的区间就是 if 的反面 56 | # [mid + 1, right] 57 | left = mid + 1 58 | return left -------------------------------------------------------------------------------- /二分法/540_有序数组中的单一元素.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jun 21 12:42:39 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | ''' 10 | 0723:这道题其实比较特殊,不符合一般的二分法模板,我们尽可能不要对mid+1,mid-1进行这类操作,可以类比153题,可以避免的时候尽量避免,因为可能会Out of range 11 | ''' 12 | #136异或问题的变体,区别在于这道题已经排好序,并且要求在logn内解决 13 | #为什么找偶数索引,因为正常来说一对一对构成的nums,偶数索引应该和后面的奇数索引构成一对,一旦他俩不等,说明结果肯定在该偶数index之前(包括该偶数) 14 | #实际上通过仅对偶数索引进行二分,我们实现了log(n/2) = logn 15 | class Solution: 16 | def singleNonDuplicate(self, nums: List[int]) -> int: 17 | left = 0 18 | right = len(nums) - 1 19 | while left < right: 20 | mid = (left+right) // 2 21 | if mid % 2 == 1: 22 | #为何此处mid - 1不会越界,因为mid此时为奇数,也就意味着前面一定有一个偶数index,永远不会越界 23 | mid -= 1 24 | #此处mid+1不会越界是因为每次取mid是取左中位数,永远有右边的数,当没有右边数的时候left==right,该退出了,不会再执行这一步 25 | #对于只有一个数的数组,left一开始就等于right,因此不会进while,进而不会执行到这一步 26 | if nums[mid] == nums[mid+1]: 27 | left = mid + 2 28 | else: 29 | right = mid 30 | return nums[left] -------------------------------------------------------------------------------- /二分法/69_Sqrt(x).py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Sep 1 12:54:31 2019 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | 10 | #69,35题都是我们可以确定解一定在left,right可以遍历到的位置,所以左后while left == right弹出后无须进一步判断left,right最后指向的同一个位置是否是题解 11 | #换句话说,一定有解,那么排除了所有不可能的解,最后的解一定是left或者right指向的解,无须进一步判断 12 | #0620 updated:切换二分法模板 13 | #x=4可以作为求mid是取左中位数还是右中位数的特例 14 | class Solution: 15 | def mySqrt(self, x: int) -> int: 16 | left = 0 17 | right = x // 2 + 1 18 | while left < right: 19 | mid = (left+right+1) // 2 20 | #注意这里有等号 21 | if mid ** 2 <= x: 22 | left = mid 23 | else: 24 | right = mid - 1 25 | #该模板的好处在于最后无须思考返回left还是right,应为两者一样,但是有时候需要判断一下left,right是否是需要的解,因为在while里没有判断 26 | return left 27 | 28 | 29 | #双指针切换 30 | #一个数的平方根小于等于这个数的一半, 31 | #right = x//2 + 1是为了照顾到1这个特例 32 | #最后要找的结果是其平方小于等于x的最大的mid 33 | class Solution: 34 | def mySqrt(self, x: int) -> int: 35 | res = 0 36 | right = x // 2 + 1 37 | left = 0 38 | #不加等于号1,9这些例子不能通过 39 | while left <= right: 40 | mid = (right+left)//2 41 | if mid ** 2 <= x: 42 | res = mid 43 | left = mid + 1 44 | 45 | else: 46 | right = mid - 1 47 | return res 48 | 49 | class Solution: 50 | def mySqrt(self, x: int) -> int: 51 | n = 1 52 | while x / n > n: 53 | n += 1 54 | if x/n == n: 55 | return n 56 | else: 57 | return n-1 58 | 59 | 60 | class Solution: 61 | def mySqrt(self, x: int) -> int: 62 | if x < 2: 63 | return x 64 | left, right = 1, x // 2 65 | while left <= right: 66 | mid = (right+left) // 2 67 | if mid > x/mid: 68 | right = mid - 1 69 | else: 70 | left = mid + 1 71 | return right -------------------------------------------------------------------------------- /二分法/744_寻找比目标字母大的最小字母.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jun 21 11:42:48 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0702 10 | 注意判断末尾的特殊情况 11 | 二分法应用的时候特别要注意特殊情况,因为很有可能最后可能的答案根本就不再搜索的范围内,可能在list边界之外 12 | ''' 13 | class Solution: 14 | def nextGreatestLetter(self, letters: List[str], target: str) -> str: 15 | left = 0 16 | right = len(letters) - 1 17 | if letters[right] <= target: 18 | return letters[0] 19 | while left < right: 20 | mid = (left+right) // 2 21 | if letters[mid] <= target: 22 | left = mid + 1 23 | else: 24 | right = mid 25 | return letters[left] 26 | 27 | class Solution: 28 | def nextGreatestLetter(self, letters: List[str], target: str) -> str: 29 | left = 0 30 | right = len(letters) - 1 31 | #注意此处有等号 32 | if ord(target) >= ord(letters[right]): 33 | return letters[0] 34 | while left < right: 35 | mid = (left+right) // 2 36 | if ord(letters[mid]) <= ord(target): 37 | left = mid + 1 38 | else: 39 | right = mid 40 | return letters[left] 41 | -------------------------------------------------------------------------------- /二分法/quick_sort.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Apr 16 10:16:26 2022 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | def quick_sort(ori_list, head, tail): 10 | if head >= tail: 11 | return ori_list 12 | pivot = ori_list[head] 13 | low = head 14 | high = tail 15 | while low < high: 16 | while low < high and ori_list[high] >= pivot: 17 | high -= 1 18 | ori_list[low] = ori_list[high] 19 | while low < high and ori_list[low] < pivot: 20 | low += 1 21 | ori_list[high] = ori_list[low] 22 | ori_list[low] = pivot 23 | quick_sort(ori_list, head, low-1) 24 | quick_sort(ori_list, low+1, tail) 25 | return ori_list 26 | 27 | sorted_list = quick_sort([3,1,2,5,4,2],0,5) 28 | print(sorted_list) -------------------------------------------------------------------------------- /位运算/136_Single Number.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Feb 6 17:49:29 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 任何数和 0 做异或运算,结果仍然是原来的数。 10 | 任何数和其自身做异或运算,结果是 0 11 | 异或运算满足交换律和结合律 12 | ''' 13 | 14 | 15 | class Solution: 16 | def singleNumber(self, nums: List[int]) -> int: 17 | res = nums[0] 18 | for i in range(1, len(nums)): 19 | res ^= nums[i] 20 | return res 21 | 22 | 23 | #把list中所有元素异或一遍,这样相同元素异或变成零,零和最后一个单独的数异或得到单独的这个数的值 24 | class Solution: 25 | def singleNumber(self, nums: List[int]) -> int: 26 | for i in range(1,len(nums)): 27 | nums[0] ^= nums[i] 28 | return nums[0] 29 | 30 | 31 | class Solution: 32 | def singleNumber(self, nums: List[int]) -> int: 33 | adict = {} 34 | for num in nums: 35 | if num not in adict: 36 | adict[num] = 1 37 | else: 38 | adict[num] += 1 39 | for word in adict.keys(): 40 | if adict[word] == 1: 41 | return word -------------------------------------------------------------------------------- /位运算/1371_每个元音包含偶数次的最长子字符串.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed May 20 18:55:59 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def findTheLongestSubstring(self, s: str) -> int: 11 | ans, status, n = 0, 0, len(s) 12 | #记录的是这个status的长度 13 | pos = [-1] * (1 << 5) 14 | #全为偶数时置为0 15 | pos[0] = 0 16 | 17 | for i in range(n): 18 | if s[i] == 'a': 19 | status ^= 1 << 0 20 | elif s[i] == 'e': 21 | status ^= 1 << 1 22 | elif s[i] == 'i': 23 | status ^= 1 << 2 24 | elif s[i] == 'o': 25 | status ^= 1 << 3 26 | elif s[i] == 'u': 27 | status ^= 1 << 4 28 | if pos[status] != -1: 29 | ans = max(ans, i + 1 - pos[status]) 30 | else: 31 | ''' 32 | 为了保证第一个字母是辅音字母时也可以输出正确的值,因为如果改为 pos[status] = i; 33 | 那相应的if语句要改为ans = max(ans, i - pos[status]); 34 | 这样当第一个字母为辅音时,status = 0,~pos[status]判为真,ans就会被赋值为0,这显然不是正确的。 35 | 同时题解中是用pos[atatus]是不是等于-1来判断前面是否出现过与status相同的奇偶性,所以也不能初始化为pos[0] = -1。 36 | 就只好多加一个1 37 | ''' 38 | pos[status] = i + 1 39 | return ans -------------------------------------------------------------------------------- /位运算/137_只出现一次的数字 II.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jun 17 14:39:11 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def singleNumber(self, nums: List[int]) -> int: 11 | counts = [0] * 32 12 | for num in nums: 13 | for j in range(32): 14 | counts[j] += num & 1 15 | num >>= 1 16 | res, m = 0, 3 17 | for i in range(32): 18 | res <<= 1 19 | res |= counts[31 - i] % m 20 | return res if counts[31] % m == 0 else ~(res ^ 0xffffffff) 21 | 22 | class Solution: 23 | def singleNumber(self, nums: List[int]) -> int: 24 | res = 0 25 | for i in range(32): 26 | cnt = 0 # 记录当前 bit 有多少个1 27 | bit = 1 << i # 记录当前要操作的 bit 28 | for num in nums: 29 | if num & bit != 0: 30 | cnt += 1 31 | if cnt % 3 != 0: 32 | # 不等于0说明唯一出现的数字在这个 bit 上是1 33 | res |= bit 34 | 35 | return res - 2 ** 32 if res >= 2 ** 31 else res 36 | 37 | ''' 38 | 如果不这么做的话测试用例是[-2,-2,1,1,-3,1,-3,-3,-4,-2] 的时候,就会输出 4294967292。 39 | 其原因在于Python是动态类型语言,在这种情况下其会将符号位置的1看成了值,而不是当作符号“负数”。 40 | 这是不对的。 正确答案应该是 - 4,-4的二进制码是 1111...100,就变成 2^32-4=4294967292,解决办法就是 减去 2 ** 32 。 41 | 42 | ''' -------------------------------------------------------------------------------- /位运算/168_Excel Sheet Column Title.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Feb 22 21:33:06 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def convertToTitle(self, n: int) -> str: 10 | if n == 0: 11 | return '' 12 | res = '' 13 | while n != 0: 14 | n = n -1 15 | temp = n % 26 16 | n = n // 26 17 | res += chr(temp+65) 18 | return res[::-1] -------------------------------------------------------------------------------- /位运算/172_Factorial Trailing Zeroes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Feb 27 20:26:23 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def trailingZeroes(self, n: int) -> int: 10 | count = 0 11 | while n >= 5: 12 | n = n // 5 13 | count += n 14 | return count 15 | -------------------------------------------------------------------------------- /位运算/190_颠倒二进制位.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu May 14 10:54:03 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | # @param n, an integer 11 | # @return an integer 12 | def reverseBits(self, n): 13 | res, power = 0, 31 14 | while n: 15 | #判断当前位是0是1,然后移动到power处 16 | res += (n & 1) << power 17 | n = n >> 1 18 | power -= 1 19 | return res -------------------------------------------------------------------------------- /位运算/191_位1的个数.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jun 23 13:34:54 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def hammingWeight(self, n: int) -> int: 11 | bit = 1 12 | count = 0 13 | for _ in range(32): 14 | if bit & n == 1: 15 | count += 1 16 | n >>= 1 17 | return count -------------------------------------------------------------------------------- /位运算/260_只出现一次的数字 III.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu May 14 09:55:49 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #结合645题 9 | class Solution: 10 | def singleNumber(self, nums: List[int]) -> List[int]: 11 | # xor 为特殊两个数的异或 12 | xor = 0 13 | for num in nums: 14 | xor = xor ^ num 15 | # bit 为xor 第一个为1的位 16 | bit = 1 17 | while xor & bit == 0: 18 | bit <<= 1 19 | #bit = bit << 1 20 | #bit = bit * 2右移 21 | # 通过和bit异或的结果,把数分为两组,两个数肯定在不同组,两个组异或出的结果就是两个数 22 | a = 0 23 | b = 0 24 | for num in nums: 25 | #bit其他位均为0,只有一位是1,这意味就是两个数不同的一位,这样,如果有数在这位也是1那么就可以得到1 26 | if num & bit == 0: 27 | a ^= num 28 | else: 29 | b ^= num 30 | return [b,a] 31 | 32 | 33 | -------------------------------------------------------------------------------- /位运算/263_丑数.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jul 1 18:26:14 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #判断特例:0不是丑数,1是丑数 9 | class Solution: 10 | def isUgly(self, num: int) -> bool: 11 | if num == 0: 12 | return False 13 | while num % 5 == 0: 14 | num = num // 5 15 | while num % 3 == 0: 16 | num = num // 3 17 | while num % 2 == 0: 18 | num = num // 2 19 | if num == 1: 20 | return True 21 | else: 22 | return False -------------------------------------------------------------------------------- /位运算/268_缺失数字.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu May 14 09:19:46 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #下标和value异或,初始值置为nums长度 9 | class Solution: 10 | def missingNumber(self, nums): 11 | missing = len(nums) 12 | for i, num in enumerate(nums): 13 | missing ^= i ^ num 14 | return missing -------------------------------------------------------------------------------- /位运算/405_Convert a Number to Hexadecimal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Feb 22 17:34:49 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def toHex(self, num: int) -> str: 10 | if num == 0: 11 | return '0' 12 | adic = {10:'a',11:'b',12:'c',13:'d',14:'e',15:'f'} 13 | res = '' 14 | if num < 0: 15 | num = num + 16**8 16 | #16**8=4,294,967,296 17 | while num != 0: 18 | temp = num % 16 19 | num = num // 16 20 | if temp >= 10: 21 | res += adic[temp] 22 | else: 23 | res += str(temp) 24 | return res[::-1] -------------------------------------------------------------------------------- /位运算/504_Base 7.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Feb 22 14:19:40 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def convertToBase7(self, num: int) -> str: 10 | if num == 0: 11 | return '0' 12 | old_num = num 13 | num = abs(num) 14 | res = '' 15 | while num != 0: 16 | temp = num % 7 17 | num = num // 7 18 | res += str(temp) 19 | if old_num < 0: 20 | return '-' + res[::-1] 21 | else: 22 | return res[::-1] -------------------------------------------------------------------------------- /位运算/50_Pow(x, n).py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon May 11 11:19:06 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | #0623:updated 10 | class Solution: 11 | def myPow(self, x: float, n: int) -> float: 12 | if x == 0: 13 | return 0 14 | res = 1 15 | if n < 0: 16 | x, n = 1 / x, -n 17 | while n: 18 | #使用与运算的时候默认就将n视为二进制了,不用做其他特殊操作 19 | if n & 1: 20 | res *= x 21 | #x *= x放到后面更新 22 | x *= x 23 | n >>= 1 24 | return res 25 | 26 | 27 | #快速幂 28 | class Solution: 29 | def myPow(self, x: float, n: int) -> float: 30 | def mulpow(n): 31 | if n == 0: 32 | return 1.0 33 | 34 | y = mulpow(n // 2) 35 | if n % 2 == 0: 36 | return y*y 37 | else: 38 | return y*y*x 39 | if n >=0 : 40 | return mulpow(n) 41 | else: 42 | return 1.0/mulpow(-n) 43 | 44 | 45 | class Solution: 46 | def myPow(self, x: float, n: int) -> float: 47 | def quickMul(N): 48 | ans = 1.0 49 | # 贡献的初始值为 x 50 | x_contribute = x 51 | # 在对 N 进行二进制拆分的同时计算答案 52 | while N > 0: 53 | if N % 2 == 1: 54 | # 如果 N 二进制表示的最低位为 1,那么需要计入贡献 55 | ans *= x_contribute 56 | # 将贡献不断地平方 57 | x_contribute *= x_contribute 58 | # 舍弃 N 二进制表示的最低位,这样我们每次只要判断最低位即可 59 | N //= 2 60 | return ans 61 | 62 | return quickMul(n) if n >= 0 else 1.0 / quickMul(-n) 63 | 64 | -------------------------------------------------------------------------------- /位运算/645_错误的集合.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jun 17 14:22:23 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def findErrorNums(self, nums: List[int]) -> List[int]: 11 | new_nums = nums + [i+1 for i in range(len(nums))] 12 | xor = 0 13 | for num in new_nums: 14 | xor ^= num 15 | bit = 1 16 | while xor & bit == 0: 17 | bit = bit << 1 18 | a = 0 19 | b = 0 20 | for num in new_nums: 21 | #千万注意此处只能和0比,因为不确定是哪一位可以与成1,such as 0000100这个与以后的结果不一定是1,但一定不是0,因此不能与1比 22 | if bit & num == 0: 23 | a ^= num 24 | else: 25 | b ^= num 26 | for num in nums: 27 | if a == num: 28 | return [a,b] 29 | return [b,a] -------------------------------------------------------------------------------- /位运算/7_Reverse Integer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Aug 31 16:18:32 2019 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0630 updated 10 | ''' 11 | class Solution: 12 | def reverse(self, x: int) -> int: 13 | newx = abs(x) 14 | reversex = 0 15 | while newx != 0: 16 | temp = newx % 10 17 | reversex = 10 * reversex + temp 18 | newx = newx // 10 19 | if x < 0: 20 | if -2**31 <= -reversex: 21 | 22 | return -reversex 23 | else: 24 | return 0 25 | else: 26 | if reversex <= 2**31 -1: 27 | return reversex 28 | else: 29 | return 0 30 | 31 | class Solution: 32 | def reverse(self, x): 33 | #-2147483648-2147483647 34 | a = abs(x) 35 | num = 0 36 | while a != 0: 37 | temp = a % 10 38 | num = num*10 + temp 39 | a = int(a / 10) 40 | if x > 0 and num < 2147483647: 41 | return num 42 | elif x < 0 and num <= 2147483647: 43 | return -num 44 | else: 45 | return 0 46 | 47 | class Solution: 48 | def reverse(self, x): 49 | #反转完了以后也不能超出范围 50 | if x >= 0 and x <= 2**31 - 1: 51 | if int(str(x)[::-1]) > 2**31 - 1: 52 | return 0 53 | else: 54 | return int(str(x)[::-1]) 55 | elif x < 0 and x >= -2**31: 56 | if -int(str(x)[1:][::-1]) < -2**31: 57 | return 0 58 | else: 59 | return -int(str(x)[1:][::-1]) 60 | else: 61 | return 0 62 | 63 | class Solution: 64 | def reverse(self, x): 65 | #-2147483648-2147483647 66 | result = 0 67 | m = abs(x) 68 | while m > 0: 69 | a = m % 10 70 | result = result * 10 + a 71 | m //= 10 72 | if x < 0 and -2**31 <= -result: 73 | return -result 74 | elif 0 < x and result <= 2**31-1: 75 | return result 76 | else: 77 | return 0 -------------------------------------------------------------------------------- /其他/13_Roman to Integer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Aug 31 17:16:58 2019 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def romanToInt(self, s: str) -> int: 10 | adic = {'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000} 11 | res = adic[s[0]] 12 | for i in range(1,len(s)): 13 | if adic[s[i]] <= adic[s[i-1]]: 14 | res = res + adic[s[i]] 15 | else: 16 | res = res - 2*adic[s[i-1]] + adic[s[i]] 17 | return res 18 | 19 | 20 | class Solution: 21 | def romanToInt(self, s: str) -> int: 22 | numeral_map = {'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000} 23 | result = 0 24 | for i in range(len(s)): 25 | if i > 0 and numeral_map[s[i]] > numeral_map[s[i-1]]: 26 | result += numeral_map[s[i]] - 2 * numeral_map[s[i-1]] 27 | else: 28 | result += numeral_map[s[i]] 29 | return result 30 | 31 | class Solution: 32 | def romanToInt(self, s: str) -> int: 33 | amap = {'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000} 34 | result = amap[s[0]] 35 | for i in range(1,len(s)): 36 | if amap[s[i]] <= amap[s[i-1]]: 37 | result += amap[s[i]] 38 | else: 39 | result += amap[s[i]] - 2 * amap[s[i-1]] 40 | return result -------------------------------------------------------------------------------- /其他/146_LRU缓存机制.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jul 27 13:39:13 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class LRUCache: 10 | from collections import OrderedDict 11 | def __init__(self, capacity: int): 12 | self.maxsize = capacity 13 | self.cache = OrderedDict() 14 | 15 | def get(self, key: int) -> int: 16 | #若在缓存中,将其移动adict的尾部,表示最近刚用过 17 | if key in self.cache: 18 | self.cache.move_to_end(key) 19 | #若不再缓存中,直接返回-1,这就是get的好处 20 | return self.cache.get(key, -1) 21 | 22 | def put(self, key: int, value: int) -> None: 23 | # 如果在cache中,删掉重新赋值 24 | if key in self.cache: 25 | del self.cache[key] 26 | # 在字典尾部添加 27 | self.cache[key] = value 28 | #若cache超了,把最前面的内容删除除掉 29 | if len(self.cache) > self.maxsize: 30 | # last=False表示先进先出,即弹出最前面的内容 31 | self.cache.popitem(last = False) -------------------------------------------------------------------------------- /其他/204_Count Primes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Feb 20 21:28:53 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #关键在于埃拉托斯特尼筛法,简称埃式筛,也叫厄拉多塞筛法: 9 | #要得到自然数n以内的全部质数,必须把不大于根号n的所有质数的倍数剔除,剩下的就是质数。 10 | #res = [1,1,1] 11 | #res[0:2] = [0,0] 12 | #print(res) 13 | class Solution: 14 | def countPrimes(self, n: int) -> int: 15 | if n <= 2: 16 | return 0 17 | res = [1] * n 18 | res[0:2] = [0,0] 19 | for i in range(2, int(n**0.5)+1): 20 | if res[i] == 1: 21 | j = i * i 22 | while j < n: 23 | res[j] = 0 24 | j = j + i 25 | return res.count(1) -------------------------------------------------------------------------------- /其他/205_同构字符串.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed May 13 18:52:29 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #记录一个字符上次出现的位置,如果两个字符串中的字符上次出现的位置一样,那么就属于同构 9 | class Solution: 10 | def isIsomorphic(self, s: str, t: str) -> bool: 11 | adict = {} 12 | bdict = {} 13 | for i in range(len(s)): 14 | if s[i] not in adict and t[i] not in bdict: 15 | adict[s[i]] = i 16 | bdict[t[i]] = i 17 | elif s[i] not in adict and t[i] in bdict: 18 | return False 19 | elif s[i] in adict and t[i] not in bdict: 20 | return False 21 | 22 | else: 23 | if adict[s[i]] != bdict[t[i]]: 24 | return False 25 | return True -------------------------------------------------------------------------------- /其他/38_Count and Say.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Aug 29 12:49:25 2019 4 | 5 | @author: leiya 6 | """ 7 | #0709 sliding window 8 | ''' 9 | 这道题的难点在于如果start,end从某个位置开始,可能会一直一样,这样在循环里可能没办法把这部分统计进去, 10 | 可以根据start,end最后是否重合来把这部分特殊情况解决掉 11 | 注意每次改变滑动窗口以后需要移动窗口指针 12 | ''' 13 | class Solution: 14 | def countAndSay(self, n: int) -> str: 15 | res = '1' 16 | for i in range(2,n+1): 17 | #每次要用新的new_res,start 18 | new_res = '' 19 | start = 0 20 | for end in range(len(res)): 21 | if res[end] != res[start]: 22 | new_res += str(end-start) + res[start] 23 | start = end 24 | if end != start: 25 | new_res += str(end-start+1) + res[start] 26 | else: 27 | new_res += '1' + res[end] 28 | res = new_res 29 | return res 30 | 31 | 32 | #updated 0629 这道题只能通过j与j+1比较,有题目本身的特性决定,每次通过小循环找到重复数字的个数 33 | class Solution: 34 | def countAndSay(self, n: int) -> str: 35 | temp = '1' 36 | for i in range(n-1): 37 | j = 0 38 | newtemp = '' 39 | while j < len(temp): 40 | count = 1 41 | while j < len(temp) - 1 and temp[j] == temp[j+1]: 42 | count += 1 43 | j += 1 44 | newtemp += str(count) + temp[j] 45 | j += 1 46 | temp = newtemp 47 | return temp 48 | 49 | class Solution: 50 | def countAndSay(self, n: int) -> str: 51 | seq = '1' 52 | for i in range(n-1): 53 | seq = self.getNext(seq) 54 | return seq 55 | def getNext(self,seq): 56 | i,next_seq = 0 57 | while i < len(seq): 58 | count = 1 59 | while i < len(seq) - 1 and seq[i] == seq[i+1]: 60 | count += 1 61 | i += 1 62 | next_seq += str(count) + seq[i] 63 | i += 1 64 | return nex_seq -------------------------------------------------------------------------------- /其他/409_最长回文串.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed May 13 18:38:54 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 只要哈希表中的值出现次数为偶数,就可以成为最长回文串的一部分; 10 | 如果是奇数,次数减一也可以成为最长回文串的一部分; 11 | 第一次出现的奇数可以不用减一,放在最长回文串的中间。 12 | ''' 13 | 14 | 15 | class Solution: 16 | def longestPalindrome(self, s: str) -> int: 17 | adict = {} 18 | count = 0 19 | flag = 0 20 | for i in range(len(s)): 21 | if s[i] not in adict: 22 | adict[s[i]] = 1 23 | else: 24 | adict[s[i]] += 1 25 | for word in adict.keys(): 26 | if adict[word] % 2 == 0: 27 | count += adict[word] 28 | else: 29 | #之所以这么搞是因为完全有可能没有奇数个的元素存在 30 | count += adict[word] - 1 31 | flag = 1 32 | return count + flag -------------------------------------------------------------------------------- /其他/454_四数相加 II.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed May 20 15:19:10 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #两个两个的找,降低时间复杂度 9 | class Solution: 10 | def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -> int: 11 | length = len(A) 12 | res = 0 13 | adict = {} 14 | for x in range(length): 15 | for y in range(length): 16 | 17 | if A[x]+B[y] not in adict: 18 | adict[A[x]+B[y]] = 1 19 | else: 20 | adict[A[x]+B[y]] += 1 21 | for i in range(length): 22 | for j in range(length): 23 | if -(C[i] + D[j]) in adict: 24 | res += adict[-(C[i] + D[j])] 25 | return res 26 | 27 | #先找三个再找最后一个 N3复杂度 28 | class Solution: 29 | def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -> int: 30 | length = len(A) 31 | res = 0 32 | adict = {} 33 | for x in range(length): 34 | for y in range(length): 35 | for z in range(length): 36 | if A[x]+B[y]+C[z] not in adict: 37 | adict[A[x]+B[y]+C[z]] = 1 38 | else: 39 | adict[A[x]+B[y]+C[z]] += 1 40 | for i in range(length): 41 | if -D[i] in adict: 42 | res += adict[-D[i]] 43 | 44 | return res 45 | -------------------------------------------------------------------------------- /其他/55_跳跃游戏.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat May 9 10:53:17 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def canJump(self, nums: List[int]) -> bool: 10 | if len(nums) == 1: 11 | return True 12 | res = 0 13 | end = 0 14 | for i in range(len(nums)-1): 15 | res = max(res, i+nums[i]) 16 | if res >= len(nums)-1: 17 | return True 18 | 19 | if i == end: 20 | if i == res: 21 | return False 22 | else: 23 | end = res 24 | 25 | 26 | class Solution: 27 | def canJump(self, nums: List[int]) -> bool: 28 | n, rightmost = len(nums), 0 29 | for i in range(n): 30 | if i <= rightmost: 31 | rightmost = max(rightmost, i + nums[i]) 32 | if rightmost >= n - 1: 33 | return True 34 | return False 35 | 36 | -------------------------------------------------------------------------------- /其他/56.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Apr 14 20:27:10 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def merge(self, intervals): 10 | 11 | intervals.sort(key=lambda x: x[0]) 12 | 13 | merged = [] 14 | for interval in intervals: 15 | # if the list of merged intervals is empty or if the current 16 | # interval does not overlap with the previous, simply append it. 17 | if merged == [] or merged[-1][1] < interval[0]: 18 | merged.append(interval) 19 | else: 20 | # otherwise, there is overlap, so we merge the current and previous 21 | # intervals. 22 | merged[-1][1] = max(merged[-1][1], interval[1]) 23 | 24 | return merged -------------------------------------------------------------------------------- /其他/58_Length of Last Word.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Aug 29 21:16:02 2019 4 | 5 | @author: leiya 6 | """ 7 | #split()的时候,多个空格当成一个空格;split() = 'a ' --- ['a'] 8 | #split(' ')的时候,多个空格都要分割,每个空格分割出来空。split(' ') = 'a ' --- ['a',''] 9 | class Solution: 10 | def lengthOfLastWord(self, s: str) -> int: 11 | s = s.split() 12 | if len(s) == 0: 13 | #s == [] 14 | return 0 15 | else : 16 | return len(s[-1]) 17 | 18 | class Solution: 19 | def lengthOfLastWord(self, s: str) -> int: 20 | if ' ' not in s: 21 | return len(s) 22 | count = 0 23 | length = 0 24 | for i in range(len(s)): 25 | if s[i] == ' ': 26 | 27 | 28 | count = 0 29 | else: 30 | count += 1 31 | length = count 32 | return length 33 | 34 | class Solution: 35 | def lengthOfLastWord(self, s: str) -> int: 36 | count = 0 37 | local_count = 0 38 | if ' ' not in s: 39 | return len(s) 40 | 41 | for i in range(len(s)): 42 | if s[i] == ' ': 43 | 44 | local_count = 0 45 | 46 | else: 47 | local_count += 1 48 | count = local_count 49 | return count -------------------------------------------------------------------------------- /其他/605_Can Place Flowers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Feb 4 16:02:22 2020 4 | 5 | @author: leiya 6 | """ 7 | class Solution: 8 | def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool: 9 | count = 0 10 | for i in range(len(flowerbed)): 11 | if flowerbed[i] == 0 and (i == 0 or flowerbed[i-1] == 0) and (i+1 == len(flowerbed) or flowerbed[i+1] == 0): 12 | #if i + 1 == len(flowerbed): 13 | #if flowerbed[0] == 1: 14 | #break 15 | flowerbed[i] = 1 16 | count += 1 17 | return count == n 18 | -------------------------------------------------------------------------------- /其他/633_Sum of Square Numbers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Feb 6 10:52:30 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | import math 10 | def judgeSquareSum(self, c: int) -> bool: 11 | i = 0 12 | j = int(c**0.5) 13 | while i <= j: 14 | if i**2 + j**2 == c: 15 | return True 16 | elif i**2 + j**2 < c: 17 | i += 1 18 | else: 19 | j -= 1 20 | return False -------------------------------------------------------------------------------- /其他/667_优美的排列 II.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 22 13:18:59 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #构造法,技术型太强 9 | #插排出现k个不同间隔的时候需要k+1个元素,剩下n-(k+1)个元素顺排,顺排只产生1个间隔,插排中出现的k个间隔中有一个是间隔1, 10 | #与顺排的重复了,所以这样下来插排+顺排一共是K个,满足要求了 11 | class Solution: 12 | def constructArray(self, n, k): 13 | """ 14 | :type n: int 15 | :type k: int 16 | :rtype: List[int] 17 | """ 18 | # 按顺序排好n-k个, 个数为1(邻差全是1) 19 | res = [] 20 | for i in range(1, n-k): 21 | res.append(i) 22 | 23 | # 剩下k个插排, 个数为k-1(插排的邻差计算方法为k-1) 24 | count = -1 25 | for i in range(n-k, n+1): 26 | count += 1 27 | res.append(i) 28 | res.append(n-count) 29 | 30 | return res[0:n] -------------------------------------------------------------------------------- /其他/66_Plus One.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Aug 29 21:43:06 2019 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | #list中每项是整数时,必须转换成str形式才可以将List用join方式变为str形式 10 | def plusOne(self, digits: List[int]) -> List[int]: 11 | alist = [] 12 | newdigits = list(map(str,digits)) 13 | aint = int(''.join(newdigits)) + 1 14 | for i in str(aint): 15 | alist.append(int(i)) 16 | return alist 17 | 18 | class Solution: 19 | def plusOne(self, digits: List[int]) -> List[int]: 20 | for i in reversed(range(len(digits))): 21 | if digits[i] == 9: 22 | digits[i] = 0 23 | else: 24 | digits[i] += 1 25 | return digits 26 | digits[0] = 1 27 | digits.append(0) 28 | return digits 29 | 30 | 31 | class Solution: 32 | def plusOne(self, digits): 33 | 34 | for i in reversed(range(len(digits))): 35 | if digits[i] < 9: 36 | digits[i] += 1 37 | return digits 38 | else: 39 | digits[i] = 0 40 | print(digits) 41 | 42 | digits.insert(0,1) 43 | return digits 44 | solution = Solution() 45 | solution.plusOne([9,9]) 46 | -------------------------------------------------------------------------------- /其他/67_Add Binary.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Sep 1 12:53:44 2019 4 | 5 | @author: leiya 6 | """ 7 | 8 | #1.str,int类型 9 | #2.变量的声明 10 | #3.while循环里变量每次循环的更新 11 | class Solution: 12 | def addBinary(self, a: str, b: str) -> str: 13 | carry = 0 14 | i = len(a) - 1 15 | j = len(b) - 1 16 | res = '' 17 | while i >= 0 or j >= 0 or carry: 18 | if i >= 0: 19 | temp1 = int(a[i]) 20 | else: 21 | temp1 = 0 22 | 23 | if j >= 0: 24 | temp2 = int(b[j]) 25 | else: 26 | temp2 = 0 27 | 28 | t = (temp1 + temp2 + carry) % 2 29 | carry = (temp1 + temp2 + carry) // 2 30 | res += str(t) 31 | i -= 1 32 | j -= 1 33 | return res[::-1] 34 | 35 | 36 | 37 | 38 | class Solution: 39 | def addBinary(self, a: str, b: str) -> str: 40 | aint = int(a,2) + int(b,2) 41 | return str(bin(aint))[2:] 42 | 43 | class Solution: 44 | def addBinary(self, a: str, b: str) -> str: 45 | result,carry,val = "",0,0 46 | for i in range(max(len(a),len(b))): 47 | val = carry 48 | if i < len(a): 49 | val += int(a[-(i+1)]) 50 | if i < len(b): 51 | val += int(b[-(i+1)]) 52 | carry,val = val // 2,val%2 53 | result += str(val) 54 | if carry: 55 | result += str(1) 56 | return result[::-1] 57 | 58 | 59 | -------------------------------------------------------------------------------- /其他/697_数组的度.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 22 13:46:32 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution(object): 10 | def findShortestSubArray(self, nums): 11 | left, right, count = {}, {}, {} 12 | for i, x in enumerate(nums): 13 | if x not in left: 14 | left[x] = i 15 | #right位置每次循环都更新 16 | right[x] = i 17 | #x如果在count中,就取出x作为key的value,若没有就取0 18 | count[x] = count.get(x, 0) + 1 19 | 20 | ans = len(nums) 21 | degree = max(count.values()) 22 | for x in count.keys(): 23 | if count[x] == degree: 24 | ans = min(ans, right[x] - left[x] + 1) 25 | 26 | return ans 27 | -------------------------------------------------------------------------------- /其他/766_托普利茨矩阵.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 22 13:56:50 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def isToeplitzMatrix(self, matrix: List[List[int]]) -> bool: 11 | if not matrix: 12 | return False 13 | 14 | row = len(matrix) 15 | col = len(matrix[0]) 16 | for i in range(row): 17 | for j in range(col): 18 | if i+1 < row and j + 1 < col: 19 | if matrix[i][j] != matrix[i+1][j+1]: 20 | return False 21 | return True -------------------------------------------------------------------------------- /其他/769_最多能完成排序的块.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 22 18:41:43 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | '''首先找到从左块开始最小块的大小。如果前 k 个元素为 [0, 1, ..., k-1], 10 | 可以直接把他们分为一个块。当我们需要检查 [0, 1, ..., n-1] 中前 k+1 个元素是不是 [0, 1, ..., k] 的时候, 11 | 只需要检查其中最大的数是不是 k 就可以了。 12 | ''' 13 | 14 | class Solution(object): 15 | def maxChunksToSorted(self, arr): 16 | ans = ma = 0 17 | for i, x in enumerate(arr): 18 | ma = max(ma, x) 19 | if ma == i: 20 | ans += 1 21 | return ans -------------------------------------------------------------------------------- /其他/93_复原IP地址.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 15 10:16:24 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def restoreIpAddresses(self, s: str) -> List[str]: 11 | size = len(s) 12 | if size < 4 or size > 12: 13 | return [] 14 | def dfs(s, size, split_times, begin, path, res): 15 | if begin == size: 16 | if split_times == 4: 17 | res.append('.'.join(path)) 18 | return 19 | #此处为剪枝操作 20 | #split == 1表示前面已经分出了一块,马上就要分出第二块地址段了,所有这里判断的是,如果前面分出的地址段都是1的情况下后面代码不足其他块的代码 21 | if size - begin < (4 - split_times) * 1 or size - begin > 3 * (4 - split_times): 22 | return 23 | 24 | for i in range(3): 25 | if begin + i >= size: 26 | break 27 | 28 | ip_segment = judge_if_ip_segment(s, begin, begin + i) 29 | 30 | if ip_segment != -1: 31 | path.append(str(ip_segment)) 32 | dfs(s, size, split_times + 1, begin + i + 1, path, res) 33 | path.pop() 34 | def judge_if_ip_segment(s, left, right): 35 | size = right - left + 1 36 | 37 | if size > 1 and s[left] == '0': 38 | return -1 39 | 40 | res = 0 41 | for i in range(left, right + 1): 42 | res = res * 10 + ord(s[i]) - ord('0') 43 | 44 | if res > 255: 45 | return - 1 46 | return res 47 | path = [] 48 | res = [] 49 | dfs(s, size, 0, 0, path, res) 50 | return res -------------------------------------------------------------------------------- /列表/118_Pascal's Triangle.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jan 29 18:42:56 2020 4 | 5 | @author: leiya 6 | """ 7 | class Solution: 8 | def generate(self, numRows: int) -> List[List[int]]: 9 | if numRows == 0: 10 | return [] 11 | if numRows == 1: 12 | return [[1]] 13 | if numRows == 2: 14 | return [[1],[1,1]] 15 | res = [[1], [1,1]] 16 | 17 | for i in range(3,numRows+1): 18 | cur_res = [1] 19 | for j in range(1,len(res[i - 2])): 20 | cur_res.append(res[i-2][j-1]+ res[i-2][j]) 21 | cur_res.append(1) 22 | res.append(cur_res) 23 | return res 24 | 25 | 26 | 27 | 28 | class Solution: 29 | def generate(self, numRows: int) -> List[List[int]]: 30 | res = [] 31 | for i in range(numRows): 32 | res.append([]) 33 | for j in range(i+1): 34 | if j == 0 or j == i: 35 | res[i].append(1) 36 | else: 37 | res[i].append(res[i-1][j-1]+res[i-1][j]) 38 | return res 39 | -------------------------------------------------------------------------------- /列表/119_Pascal's Triangle II.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jan 30 21:35:46 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def getRow(self, rowIndex: int) -> List[int]: 10 | res = [] 11 | for i in range(rowIndex+1): 12 | res.append([]) 13 | for j in range(i+1): 14 | if j == 0 or j == i: 15 | res[i].append(1) 16 | else: 17 | res[i].append(res[i-1][j-1]+res[i-1][j]) 18 | return res[rowIndex] 19 | 20 | -------------------------------------------------------------------------------- /列表/167_Two Sum II - Input array is sorted.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Aug 31 15:29:17 2019 4 | 5 | @author: leiya 6 | """ 7 | #2020/2/6 8 | class Solution: 9 | def twoSum(self, numbers: List[int], target: int) -> List[int]: 10 | i = 0 11 | j = len(numbers)-1 12 | while i < j: 13 | if numbers[i] + numbers[j] == target: 14 | return [i+1,j+1] 15 | elif numbers[i] + numbers[j] < target: 16 | i += 1 17 | else: 18 | j -= 1 19 | 20 | 21 | class Solution: 22 | def twoSum(self, numbers: List[int], target: int) -> List[int]: 23 | adict = {} 24 | for i in range(len(numbers)): 25 | temp = target - numbers[i] 26 | if temp not in adict: 27 | adict[numbers[i]] = i 28 | else: 29 | return [adict[temp]+1,i+1] 30 | 31 | 32 | class Solution: 33 | def twoSum(self, numbers: List[int], target: int) -> List[int]: 34 | '''two pointers because of sorted list''' 35 | start = 0 36 | end = len(numbers) - 1 37 | while end > start: 38 | if numbers[start] + numbers[end] > target: 39 | end -= 1 40 | elif numbers[start] + numbers[end] < target: 41 | start += 1 42 | else: 43 | return [start+1,end+1] -------------------------------------------------------------------------------- /列表/225_Implement Stack using Queues.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Feb 16 17:32:28 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class MyStack: 9 | 10 | def __init__(self): 11 | """ 12 | Initialize your data structure here. 13 | """ 14 | self.queue = [] 15 | 16 | def push(self, x: int) -> None: 17 | """ 18 | Push element x onto stack. 19 | """ 20 | self.queue.append(x) 21 | 22 | def pop(self) -> int: 23 | """ 24 | Removes the element on top of the stack and returns that element. 25 | """ 26 | return self.queue.pop() 27 | 28 | def top(self) -> int: 29 | """ 30 | Get the top element. 31 | """ 32 | return self.queue[-1] 33 | 34 | def empty(self) -> bool: 35 | """ 36 | Returns whether the stack is empty. 37 | """ 38 | if self.queue: 39 | return False 40 | else: 41 | return True 42 | 43 | 44 | # Your MyStack object will be instantiated and called as such: 45 | # obj = MyStack() 46 | # obj.push(x) 47 | # param_2 = obj.pop() 48 | # param_3 = obj.top() 49 | # param_4 = obj.empty() -------------------------------------------------------------------------------- /列表/232_Implement Queue using Stacks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Feb 16 17:27:43 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class MyQueue: 9 | 10 | def __init__(self): 11 | """ 12 | Initialize your data structure here. 13 | """ 14 | self.queue = [] 15 | 16 | def push(self, x: int) -> None: 17 | """ 18 | Push element x to the back of queue. 19 | """ 20 | self.queue.append(x) 21 | 22 | 23 | def pop(self) -> int: 24 | """ 25 | Removes the element from in front of queue and returns that element. 26 | """ 27 | x = self.queue[0] 28 | self.queue.pop(0) 29 | return x 30 | 31 | def peek(self) -> int: 32 | """ 33 | Get the front element. 34 | """ 35 | return self.queue[0] 36 | 37 | def empty(self) -> bool: 38 | """ 39 | Returns whether the queue is empty. 40 | """ 41 | if self.queue: 42 | return False 43 | else: 44 | return True 45 | 46 | 47 | # Your MyQueue object will be instantiated and called as such: 48 | # obj = MyQueue() 49 | # obj.push(x) 50 | # param_2 = obj.pop() 51 | # param_3 = obj.peek() 52 | # param_4 = obj.empty() -------------------------------------------------------------------------------- /列表/27_Remove Element.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Aug 28 09:16:20 2019 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def removeElement(self, nums: List[int], val: int) -> int: 10 | i = 0 11 | while i <= len(nums)-1: 12 | if nums[i] == val: 13 | nums.pop(i) 14 | else: 15 | i += 1 16 | return len(nums) 17 | 18 | class Solution: 19 | def removeElement(self, nums: List[int], val: int) -> int: 20 | #two pointers from start and end 21 | if not nums: 22 | return 0 23 | pre = 0 24 | post = len(nums) - 1 25 | while pre < post: 26 | if nums[pre] == val: 27 | if nums[post] == val: 28 | post -= 1 29 | else: 30 | nums[pre],nums[post] = nums[post],nums[pre] 31 | pre += 1 32 | else: 33 | pre += 1 34 | if nums[pre] == val: 35 | return pre 36 | else: 37 | return pre + 1 38 | 39 | class Solution: 40 | def removeElement(self, nums: List[int], val: int) -> int: 41 | 42 | pre = 0 43 | post = len(nums) - 1 44 | while pre <= post: 45 | if nums[pre] == val: 46 | nums[pre],nums[post] = nums[post],nums[pre] 47 | post -= 1 48 | else: 49 | pre += 1 50 | return post + 1 51 | -------------------------------------------------------------------------------- /列表/54_螺旋矩阵.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Jun 5 09:04:09 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #注意边界条件 9 | class Solution: 10 | def spiralOrder(self, matrix:[[int]]) -> [int]: 11 | if not matrix: return [] 12 | l, r, t, b, res = 0, len(matrix[0]) - 1, 0, len(matrix) - 1, [] 13 | while True: 14 | for i in range(l, r+1): 15 | res.append(matrix[l][i]) 16 | t += 1 17 | if t > b: 18 | break 19 | for i in range(t, b+1): 20 | res.append(matrix[i][r]) 21 | r -= 1 22 | if r < l: 23 | break 24 | for i in range(r, l-1, -1): 25 | res.append(matrix[b][i]) 26 | b -= 1 27 | if b < t: 28 | break 29 | for i in range(b, t-1, -1): 30 | res.append(matrix[i][l]) 31 | l += 1 32 | if l > r: 33 | break 34 | return res -------------------------------------------------------------------------------- /列表/565_数组嵌套.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 22 14:10:15 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #brute force, 超时 9 | class Solution: 10 | def arrayNesting(self, nums: List[int]) -> int: 11 | max_length = 1 12 | for i in range(len(nums)): 13 | memory = [i] 14 | length = 1 15 | while nums[i] not in memory: 16 | i = nums[i] 17 | memory.append(i) 18 | length += 1 19 | max_length = max(max_length,length) 20 | return max_length 21 | 22 | 23 | #注意剪枝 24 | class Solution: 25 | def arrayNesting(self, nums: List[int]) -> int: 26 | d = {} 27 | for i in range(len(nums)): 28 | d[i] = nums[i] 29 | count = 0 30 | i = 0 31 | while i < len(nums): 32 | tmp = i 33 | ans = 0 34 | while d[tmp] != '#': 35 | ans += 1 36 | res = d[tmp] 37 | d[tmp] = '#' 38 | tmp = res 39 | i += 1 40 | count = max(count,ans) 41 | return count -------------------------------------------------------------------------------- /列表/566_重塑矩阵.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jun 16 14:14:22 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def matrixReshape(self, nums: List[List[int]], r: int, c: int) -> List[List[int]]: 10 | row = len(nums) 11 | col = len(nums[0]) 12 | if row * col != r * c: 13 | return nums 14 | new_num = [[0 for _ in range(c)] for _ in range(r)] 15 | for i in range(row*col): 16 | new_num[i//c][i%c] = nums[i//col][i%col] 17 | 18 | return new_num -------------------------------------------------------------------------------- /前缀和/1248_统计「优美子数组」.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri May 15 13:52:29 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | ''' 10 | 0712 11 | 前缀和+滑动窗口 12 | 参考:930,992(based on 904) 13 | ''' 14 | class Solution: 15 | def numberOfSubarrays(self, nums: List[int], k: int) -> int: 16 | def atmost(nums,k): 17 | start = 0 18 | count = 0 19 | odd_count = 0 20 | for end in range(len(nums)): 21 | if nums[end] % 2 != 0: 22 | odd_count += 1 23 | while odd_count > k: 24 | if nums[start] % 2 != 0: 25 | odd_count -= 1 26 | start += 1 27 | count += end-start+1 28 | #注意return返回的位置,要在for循环外返回 29 | return count 30 | return atmost(nums,k) - atmost(nums,k-1) 31 | 32 | 33 | #前缀和+差分+哈希表 34 | class Solution: 35 | def numberOfSubarrays(self, nums: List[int], k: int) -> int: 36 | #dict中存放到这个位置的奇数的个数 37 | adict = {} 38 | adict[0] = 1 39 | #temp是奇数的个数 40 | temp = 0 41 | res = 0 42 | for i in range(len(nums)): 43 | if nums[i] % 2 == 1: 44 | temp += 1 45 | if temp - k in adict: 46 | res += adict[temp-k] 47 | if temp in adict: 48 | adict[temp] += 1 49 | else: 50 | adict[temp] = 1 51 | 52 | return res 53 | 54 | class Solution: 55 | def numberOfSubarrays(self, nums: List[int], k: int) -> int: 56 | odd_count = 0 57 | adict = {} 58 | adict[0] = 1 59 | count = 0 60 | for num in nums: 61 | if num % 2 != 0: 62 | odd_count += 1 63 | if odd_count - k in adict: 64 | count += adict[odd_count-k] 65 | if odd_count not in adict: 66 | adict[odd_count] = 1 67 | else: 68 | adict[odd_count] += 1 69 | return count -------------------------------------------------------------------------------- /前缀和/1371_每个元音包含偶数次的最长子字符串.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed May 20 18:55:59 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def findTheLongestSubstring(self, s: str) -> int: 11 | ans, status, n = 0, 0, len(s) 12 | #记录的是这个status的长度 13 | pos = [-1] * (1 << 5) 14 | #全为偶数时置为0 15 | pos[0] = 0 16 | 17 | for i in range(n): 18 | if s[i] == 'a': 19 | status ^= 1 << 0 20 | elif s[i] == 'e': 21 | status ^= 1 << 1 22 | elif s[i] == 'i': 23 | status ^= 1 << 2 24 | elif s[i] == 'o': 25 | status ^= 1 << 3 26 | elif s[i] == 'u': 27 | status ^= 1 << 4 28 | if pos[status] != -1: 29 | ans = max(ans, i + 1 - pos[status]) 30 | else: 31 | ''' 32 | 为了保证第一个字母是辅音字母时也可以输出正确的值,因为如果改为 pos[status] = i; 33 | 那相应的if语句要改为ans = max(ans, i - pos[status]); 34 | 这样当第一个字母为辅音时,status = 0,~pos[status]判为真,ans就会被赋值为0,这显然不是正确的。 35 | 同时题解中是用pos[atatus]是不是等于-1来判断前面是否出现过与status相同的奇偶性,所以也不能初始化为pos[0] = -1。 36 | 就只好多加一个1 37 | ''' 38 | pos[status] = i + 1 39 | return ans -------------------------------------------------------------------------------- /前缀和/1_两数之和.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jul 13 11:05:49 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def twoSum(self, nums: List[int], target: int) -> List[int]: 11 | adict = {} 12 | for i in range(len(nums)): 13 | if target - nums[i] in adict: 14 | return [adict[target - nums[i]],i] 15 | else: 16 | adict[nums[i]] = i -------------------------------------------------------------------------------- /前缀和/560_和为K的子数组.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri May 15 12:52:40 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0725 10 | 为什么这题不可以用双指针/滑动窗口:因为nums[i]可以小于0,也就是说右指针i向后移1位不能保证区间会增大, 11 | 左指针j向后移1位也不能保证区间和会减小。给定j,i的位置没有二段性,vice versa。 12 | ''' 13 | class Solution: 14 | def subarraySum(self, nums: List[int], k: int) -> int: 15 | adict = {} 16 | adict[0] = 1 17 | cur_sum = 0 18 | count = 0 19 | for num in nums: 20 | cur_sum += num 21 | if cur_sum - k in adict: 22 | count += adict[cur_sum-k] 23 | if cur_sum not in adict: 24 | adict[cur_sum] = 1 25 | else: 26 | adict[cur_sum] += 1 27 | return count 28 | 29 | 30 | #前缀和+哈希表优化(存储前缀和各个值出现的个数) 31 | class Solution: 32 | def subarraySum(self, nums: List[int], k: int) -> int: 33 | adict = {} 34 | adict[0] = 1 35 | cur_sum = 0 36 | res = 0 37 | for i in range(len(nums)): 38 | cur_sum += nums[i] 39 | ''' 40 | 下面的两个if是有先后顺序的,必须严格按照当前顺序,因为必须先更新res再更新adict[cur_sum] 41 | ''' 42 | if cur_sum - k in adict: 43 | res += adict[cur_sum-k] 44 | if cur_sum in adict: 45 | adict[cur_sum] += 1 46 | else: 47 | adict[cur_sum] = 1 48 | return res 49 | 50 | #滑动窗口,超时 51 | def subarraySum(nums, k): 52 | start = 0 53 | count = 0 54 | res = 0 55 | while start < len(nums): 56 | for i in range(start, len(nums)): 57 | res += nums[i] 58 | if res == k: 59 | count += 1 60 | 61 | 62 | res = 0 63 | start += 1 64 | return count 65 | 66 | a = subarraySum([1,2,1,2,1],3) 67 | print(a) 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /前缀和/930_和相同的二元子数组.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jul 12 14:19:38 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 这个转换形式很难想出来,不如采用前缀和来解决这道问题 10 | 给定一个0,1数组,你可以选择最多S个1和任意个0,你的选择数减去 给定一个0,1数组,你可以选择最多S - 1个1和任意个0,你的选择数 11 | ''' 12 | class Solution: 13 | def numSubarraysWithSum(self, A: List[int], S: int) -> int: 14 | def atMostK(A, S): 15 | if S < 0: 16 | return 0 17 | start = 0 18 | count = 0 19 | sum_ = 0 20 | for end in range(len(A)): 21 | sum_ += A[end] 22 | while sum_ > S: 23 | sum_ -= A[start] 24 | start += 1 25 | count += end - start + 1 26 | return count 27 | return atMostK(A, S) - atMostK(A, S - 1) 28 | 29 | 30 | class Solution: 31 | def numSubarraysWithSum(self, A: List[int], S: int) -> int: 32 | def atMostK(A, S): 33 | if S < 0: 34 | return 0 35 | i = res = 0 36 | 37 | for j in range(len(A)): 38 | S -= A[j] 39 | while S < 0: 40 | S += A[i] 41 | i += 1 42 | res += j - i + 1 43 | return res 44 | return atMostK(A, S) - atMostK(A, S - 1) -------------------------------------------------------------------------------- /前缀和/992_K 个不同整数的子数组.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jul 12 15:37:46 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | #与930解法类似,需要思考的是为什么需要这么去做 10 | ''' 11 | 904水果题相当于是这道题的基础,只是求了最多有两种类型的时候的情况,相当于求的atmost(A,2),因此没有借用前缀和的思想, 12 | 这道题之所以需要前缀和是因为它规定恰好为K个,因此可以用前缀和的思想来卡出恰好的种类数 13 | 与904的另一个区别是,输出不一样,904输出最多选择两种数字的情况下,最大的连续子序列长度,这道题是计算最多选择两种数字情况下有多少种子串的选择 14 | ''' 15 | class Solution: 16 | def subarraysWithKDistinct(self, A: List[int], K: int) -> int: 17 | ''' 18 | atmost的含义是最多(最多意味着可以小于等于,但不能超过)有K个不同种类的可能个数是多少个 19 | 比如,当前有n个字符,判断成立,那么新多出来n个组合种类,可以从后向前看,n,(n,n-1),(n,n-1,n-2)...,一共新多出来n种方式 20 | 之所以这么计算是因为可以用常规模式计算出这种定义形式的个数,无须虚拟的start进行试探 21 | 因为这道题相对于leetcode.003等常规滑动窗口来说,收缩窗口的时候不是仅从start向前收缩就可以的,有些解可能需要start向前收缩的同时end向回收缩 22 | ''' 23 | def atmost(A,k): 24 | start = 0 25 | adict = {} 26 | count = 0 27 | for end in range(len(A)): 28 | if A[end] not in adict: 29 | adict[A[end]] = 1 30 | else: 31 | adict[A[end]] += 1 32 | while len(adict) > k: 33 | adict[A[start]] -= 1 34 | if adict[A[start]] == 0: 35 | del adict[A[start]] 36 | start += 1 37 | #包含A[end]这个数在窗口[start,end]范围内所有可能的连续子串个数就等于当前窗口的长度 38 | count += end-start+1 39 | return count 40 | return atmost(A,K)-atmost(A,K-1) -------------------------------------------------------------------------------- /前缀和/前缀和扩展_和不为0的连续子数组个数.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Jul 25 15:08:24 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #和不为0的连续子数组个数 9 | class Solution: 10 | def subarraySum(self, nums): 11 | adict = {} 12 | adict[0] = 1 13 | sum = 0 14 | count = 0 15 | for index,num in enumerate(nums): 16 | sum += num 17 | #这里的计数可以这样理解:1,2,3现在遍历到3了,正常来说多加一个3应该多加了3个连续子数组,[3],[2,3],[1,2,3] 18 | #可以从后向前数 19 | count += index+1 20 | #如果之前的和和现在的和一样的话,那么意味着现在的个数减去之前sum出现的个数,就可以得到不为0的个数,因为两者之间就是和为0的个数, 21 | #不然不会出现之前和为a,现在和还是a的情况,这就说明两者之间的部分的和是0 22 | if sum in adict.keys(): 23 | count -= adict[sum] 24 | if sum not in adict: 25 | adict[sum] = 1 26 | else: 27 | adict[sum] += 1 28 | return count 29 | 30 | solution = Solution() 31 | res = solution.subarraySum([-1,0,1]) 32 | print(res) -------------------------------------------------------------------------------- /动态规划/1143_最长公共子序列.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun May 10 12:32:33 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #两个字符串的动态规划问题,联系72题,编辑距离 9 | '''因为子序列类型的问题,穷举出所有可能的结果都不容易,而动态规划算法做的就是穷举 + 剪枝, 10 | 它俩天生一对儿。所以可以说只要涉及子序列问题,十有八九都需要动态规划来解决''' 11 | #要设定dp的0,0位置为两个字符串都为空时候对应的最长公共子串 12 | class Solution: 13 | def longestCommonSubsequence(self, text1: str, text2: str) -> int: 14 | row = len(text1) 15 | col = len(text2) 16 | dp = [[0 for _ in range(col+1)] for _ in range(row+1)] 17 | 18 | for i in range(1, row+1): 19 | for j in range(1, col+1): 20 | #dp[i][j]对应第i,j个字母,也就是字符串中index是i-1,j-1对应的字母 21 | if text1[i-1] == text2[j-1]: 22 | dp[i][j] = dp[i-1][j-1] + 1 23 | else: 24 | dp[i][j] = max(dp[i-1][j], dp[i][j-1]) 25 | return dp[-1][-1] 26 | 27 | class Solution: 28 | def longestCommonSubsequence(self, text1: str, text2: str) -> int: 29 | dp = [[0 for _ in range(len(text2)+1)] for _ in range(len(text1)+1)] 30 | 31 | for i in range(1, len(text1)+1): 32 | for j in range(1, len(text2)+1): 33 | if text1[i-1] == text2[j-1]: 34 | dp[i][j] = dp[i-1][j-1] + 1 35 | else: 36 | dp[i][j] = max(dp[i-1][j], dp[i][j-1]) 37 | return dp[-1][-1] 38 | 39 | -------------------------------------------------------------------------------- /动态规划/122_Best Time to Buy and Sell Stock II.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Feb 1 21:37:28 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | ''' 10 | 0713 11 | 与121唯一不同在于dp[i][1]的更新策略,本质在于dp[i][0]的含义是在i及i天之前某个时刻卖出stock后手里有cash,无stock的最大利润 12 | ''' 13 | 14 | class Solution: 15 | def maxProfit(self, prices: List[int]) -> int: 16 | if not prices: 17 | return 0 18 | dp = [[0,0] for _ in range(len(prices))] 19 | dp[0][1] = -prices[0] 20 | #注意从1开始 21 | for i in range(1, len(prices)): 22 | dp[i][0] = max(dp[i-1][1]+prices[i],dp[i-1][0]) 23 | dp[i][1] = max(dp[i-1][1],dp[i-1][0]-prices[i]) 24 | return dp[-1][0] 25 | 26 | 27 | #动态规划 28 | class Solution: 29 | def maxProfit(self, prices: List[int]) -> int: 30 | dp = [0 for _ in range(len(prices))] 31 | for i in range(1, len(prices)): 32 | if prices[i] > prices[i-1]: 33 | dp[i] = dp[i-1] + prices[i] - prices[i-1] 34 | else: 35 | dp[i] = dp[i-1] 36 | return dp[-1] 37 | 38 | 39 | #贪心 40 | class Solution: 41 | def maxProfit(self, prices: List[int]) -> int: 42 | price = 0 43 | for i in range(1,len(prices)): 44 | if prices[i] > prices[i-1]: 45 | price += prices[i] - prices[i-1] 46 | return price 47 | -------------------------------------------------------------------------------- /动态规划/123_买卖股票的最佳时机 III.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jul 13 14:45:31 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def maxProfit(self, prices: List[int]) -> int: 11 | if not prices: 12 | return 0 13 | dp = [[0,0,0,0] for _ in range(len(prices))] 14 | dp[0][0] = -prices[0] 15 | #因为在更新dp[i][3]的时候可能会出现比0小的数,因为dp[i-1][2]-prices[i] 16 | #所以这个时候dp[i][3]不能默认是0,相反dp[i][4]就可以默认是0 17 | dp[0][2] = float('-inf') 18 | 19 | 20 | # dp[j]:i 表示 [0, i] 区间里,状态为 j 的最大收益 21 | # j = 0:什么都不操作,没有意义,不会更新最大利润 22 | # j = 1:第 1 次买入一支股票 23 | # j = 2:第 1 次卖出一支股票 24 | # j = 3:第 2 次买入一支股票 25 | # j = 4:第 2 次卖出一支股票 26 | 27 | for i in range(1, len(prices)): 28 | dp[i][0] = max(dp[i-1][0], - prices[i]) 29 | dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i]) 30 | dp[i][2] = max(dp[i-1][2], dp[i-1][1] - prices[i]) 31 | dp[i][3] = max(dp[i-1][3], dp[i-1][2] + prices[i]) 32 | return max(dp[-1][1], dp[-1][3]) 33 | 34 | class Solution: 35 | def maxProfit(self, prices: List[int]) -> int: 36 | if not prices: 37 | return 0 38 | dp = [[0,0,0,0,0] for _ in range(len(prices))] 39 | dp[0][0] = 0 40 | dp[0][1] = -prices[0] 41 | 42 | dp[0][3] = float('-inf') 43 | dp[0][4] = float('-inf') 44 | 45 | # dp[j]:i 表示 [0, i] 区间里,状态为 j 的最大收益 46 | # j = 0:什么都不操作 47 | # j = 1:第 1 次买入一支股票 48 | # j = 2:第 1 次卖出一支股票 49 | # j = 3:第 2 次买入一支股票 50 | # j = 4:第 2 次卖出一支股票 51 | 52 | for i in range(1, len(prices)): 53 | dp[i][0] = 0 54 | dp[i][1] = max(dp[i-1][1], - prices[i]) 55 | dp[i][2] = max(dp[i-1][2], dp[i-1][1] + prices[i]) 56 | dp[i][3] = max(dp[i-1][3], dp[i-1][2] - prices[i]) 57 | dp[i][4] = max(dp[i-1][4], dp[i-1][3] + prices[i]) 58 | return max(0, dp[-1][2], dp[-1][4]) -------------------------------------------------------------------------------- /动态规划/1277_统计全为1的正方形子矩阵.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri May 8 10:10:11 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def countSquares(self, matrix: List[List[int]]) -> int: 10 | dp = [[0 for _ in range(len(matrix[0]))] for _ in range(len(matrix))] 11 | 12 | count = 0 13 | for i in range(len(matrix)): 14 | for j in range(len(matrix[i])): 15 | if matrix[i][j] == 1: 16 | if i == 0 or j == 0: 17 | dp[i][j] = 1 18 | 19 | else: 20 | dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])+1 21 | 22 | count += dp[i][j] 23 | return count -------------------------------------------------------------------------------- /动态规划/152_乘积最大子数组.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon May 18 09:01:49 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def maxProduct(self, nums: List[int]) -> int: 11 | max_dp = nums[:] 12 | min_dp = nums[:] 13 | for i in range(1, len(nums)): 14 | max_dp[i] = max(max_dp[i], max_dp[i-1]*max_dp[i], max_dp[i]*min_dp[i-1]) 15 | min_dp[i] = min(min_dp[i], min_dp[i-1]*min_dp[i], min_dp[i]*max_dp[i-1]) 16 | return max(max(max_dp),max(min_dp)) -------------------------------------------------------------------------------- /动态规划/188_买卖股票的最佳时机 IV.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jul 13 15:15:02 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def maxProfit(self, k: int, prices: List[int]) -> int: 11 | size = len(prices) 12 | if size < 2 or k == 0: 13 | return 0 14 | if k >= size // 2: 15 | res = 0 16 | for i in range(1, size): 17 | if prices[i] > prices[i - 1]: 18 | res += (prices[i] - prices[i - 1]) 19 | return res 20 | #初始化:把持股的部分都设置为一个较大的负值 21 | dp = [[[0, float('-inf')] for _ in range(k + 1)] for _ in range(size + 1)] 22 | 23 | for i in range(1, size + 1): 24 | for j in range(1, k + 1): 25 | dp[i][j][1] = max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i - 1]) 26 | dp[i][j][0] = max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i - 1]) 27 | 28 | return dp[size][k][0] 29 | 30 | 31 | class Solution: 32 | def maxProfit(self, k: int, prices: List[int]) -> int: 33 | size = len(prices) 34 | if size < 2 or k == 0: 35 | return 0 36 | if k >= size // 2: 37 | res = 0 38 | for i in range(1, size): 39 | if prices[i] > prices[i - 1]: 40 | res += (prices[i] - prices[i - 1]) 41 | return res 42 | #初始化:把持股的部分都设置为一个较大的负值 43 | dp = [[[0, 0] for _ in range(k + 1)] for _ in range(size + 1)] 44 | for i in range(len(dp)): 45 | for j in range(len(dp[i])): 46 | dp[0][j][1] = float('-inf') 47 | break 48 | for i in range(1, size + 1): 49 | for j in range(1, k + 1): 50 | dp[i][j][1] = max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i - 1]) 51 | dp[i][j][0] = max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i - 1]) 52 | 53 | return dp[size][k][0] 54 | 55 | -------------------------------------------------------------------------------- /动态规划/198_打家劫舍.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Apr 12 16:49:26 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #注意边界值 9 | class Solution: 10 | def rob(self, nums: List[int]) -> int: 11 | dp = [0 for _ in range(len(nums)+2)] 12 | for i in range(2, len(nums)+2): 13 | dp[i] = max(dp[i-1], dp[i-2]+nums[i-2]) 14 | return dp[-1] 15 | 16 | class Solution: 17 | def rob(self, nums: List[int]) -> int: 18 | #if not nums: 19 | #return 0 20 | dp = [0 for i in range(len(nums)+2)] 21 | dp[2:] = nums 22 | for i in range(2, len(nums)+2): 23 | dp[i] = max(dp[i-2]+dp[i], dp[i-1]) 24 | return dp[-1] 25 | 26 | 27 | 28 | class Solution: 29 | def rob(self, nums: List[int]) -> int: 30 | pre2 = 0 31 | pre1 = 0 32 | for i in range(len(nums)): 33 | cur = max(pre2+nums[i], pre1) 34 | pre2 = pre1 35 | pre1 = cur 36 | return pre1 -------------------------------------------------------------------------------- /动态规划/213_打家劫舍 II.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Apr 12 17:02:39 2020 4 | 5 | @author: leiya 6 | """ 7 | #浅拷贝与深拷贝,特殊情况的处理 8 | #即在这种情况下只有一个元素的时候会出现问题,因为一开始对nums进行了切片操作 9 | 10 | class Solution: 11 | def rob(self, nums: List[int]) -> int: 12 | if len(nums) == 1: 13 | return nums[0] 14 | dp1 = [0 for _ in range(len(nums[:-1])+2)] 15 | dp2 = [0 for _ in range(len(nums[1:])+2)] 16 | for i in range(2,len(nums[:-1])+2): 17 | dp1[i] = max(dp1[i-1],dp1[i-2]+nums[:-1][i-2]) 18 | for j in range(2,len(nums[1:])+2): 19 | dp2[j] = max(dp2[j-1],dp2[j-2]+nums[1:][j-2]) 20 | return max(dp1[-1],dp2[-1]) 21 | 22 | 23 | class Solution: 24 | def rob(self, nums: List[int]) -> int: 25 | if len(nums) == 1: 26 | return nums[0] 27 | dp = [0 for i in range(len(nums)+1)] 28 | dp1 = dp[:] 29 | dp[2:] = nums[1:] 30 | dp1[2:] = nums[:-1] 31 | for i in range(2, len(dp)): 32 | dp[i] = max(dp[i-2]+dp[i], dp[i-1]) 33 | dp1[i] = max(dp1[i-2]+dp1[i], dp1[i-1]) 34 | return max(dp[-1], dp1[-1]) 35 | 36 | 37 | class Solution: 38 | def rob(self, nums: List[int]) -> int: 39 | if len(nums) == 1: 40 | return nums[0] 41 | pre2 = 0 42 | pre1 = 0 43 | for i in range(1, len(nums)): 44 | cur = max(pre1, pre2 + nums[i]) 45 | pre2 = pre1 46 | pre1 = cur 47 | pre4 = 0 48 | pre3 = 0 49 | for i in range(0, len(nums)-1): 50 | cur1 = max(pre3, pre4 + nums[i]) 51 | pre4 = pre3 52 | pre3 = cur1 53 | return max(pre1, pre3) -------------------------------------------------------------------------------- /动态规划/221_最大正方形.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri May 8 09:47:27 2020 4 | 5 | @author: leiya 6 | """ 7 | #矩阵中的的数据是string类型而非int类型 8 | #dp[i][j]状态指[i,j]为右下角可以产生的最大正方形 9 | class Solution: 10 | def maximalSquare(self, matrix: List[List[str]]) -> int: 11 | maxedge = 0 12 | dp = [[0 for _ in range(len(matrix[0]))] for _ in range(len(matrix))] 13 | for i in range(len(matrix)): 14 | for j in range(len(matrix[i])): 15 | if matrix[i][j] == '1': 16 | if i == 0 or j == 0: 17 | dp[i][j] = 1 18 | else: 19 | dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])+1 20 | maxedge = max(maxedge, dp[i][j]) 21 | return maxedge ** 2 -------------------------------------------------------------------------------- /动态规划/300_最长上升子序列.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat May 9 15:02:56 2020 4 | 5 | @author: leiya 6 | """ 7 | #每个dp存储以当前位置为结尾可以形成的最长上升子串 8 | class Solution: 9 | def lengthOfLIS(self, nums: List[int]) -> int: 10 | #注意nums不存在的情况 11 | if not nums: 12 | return 0 13 | dp = [1 for _ in range(len(nums))] 14 | 15 | for i in range(1, len(nums)): 16 | for j in range(i): 17 | if nums[j] < nums[i]: 18 | dp[i] = max(dp[i], dp[j]+1) 19 | 20 | return max(dp) 21 | -------------------------------------------------------------------------------- /动态规划/309_最佳买卖股票时机含冷冻期.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue May 19 17:43:38 2020 4 | 5 | @author: leiya 6 | """ 7 | #注意通过多设置维度,消除后效性 8 | #0:not hold cash in hand after selling 1:hold 2:freeze 9 | #注意判断特殊情况 10 | 11 | 12 | ''' 13 | 0710 14 | dp[i][0] 表示第i天结束,处于可以买入的状态(即手中不持有股票)的收益的最大值 0->1->2;2->0 15 | dp[i][1] 表示第i天结束,手中有股票的状态的收益的最大值 16 | dp[i][2] 表示第i天结束,处于冷冻期的收益的最大值 17 | 0713 18 | 画出状态转换图更容易理解 19 | ''' 20 | class Solution: 21 | def maxProfit(self, prices: List[int]) -> int: 22 | if not prices: 23 | return 0 24 | dp = [[0,0,0] for _ in range(len(prices))] 25 | #一旦涉及到超过0的index,一定要先判断当前数据结构是否存在内容 26 | dp[0][1] = -prices[0] 27 | for i in range(1,len(prices)): 28 | dp[i ][0] = max(dp[i-1][0], dp[i-1][2]) 29 | dp[i ][1] = max(dp[i-1][0] - prices[i], dp[i-1][1]) 30 | dp[i][2] = dp[i-1][1] + prices[i] 31 | return max(dp[-1][0],dp[-1][2]) 32 | 33 | 34 | class Solution: 35 | def maxProfit(self, prices: List[int]) -> int: 36 | #判断特例 37 | if len(prices) < 2: 38 | return 0 39 | 40 | dp = [[0, 0, 0] for _ in range(len(prices))] 41 | dp[0][1] = -prices[0] 42 | 43 | 44 | for i in range(1, len(prices)): 45 | dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]) 46 | dp[i][1] = max(dp[i - 1][1], dp[i - 1][2] - prices[i]) 47 | dp[i][2] = dp[i-1][0] 48 | return max(dp[-1][0], dp[-1][2]) 49 | 50 | 51 | class Solution: 52 | def maxProfit(self, prices: List[int]) -> int: 53 | if not prices: 54 | return 0 55 | l = len(prices) 56 | dp = [[0, 0] for _ in range(l+1)] 57 | dp[0][1] = float('-inf') 58 | dp[1][1] = -prices[0] 59 | for i in range(2, l+1): # 因为下面有i-2所以从2开始, 自行去填0-1的base case 60 | dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i-1]) 61 | dp[i][1] = max(dp[i-1][1], dp[i-2][0]-prices[i-1]) 62 | return dp[-1][0] -------------------------------------------------------------------------------- /动态规划/337_打家劫舍 III.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jul 26 10:14:10 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def rob(self, root: TreeNode) -> int: 11 | def dfs(root): 12 | if not root: 13 | return [0] * 2 14 | ''' 15 | le_0表示对于左子树,不偷左子树的根,le_1表示对于左子树,偷其根 16 | ''' 17 | ''' 18 | 相当于每次把子树中的最大值传回上层树,dp的思想 19 | ''' 20 | le_0,le_1 = dfs(root.left) 21 | ri_0,ri_1 = dfs(root.right) 22 | off_root = max(le_1+ri_1,le_0+ri_0,le_1+ri_0,le_0+ri_1) 23 | on_root = le_0+ri_0+root.val 24 | return [off_root,on_root] 25 | return max(dfs(root)) -------------------------------------------------------------------------------- /动态规划/376_摆动序列.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun May 10 11:54:59 2020 4 | 5 | @author: leiya 6 | """ 7 | #双dp问题 8 | #用两个up, down dp数列 9 | #小循环内也要用max实时更新当前值 up[i] = max(up[i], down[j] + 1) 10 | class Solution: 11 | def wiggleMaxLength(self, nums: List[int]) -> int: 12 | #注意空的情况 13 | if not nums: 14 | return 0 15 | down = [1 for _ in range(len(nums))] 16 | up = [1 for _ in range(len(nums))] 17 | 18 | for i in range(1, len(nums)): 19 | for j in range(i): 20 | if nums[i] > nums[j]: 21 | up[i] = max(up[i], down[j] + 1) 22 | #这里不能用else,注意相等元素出现的可能性 23 | elif nums[i] < nums[j]: 24 | down[i] = max(down[i], up[j] + 1) 25 | 26 | return max(max(down),max(up)) -------------------------------------------------------------------------------- /动态规划/413_等差数列划分.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat May 9 13:58:37 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def numberOfArithmeticSlices(self, A: List[int]) -> int: 10 | dp = [0 for _ in range(len(A))] 11 | count = 0 12 | for i in range(2, len(A)): 13 | if A[i] - A[i-1] == A[i-1] - A[i-2]: 14 | dp[i] = dp[i-1] + 1 15 | count += dp[i] 16 | return count -------------------------------------------------------------------------------- /动态规划/494_目标和.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jul 6 09:26:04 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 找到nums一个正子集和一个负子集,使得总和等于target,统计这种可能性的总数 10 | 11 | sum(P) - sum(N) = target (两边同时加上sum(P)+sum(N)) 12 | sum(P) + sum(N) + sum(P) - sum(N) = target + sum(P) + sum(N) (因为 sum(P) + sum(N) = sum(nums)) 13 | 2 * sum(P) = target + sum(nums) 14 | sum(P) = (target + sum(nums)) // 2 15 | ''' 16 | #可以转换成求子集和的问题,即01背包问题 17 | class Solution: 18 | def findTargetSumWays(self, nums: List[int], S: int) -> int: 19 | if sum(nums) < S or (sum(nums) + S) % 2 == 1: 20 | return 0 21 | P = (sum(nums) + S) // 2 22 | dp = [1] + [0 for _ in range(P)] 23 | for num in nums: 24 | j = P 25 | while j >= num: 26 | dp[j] = dp[j] + dp[j - num] #dp[j]为不选当前i,dp[j-num]为选择当前i对应的value 27 | j -= 1 28 | return dp[-1] -------------------------------------------------------------------------------- /动态规划/583_两个字符串的删除操作.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon May 11 13:26:15 2020 4 | 5 | @author: leiya 6 | """ 7 | #方法一:直接动态规划 8 | #方法二:只需要求出最公共长子序列(1143题),然后算出总字符串长度,减去二倍的子序列长度 9 | 10 | class Solution: 11 | def minDistance(self, word1: str, word2: str) -> int: 12 | dp = [[0 for _ in range(len(word2)+1)] for _ in range(len(word1)+1)] 13 | 14 | for i in range(1, len(word1)+1): 15 | dp[i][0] = dp[i-1][0] + 1 16 | for j in range(1, len(word2)+1): 17 | dp[0][j] = dp[0][j-1] + 1 18 | 19 | 20 | for i in range(1, len(word1)+1): 21 | for j in range(1, len(word2)+1): 22 | if word1[i-1] == word2[j-1]: 23 | dp[i][j] = dp[i-1][j-1] 24 | else: 25 | dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]+1) + 1 26 | #dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + 1 27 | 28 | return dp[-1][-1] 29 | -------------------------------------------------------------------------------- /动态规划/5_最长回文子串.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 15 13:19:26 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def longestPalindrome(self, s: str) -> str: 11 | if not s: 12 | return '' 13 | length = len(s) 14 | max_length = 1 15 | current_index = 0 16 | dp = [[False for _ in range(length)] for _ in range(length)] 17 | for i in range(length): 18 | dp[i][i] = True 19 | for j in range(1,length): 20 | for i in range(j): 21 | #注意只有两个元素时的特殊情况 22 | if s[i] == s[j]: 23 | if j-i < 3: 24 | dp[i][j] = True 25 | else: 26 | dp[i][j] = dp[i+1][j-1] 27 | 28 | if dp[i][j]: 29 | current_length = j - i + 1 30 | if current_length > max_length: 31 | max_length = current_length 32 | current_index = i 33 | return s[current_index:current_index+max_length] 34 | 35 | 36 | #注意max_len的初始化要初始化成1,因为最坏的结果也是有一个单独字母构成回文子串 37 | class Solution: 38 | def longestPalindrome(self, s: str) -> str: 39 | size = len(s) 40 | if size < 2: 41 | return s 42 | 43 | dp = [[False for _ in range(size)] for _ in range(size)] 44 | 45 | max_len = 1 46 | start = 0 47 | 48 | for i in range(size): 49 | dp[i][i] = True 50 | 51 | for j in range(1, size): 52 | for i in range(0, j): 53 | if s[i] == s[j]: 54 | if j - i < 3: 55 | dp[i][j] = True 56 | else: 57 | dp[i][j] = dp[i + 1][j - 1] 58 | else: 59 | dp[i][j] = False 60 | 61 | if dp[i][j]: 62 | cur_len = j - i + 1 63 | if cur_len > max_len: 64 | max_len = cur_len 65 | start = i 66 | return s[start:start + max_len] -------------------------------------------------------------------------------- /动态规划/62_不同路径.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat May 9 12:20:08 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | ''' 10 | 0706 11 | ''' 12 | #精简版 13 | class Solution: 14 | def uniquePaths(self, m: int, n: int) -> int: 15 | dp = [[1 for _ in range(n)] for _ in range(m)] 16 | for i in range(1, m): 17 | for j in range(1, n): 18 | dp[i][j] = dp[i-1][j] + dp[i][j-1] 19 | return dp[-1][-1] 20 | 21 | 22 | class Solution: 23 | def uniquePaths(self, m: int, n: int) -> int: 24 | dp = [[0 for _ in range(n)] for _ in range(m)] 25 | 26 | for i in range(m): 27 | for j in range(n): 28 | if i != 0 and j != 0: 29 | dp[i][j] = dp[i][j-1] + dp[i-1][j] 30 | else: 31 | dp[i][j] = 1 32 | return dp[-1][-1] 33 | 34 | 35 | class Solution: 36 | def uniquePaths(self, m: int, n: int) -> int: 37 | dp = [[0 for _ in range(n)] for _ in range(m)] 38 | 39 | for i in range(m): 40 | for j in range(n): 41 | if i != 0 and j != 0: 42 | dp[i][j] = dp[i][j-1] + dp[i-1][j] 43 | elif i == 0 or j == 0: 44 | dp[i][j] = 1 45 | return dp[-1][-1] -------------------------------------------------------------------------------- /动态规划/63_不同路径 II.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jul 6 10:03:44 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int: 11 | if not obstacleGrid: 12 | return 0 13 | row = len(obstacleGrid) 14 | col = len(obstacleGrid[0]) 15 | dp = [[0 for _ in range(col)] for _ in range(row)] 16 | for i in range(row): 17 | for j in range(col): 18 | if obstacleGrid[i][j] == 1: 19 | dp[i][j] = 0 20 | else: 21 | if i == 0 and j == 0: 22 | dp[i][j] = 1 23 | elif i == 0 and j != 0: 24 | dp[i][j] = dp[i][j-1] 25 | elif i != 0 and j == 0: 26 | dp[i][j] = dp[i-1][j] 27 | else: 28 | dp[i][j] = dp[i-1][j] + dp[i][j-1] 29 | return dp[-1][-1] -------------------------------------------------------------------------------- /动态规划/646_最长数对链.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun May 10 11:17:18 2020 4 | 5 | @author: leiya 6 | """ 7 | #特殊排序方法: pairs.sort(key=lambda x:x[1]) 8 | 9 | class Solution: 10 | def findLongestChain(self, pairs: List[List[int]]) -> int: 11 | dp = [1 for _ in range(len(pairs))] 12 | pairs.sort(key=lambda x:x[1]) 13 | for i in range(1, len(pairs)): 14 | for j in range(i): 15 | if pairs[j][1] < pairs[i][0]: 16 | dp[i] = max(dp[i], dp[j]+1) 17 | return max(dp) -------------------------------------------------------------------------------- /动态规划/647_回文子串.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed May 13 19:20:05 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | """ 9 | (1)思路:动态规划 10 | 我们以dp[i][j]表示区间[i, j]之间的子串是否为回文子串,这样可以思考这样三种情况的回文子串: 11 | - 子串长度为1,例如 a 一定为回文子串,即 i=j 的情况 12 | - 子串长度为2,且字符相同,例如 aa 一定为回文自传,即 s[i] = s[j] and j-i = 1 13 | - 子串长度大于2,符合 abcba 形式的为回文子串,根据回文子串的定义,那么 abcba 去掉两边字符,仍为回文 14 | 子串,即bcb,转换成方程形式即 dp[i][j] = dp[i+1][j-1] and j-i > 1 and s[i] = s[j] 15 | 剩下的均为不符合条件,即非回文子串。 16 | 17 | (2)复杂度: 18 | - 时间复杂度:O(N^2) 19 | - 空间复杂度:O(N^2) 20 | """ 21 | 22 | 23 | #0319 24 | class Solution: 25 | def countSubstrings(self, s: str) -> int: 26 | if not s: 27 | return 0 28 | size = len(s) 29 | dp = [[False for _ in range(size)] for _ in range(size)] 30 | count = 0 31 | for i in range(size): 32 | dp[i][i] = True 33 | count += 1 34 | for j in range(1,size): 35 | for i in range(j): 36 | if s[i] == s[j]: 37 | if j-i < 3: 38 | dp[i][j] = True 39 | count += 1 40 | else: 41 | dp[i][j] = dp[i+1][j-1] 42 | if dp[i][j]: 43 | count += 1 44 | return count 45 | 46 | #updated 0630 基本和5一样的思路,就是判断回文的个数是基于判断各种情况下是否是回文,5是判断所有回文中最长的那个,都是dp之后的变体 47 | class Solution: 48 | def countSubstrings(self, s: str) -> int: 49 | if not s: 50 | return 0 51 | size = len(s) 52 | dp = [[False for _ in range(size)] for _ in range(size)] 53 | count = 0 54 | for i in range(size): 55 | dp[i][i] = True 56 | count += 1 57 | for j in range(1,size): 58 | for i in range(j): 59 | if s[i] == s[j] and j-i==1: 60 | dp[i][j] = True 61 | count += 1 62 | elif s[i] == s[j] and j-i > 1: 63 | dp[i][j] = dp[i+1][j-1] 64 | if dp[i][j]: 65 | count += 1 66 | return count 67 | 68 | -------------------------------------------------------------------------------- /动态规划/64_最小路径和.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat May 9 12:13:15 2020 4 | 5 | @author: leiya 6 | """ 7 | #注意用dp时候先给dp声明空间 8 | #注意迭代公式里还有上步更新的dp值 9 | class Solution: 10 | def minPathSum(self, grid: List[List[int]]) -> int: 11 | row = len(grid) 12 | col = len(grid[0]) 13 | dp = [[0 for _ in range(col)] for _ in range(row)] 14 | for i in range(row): 15 | for j in range(col): 16 | if i != 0 and j != 0: 17 | dp[i][j] = min(dp[i][j-1], dp[i-1][j]) + grid[i][j] 18 | elif i == 0 and j == 0: 19 | dp[i][j] = grid[i][j] 20 | elif i == 0 and j != 0: 21 | dp[i][j] = dp[i][j-1] + grid[i][j] 22 | else: 23 | dp[i][j] = dp[i-1][j] + grid[i][j] 24 | return dp[-1][-1] -------------------------------------------------------------------------------- /动态规划/650_只有两个键的键盘.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon May 11 14:06:43 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 关于转移公式需要强调的是:转移公式可以理解为求完全分解某个数后的各项质因子的和, 10 | 之所以找一个质因子,是因为dp[i]已经涵盖了自然数i的质因子完全(即没有可以继续分的非质数存在)分解后的和, 11 | 即各项质因子的和就是最后的最少操作次数。 12 | ''' 13 | #dp[i] 通过复制粘贴操作,得到 i 个字符,最少需要几步操作 14 | #质数:指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数 15 | #为什么非得拆解成质因子呢?因为当一个因子还可以分解成更小的因子的时候,那么分解后的结果会更小 16 | 17 | #转移公式可以理解为以完全分解公因式后的的各项质因子的和,之所以找一个质因子,是因为dp[i]已经涵盖了质因子的分解后的和 18 | #即各项质因子的和就是最后的最少操作次数 19 | class Solution: 20 | def minSteps(self, n: int) -> int: 21 | dp = [0 for _ in range(n+1)] 22 | for i in range(2, n+1): 23 | dp[i] = i 24 | #此循环在判断j是否为i的质因子,只需要在这里面找到一个符合要求的质因子,因为该质因子对应的另一个质因子一定在前面被求过了 25 | #该for循环执行结束后如果仍没有进入if说明i是质数,他的次数就是dp[i] = i 26 | #for j in range(2, i): 27 | for j in range(2, int(sqrt(i))+1): 28 | if i % j == 0: 29 | dp[i] = dp[j] + dp[i//j] 30 | break 31 | return dp[-1] -------------------------------------------------------------------------------- /动态规划/70_Climbing Stairs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Sep 1 13:26:06 2019 4 | 5 | @author: leiya 6 | 7 | """ 8 | 9 | 10 | class Solution: 11 | def climbStairs(self, n: int) -> int: 12 | #没有台阶时候算有一种走法 13 | if n == 0: 14 | return 1 15 | if n == 1 or n == 2: 16 | return n 17 | dp_i_1 = 2 18 | dp_i_2 = 1 19 | for i in range(2, n): 20 | dp_i = dp_i_1 + dp_i_2 21 | dp_i_2 = dp_i_1 22 | dp_i_1 = dp_i 23 | 24 | return dp_i_1 25 | #明确dp每个位置上的状态定义 26 | class Solution: 27 | def climbStairs(self, n: int) -> int: 28 | if n == 1 or n == 2: 29 | return n 30 | dp = [0 for _ in range(n+1)] 31 | dp[1] = 1 32 | dp[2] = 2 33 | 34 | for i in range(3,n+1): 35 | dp[i] = dp[i-1] + dp[i-2] 36 | return dp[-1] 37 | 38 | 39 | 40 | class Solution: 41 | def climbStairs(self, n: int) -> int: 42 | if n == 1: 43 | return 1 44 | if n == 2: 45 | return 2 46 | twobefore = 1 47 | onebefore = 2 48 | for i in range(2,n): 49 | twobefore, onebefore = onebefore, twobefore + onebefore 50 | return onebefore 51 | 52 | class Solution: 53 | def climbStairs(self, n: int) -> int: 54 | if n == 1: 55 | return 1 56 | if n == 2: 57 | return 2 58 | return self.climbStairs(n-1)+self.climbStairs(n-2) 59 | 60 | class Solution: 61 | def climbStairs(self, n: int) -> int: 62 | prev, current = 0, 1 63 | for i in range(n): 64 | prev,current = current, prev+current 65 | return current 66 | -------------------------------------------------------------------------------- /动态规划/714_买卖股票的最佳时机含手续费.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue May 19 17:48:50 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | ''' 10 | 0713 11 | 统一了121,122,309,714的状态定义以及模板 12 | ''' 13 | class Solution: 14 | def maxProfit(self, prices: List[int], fee: int) -> int: 15 | if not prices: 16 | return 0 17 | dp = [[0,0] for _ in range(len(prices))] 18 | #默认每次买入的时候收手续费,也可以写成每次卖出的时候收手续费,这里买入要花一笔钱,索性就一起花了 19 | dp[0][1] = -prices[0] - fee 20 | for i in range(1, len(prices)): 21 | dp[i][0] = max(dp[i-1][0],dp[i-1][1]+prices[i]) 22 | dp[i][1] = max(dp[i-1][1],dp[i-1][0]-prices[i]-fee) 23 | return dp[-1][0] 24 | 25 | 26 | class Solution: 27 | def maxProfit(self, prices: List[int], fee: int) -> int: 28 | size = len(prices) 29 | 30 | if size < 2: 31 | return 0 32 | 33 | # dp[i][j] 表示 [0, i] 区间内,到第 i 天(从 0 开始)状态为 j 时的最大收益 34 | # j = 0 表示不持股,j = 1 表示持股 35 | # 并且规定在买入股票的时候,扣除手续费 36 | 37 | dp = [[0, 0] for _ in range(size)] 38 | dp[0][0] = 0 39 | dp[0][1] = -prices[0] - fee 40 | 41 | for i in range(1, size): 42 | dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]) 43 | dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i] - fee) 44 | return dp[-1][0] -------------------------------------------------------------------------------- /动态规划/72_编辑距离.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon May 11 08:48:55 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0701:因为引入了两个空字符串,所以需要注意索引问题 10 | ''' 11 | 12 | class Solution: 13 | def minDistance(self, word1: str, word2: str) -> int: 14 | row = len(word1) 15 | col = len(word2) 16 | dp = [[0 for _ in range(col+1)] for _ in range(row+1)] 17 | for i in range(1, row+1): 18 | dp[i][0] = dp[i-1][0] + 1 19 | for j in range(1, col+1): 20 | dp[0][j] = dp[0][j-1] + 1 21 | for i in range(1, row+1): 22 | for j in range(1, col+1): 23 | if word1[i-1] == word2[j-1]: 24 | dp[i][j] = min(dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]) 25 | else: 26 | dp[i][j] = min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1 27 | return dp[-1][-1] 28 | 29 | 30 | class Solution: 31 | def minDistance(self, word1: str, word2: str) -> int: 32 | dp = [[0 for _ in range(len(word2)+1)] for _ in range(len(word1)+1)] 33 | 34 | for j in range(1, len(word2)+1): 35 | dp[0][j] = dp[0][j-1] + 1 36 | for i in range(1, len(word1)+1): 37 | dp[i][0] = dp[i-1][0] + 1 38 | 39 | for i in range(1, len(word1)+1): 40 | for j in range(1, len(word2)+1): 41 | if word1[i-1] == word2[j-1]: 42 | dp[i][j] = dp[i-1][j-1] 43 | else: 44 | dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1 45 | return dp[-1][-1] -------------------------------------------------------------------------------- /动态规划/983_最低票价.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed May 6 15:25:58 2020 4 | 5 | @author: leiya 6 | """ 7 | #用max(0, day-7)防止越界问题 8 | 9 | class Solution: 10 | def mincostTickets(self, days: List[int], costs: List[int]) -> int: 11 | day_pointer = 0 12 | dp = [0 for _ in range(0, days[-1]+1)] 13 | 14 | for day in range(1, len(dp)): 15 | if day != days[day_pointer]: 16 | dp[day] = dp[day-1] 17 | else: 18 | #用max(0, day-7)防止越界问题 19 | dp[day] = min(dp[max(0, day - 7)]+costs[1], 20 | dp[max(0, day - 30)]+costs[2], 21 | dp[max(0, day -1 )]+costs[0]) 22 | day_pointer += 1 23 | return dp[-1] -------------------------------------------------------------------------------- /单调栈/496_下一个更大元素 I.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jul 1 13:37:52 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | ''' 10 | 0710 11 | ''' 12 | class Solution: 13 | def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]: 14 | stack = [] 15 | res = [-1 for _ in range(len(nums1))] 16 | for num in nums2: 17 | while stack and num > nums1[stack[-1]]: 18 | res[stack.pop()] = num 19 | if num in nums1: 20 | stack.append(nums1.index(num)) 21 | return res 22 | 23 | 24 | #503变体 25 | #遍历nums2,因为实际上答案的顺序是存在于nums2中的,将nums2中每次遍历到的值在nums1中找到对应的index压入栈中,之后跟739一样 26 | class Solution: 27 | def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]: 28 | stack = [] 29 | res = [-1 for _ in range(len(nums1))] 30 | for i in range(len(nums2)): 31 | while stack and nums1[stack[-1]] < nums2[i]: 32 | res[stack.pop()] = nums2[i] 33 | if nums2[i] in nums1: 34 | stack.append(nums1.index(nums2[i])) 35 | return res -------------------------------------------------------------------------------- /单调栈/503_Next Greater Element II.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Feb 18 20:30:52 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0713 10 | 统一单调栈模板 11 | ''' 12 | class Solution: 13 | def nextGreaterElements(self, nums: List[int]) -> List[int]: 14 | stack = [] 15 | res = [-1 for _ in range(len(nums))] 16 | for index in range(len(nums)*2): 17 | index = index % len(nums) 18 | while stack and nums[stack[-1]] < nums[index]: 19 | res[stack.pop()] = nums[index] 20 | stack.append(index) 21 | return res 22 | 23 | 24 | class Solution: 25 | def nextGreaterElements(self, nums: List[int]) -> List[int]: 26 | length = len(nums) 27 | res = [-1] * length 28 | stack = [] 29 | for i in range(length*2): 30 | index = i % length 31 | while stack and nums[index] > nums[stack[-1]]: 32 | res[stack.pop()] = nums[index] 33 | stack.append(index) 34 | return res 35 | -------------------------------------------------------------------------------- /双指针/11_盛最多水的容器.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jul 14 15:54:07 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #双指针,考虑状态的剪枝 9 | class Solution: 10 | def maxArea(self, height: List[int]) -> int: 11 | start = 0 12 | end = len(height) - 1 13 | res = 0 14 | while start < end: 15 | if height[start] < height[end]: 16 | res = max(res, height[start] * (end - start)) 17 | start += 1 18 | else: 19 | res = max(res, height[end] * (end - start)) 20 | end -= 1 21 | return res -------------------------------------------------------------------------------- /双指针/151_翻转字符串里的单词.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jul 9 13:57:39 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #split() 方法将单词间的 “多个空格看作一个空格” 9 | class Solution: 10 | def reverseWords(self, s: str) -> str: 11 | nums = s.split() 12 | return ' '.join(nums[::-1]) 13 | 14 | 15 | #双指针后序遍历 16 | #str.strip()可以去除字符串前后的空格 17 | class Solution: 18 | def reverseWords(self, s: str) -> str: 19 | s = s.strip() # 删除首尾空格 20 | i = j = len(s) - 1 21 | res = [] 22 | while i >= 0: 23 | while i >= 0 and s[i] != ' ': 24 | i -= 1 # 搜索首个空格 25 | res.append(s[i + 1: j + 1]) # 添加单词 26 | while s[i] == ' ': 27 | i -= 1 # 跳过单词间空格 28 | j = i # j 指向下个单词的尾字符 29 | return ' '.join(res) # 拼接并返回 30 | 31 | -------------------------------------------------------------------------------- /双指针/15_三数之和.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Jun 12 09:30:27 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #sort+三指针 9 | #一个指针从头开始,另外两个指针一左一右夹逼寻找,这也是sort的意义 10 | class Solution: 11 | def threeSum(self, nums: List[int]) -> List[List[int]]: 12 | n = len(nums) 13 | res=[] 14 | if(not nums or n<3): 15 | return [] 16 | nums.sort() 17 | res=[] 18 | for i in range(n-2): 19 | if(nums[i]>0): 20 | return res 21 | if(i>0 and nums[i]==nums[i-1]): 22 | continue 23 | L=i+1 24 | R=n-1 25 | while(L0): 35 | R=R-1 36 | else: 37 | L=L+1 38 | return res -------------------------------------------------------------------------------- /双指针/392_判断子序列.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jul 27 09:20:25 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def isSubsequence(self, s: str, t: str) -> bool: 11 | l1 = 0 12 | l2 = 0 13 | while l1 < len(s) and l2 < len(t): 14 | while l2 < len(t) and t[l2] != s[l1]: 15 | l2 += 1 16 | if l2 >= len(t): 17 | return False 18 | l1 += 1 19 | l2 += 1 20 | if l1 >= len(s): 21 | return True 22 | return False -------------------------------------------------------------------------------- /回溯/216_组合总和 III.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 15 08:45:10 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0702 形参过多,形参顺序不要传错位 10 | 0728: 用start_index来收缩每层的解空间范围 11 | ''' 12 | class Solution: 13 | def combinationSum3(self, k: int, n: int) -> List[List[int]]: 14 | #无重复数组且解中无重复数字 15 | start_index = 1 16 | def dfs(k,n,res,path,start_index): 17 | if k == 0 and n == 0: 18 | res.append(path[:]) 19 | return 20 | for i in range(start_index,10): 21 | if i <= n: 22 | path.append(i) 23 | dfs(k-1,n-i,res,path,i+1) 24 | path.pop() 25 | path = [] 26 | res = [] 27 | dfs(k,n,res,path,start_index) 28 | return res 29 | 30 | 31 | class Solution: 32 | def combinationSum3(self, k: int, n: int) -> List[List[int]]: 33 | start_index = 1 34 | def dfs(start_index,k,n,path,res): 35 | if n == 0 and k == 0: 36 | res.append(path[:]) 37 | return 38 | if (n == 0 and k != 0) or (n != 0 and k == 0): 39 | return 40 | for i in range(start_index,10): 41 | if i <= n: 42 | path.append(i) 43 | dfs(i+1,k-1,n-i,path,res) 44 | path.pop() 45 | path = [] 46 | res = [] 47 | dfs(start_index,k,n,path,res) 48 | return res 49 | 50 | -------------------------------------------------------------------------------- /回溯/36_有效的数独.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jul 19 10:54:26 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def isValidSudoku(self, board: List[List[str]]) -> bool: 11 | row = [set() for _ in range(9)] 12 | col = [set() for _ in range(9)] 13 | square = [[set() for _ in range(3)] for _ in range(3)] 14 | for i in range(9): 15 | for j in range(9): 16 | if board[i][j] != '.': 17 | if board[i][j] in row[i] or board[i][j] in col[j] or board[i][j] in square[i//3][j//3]: 18 | return False 19 | else: 20 | row[i].add(board[i][j]) 21 | col[j].add(board[i][j]) 22 | square[i//3][j//3].add(board[i][j]) 23 | return True -------------------------------------------------------------------------------- /回溯/46_全排列.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat May 16 15:51:10 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | ''' 10 | 220326 11 | for循环控制每一层,dfs在通过for循环选定该层数据后进入下一层 12 | 0716:如果希望在调用dfs之后用到res,就不能在dfs这个函数里对res重新赋值,只能对res进行append之类的操作,一旦重新赋值 13 | res就不是原来传入的res了,如果只是对现有res做一些操作,那么还是原来的res 14 | ''' 15 | #不能用start_index来减小子空间,只能用used来判断子空间取值 16 | class Solution: 17 | def permute(self, nums): 18 | #设置现场 19 | #递归 20 | #恢复现场 21 | used = [False for _ in range(len(nums))] 22 | res = [] 23 | #print(id(res)) 24 | path = [] 25 | def put(nums,depth,used,path,res): 26 | if depth == len(nums): 27 | #此处需要深拷贝 28 | res.append(copy.deepcopy(path)) 29 | #print(id(res)) 30 | return 31 | for i in range(len(nums)): 32 | if not used[i]: 33 | #设置现场需要两步 34 | path.append(nums[i]) 35 | used[i] = True 36 | #递归 37 | put(nums,depth+1,used,path,res) 38 | #恢复现场需要两步 39 | used[i] = False 40 | path.pop() 41 | 42 | put(nums,0,used,path,res) 43 | #print(id(res)) 44 | return res 45 | solution = Solution() 46 | solution.permute([1,2,3]) 47 | 48 | -------------------------------------------------------------------------------- /回溯/77_组合.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jun 11 15:40:16 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def combine(self, n: int, k: int) -> List[List[int]]: 11 | def dfs(path,k,depth,res,start,n): 12 | if depth == k: 13 | res.append(copy.deepcopy(path)) 14 | return 15 | 16 | for i in range(start,n+1): 17 | path.append(i) 18 | dfs(path,k,depth+1,res,i+1,n) 19 | path.pop() 20 | res = [] 21 | path = [] 22 | depth = 0 23 | start = 1 24 | dfs(path,k,depth,res,start,n) 25 | return res -------------------------------------------------------------------------------- /回溯/78_子集.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jun 14 16:40:54 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def subsets(self, nums: List[int]) -> List[List[int]]: 11 | #判断解空间变化情况:不重复,与顺序无关,因此无须使用used 12 | def dfs(nums,res,path,start_index): 13 | res.append(path[:]) 14 | #这段判断没有实际效果,但是为了便于理解可以加上 15 | if start_index == len(nums): 16 | return 17 | for i in range(start_index,len(nums)): 18 | path.append(nums[i]) 19 | dfs(nums,res,path,i+1) 20 | path.pop() 21 | res = [] 22 | path = [] 23 | start_index = 0 24 | dfs(nums,res,path,start_index) 25 | return res 26 | 27 | 28 | #线性缩小解空间,可以引入start_index 29 | class Solution: 30 | def subsets(self, nums: List[int]) -> List[List[int]]: 31 | def dfs(nums,path,res,start): 32 | ''' 33 | start == len(nums)的时候直接返回了 34 | ''' 35 | res.append(path[:]) 36 | for i in range(start, len(nums)): 37 | path.append(nums[i]) 38 | dfs(nums,path,res,i+1) 39 | path.pop() 40 | res = [] 41 | path = [] 42 | start = 0 43 | dfs(nums,path,res,start) 44 | return res -------------------------------------------------------------------------------- /回溯/90_子集 II.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jun 14 16:46:43 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | ''' 10 | 0712 11 | 凡是剪枝问题,一开始就把nums从小到大排序,这一点非常容易忘记 12 | ''' 13 | #注意此时输入的nums无须且有重复数字,这意味着可能会搜索到相同的结果 14 | #存在重复数字可能搜索到相同结果哦的情况下,首先将nums排序,之后再本层内判断是否有相同nums[i],如果有则跳过 15 | class Solution: 16 | def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: 17 | nums.sort() 18 | def dfs(nums,path,res,start): 19 | res.append(path[:]) 20 | for i in range(start,len(nums)): 21 | if i > start and nums[i] == nums[i-1]: 22 | continue 23 | path.append(nums[i]) 24 | dfs(nums,path,res,i+1) 25 | path.pop() 26 | res = [] 27 | path = [] 28 | start = 0 29 | dfs(nums,path,res,start) 30 | return res -------------------------------------------------------------------------------- /图(有向图判环+无向图判环_染色+连通量)/210_course2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Apr 30 17:50:52 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]: 10 | indegrees = [0 for _ in range(numCourses)] 11 | adjacency = [[] for _ in range(numCourses)] 12 | res = [] 13 | for node,node1 in prerequisites: 14 | indegrees[node] += 1 15 | adjacency[node1].append(node) 16 | queue = [] 17 | for i in range(len(indegrees)): 18 | if indegrees[i] == 0: 19 | queue.append(i) 20 | while queue: 21 | pop_node = queue.pop(0) 22 | res.append(pop_node) 23 | numCourses -= 1 24 | for next_node in adjacency[pop_node]: 25 | indegrees[next_node] -= 1 26 | if indegrees[next_node] == 0: 27 | queue.append(next_node) 28 | if numCourses == 0: 29 | return res 30 | else: 31 | return [] -------------------------------------------------------------------------------- /图(有向图判环+无向图判环_染色+连通量)/886_可能的二分法.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jul 9 22:25:07 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 这道题最重要的知识在于对图的理解 10 | 节点间关系平等,因此是无向图,无向图的邻接表必须清楚的找到所有与该节点相连的node 11 | 而有向图的邻接表只需要找到该节点的出node,这是两者很大的区别 12 | 此外,无向图有无环可用并查集判断,有向图有无环可用拓扑排序(邻接表+入度表)判断 13 | 0729: 需要留意node从0还是从1开始计数 14 | ''' 15 | class Solution: 16 | def possibleBipartition(self, N: int, dislikes: List[List[int]]) -> bool: 17 | #bfs+color 18 | color = {} 19 | adjacency = [[] for _ in range(N+1)] 20 | for node1,node2 in dislikes: 21 | if node1 not in adjacency[node2]: 22 | adjacency[node2].append(node1) 23 | if node2 not in adjacency[node1]: 24 | adjacency[node1].append(node2) 25 | for node in range(1, len(adjacency)): 26 | if node not in color: 27 | color[node] = 0 28 | queue = [node] 29 | while queue: 30 | pop_node = queue.pop(0) 31 | for next_node in adjacency[pop_node]: 32 | if next_node not in color: 33 | color[next_node] = color[pop_node] ^ 1 34 | queue.append(next_node) 35 | elif color[next_node] == color[pop_node]: 36 | return False 37 | return True 38 | 39 | -------------------------------------------------------------------------------- /图(有向图判环+无向图判环_染色+连通量)/图的学习顺序.txt: -------------------------------------------------------------------------------- 1 | """ 2 | Created on Fri Mar 4 20:44:22 2022 3 | 4 | @author: leiya 5 | """ 6 | 7 | 8 | 1.无向图的非递归形式的dfs,bfs 9 | 2.无向图bfs按每层遍历找最短路径 10 | 3.无向图的染色问题 11 | 4.无向图判断环用并查集,有向图判断环用bfs+拓扑排序 12 | -------------------------------------------------------------------------------- /图(遍历)/542_01矩阵.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jun 9 15:54:03 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def updateMatrix(self, matrix: List[List[int]]) -> List[List[int]]: 11 | row = len(matrix) 12 | col = len(matrix[0]) 13 | res_mark = [[-1 for _ in range(col)] for _ in range(row)] 14 | queue = [] 15 | for i in range(row): 16 | for j in range(col): 17 | if matrix[i][j] == 0: 18 | res_mark[i][j] = 0 19 | queue.append((i,j)) 20 | depth = 0 21 | while queue: 22 | depth += 1 23 | current_len = len(queue) 24 | for _ in range(current_len): 25 | cur_i, cur_j = queue.pop(0) 26 | for x,y in [(1,0),(-1,0),(0,1),(0,-1)]: 27 | temp_i = cur_i + x 28 | temp_j = cur_j + y 29 | if 0 <= temp_i and temp_i < row and 0 <= temp_j and temp_j < col and res_mark[temp_i][temp_j] == -1: 30 | res_mark[temp_i][temp_j] = depth 31 | queue.append((temp_i,temp_j)) 32 | return res_mark -------------------------------------------------------------------------------- /堆(优先队列)/215_数组中的第K个最大元素.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 29 09:13:05 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def findKthLargest(self, nums: List[int], k: int) -> int: 11 | #quick sort从大到小排序 12 | def quick_sort(nums,head,tail): 13 | if head >= tail: 14 | #真正写quick_sort的时候这里需要返回Nums 15 | return nums 16 | #为了避免nums本身有序导致快排失效,partition起到不应该有的分而治之,减治的效果,时间复杂度降至0n2 17 | random_index = random.randint(head,tail) 18 | nums[random_index], nums[head] = nums[head], nums[random_index] 19 | pivot = nums[head] 20 | low = head 21 | high = tail 22 | while low < high: 23 | while low < high and nums[high] <= pivot: 24 | high -= 1 25 | nums[low] = nums[high] 26 | while low < high and nums[low] > pivot: 27 | low += 1 28 | nums[high] = nums[low] 29 | nums[low] = pivot 30 | if low == k-1: 31 | return nums 32 | quick_sort(nums,head,low-1) 33 | quick_sort(nums,low+1,tail) 34 | return nums 35 | res = quick_sort(nums,0,len(nums)-1) 36 | return res[k-1] 37 | 38 | #小根堆,堆顶元素永远是堆中最小的元素 39 | 40 | class Solution: 41 | def findKthLargest(self, nums: List[int], k: int) -> int: 42 | import heapq 43 | queue = [] 44 | for index in range(k): 45 | heapq.heappush(queue,nums[index]) 46 | for index in range(k,len(nums)): 47 | heap_top = queue[0] 48 | if nums[index] > heap_top: 49 | #弹出并返回最小值,然后将heapqreplace方法中item的值插入到堆中 50 | heapq.heapreplace(queue,nums[index]) 51 | return queue[0] 52 | 53 | -------------------------------------------------------------------------------- /堆(优先队列)/692_前K个高频单词.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Aug 12 09:04:36 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def topKFrequent(self, words: List[str], k: int) -> List[str]: 11 | hash_map = {} 12 | for word in words: 13 | if word in hash_map: 14 | hash_map[word] +=1 15 | else: 16 | hash_map[word] =1 17 | 18 | heap = [(word,freq) for word,freq in hash_map.items()] 19 | 20 | def change_heap(arr,heapsort,root): 21 | left , right ,tmp = 2*root + 1, 2*root + 2, root 22 | # 比较哈希表值大小,一样则比较字母大小 23 | if left < heapsort and (arr[left][1] > arr[tmp][1] or (arr[left][1] == arr[tmp][1] and arr[left][0] < arr[tmp][0]) ): 24 | tmp = left 25 | if right < heapsort and (arr[right][1] > arr[tmp][1] or (arr[right][1] == arr[tmp][1] and arr[right][0] < arr[tmp][0]) ): 26 | tmp = right 27 | if tmp != root: 28 | arr[root],arr[tmp] = arr[tmp], arr[root] 29 | change_heap(arr,heapsort,tmp) 30 | 31 | 32 | 33 | def build_heap(arr): 34 | heapsort = len(arr) 35 | for i in range((heapsort)//2,-1,-1): 36 | change_heap(arr,heapsort,i) 37 | 38 | def heap_sort(arr,k): 39 | build_heap(arr) 40 | res = [] 41 | ans = 1 42 | for i in range(len(arr)-1,-1,-1): 43 | res.append(arr[0][0]) 44 | arr[0],arr[i] = arr[i],arr[0] 45 | change_heap(arr,i,0) 46 | if ans == k: 47 | return res 48 | else: 49 | ans +=1 50 | return res 51 | return heap_sort(heap,k) -------------------------------------------------------------------------------- /排序/215_数组中的第K个最大元素.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 29 09:13:05 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def findKthLargest(self, nums: List[int], k: int) -> int: 11 | #quick sort从大到小排序 12 | def quick_sort(nums,head,tail): 13 | if head >= tail: 14 | #真正写quick_sort的时候这里需要返回Nums 15 | return nums 16 | #为了避免nums本身有序导致快排失效,partition起到不应该有的分而治之,减治的效果,时间复杂度降至0n2 17 | random_index = random.randint(head,tail) 18 | nums[random_index], nums[head] = nums[head], nums[random_index] 19 | pivot = nums[head] 20 | low = head 21 | high = tail 22 | while low < high: 23 | while low < high and nums[high] <= pivot: 24 | high -= 1 25 | nums[low] = nums[high] 26 | while low < high and nums[low] > pivot: 27 | low += 1 28 | nums[high] = nums[low] 29 | nums[low] = pivot 30 | if low == k-1: 31 | return nums 32 | quick_sort(nums,head,low-1) 33 | quick_sort(nums,low+1,tail) 34 | return nums 35 | res = quick_sort(nums,0,len(nums)-1) 36 | return res[k-1] 37 | 38 | #小根堆,堆顶元素永远是堆中最小的元素 39 | class Solution: 40 | import heapq 41 | def findKthLargest(self, nums: List[int], k: int) -> int: 42 | size = len(nums) 43 | L = [] 44 | for index in range(k): 45 | heapq.heappush(L,nums[index]) 46 | for index in range(k,size): 47 | top = L[0] 48 | if nums[index] > top: 49 | #弹出并返回最小值,然后将heapqreplace方法中item的值插入到堆中 50 | heapq.heapreplace(L,nums[index]) 51 | return L[0] -------------------------------------------------------------------------------- /栈/155_Min Stack.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Feb 7 16:36:10 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | #[] is not None,空列表不是None 10 | #辅助栈 11 | class MinStack: 12 | 13 | def __init__(self): 14 | """ 15 | initialize your data structure here. 16 | """ 17 | self.stack = [] 18 | self.helpstack = [] 19 | 20 | 21 | def push(self, x: int) -> None: 22 | self.stack.append(x) 23 | if len(self.helpstack) == 0 or x < self.helpstack[-1]: 24 | self.helpstack.append(x) 25 | else: 26 | self.helpstack.append(self.helpstack[-1]) 27 | 28 | def pop(self) -> None: 29 | if len(self.stack) != 0: 30 | self.stack.pop() 31 | self.helpstack.pop() 32 | 33 | def top(self) -> int: 34 | if len(self.stack) != 0: 35 | return self.stack[-1] 36 | 37 | def getMin(self) -> int: 38 | if len(self.helpstack) != 0: 39 | return self.helpstack[-1] 40 | 41 | 42 | 43 | # Your MinStack object will be instantiated and called as such: 44 | # obj = MinStack() 45 | # obj.push(x) 46 | # obj.pop() 47 | # param_3 = obj.top() 48 | # param_4 = obj.getMin() -------------------------------------------------------------------------------- /栈/946_验证栈序列.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Sep 17 09:48:56 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool: 11 | stack = [] 12 | start = 0 13 | for num in pushed: 14 | stack.append(num) 15 | while stack and stack[-1] == popped[start]: 16 | stack.pop() 17 | start += 1 18 | if not stack: 19 | return True 20 | else: 21 | return False -------------------------------------------------------------------------------- /树/100_Same Tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Sep 4 10:45:34 2019 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0909 10 | ''' 11 | class Solution: 12 | def isSameTree(self, p: TreeNode, q: TreeNode) -> bool: 13 | if not p and not q: 14 | return True 15 | if not p and q or not q and p: 16 | return False 17 | if p.val == q.val and self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right): 18 | return True 19 | else: 20 | return False 21 | 22 | # Definition for a binary tree node. 23 | # class TreeNode: 24 | # def __init__(self, x): 25 | # self.val = x 26 | # self.left = None 27 | # self.right = None 28 | 29 | class Solution: 30 | def isSameTree(self, p: TreeNode, q: TreeNode) -> bool: 31 | if p is None and q is None: 32 | return True 33 | if p is None or q is None: 34 | return False 35 | if p.val != q.val: 36 | return False 37 | else: 38 | return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right) 39 | 40 | class Solution: 41 | def isSameTree(self, p: TreeNode, q: TreeNode) -> bool: 42 | if p and q: 43 | return p.val == q.val and self.isSameTree(p.left,q.left) and self.isSameTree(p.right,q.right) 44 | else: 45 | return p == q 46 | 47 | class Solution: 48 | def isSameTree(self, p: TreeNode, q: TreeNode) -> bool: 49 | if p is None and q is None: 50 | return True 51 | if p is not None and q is not None: 52 | return p.val == q.val and self.isSameTree(p.left,q.left) and self.isSameTree(p.right,q.right) 53 | else: 54 | return False 55 | 56 | -------------------------------------------------------------------------------- /树/102_Binary Tree Level Order Traversal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Apr 8 22:15:40 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #注意最后的结果是每个node中的val,不是node本身 9 | class Solution: 10 | def levelOrder(self, root: TreeNode) -> List[List[int]]: 11 | res = [] 12 | if root is None: 13 | return res 14 | 15 | cur_queue = [root] 16 | 17 | while cur_queue: 18 | cur_res = [] 19 | next_queue = [] 20 | for node in cur_queue: 21 | cur_res.append(node.val) 22 | if node.left is not None: 23 | next_queue.append(node.left) 24 | if node.right is not None: 25 | next_queue.append(node.right) 26 | cur_queue = next_queue 27 | res.append(cur_res) 28 | return res 29 | 30 | #方法二,更普遍 31 | class Solution: 32 | def levelOrder(self, root: TreeNode) -> List[List[int]]: 33 | #两种方式 34 | res = [] 35 | if not root: 36 | return res 37 | cur_queue = [root] 38 | while cur_queue: 39 | current_len = len(cur_queue) 40 | cur_res = [] 41 | for i in range(current_len): 42 | node = cur_queue.pop(0) 43 | cur_res.append(node.val) 44 | if node.left is not None: 45 | cur_queue.append(node.left) 46 | if node.right is not None: 47 | cur_queue.append(node.right) 48 | res.append(cur_res) 49 | 50 | return res -------------------------------------------------------------------------------- /树/103_二叉树的锯齿形层次遍历.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jul 9 13:36:22 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]: 11 | count = 0 12 | res = [] 13 | if not root: 14 | return res 15 | 16 | queue = [root] 17 | while queue: 18 | count += 1 19 | cur_res = [] 20 | current_len = len(queue) 21 | for _ in range(current_len): 22 | pop_node = queue.pop(0) 23 | cur_res.append(pop_node.val) 24 | if pop_node.left: 25 | queue.append(pop_node.left) 26 | if pop_node.right: 27 | queue.append(pop_node.right) 28 | if count % 2 == 0: 29 | res.append(cur_res[::-1]) 30 | else: 31 | res.append(cur_res) 32 | return res -------------------------------------------------------------------------------- /树/104_Maximum Depth of Binary Tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Sep 4 15:26:08 2019 4 | 5 | @author: leiya 6 | """ 7 | #dfs或者bfs找到最大层数即是最大深度 8 | 9 | # Definition for a binary tree node. 10 | # class TreeNode: 11 | # def __init__(self, x): 12 | # self.val = x 13 | # self.left = None 14 | # self.right = None 15 | 16 | class Solution: 17 | def maxDepth(self, root: TreeNode) -> int: 18 | 19 | if root is None: 20 | return 0 21 | else: 22 | return max(self.maxDepth(root.left),self.maxDepth(root.right))+1 23 | 24 | 25 | #updated: 0628 26 | class Solution: 27 | def maxDepth(self, root: TreeNode) -> int: 28 | if not root: 29 | return 0 30 | queue = [root] 31 | count = 0 32 | while queue: 33 | current_len = len(queue) 34 | count += 1 35 | for _ in range(current_len): 36 | pop_node = queue.pop(0) 37 | if pop_node.left is not None: 38 | queue.append(pop_node.left) 39 | if pop_node.right is not None: 40 | queue.append(pop_node.right) 41 | return count 42 | 43 | class Solution: 44 | def maxDepth(self, root: TreeNode) -> int: 45 | if root is None: 46 | return 0 47 | cur_queue = [root] 48 | count = 0 49 | while cur_queue: 50 | next_queue = [] 51 | for node in cur_queue: 52 | if node.left is not None: 53 | next_queue.append(node.left) 54 | if node.right is not None: 55 | next_queue.append(node.right) 56 | 57 | count += 1 58 | cur_queue = next_queue 59 | return count -------------------------------------------------------------------------------- /树/105_从前序与中序遍历序列构造二叉树.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Jun 20 08:08:23 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | inorder.index(preorder[0]) 这一步获取根的索引值, 10 | 题目说树中的各个节点的值都不相同,也确保了这步得到的结果是唯一准确的。 11 | 而且这个idx还能当长度用相当于 左+根 的长度,因为 左+根 和 根+左 是等长的。 12 | 13 | ''' 14 | 15 | class Solution: 16 | def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode: 17 | if not preorder or not inorder: 18 | return None 19 | if len(inorder) == 1: 20 | return TreeNode(inorder[0]) 21 | root = TreeNode(preorder[0]) 22 | index = inorder.index(root.val) 23 | #分别在preorder和inorder中找到符合要求的所有左子树节点和右子树节点,作为完整的树传入下一次递归中 24 | left_node = self.buildTree(preorder[1:1+index],inorder[:index]) 25 | right_node = self.buildTree(preorder[1+index:],inorder[index+1:]) 26 | root.left = left_node 27 | root.right = right_node 28 | return root -------------------------------------------------------------------------------- /树/106_从中序与后序遍历序列构造二叉树.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Jun 20 09:03:47 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode: 11 | if not postorder or not inorder: 12 | return None 13 | root = TreeNode(postorder[-1]) 14 | index = inorder.index(root.val) 15 | #left + root = index+1个,那么在postorder中从零开始到index正好有index个left nodes 16 | left_node = self.buildTree(inorder[:index],postorder[:index]) 17 | right_node = self.buildTree(inorder[index+1:],postorder[index:-1]) 18 | root.left = left_node 19 | root.right = right_node 20 | return root -------------------------------------------------------------------------------- /树/110_Balanced Binary Tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Dec 3 21:16:15 2019 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0909 10 | 这道题的难点在于找最大深度的时候,不是单纯的找到root的左子树和右子树的最大深度比较一次就可以, 11 | 而是要对于所有root的左右子树都进行比较,这就要求,每深入一次递归,都要尽心最大深度的比较 12 | ''' 13 | #updated 0628:在104基础上扩展,首先需要了解如何用递归找到一棵树的depth 14 | class Solution(object): 15 | def isBalanced(self, root): 16 | 17 | def check(root): 18 | if root is None: 19 | return 0 20 | left = check(root.left) 21 | right = check(root.right) 22 | if left == -1 or right == -1 or abs(left - right) > 1: 23 | return -1 24 | return 1 + max(left, right) 25 | 26 | return check(root) != -1 27 | 28 | 29 | class Solution: 30 | def isBalanced(self, root: TreeNode) -> bool: 31 | def get_height(root): 32 | if root is None: 33 | return 0 34 | left_height,right_height = get_height(root.left),get_height(root.right) 35 | if left_height < 0 or right_height < 0 or abs(left_height - right_height) > 1: 36 | return -1 37 | return max(left_height, right_height) + 1 38 | return (get_height(root) >= 0) -------------------------------------------------------------------------------- /树/111_Minimum Depth of Binary Tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Jan 25 21:24:51 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0909 10 | ''' 11 | class Solution: 12 | def minDepth(self, root: TreeNode) -> int: 13 | if not root: 14 | return 0 15 | left_depth = self.minDepth(root.left) 16 | right_depth = self.minDepth(root.right) 17 | if left_depth == 0 or right_depth == 0: 18 | return max(left_depth, right_depth) + 1 19 | else: 20 | return min(left_depth, right_depth) + 1 21 | 22 | class Solution: 23 | def minDepth(self, root: TreeNode) -> int: 24 | 25 | if root is None: 26 | return 0 27 | ''' 28 | 难点在于如果当前传入的root左右根中有一个不存在一个存在,那么以当前root为树的最小深度实际上是左右子树中最大的那个,这一点有些反常识 29 | ''' 30 | if root.left is not None and root.right is not None: 31 | return min(self.minDepth(root.left),self.minDepth(root.right)) + 1 32 | else: 33 | return max(self.minDepth(root.left),self.minDepth(root.right)) + 1 -------------------------------------------------------------------------------- /树/112_Path Sum.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jan 27 17:28:29 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | # Definition for a binary tree node. 9 | # class TreeNode: 10 | # def __init__(self, x): 11 | # self.val = x 12 | # self.left = None 13 | # self.right = None 14 | 15 | ''' 16 | 0721 17 | dfs的意义是能不能在当前给定的root代表的树中找到一组路径和为sum,这个模型最终重复到只有一个root的时候,如果只有一个root的时候不满足sum,那么就不对 18 | ''' 19 | class Solution: 20 | def hasPathSum(self, root: TreeNode, sum: int) -> bool: 21 | def dfs(root,sum): 22 | if not root: 23 | return False 24 | ''' 25 | 注意符合要求的条件在于该node的左右都没有node了(意味着它是叶子节点,不加这个判断没办法确定是叶子节点)且他的值为sum 26 | ''' 27 | if not root.left and not root.right and root.val == sum: 28 | return True 29 | else: 30 | return dfs(root.left,sum-root.val) or dfs(root.right,sum-root.val) 31 | 32 | if dfs(root,sum): 33 | return True 34 | else: 35 | return False 36 | 37 | class Solution: 38 | def hasPathSum(self, root: TreeNode, sum: int) -> bool: 39 | if root is None: 40 | return False 41 | if root.left is None and root.right is None and root.val == sum: 42 | return True 43 | else: 44 | return self.hasPathSum(root.left,sum - root.val) or self.hasPathSum(root.right,sum-root.val) 45 | -------------------------------------------------------------------------------- /树/144_Binary Tree Preorder Traversal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Mar 29 06:40:31 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def preorderTraversal(self, root: TreeNode) -> List[int]: 10 | res = [] 11 | if not root: 12 | return res 13 | stack = [root] 14 | while stack: 15 | pop_node = stack.pop() 16 | res.append(pop_node.val) 17 | if pop_node.right: 18 | stack.append(pop_node.right) 19 | if pop_node.left: 20 | stack.append(pop_node.left) 21 | return res 22 | 23 | #由于在原有函数上直接写递归容易出现错误,所以最好单独写递归函数 24 | class Solution: 25 | def preorderTraversal(self, root: TreeNode) -> List[int]: 26 | def dfs(root): 27 | if root is None: 28 | return 29 | res.append(root.val) 30 | dfs(root.left) 31 | dfs(root.right) 32 | res = [] 33 | dfs(root) 34 | return res 35 | 36 | 、 37 | 38 | 39 | -------------------------------------------------------------------------------- /树/145_Binary Tree Postorder Traversal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Mar 29 07:31:14 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def postorderTraversal(self, root: TreeNode) -> List[int]: 10 | res = [] 11 | def dfs(root): 12 | if root is None: 13 | return 14 | dfs(root.left) 15 | dfs(root.right) 16 | res.append(root.val) 17 | dfs(root) 18 | return res 19 | 20 | 21 | class Solution: 22 | def postorderTraversal(self, root: TreeNode) -> List[int]: 23 | res = [] 24 | if not root: 25 | return res 26 | stack = [root] 27 | while stack: 28 | node = stack.pop() 29 | if node.left : 30 | stack.append(node.left) 31 | if node.right: 32 | stack.append(node.right) 33 | res.append(node.val) 34 | return res[::-1] 35 | 36 | -------------------------------------------------------------------------------- /树/226_翻转二叉树.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri May 8 11:57:52 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def invertTree(self, root: TreeNode) -> TreeNode: 10 | if not root: 11 | return None 12 | root.left, root.right = self.invertTree(root.right), self.invertTree(root.left) 13 | return root 14 | 15 | 16 | #层次遍历+交换左右节点 17 | class Solution: 18 | def invertTree(self, root: TreeNode) -> TreeNode: 19 | if root is None: 20 | return root 21 | queue = [root] 22 | while queue: 23 | pop_node = queue.pop(0) 24 | if pop_node.left is not None: 25 | queue.append(pop_node.left) 26 | if pop_node.right is not None: 27 | queue.append(pop_node.right) 28 | pop_node.left, pop_node.right = pop_node.right, pop_node.left 29 | 30 | return root 31 | 32 | 33 | class Solution: 34 | def invertTree(self, root: TreeNode) -> TreeNode: 35 | if root is None: 36 | return 37 | root.left, root.right = root.right, root.left 38 | self.invertTree(root.left) 39 | self.invertTree(root.right) 40 | return root 41 | 42 | 43 | class Solution: 44 | def invertTree(self, root: TreeNode) -> TreeNode: 45 | if root is None: 46 | return root 47 | queue = [root] 48 | while queue: 49 | pop_node = queue.pop(0) 50 | pop_node.left, pop_node.right = pop_node.right, pop_node.left 51 | if pop_node.left is not None: 52 | queue.append(pop_node.left) 53 | if pop_node.right is not None: 54 | queue.append(pop_node.right) 55 | 56 | 57 | return root -------------------------------------------------------------------------------- /树/236_二叉树的最近公共祖先.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun May 10 09:35:50 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0719:统一模板 10 | 递归理解:在最高层上,我们需要判断root.left,root.right是否可以找到p,q,如果都找到则结果为root,反之需要分类处理 11 | 我们希望函数返回的是当前树的最近公共祖先 12 | ''' 13 | 14 | class Solution: 15 | def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': 16 | if not root: 17 | return None 18 | if root == p or root == q: 19 | return root 20 | left_node = self.lowestCommonAncestor(root.left,p,q) 21 | right_node = self.lowestCommonAncestor(root.right,p,q) 22 | if not left_node and not right_node: 23 | return None 24 | if left_node and not right_node: 25 | return left_node 26 | if not left_node and right_node: 27 | return right_node 28 | if left_node and right_node: 29 | return root 30 | 31 | 32 | class Solution: 33 | def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': 34 | if not root: 35 | return None 36 | if root is q or root is p: 37 | return root 38 | #最高层面上来看的话left中包含的是在root的左子树中p,q存在的最深层的公共祖先,也可能返回的就是p,q中一个node所在的位置, 39 | #因为p,q可能在不同子树中 40 | left = self.lowestCommonAncestor(root.left, p, q) 41 | right = self.lowestCommonAncestor(root.right, p, q) 42 | if not left and not right: 43 | return None 44 | elif not left and right: 45 | return right 46 | elif left and not right: 47 | return left 48 | else: 49 | return root -------------------------------------------------------------------------------- /树/297_二叉树的序列化与反序列化.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Aug 1 11:36:26 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Codec: 10 | 11 | def serialize(self, root): 12 | """Encodes a tree to a single string. 13 | 14 | :type root: TreeNode 15 | :rtype: str 16 | """ 17 | if not root: 18 | return '[]' 19 | queue = [root] 20 | res = [] 21 | while queue: 22 | pop_node = queue.pop(0) 23 | if pop_node: 24 | res.append(str(pop_node.val)) 25 | queue.append(pop_node.left) 26 | queue.append(pop_node.right) 27 | else: 28 | res.append('null') 29 | return '['+','.join(res)+']' 30 | 31 | 32 | def deserialize(self, data): 33 | """Decodes your encoded data to tree. 34 | 35 | :type data: str 36 | :rtype: TreeNode 37 | """ 38 | if data == '[]': 39 | return 40 | vals = data[1:-1].split(',') 41 | index = 1 42 | root = TreeNode(int(vals[0])) 43 | queue = [root] 44 | while queue: 45 | pop_node = queue.pop(0) 46 | if vals[index] != 'null': 47 | pop_node.left = TreeNode(int(vals[index])) 48 | queue.append(pop_node.left) 49 | index += 1 50 | if vals[index] != 'null': 51 | pop_node.right = TreeNode(int(vals[index])) 52 | queue.append(pop_node.right) 53 | index += 1 54 | return root -------------------------------------------------------------------------------- /树/572_另一课树的子树.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu May 7 16:59:59 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #based on 100.The Same Tree 9 | class Solution: 10 | def isSubtree(self, s: TreeNode, t: TreeNode) -> bool: 11 | def isSametree(s,t): 12 | 13 | if not s and not t: 14 | return True 15 | if not s or not t: 16 | return False 17 | if s.val == t.val: 18 | return isSametree(s.left,t.left) and isSametree(s.right, t.right) 19 | else: 20 | return False 21 | 22 | if not s and not t: 23 | return True 24 | if not s or not t: 25 | return False 26 | else: 27 | return isSametree(s, t) or self.isSubtree(s.left, t) or self.isSubtree(s.right, t) -------------------------------------------------------------------------------- /树/94_Binary Tree Inorder Traversal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Mar 29 07:30:53 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def inorderTraversal(self, root: TreeNode) -> List[int]: 11 | res = [] 12 | if not root: 13 | return res 14 | cur = root 15 | stack = [] 16 | while stack or cur: 17 | while cur: 18 | stack.append(cur) 19 | cur = cur.left 20 | pop_node = stack.pop() 21 | res.append(pop_node.val) 22 | cur = pop_node.right 23 | return res 24 | 25 | class Solution: 26 | def inorderTraversal(self, root: TreeNode) -> List[int]: 27 | res = [] 28 | cur = root 29 | stack = [] 30 | while stack or cur: 31 | while cur: 32 | stack.append(cur) 33 | cur = cur.left 34 | pop_node = stack.pop() 35 | res.append(pop_node.val) 36 | cur = pop_node.right 37 | return res 38 | 39 | class Solution: 40 | def inorderTraversal(self, root: TreeNode) -> List[int]: 41 | res = [] 42 | def dfs(root): 43 | if root is None: 44 | return 45 | dfs(root.left) 46 | res.append(root.val) 47 | dfs(root.right) 48 | dfs(root) 49 | return res -------------------------------------------------------------------------------- /树/951_翻转等价二叉树.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jul 27 09:40:46 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution(object): 9 | def flipEquiv(self, root1, root2): 10 | if not root1 and not root2: 11 | return True 12 | if not root1 or not root2 or root1.val != root2.val: 13 | return False 14 | 15 | return (self.flipEquiv(root1.left, root2.left) and 16 | self.flipEquiv(root1.right, root2.right) or 17 | self.flipEquiv(root1.left, root2.right) and 18 | self.flipEquiv(root1.right, root2.left)) 19 | -------------------------------------------------------------------------------- /树/98_验证二叉搜索树.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue May 5 21:44:14 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0909 10 | 中序遍历当前从queue从弹出的pop_node.val一定比之前一个弹出来的node的val大,这保证了二叉搜索树的性质 11 | ''' 12 | #节点的左子树只包含小于当前节点的数 13 | #节点的右子树只包含大于当前节点的数,这意味着节点右子树中的所有节点都大于当前节点,即右子树中的左子树也要大于当前节点 14 | class Solution: 15 | def isValidBST(self, root: TreeNode) -> bool: 16 | #if root is None: 17 | #return True 18 | #为了让第一次的pop_node可以顺利通过,temp要初始化一个无穷小 19 | temp = float('-inf') 20 | stack = [] 21 | cur = root 22 | #除了层次遍历用queue,其他遍历均用stack,不要搞错了 23 | while stack or cur: 24 | while cur: 25 | stack.append(cur) 26 | cur = cur.left 27 | pop_node = stack.pop() 28 | #此处应该有等于号 29 | if pop_node.val <= temp: 30 | return False 31 | else: 32 | temp = pop_node.val 33 | cur = pop_node.right 34 | return True 35 | 36 | #递归形式 37 | class Solution: 38 | def isValidBST(self, root: TreeNode) -> bool: 39 | def helper(node, lower = float('-inf'), upper = float('inf')): 40 | if not node: 41 | return True 42 | if node.val <= lower or node.val >= upper: 43 | return False 44 | if not helper(node.left, lower, node.val): 45 | return False 46 | if not helper(node.right, node.val, upper): 47 | return False 48 | return True 49 | 50 | return helper(root) -------------------------------------------------------------------------------- /树/bfs+dfs/102_Binary Tree Level Order Traversal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Apr 8 22:15:40 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #注意最后的结果是每个node中的val,不是node本身 9 | class Solution: 10 | def levelOrder(self, root: TreeNode) -> List[List[int]]: 11 | res = [] 12 | if root is None: 13 | return res 14 | 15 | cur_queue = [root] 16 | 17 | while cur_queue: 18 | cur_res = [] 19 | next_queue = [] 20 | for node in cur_queue: 21 | cur_res.append(node.val) 22 | if node.left is not None: 23 | next_queue.append(node.left) 24 | if node.right is not None: 25 | next_queue.append(node.right) 26 | cur_queue = next_queue 27 | res.append(cur_res) 28 | return res 29 | 30 | #方法二,更普遍 31 | class Solution: 32 | def levelOrder(self, root: TreeNode) -> List[List[int]]: 33 | #两种方式 34 | res = [] 35 | if not root: 36 | return res 37 | cur_queue = [root] 38 | while cur_queue: 39 | current_len = len(cur_queue) 40 | cur_res = [] 41 | for i in range(current_len): 42 | node = cur_queue.pop(0) 43 | cur_res.append(node.val) 44 | if node.left is not None: 45 | cur_queue.append(node.left) 46 | if node.right is not None: 47 | cur_queue.append(node.right) 48 | res.append(cur_res) 49 | 50 | return res -------------------------------------------------------------------------------- /树/bfs+dfs/103_二叉树的锯齿形层次遍历.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jul 9 13:36:22 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]: 11 | count = 0 12 | res = [] 13 | if not root: 14 | return res 15 | 16 | queue = [root] 17 | while queue: 18 | count += 1 19 | cur_res = [] 20 | current_len = len(queue) 21 | for _ in range(current_len): 22 | pop_node = queue.pop(0) 23 | cur_res.append(pop_node.val) 24 | if pop_node.left: 25 | queue.append(pop_node.left) 26 | if pop_node.right: 27 | queue.append(pop_node.right) 28 | if count % 2 == 0: 29 | res.append(cur_res[::-1]) 30 | else: 31 | res.append(cur_res) 32 | return res -------------------------------------------------------------------------------- /树/bfs+dfs/144_Binary Tree Preorder Traversal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Mar 29 06:40:31 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def preorderTraversal(self, root: TreeNode) -> List[int]: 10 | res = [] 11 | if not root: 12 | return res 13 | stack = [root] 14 | while stack: 15 | pop_node = stack.pop() 16 | res.append(pop_node.val) 17 | if pop_node.right: 18 | stack.append(pop_node.right) 19 | if pop_node.left: 20 | stack.append(pop_node.left) 21 | return res 22 | 23 | #由于在原有函数上直接写递归容易出现错误,所以最好单独写递归函数 24 | class Solution: 25 | def preorderTraversal(self, root: TreeNode) -> List[int]: 26 | def dfs(root): 27 | if root is None: 28 | return 29 | res.append(root.val) 30 | dfs(root.left) 31 | dfs(root.right) 32 | res = [] 33 | dfs(root) 34 | return res 35 | 36 | 、 37 | 38 | 39 | -------------------------------------------------------------------------------- /树/bfs+dfs/145_Binary Tree Postorder Traversal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Mar 29 07:31:14 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def postorderTraversal(self, root: TreeNode) -> List[int]: 10 | res = [] 11 | def dfs(root): 12 | if root is None: 13 | return 14 | dfs(root.left) 15 | dfs(root.right) 16 | res.append(root.val) 17 | dfs(root) 18 | return res 19 | 20 | 21 | class Solution: 22 | def postorderTraversal(self, root: TreeNode) -> List[int]: 23 | res = [] 24 | if not root: 25 | return res 26 | stack = [root] 27 | while stack: 28 | node = stack.pop() 29 | if node.left : 30 | stack.append(node.left) 31 | if node.right: 32 | stack.append(node.right) 33 | res.append(node.val) 34 | return res[::-1] 35 | 36 | -------------------------------------------------------------------------------- /树/bfs+dfs/94_Binary Tree Inorder Traversal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Mar 29 07:30:53 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def inorderTraversal(self, root: TreeNode) -> List[int]: 11 | res = [] 12 | if not root: 13 | return res 14 | cur = root 15 | stack = [] 16 | while stack or cur: 17 | while cur: 18 | stack.append(cur) 19 | cur = cur.left 20 | #第一次弹出的实际上是一开始能找到的最左边的左节点,其实就是最后一个节点的根,然后判断这个根是否有右子树 21 | pop_node = stack.pop() 22 | res.append(pop_node.val) 23 | #cur每次初始化成一棵新树的root,然后根据while找到这棵新树的最左的左节点,同时把找的路径记录下来,记录方式就是把每个root记下来 24 | cur = pop_node.right 25 | return res 26 | 27 | class Solution: 28 | def inorderTraversal(self, root: TreeNode) -> List[int]: 29 | res = [] 30 | cur = root 31 | stack = [] 32 | while stack or cur: 33 | while cur: 34 | stack.append(cur) 35 | cur = cur.left 36 | pop_node = stack.pop() 37 | res.append(pop_node.val) 38 | cur = pop_node.right 39 | return res 40 | 41 | class Solution: 42 | def inorderTraversal(self, root: TreeNode) -> List[int]: 43 | res = [] 44 | def dfs(root): 45 | if root is None: 46 | return 47 | dfs(root.left) 48 | res.append(root.val) 49 | dfs(root.right) 50 | dfs(root) 51 | return res -------------------------------------------------------------------------------- /树/bfs+dfs/tree_readme.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Feb 13 09:05:44 2022 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | dfs 6 道基础题 + bfs 3 道基础题 10 | dfs思考方式 11 | 几道关于dfs的巩固题 226 100 101 12 | 二叉树的不同构造方式 13 | 二叉树与回溯的交叉问题 14 | 由dfs延伸到回溯与动态规划的区别 15 | 由tree中的dfs、bfs延申到图中的扩展 -------------------------------------------------------------------------------- /树/前中后遍历数组的搜索/105_从前序与中序遍历序列构造二叉树.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Jun 20 08:08:23 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | inorder.index(preorder[0]) 这一步获取根的索引值, 10 | 题目说树中的各个节点的值都不相同,也确保了这步得到的结果是唯一准确的。 11 | 而且这个idx还能当长度用相当于 左+根 的长度,因为 左+根 和 根+左 是等长的。 12 | 13 | ''' 14 | 15 | class Solution: 16 | def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode: 17 | if not preorder or not inorder: 18 | return None 19 | if len(inorder) == 1: 20 | return TreeNode(inorder[0]) 21 | root = TreeNode(preorder[0]) 22 | index = inorder.index(root.val) 23 | #分别在preorder和inorder中找到符合要求的所有左子树节点和右子树节点,作为完整的树传入下一次递归中 24 | left_node = self.buildTree(preorder[1:1+index],inorder[:index]) 25 | right_node = self.buildTree(preorder[1+index:],inorder[index+1:]) 26 | root.left = left_node 27 | root.right = right_node 28 | return root -------------------------------------------------------------------------------- /树/前中后遍历数组的搜索/106_从中序与后序遍历序列构造二叉树.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Jun 20 09:03:47 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode: 11 | if not postorder or not inorder: 12 | return None 13 | root = TreeNode(postorder[-1]) 14 | index = inorder.index(root.val) 15 | #left + root = index+1个,那么在postorder中从零开始到index正好有index个left nodes 16 | left_node = self.buildTree(inorder[:index],postorder[:index]) 17 | right_node = self.buildTree(inorder[index+1:],postorder[index:-1]) 18 | root.left = left_node 19 | root.right = right_node 20 | return root -------------------------------------------------------------------------------- /滑动窗口/1004_最大连续1的个数 III.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jul 12 16:17:33 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0718 10 | ''' 11 | class Solution: 12 | def longestOnes(self, A: List[int], K: int) -> int: 13 | start = 0 14 | max_len = float('-inf') 15 | count_one = 0 16 | for end in range(len(A)): 17 | if A[end] == 1: 18 | count_one += 1 19 | while count_one + K < end-start+1: 20 | if A[start] == 1: 21 | count_one -= 1 22 | start += 1 23 | max_len = max(max_len,end-start+1) 24 | if max_len == float('-inf'): 25 | return 0 26 | else: 27 | return max_len 28 | 29 | class Solution: 30 | def longestOnes(self, A: List[int], K: int) -> int: 31 | #标准滑动窗口 32 | start = 0 33 | max_len = float('-inf') 34 | count = 0 35 | for end in range(len(A)): 36 | if A[end] == 1: 37 | count += 1 38 | #while start < len(A) and end-start+1 > count + K: 39 | while end-start+1 > count + K: 40 | if A[start] == 1: 41 | count -= 1 42 | start += 1 43 | max_len = max(max_len,end-start+1) 44 | return max_len -------------------------------------------------------------------------------- /滑动窗口/1248_统计「优美子数组」.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri May 15 13:52:29 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | ''' 10 | 0712 11 | 前缀和+滑动窗口 12 | 参考:930,992(based on 904) 13 | ''' 14 | class Solution: 15 | def numberOfSubarrays(self, nums: List[int], k: int) -> int: 16 | def atmost(nums,k): 17 | start = 0 18 | count = 0 19 | odd_count = 0 20 | for end in range(len(nums)): 21 | if nums[end] % 2 != 0: 22 | odd_count += 1 23 | while odd_count > k: 24 | if nums[start] % 2 != 0: 25 | odd_count -= 1 26 | start += 1 27 | count += end-start+1 28 | #注意return返回的位置,要在for循环外返回 29 | return count 30 | return atmost(nums,k) - atmost(nums,k-1) 31 | 32 | 33 | #前缀和+差分+哈希表 34 | class Solution: 35 | def numberOfSubarrays(self, nums: List[int], k: int) -> int: 36 | #dict中存放到这个位置的奇数的个数 37 | adict = {} 38 | adict[0] = 1 39 | #temp是奇数的个数 40 | temp = 0 41 | res = 0 42 | for i in range(len(nums)): 43 | if nums[i] % 2 == 1: 44 | temp += 1 45 | if temp - k in adict: 46 | res += adict[temp-k] 47 | if temp in adict: 48 | adict[temp] += 1 49 | else: 50 | adict[temp] = 1 51 | 52 | return res 53 | 54 | class Solution: 55 | def numberOfSubarrays(self, nums: List[int], k: int) -> int: 56 | count = 0 57 | res = 0 58 | adict = {} 59 | adict[0] = 1 60 | for i in range(len(nums)): 61 | if nums[i] % 2 == 1: 62 | count += 1 63 | if count - k in adict: 64 | res += adict[count-k] 65 | if count in adict: 66 | adict[count] += 1 67 | else: 68 | adict[count] = 1 69 | return res -------------------------------------------------------------------------------- /滑动窗口/209_长度最小的子数组.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jul 12 13:17:24 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0712 10 | 这道题在滑动窗口中稍微有些特殊 11 | 我们说过,尽量将对输出的更新放到小循环外面,即尽量不要在缩小窗口的时候更新输出,以防特殊情况 12 | 但是这道题需要将更新放入小循环中,因为只有在小循环中才能找到需要更新的精确数值,一旦出了小循环,就不是如何要求的输出值了 13 | 在这道题里就是出了小循环,该滑动窗口和 int: 26 | #sum_>=s的时候可以收缩窗口,这就是我们要找的while条件,因为 27 | #窗口不定长,所以用while,典型的不定长滑动窗口 28 | start = 0 29 | min_len = float('inf') 30 | sum_ = 0 31 | for end in range(len(nums)): 32 | sum_ += nums[end] 33 | while sum_ >= s: 34 | min_len = min(min_len,end-start+1) 35 | sum_ -= nums[start] 36 | start += 1 37 | 38 | if min_len == float('inf'): 39 | return 0 40 | else: 41 | return min_len -------------------------------------------------------------------------------- /滑动窗口/28_Implement strStr().py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Aug 28 10:46:00 2019 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0712 10 | 滑动窗口 11 | 思路:拿到一个题先去判断窗口是否固定(easy),是否可变(medium),是否end在start前进的时候需要回缩(hard) 12 | 这道题的升级版是567,只要needle中的元素全在窗口中就行,可以打乱重组,需要用hashmap去比较 13 | ''' 14 | 15 | class Solution: 16 | def strStr(self, haystack: str, needle: str) -> int: 17 | #定长滑动窗口 18 | start = 0 19 | '''当 needle 是空字符串时haystack[0:0]返回也是0,即haystack[start:end+1] == needle成立,函数返回0''' 20 | for end in range(len(needle)-1,len(haystack)): 21 | if haystack[start:end+1] == needle: 22 | return start 23 | start += 1 24 | return -1 25 | 26 | ''' 27 | 0718 非模板写法,但是更好理解 28 | ''' 29 | class Solution: 30 | def strStr(self, haystack: str, needle: str) -> int: 31 | start = 0 32 | end = len(needle) 33 | while end < len(haystack) and haystack[start:end] != needle: 34 | start += 1 35 | end += 1 36 | if haystack[start:end] == needle: 37 | return start 38 | else: 39 | return -1 40 | 41 | #------------------------------------------------------------ 42 | class Solution: 43 | def strStr(self, haystack: str, needle: str) -> int: 44 | if needle == '': 45 | return 0 46 | if needle not in haystack: 47 | return -1 48 | else: 49 | return haystack.index(needle) 50 | 51 | 52 | class Solution: 53 | def strStr(self, haystack: str, needle: str) -> int: 54 | if not needle: 55 | return 0 56 | if needle in haystack: 57 | return haystack.index(needle) 58 | else: 59 | return -1 60 | 61 | 62 | -------------------------------------------------------------------------------- /滑动窗口/567_字符串的排列.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jul 12 17:23:16 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #定长滑动窗口 9 | # based on 28 10 | # 定长滑动窗口,手动移动start指针,自动移动end指针,好处是end指针可以根据规定的范围知道何时自动停止 11 | class Solution: 12 | def checkInclusion(self, s1: str, s2: str) -> bool: 13 | from collections import Counter 14 | start = 0 15 | ori_ = Counter(s1) 16 | for end in range(len(s1)-1,len(s2)): 17 | if ori_ == Counter(s2[start:end+1]): 18 | return True 19 | start += 1 20 | return False 21 | 22 | 23 | class Solution: 24 | def checkInclusion(self, s1: str, s2: str) -> bool: 25 | import collections 26 | ori_dict = collections.Counter(s1) 27 | 28 | start = 0 29 | end = len(s1) - 1 30 | for i in range(end, len(s2)): 31 | if collections.Counter(s2[start:i+1]) == ori_dict: 32 | return True 33 | start += 1 34 | 35 | return False -------------------------------------------------------------------------------- /滑动窗口/992_K 个不同整数的子数组.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jul 12 15:37:46 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0713 10 | 类比:560是一道前缀和的题,但是不能用这种滑动窗口的方法,问题出在题目的本质, 11 | 560要求找到和恰为k的连续子串个数,我们的方法只能用于恰为k个的情况,930特殊的点在于可以把和转化为求k个1,但是单纯求和恰为k滑动窗口是没办法解决的, 12 | 因为还是会出现start向前收缩的时候end需要往回收缩的情况,这一点我们的滑动窗口模板没办法解决 13 | 14 | 但是基于1 two sum问题,我们可以培养一种直觉,求和恰为k的这种模板是典型的前缀和解题思路,在一次遍历过程中先把当前看似无用的值存起来, 15 | 后面一旦发现有用再调用出来 16 | ''' 17 | #与930解法类似,需要思考的是为什么需要这么去做 18 | ''' 19 | 904水果题相当于是这道题的基础,只是求了最多有两种类型的时候的情况,相当于求的atmost(A,2),因此没有借用前缀和的思想, 20 | 这道题之所以需要前缀和是因为它规定恰好为K个,因此可以用前缀和的思想来卡出恰好的种类数 21 | 与904的另一个区别是,输出不一样,904输出最多选择两种数字的情况下,最大的连续子序列长度,这道题是计算最多选择两种数字情况下有多少种子串的选择 22 | ''' 23 | class Solution: 24 | def subarraysWithKDistinct(self, A: List[int], K: int) -> int: 25 | ''' 26 | atmost的含义是最多(最多意味着可以小于等于,但不能超过)有K个不同种类的可能个数是多少个 27 | 比如,当前有n个字符,判断成立,那么新多出来n个组合种类,可以从后向前看,n,(n,n-1),(n,n-1,n-2)...,一共新多出来n种方式 28 | 之所以这么计算是因为可以用常规模式计算出这种定义形式的个数,无须虚拟的start进行试探 29 | 因为这道题相对于leetcode.003等常规滑动窗口来说,收缩窗口的时候不是仅从start向前收缩就可以的,有些解可能需要start向前收缩的同时end向回收缩 30 | ''' 31 | def atmost(A,k): 32 | start = 0 33 | adict = {} 34 | count = 0 35 | for end in range(len(A)): 36 | if A[end] not in adict: 37 | adict[A[end]] = 1 38 | else: 39 | adict[A[end]] += 1 40 | while len(adict) > k: 41 | adict[A[start]] -= 1 42 | if adict[A[start]] == 0: 43 | del adict[A[start]] 44 | start += 1 45 | #包含A[end]这个数在窗口[start,end]范围内所有可能的连续子串个数就等于当前窗口的长度 46 | count += end-start+1 47 | return count 48 | return atmost(A,K)-atmost(A,K-1) -------------------------------------------------------------------------------- /滑动窗口/滑动窗口.md: -------------------------------------------------------------------------------- 1 | 滑动窗口 2 | 3 | 定长:28->567 4 | 5 | 不定长:3,76,209,904,1004 6 | 7 | 不定长+状态回缩:930,992,1248 -------------------------------------------------------------------------------- /股票题/122_Best Time to Buy and Sell Stock II.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Feb 1 21:37:28 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | ''' 10 | 0713 11 | 与121唯一不同在于dp[i][1]的更新策略,本质在于dp[i][0]的含义是在i及i天之前某个时刻卖出stock后手里有cash,无stock的最大利润 12 | ''' 13 | 14 | class Solution: 15 | def maxProfit(self, prices: List[int]) -> int: 16 | if not prices: 17 | return 0 18 | dp = [[0,0] for _ in range(len(prices))] 19 | dp[0][1] = -prices[0] 20 | #注意从1开始 21 | for i in range(1, len(prices)): 22 | dp[i][0] = max(dp[i-1][1]+prices[i],dp[i-1][0]) 23 | dp[i][1] = max(dp[i-1][1],dp[i-1][0]-prices[i]) 24 | return dp[-1][0] 25 | 26 | 27 | #动态规划 28 | class Solution: 29 | def maxProfit(self, prices: List[int]) -> int: 30 | dp = [0 for _ in range(len(prices))] 31 | for i in range(1, len(prices)): 32 | if prices[i] > prices[i-1]: 33 | dp[i] = dp[i-1] + prices[i] - prices[i-1] 34 | else: 35 | dp[i] = dp[i-1] 36 | return dp[-1] 37 | 38 | 39 | #贪心 40 | class Solution: 41 | def maxProfit(self, prices: List[int]) -> int: 42 | price = 0 43 | for i in range(1,len(prices)): 44 | if prices[i] > prices[i-1]: 45 | price += prices[i] - prices[i-1] 46 | return price 47 | -------------------------------------------------------------------------------- /股票题/123_买卖股票的最佳时机 III.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jul 13 14:45:31 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def maxProfit(self, prices: List[int]) -> int: 11 | if not prices: 12 | return 0 13 | dp = [[0,0,0,0] for _ in range(len(prices))] 14 | dp[0][0] = -prices[0] 15 | #因为在更新dp[i][2]的时候可能会出现比0小的数,因为dp[i-1][1]-prices[i] 16 | #所以这个时候dp[i][2]不能默认是0,相反dp[i][3]就可以默认是0 17 | #dp[0][1]不需要更新是因为第一次卖出的时候一定式prices[i] > dp[i-1][0]才会卖出 18 | dp[0][2] = float('-inf') 19 | 20 | 21 | # dp[j]:i 表示 [0, i] 区间里,状态为 j 的最大收益 22 | # j = 0:什么都不操作,没有意义,不会更新最大利润 23 | # j = 1:第 1 次买入一支股票 24 | # j = 2:第 1 次卖出一支股票 25 | # j = 3:第 2 次买入一支股票 26 | # j = 4:第 2 次卖出一支股票 27 | 28 | for i in range(1, len(prices)): 29 | dp[i][0] = max(dp[i-1][0], - prices[i]) 30 | dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i]) 31 | dp[i][2] = max(dp[i-1][2], dp[i-1][1] - prices[i]) 32 | dp[i][3] = max(dp[i-1][3], dp[i-1][2] + prices[i]) 33 | return max(dp[-1][1], dp[-1][3]) 34 | 35 | class Solution: 36 | def maxProfit(self, prices: List[int]) -> int: 37 | if not prices: 38 | return 0 39 | dp = [[0,0,0,0,0] for _ in range(len(prices))] 40 | dp[0][0] = 0 41 | dp[0][1] = -prices[0] 42 | 43 | dp[0][3] = float('-inf') 44 | dp[0][4] = float('-inf') 45 | 46 | # dp[j]:i 表示 [0, i] 区间里,状态为 j 的最大收益 47 | # j = 0:什么都不操作 48 | # j = 1:第 1 次买入一支股票 49 | # j = 2:第 1 次卖出一支股票 50 | # j = 3:第 2 次买入一支股票 51 | # j = 4:第 2 次卖出一支股票 52 | 53 | for i in range(1, len(prices)): 54 | dp[i][0] = 0 55 | dp[i][1] = max(dp[i-1][1], - prices[i]) 56 | dp[i][2] = max(dp[i-1][2], dp[i-1][1] + prices[i]) 57 | dp[i][3] = max(dp[i-1][3], dp[i-1][2] - prices[i]) 58 | dp[i][4] = max(dp[i-1][4], dp[i-1][3] + prices[i]) 59 | return max(0, dp[-1][2], dp[-1][4]) -------------------------------------------------------------------------------- /股票题/188_买卖股票的最佳时机 IV.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jul 13 15:15:02 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def maxProfit(self, k: int, prices: List[int]) -> int: 11 | size = len(prices) 12 | if size < 2 or k == 0: 13 | return 0 14 | if k >= size // 2: 15 | res = 0 16 | for i in range(1, size): 17 | if prices[i] > prices[i - 1]: 18 | res += (prices[i] - prices[i - 1]) 19 | return res 20 | #初始化:把持股的部分都设置为一个较大的负值 21 | dp = [[[0, float('-inf')] for _ in range(k + 1)] for _ in range(size + 1)] 22 | 23 | for i in range(1, size + 1): 24 | for j in range(1, k + 1): 25 | dp[i][j][1] = max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i - 1]) 26 | dp[i][j][0] = max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i - 1]) 27 | 28 | return dp[size][k][0] 29 | 30 | 31 | class Solution: 32 | def maxProfit(self, k: int, prices: List[int]) -> int: 33 | size = len(prices) 34 | if size < 2 or k == 0: 35 | return 0 36 | if k >= size // 2: 37 | res = 0 38 | for i in range(1, size): 39 | if prices[i] > prices[i - 1]: 40 | res += (prices[i] - prices[i - 1]) 41 | return res 42 | #初始化:把持股的部分都设置为一个较大的负值 43 | dp = [[[0, 0] for _ in range(k + 1)] for _ in range(size + 1)] 44 | for i in range(len(dp)): 45 | for j in range(len(dp[i])): 46 | dp[0][j][1] = float('-inf') 47 | break 48 | for i in range(1, size + 1): 49 | for j in range(1, k + 1): 50 | dp[i][j][1] = max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i - 1]) 51 | dp[i][j][0] = max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i - 1]) 52 | 53 | return dp[size][k][0] 54 | 55 | -------------------------------------------------------------------------------- /股票题/714_买卖股票的最佳时机含手续费.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue May 19 17:48:50 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | ''' 10 | 0713 11 | 统一了121,122,309,714的状态定义以及模板 12 | ''' 13 | class Solution: 14 | def maxProfit(self, prices: List[int], fee: int) -> int: 15 | if not prices: 16 | return 0 17 | dp = [[0,0] for _ in range(len(prices))] 18 | #默认每次买入的时候收手续费,也可以写成每次卖出的时候收手续费,这里买入要花一笔钱,索性就一起花了 19 | dp[0][1] = -prices[0] - fee 20 | for i in range(1, len(prices)): 21 | dp[i][0] = max(dp[i-1][0],dp[i-1][1]+prices[i]) 22 | dp[i][1] = max(dp[i-1][1],dp[i-1][0]-prices[i]-fee) 23 | return dp[-1][0] 24 | 25 | 26 | class Solution: 27 | def maxProfit(self, prices: List[int], fee: int) -> int: 28 | size = len(prices) 29 | 30 | if size < 2: 31 | return 0 32 | 33 | # dp[i][j] 表示 [0, i] 区间内,到第 i 天(从 0 开始)状态为 j 时的最大收益 34 | # j = 0 表示不持股,j = 1 表示持股 35 | # 并且规定在买入股票的时候,扣除手续费 36 | 37 | dp = [[0, 0] for _ in range(size)] 38 | dp[0][0] = 0 39 | dp[0][1] = -prices[0] - fee 40 | 41 | for i in range(1, size): 42 | dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]) 43 | dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i] - fee) 44 | return dp[-1][0] -------------------------------------------------------------------------------- /链表/141_Linked List Cycle.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Feb 6 18:11:54 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #使用双指针,一个指针每次移动一个节点,一个指针每次移动两个节点,如果存在环,那么这两个指针一定会相遇。 9 | 10 | class Solution: 11 | def hasCycle(self, head: ListNode) -> bool: 12 | #快慢指针 13 | #注意一开始head不存在的情况 14 | if not head: 15 | return False 16 | 17 | ''' 18 | 一开始尽量让快慢指针错开,因为一开始指向相同的node的话,在只有一个node且无环的情况下会误判 19 | ''' 20 | slow = head 21 | fast = head.next 22 | while fast and fast.next and slow != fast: 23 | fast = fast.next.next 24 | slow = slow.next 25 | if slow == fast: 26 | return True 27 | else: 28 | return False 29 | 30 | 31 | class Solution: 32 | def hasCycle(self, head: ListNode) -> bool: 33 | if head is None: 34 | return False 35 | slow = head 36 | fast = head.next 37 | while slow is not None and fast is not None and fast.next is not None: 38 | if slow == fast: 39 | return True 40 | slow = slow.next 41 | fast = fast.next.next 42 | return False 43 | 44 | #运行时间过长 45 | class Solution: 46 | def hasCycle(self, head: ListNode) -> bool: 47 | slow = head 48 | fast = head 49 | while slow is not None: 50 | while fast is not None: 51 | if slow == fast.next: 52 | return True 53 | else: 54 | fast = fast.next 55 | slow = slow.next 56 | fast = slow 57 | return False -------------------------------------------------------------------------------- /链表/160_Intersection of Two Linked Lists.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Feb 7 16:52:49 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #谁指向谁,第一个谁在等式左边 9 | # Definition for singly-linked list. 10 | # class ListNode: 11 | # def __init__(self, x): 12 | # self.val = x 13 | # self.next = None 14 | 15 | class Solution: 16 | def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode: 17 | ahead = headA 18 | bhead = headB 19 | while ahead != bhead: 20 | if ahead is None: 21 | ahead = headB 22 | else: 23 | ahead = ahead.next 24 | if bhead is None: 25 | bhead = headA 26 | else: 27 | bhead = bhead.next 28 | return ahead -------------------------------------------------------------------------------- /链表/19_Remove Nth Node From End of List.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Feb 12 21:15:34 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | 10 | ''' 11 | 0711 12 | On时间复杂度实现,间隔n一次扫描 13 | ''' 14 | 15 | class Solution: 16 | def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode: 17 | cur = head 18 | fur = cur 19 | pre = None 20 | for _ in range(n): 21 | fur = fur.next 22 | #注意判断将头节点去掉的特殊情况 23 | if not fur: 24 | return head.next 25 | while fur: 26 | pre = cur 27 | cur = cur.next 28 | fur = fur.next 29 | pre.next = cur.next 30 | return head 31 | 32 | # Definition for singly-linked list. 33 | # class ListNode: 34 | # def __init__(self, x): 35 | # self.val = x 36 | # self.next = None 37 | 38 | class Solution: 39 | def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode: 40 | cur = head 41 | count = 0 42 | while cur is not None: 43 | count += 1 44 | cur = cur.next 45 | n_inorder = count - n + 1 46 | pre = None 47 | cur = head 48 | count1 = 1 49 | while cur is not None: 50 | if count1 == n_inorder: 51 | if pre is None: 52 | head = cur.next 53 | else: 54 | pre.next = cur.next 55 | break 56 | else: 57 | count1 += 1 58 | pre = cur 59 | cur = cur.next 60 | return head 61 | -------------------------------------------------------------------------------- /链表/206_Reverse Linked List.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Feb 12 20:34:05 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0709 10 | 一开始不能直接将cur.next = pre因为之后还需要将cur移动到cur.next,如果一开始先动了cur.next, 11 | 那么下次cur移动到的cur.next的位置就不是一开始的位置了 12 | ''' 13 | class Solution: 14 | def reverseList(self, head: ListNode) -> ListNode: 15 | pre = None 16 | cur = head 17 | while cur is not None: 18 | 19 | temp = cur.next 20 | cur.next = pre 21 | pre = cur 22 | cur = temp 23 | return pre -------------------------------------------------------------------------------- /链表/21_Merge Two Sorted Lists.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Aug 27 10:29:03 2019 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0626 updated:更新新的写法 10 | 0709 l1,l2当前指向的位置是还没有比较的位置 -->参考88题 11 | 0714 注意每次修改完pre指向以后,即使更新pre,最好画个图,单纯脑子想可能忘记一些要更新的值 12 | ''' 13 | class Solution: 14 | def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode: 15 | dummynode = ListNode(0) 16 | pre = dummynode 17 | while l1 and l2: 18 | if l1.val <= l2.val: 19 | pre.next = l1 20 | pre = l1 21 | l1 = l1.next 22 | else: 23 | pre.next = l2 24 | pre = l2 25 | l2 = l2.next 26 | if l1: 27 | pre.next = l1 28 | if l2: 29 | pre.next = l2 30 | return dummynode.next 31 | 32 | class Solution: 33 | def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode: 34 | cur = dummy= ListNode(0) 35 | while l1 and l2: 36 | if l1.val <= l2.val: 37 | cur.next = l1 38 | l1 = l1.next 39 | else: 40 | cur.next = l2 41 | l2 = l2.next 42 | cur = cur.next 43 | if l1: 44 | cur.next = l1 45 | else: 46 | cur.next = l2 47 | return dummy.next 48 | 49 | class Solution: 50 | def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode: 51 | cur = dummy = ListNode(0) 52 | #must use this type because dummy and cur must be the same site at the beginning 53 | #use dummy save the head site of listnode(0) 54 | while l1 and l2: 55 | if l1.val < l2.val: 56 | cur.next = l1 57 | l1 = l1.next 58 | else: 59 | cur.next = l2 60 | l2 = l2.next 61 | 62 | cur = cur.next 63 | cur.next = l1 or l2 64 | 65 | return dummy.next 66 | 67 | -------------------------------------------------------------------------------- /链表/234_回文链表.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jul 22 09:38:15 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def isPalindrome(self, head: ListNode) -> bool: 11 | #转换成array再判断:可能有负值存在 12 | if not head: 13 | return True 14 | cur = head 15 | s = [] 16 | while cur: 17 | s.append(cur.val) 18 | cur = cur.next 19 | if s == s[::-1]: 20 | return True 21 | else: 22 | return False -------------------------------------------------------------------------------- /链表/237_删除链表中的节点.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jun 23 11:28:11 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | 9 | class Solution: 10 | def deleteNode(self, node): 11 | """ 12 | :type node: ListNode 13 | :rtype: void Do not return anything, modify node in-place instead. 14 | """ 15 | node.val = node.next.val 16 | node.next = node.next.next -------------------------------------------------------------------------------- /链表/24_Swap Nodes in Pairs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Feb 15 16:49:03 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #date:0516 9 | class Solution: 10 | def swapPairs(self, head: ListNode) -> ListNode: 11 | dummynode = ListNode(0) 12 | dummynode.next = head 13 | pre = dummynode 14 | cur = head 15 | 16 | ''' 17 | fur = cur.next 18 | while cur and fur: 19 | 这种写法是不对的,我们只更新cur,通过更新cur来间接更新fur,如果直接这么写会十分费力,在循环里会引入判断,即判断更新后的cur是否存在 20 | 因为一旦cur不存在,fur = cur.next就会报错 21 | ''' 22 | while cur and cur.next: 23 | fur = cur.next 24 | pre.next = fur 25 | pre = cur 26 | #cur.next = fur.next 这步非常关键,因为如果后面有单一节点,那么没这步就连不上了 27 | cur.next = fur.next 28 | cur = fur.next 29 | fur.next = pre 30 | return dummynode.next 31 | 32 | 33 | #构建虚假表头+单指针 34 | class Solution: 35 | def swapPairs(self, head: ListNode) -> ListNode: 36 | dummynode = ListNode(0) 37 | pre = dummynode 38 | pre.next = head 39 | while pre.next is not None and pre.next.next is not None: 40 | l1 = pre.next 41 | l2 = pre.next.next 42 | l1.next = l2.next 43 | l2.next = l1 44 | pre.next = l2 45 | pre = l1 46 | return dummynode.next 47 | 48 | -------------------------------------------------------------------------------- /链表/25_K 个一组翻转链表.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat May 16 09:38:08 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | #引入dummynode,然后将tail首先指向dummynode,然后移动tail判断即将翻转的链表个数是否足够 9 | class Solution: 10 | def reverse_linked(self, head, tail): 11 | pre = None 12 | cur = head 13 | #注意,此处与单纯翻转链表不同,因为我们相当于在一个长链表中翻转某一部分的链表, 14 | #因此不能用while cur:来作为终止条件,因为这会找到长链表的最后一个元素 15 | while pre != tail: 16 | temp = cur.next 17 | cur.next = pre 18 | pre = cur 19 | cur = temp 20 | return tail, head 21 | def reverseKGroup(self, head: ListNode, k: int) -> ListNode: 22 | pre = ListNode(0) 23 | pre.next = head 24 | hair = pre 25 | tail = pre 26 | while head: 27 | for i in range(k): 28 | tail = tail.next 29 | if tail is None: 30 | return hair.next 31 | else: 32 | nex = tail.next 33 | head, tail = self.reverse_linked(head, tail) 34 | 35 | pre.next = head 36 | tail.next = nex 37 | head = nex 38 | pre = tail 39 | return hair.next -------------------------------------------------------------------------------- /链表/83_Remove Duplicates from Sorted List.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Sep 1 14:34:16 2019 4 | 5 | @author: leiya 6 | """ 7 | 8 | # Definition for singly-linked list. 9 | # class ListNode: 10 | # def __init__(self, x): 11 | # self.val = x 12 | # self.next = None 13 | 14 | ''' 15 | 0715: cur and cur.next要同时判断,这道题与24可一起思考,即为什么要同时确保cur,cur.next都存在 16 | ''' 17 | class Solution: 18 | def deleteDuplicates(self, head: ListNode) -> ListNode: 19 | cur = head 20 | while cur and cur.next: 21 | if cur.val == cur.next.val: 22 | cur.next = cur.next.next 23 | else: 24 | cur = cur.next 25 | return head 26 | 27 | 28 | class Solution: 29 | def deleteDuplicates(self, head: ListNode) -> ListNode: 30 | cur = head 31 | while cur: 32 | forward = cur.next 33 | while forward: 34 | if cur.val == forward.val: 35 | forward = forward.next 36 | else: 37 | break 38 | cur.next = forward 39 | cur = cur.next 40 | return head -------------------------------------------------------------------------------- /链表/92_Reverse Linked List II.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Feb 13 16:12:32 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | class Solution: 9 | def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode: 10 | 11 | if m == n: 12 | return head 13 | 14 | dummyNode = ListNode(0) 15 | dummyNode.next = head 16 | fixm = dummyNode 17 | 18 | for i in range(m - 1): 19 | fixm = fixm.next 20 | 21 | # reverse the [m, n] nodes 22 | pre = None 23 | cur = fixm.next 24 | for i in range(n - m + 1): 25 | temp = cur.next 26 | cur.next = pre 27 | pre = cur 28 | cur = temp 29 | 30 | fixm.next.next = cur 31 | fixm.next = pre 32 | 33 | return dummyNode.next 34 | 35 | class Solution: 36 | def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode: 37 | dummynode = ListNode(0) 38 | dummynode.next = head 39 | fixm = dummynode 40 | for i in range(m-1): 41 | fixm = fixm.next 42 | pre = None 43 | cur = fixm.next 44 | for i in range(n-m+1): 45 | temp = cur.next 46 | cur.next = pre 47 | pre = cur 48 | cur = temp 49 | fixm.next.next = cur 50 | fixm.next = pre 51 | return dummynode.next -------------------------------------------------------------------------------- /链表/offer06_从尾到头打印链表.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Jun 20 07:38:08 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0714 10 | 用辅助栈颠倒链表 11 | ''' 12 | class Solution: 13 | def reversePrint(self, head: ListNode) -> List[int]: 14 | if not head: 15 | return [] 16 | res = [] 17 | stack = [] 18 | cur = head 19 | while cur: 20 | stack.append(cur.val) 21 | cur = cur.next 22 | while stack: 23 | pop_node = stack.pop() 24 | res.append(pop_node) 25 | return res 26 | 27 | 28 | class Solution: 29 | def reversePrint(self, head: ListNode) -> List[int]: 30 | if not head: 31 | return [] 32 | res = [] 33 | while head: 34 | res.append(head.val) 35 | head = head.next 36 | return res[::-1] -------------------------------------------------------------------------------- /链表/offer18_删除链表的节点.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jun 23 11:29:51 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0714 10 | ''' 11 | class Solution: 12 | def deleteNode(self, head: ListNode, val: int) -> ListNode: 13 | dummynode = ListNode(100) 14 | dummynode.next = head 15 | pre = dummynode 16 | cur = head 17 | while cur.val != val: 18 | pre = cur 19 | cur = cur.next 20 | pre.next = cur.next 21 | return dummynode.next 22 | 23 | class Solution: 24 | def deleteNode(self, head: ListNode, val: int) -> ListNode: 25 | dummynode = ListNode(0) 26 | dummynode.next = head 27 | pre = dummynode 28 | cur = head 29 | while cur: 30 | if cur.val == val: 31 | pre.next = cur.next 32 | #注意返回位置,找到了就返回,否则放到while外面永远结束不了,构成了死循环 33 | return dummynode.next 34 | else: 35 | pre = cur 36 | cur = cur.next -------------------------------------------------------------------------------- /链表/offer22_链表中倒数第k个节点.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Jun 26 15:33:00 2020 4 | 5 | @author: leiya 6 | """ 7 | 8 | ''' 9 | 0714 10 | 间隔K比较好想,问题在于不要找当前node的前一个node,因为这样需要for _ in range(k+1),理论上没问题 11 | 但是当倒数第k个恰好是头节点的时候,fur就变成了none.next,需出现错误 12 | ''' 13 | 14 | class Solution: 15 | def getKthFromEnd(self, head: ListNode, k: int) -> ListNode: 16 | cur = head 17 | fur = head 18 | for _ in range(k): 19 | fur = fur.next 20 | while fur: 21 | cur = cur.next 22 | fur = fur.next 23 | return cur 24 | 25 | 26 | class Solution: 27 | def getKthFromEnd(self, head: ListNode, k: int) -> ListNode: 28 | 29 | #two pointers 相距K 30 | pre = head 31 | cur = head 32 | for _ in range(k): 33 | cur = cur.next 34 | while cur: 35 | pre = pre.next 36 | cur = cur.next 37 | return pre -------------------------------------------------------------------------------- /链表/刷题顺序.md: -------------------------------------------------------------------------------- 1 | 顺序 2 | 3 | 06-18-22-206-92-160/141(循环链表) --------------------------------------------------------------------------------