├── 0.排序 ├── 347_topKFrequent.py ├── bubble_sort.py ├── heapify_sort.py ├── merge_sort.py ├── quick_sort.py └── sort_最小的k个数.py ├── 1.动态规划 ├── 121-maxProfit.py ├── 122-maxProfit.py ├── 1277-countSquares.py ├── 139-wordBreak.py ├── 221_maximalSquare.py ├── 300-lengthOfLIS.py ├── 322-coinChange.py ├── 416-canPartition.py ├── 494-findTargetSumWays.py ├── 5-longestPalindrome.py ├── 53-maxSubArray.py ├── 62-uniquePaths.py ├── 64-minPathSum.py ├── 647-countSubstrings.py ├── 70-climbStairs.py ├── 78-subsets.py └── 983-mincostTickets.py ├── 10.树 ├── 100-isSameTree.py ├── 101_isSymmetric.py ├── 102-levelOrder.py ├── 103-zigzagLevelOrder.py ├── 104_maxDepth.py ├── 105-buildTree.py ├── 106-buildTree.py ├── 107-levelOrderBottom.py ├── 108-sortedArrayToBST.py ├── 109-sortedListToBST.py ├── 110-isBalanced.py ├── 113_pathSum.py ├── 114-flatten.py ├── 144-preorderTraversal.py ├── 145-postorderTraversal.py ├── 199-rightSideView.py ├── 222-countNodes.py ├── 226-invertTree.py ├── 230-kthSmallest.py ├── 437-pathSum.py ├── 450-deleteNode.py ├── 538-convertBST.py ├── 543-diameterOfBinaryTree.py ├── 572-isSubtree.py ├── 617-mergeTrees.py ├── 654-constructMaximumBinaryTree.py ├── 701-insertIntoBST.py ├── 94-inorderTraversal.py ├── 95_generateTrees.py ├── 96-numTrees.py ├── 98-isValidBST.py └── post_pre_in_order_traversal.py ├── 11.图 └── 207-canFinish.py ├── 12.位运算 ├── 136-singleNumber.py └── 461_hammingDistance.py ├── 2.递归+DFS+BFS+回溯 ├── 131_partition.py ├── 17_letterCombinations.py ├── 200-numIsland.py ├── 22-generateParenthesis.py ├── 28-Permutation.py ├── 39-combinationSum.py ├── 4-findMedianSortedArrays.py ├── 416-canPartition.py ├── 46-permute.py ├── 698-canPartitionKSubsets.py ├── 78-subsets.py └── 79-exist.py ├── 3.栈 ├── 155-stack_minist.py ├── 20-valid_brackets.py └── 739-dailyTemperatures.py ├── 4.HashDict ├── 128-longestConsecutive.py ├── 1_twoSum.py ├── 236_publicTreeNode.py └── 560-subarraySum.py ├── 5.链表 ├── 141-hasCycle.py ├── 147-insertionSortList.py ├── 148-sortLIst.py ├── 160-getIntersectionNode.py ├── 19_removeNthFromEnd.py ├── 2-add_two_num.py ├── 21-mergeTwoLists.py ├── 23-mergeKLists.py └── 25-reverseKGroup.py ├── 6.数组 ├── 26-removeDuplicates.py ├── 289-gameOfLife.py ├── 4-findMedianSortedArrays.py ├── 406-reconstructQueue.py ├── 448-findDisappearedNumbers.py ├── 54-spiralOrder.py ├── 56-merge.py └── 75-sortColors.py ├── 7.堆 ├── 215_findKthLargest.py ├── 23-mergeKLists.py ├── 347_topKFrequent.py └── 692-topKFrequent.py ├── 8.二分+分治 ├── 287-findDuplicate.py ├── 300-lengthOfLIS.py ├── 33-search.py ├── 34_searchRange.py └── 53-maxSubArray.py ├── 9.双指针+滑动窗口 ├── 11-maxArea.py ├── 141-hasCycle.py ├── 15-threeSum.py ├── 16-threeSumClosest.py ├── 209_minSubArrayLen.py ├── 239-maxSlidingWindow.py ├── 26-removeDuplicates.py ├── 287-findDuplicate.py ├── 300-lengthOfLIS.py ├── 3_lengthOfLongestSubstring.py ├── 438-findAnagrams.py ├── 5-longestPalindrome.py ├── 581-findUnsortedSubarray.py ├── 647-countSubstrings.py └── 76-minWindow.py └── README.md /0.排序/347_topKFrequent.py: -------------------------------------------------------------------------------- 1 | """347. 前 K 个高频元素 2 | 3 | 给定一个非空的整数数组,返回其中出现频率前 k 高的元素。 4 | 5 | 示例 1: 6 | 输入: nums = [1,1,1,2,2,3], k = 2 7 | 输出: [1,2] 8 | 示例 2: 9 | 10 | 输入: nums = [1], k = 1 11 | 输出: [1] 12 | 说明: 13 | 14 | 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。 15 | 你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。 16 | """ 17 | 18 | 19 | # 排序 20 | class Solution(object): 21 | def topKFrequent(self, nums, k): 22 | """ 23 | :type nums: List[int] 24 | :type k: int 25 | :rtype: List[int] 26 | """ 27 | from collections import Counter # todo Counter 28 | count = Counter(nums) 29 | res = sorted(count.items(), key=lambda x: x[1], reverse=True) # todo items(), key= 30 | print(res) 31 | return [i[0] for i in res[:k]] 32 | 33 | 34 | # 堆 35 | class Solution(object): 36 | def topKFrequent(self, nums, k): 37 | from collections import Counter 38 | import heapq 39 | count = Counter(nums) 40 | 41 | return heapq.nlargest(k, count.keys(), key=count.get) 42 | 43 | 44 | class Solution(object): 45 | def topKFrequent(self, words, k): 46 | import collections 47 | import heapq 48 | count = collections.Counter(words) 49 | heap = [(-freq, word) for word, freq in count.items()] 50 | heapq.heapify(heap) 51 | print(heap) 52 | return [heapq.heappop(heap)[1] for _ in range(k)] 53 | 54 | 55 | nums = [4, 1, -1, 2, -1, 2, 3] 56 | k = 2 57 | a = Solution() 58 | print(a.topKFrequent(nums, k)) 59 | -------------------------------------------------------------------------------- /0.排序/bubble_sort.py: -------------------------------------------------------------------------------- 1 | def bubble_sort(nums): 2 | for i in range(len(nums) - 1): # 这个循环负责设置冒泡排序进行的次数(比如说n个数,则只要进行n-1次冒泡,就可以把这个n个数排序好,对吧) 3 | for j in range(len(nums) - i - 1): 4 | """这里这个j呢就是控制每一次具体的冒泡过程,请你想一想,我们第一次冒泡需要冒几次, 5 | 也就是说需要比较几次,假如有三个数,那只需要两次就可以了,当下一次时,最后一个已经是有序的了, 6 | 所以说少冒泡一次,所以这里j每次都会减去i的值,即不用冒“无用之泡泡”""" 7 | if nums[j] > nums[j + 1]: # todo 这里是j>j+1 8 | nums[j], nums[j + 1] = nums[j + 1], nums[j] 9 | 10 | return nums 11 | -------------------------------------------------------------------------------- /0.排序/heapify_sort.py: -------------------------------------------------------------------------------- 1 | # 堆排序 2 | """ 3 | a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆; 4 | b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端; 5 | c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。 6 | """ 7 | 8 | 9 | def heapify(arr, n, i): 10 | largest = i 11 | l = 2 * i + 1 # left = 2*i + 1 12 | r = 2 * i + 2 # right = 2*i + 2 13 | 14 | if l < n and arr[i] < arr[l]: 15 | largest = l 16 | 17 | if r < n and arr[largest] < arr[r]: 18 | largest = r 19 | 20 | if largest != i: 21 | arr[i], arr[largest] = arr[largest], arr[i] # 交换 22 | heapify(arr, n, largest) 23 | 24 | 25 | def heapSort(arr): 26 | n = len(arr) 27 | 28 | # Build a maxheap. 29 | for i in range(n, -1, -1): 30 | heapify(arr, n, i) 31 | 32 | # 一个个交换元素 33 | for i in range(n - 1, 0, -1): 34 | arr[i], arr[0] = arr[0], arr[i] # 交换 35 | heapify(arr, i, 0) 36 | 37 | 38 | arr = [12, 11, 13, 5, 6, 7] 39 | heapSort(arr) 40 | n = len(arr) 41 | print("排序后") 42 | for i in range(n): 43 | print("%d" % arr[i]), 44 | -------------------------------------------------------------------------------- /0.排序/merge_sort.py: -------------------------------------------------------------------------------- 1 | ## 归并排序 2 | def merge_sort(alist): 3 | if len(alist) <= 1: 4 | return alist 5 | # 二分分解 6 | num = int(len(alist) / 2) 7 | left = merge_sort(alist[:num]) 8 | right = merge_sort(alist[num:]) 9 | # 合并 10 | return merge(left, right) 11 | 12 | 13 | def merge(left, right): 14 | '''合并操作,将两个有序数组left[]和right[]合并成一个大的有序数组''' 15 | # left与right的下标指针 16 | l, r = 0, 0 17 | result = [] 18 | while l < len(left) and r < len(right): 19 | if left[l] < right[r]: 20 | result.append(left[l]) 21 | l += 1 22 | else: 23 | result.append(right[r]) 24 | r += 1 25 | result += left[l:] 26 | result += right[r:] 27 | return result 28 | 29 | 30 | alist = [54, 26, 93, 17, 77, 31, 44, 55, 20] 31 | sorted_alist = merge_sort(alist) 32 | print('merge_sort:', sorted_alist) 33 | -------------------------------------------------------------------------------- /0.排序/quick_sort.py: -------------------------------------------------------------------------------- 1 | # 快速排序函数 2 | def quickSort(arr, low, high): 3 | def partition(arr, low, high): 4 | i = (low - 1) # 最小元素索引 5 | pivot = arr[high] 6 | print(11111, arr, pivot, low, high) 7 | for j in range(low, high): 8 | # 当前元素小于或等于 pivot 9 | if arr[j] <= pivot: 10 | i = i + 1 11 | arr[i], arr[j] = arr[j], arr[i] 12 | print(22222, arr) 13 | arr[i + 1], arr[high] = arr[high], arr[i + 1] 14 | print(22222, arr) 15 | return (i + 1) 16 | 17 | if low < high: 18 | pi = partition(arr, low, high) 19 | print(33333, pi) 20 | quickSort(arr, low, pi - 1) 21 | quickSort(arr, pi + 1, high) 22 | 23 | 24 | arr = [10, 7, 8, 9, 1, 5] 25 | n = len(arr) 26 | quickSort(arr, 0, n - 1) 27 | print("quickSort排序后的数组:", arr) 28 | -------------------------------------------------------------------------------- /0.排序/sort_最小的k个数.py: -------------------------------------------------------------------------------- 1 | """ 2 | 1、k轮冒泡排序:O(KN) 3 | 快排:O(nlogn) 4 | 快排中的Partition思想:O(N):因为我们是要找下标为k的元素,总之可以看作每次调用 partition 遍历的元素数目都是上一次遍历的 1/2,因此时间复杂度是 N + N/2 + N/4 + … + N/N = 2N, 因此时间复杂度是 O(N) 5 | 堆排序:O(nlogN):每次新人与堆顶PK,PK结束之后维护候选人重新为大顶堆,每次是logK。 6 | 数据很大的时候选择堆排序!:堆排序方法将全部排序降为部分排序,这在海量数据中查找topK中是很有用的,当数据是增量式,或者无法全部加载进内存中时,只开辟一小部分空间存储k个数字还是可以实现的。 7 | """ 8 | 9 | # 堆 10 | """ 11 | 最大堆 12 | A:数组 13 | i:父节点的index 14 | size:堆的大小 15 | 16 | 详细:https://www.cnblogs.com/chengxiao/p/6129630.html 17 | 实现:https://zhuanlan.zhihu.com/p/77527032 18 | a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆; 19 | b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端; 20 | c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。 21 | """ 22 | 23 | 24 | def heap_adjust(A, i, size): 25 | left = 2 * i + 1 26 | right = 2 * i + 2 27 | max_index = i 28 | if left < size and A[left] > A[max_index]: 29 | max_index = left 30 | if right < size and A[right] > A[max_index]: 31 | max_index = right 32 | if max_index != i: 33 | A[max_index], A[i] = A[i], A[max_index] 34 | heap_adjust(A, max_index, size) # 以替换的点为父节点,再调整所在的堆 35 | 36 | 37 | def build_heap(A, size): 38 | for i in range(size // 2, -1, -1): 39 | heap_adjust(A, i, size) 40 | 41 | 42 | def heap_sort(A): 43 | size = len(A) 44 | build_heap(A, size) # 初始化堆 45 | for i in range(len(A) - 1, 0, -1): 46 | A[i], A[0] = A[0], A[i] 47 | heap_adjust(A, 0, i) 48 | return A 49 | 50 | 51 | nums = [2, 7, 8, 1, 5, 4, 3, 9, 6] 52 | print(heap_sort(nums)) 53 | 54 | """ 建立最小堆""" 55 | 56 | 57 | def heap_adjust(A, i, size): 58 | left = 2 * i + 1 59 | right = 2 * i + 2 60 | min_index = i 61 | if left < size and A[left] < A[min_index]: 62 | min_index = left 63 | if right < size and A[right] < A[min_index]: 64 | min_index = right 65 | if min_index != i: 66 | temp = A[i] 67 | A[i] = A[min_index] 68 | A[min_index] = temp 69 | heap_adjust(A, min_index, size) 70 | return A 71 | 72 | 73 | def build_heap(A, size): 74 | for i in range(size // 2, -1, -1): 75 | heap_adjust(A, i, size) 76 | return A 77 | 78 | 79 | def heap_sort(A): 80 | size = len(A) 81 | A = build_heap(A, size) 82 | for i in range(len(A) - 1, 0, -1): 83 | A[i], A[0] = A[0], A[i] 84 | A = heap_adjust(A, 0, i) 85 | return A 86 | 87 | 88 | # 这里假设k = 3 取top 3的数字 89 | nums = [2, 7, 8, 1, 5, 4, 3, 9, 6] 90 | # print(heap_sort(nums)) 91 | b = build_heap(nums[:3], 3) 92 | # 从k+1开始,依次比较,然后替换 93 | for i in range(3, len(nums)): 94 | if nums[i] > b[0]: 95 | temp = nums[i] 96 | nums[i] = b[0] 97 | b[0] = temp 98 | b = heap_adjust(b, 0, 3) 99 | print(i, nums[:i + 1], b) 100 | print(b[0]) 101 | -------------------------------------------------------------------------------- /1.动态规划/121-maxProfit.py: -------------------------------------------------------------------------------- 1 | """ 2 | 买卖股票最佳时期: 3 | 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 4 | 如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。 5 | 注意:你不能在买入股票前卖出股票。 6 | 7 | 示例 1: 8 | 输入: [7,1,5,3,6,4] 9 | 输出: 5 10 | 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 11 | 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。 12 | 示例 2: 13 | 输入: [7,6,4,3,1] 14 | 输出: 0 15 | 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 16 | 17 | 题解1: 18 | dp[i] 表示前 i 天的最大利润,因为我们始终要使利润最大化,则: 19 | dp[i] = max(dp[i-1], prices[i]-minprice) 20 | 21 | 题解2: 22 | 如果你学过高等数学,对牛顿莱布尼茨公式有印象的话: 23 | ∫f(x)dx==F(b)−F(a) 24 | 25 | 只不过,在我这里,F() 函数不是连续的,而是离散化的a和b表示数组的下标。但是这不影响我们得出正确的结论。 26 | 总结下:区间和可以转换成求差的问题,求差问题,也可以转换成区间和的问题。 27 | 在上面的公式中,我们把 F()表示的数组称为前缀和。 28 | 最大连续子数组和可以使用动态规划求解, dp[i]表示以 i为结尾的最大连续子数组和,递推公式为: 29 | dp[i] = max(0, dp[i-1]) 30 | """ 31 | from typing import List 32 | 33 | 34 | # 我的 35 | class Solution: 36 | def maxProfit(self, prices: List[int]) -> int: 37 | if len(prices) <= 1: 38 | return 0 39 | 40 | nums = [prices[i] - prices[i - 1] for i in range(1, len(prices))] 41 | dp = [0] * (len(prices) - 1) 42 | for i in range(len(dp)): 43 | if i == 0: 44 | dp[i] = max(0, nums[i]) 45 | else: 46 | dp[i] = max(dp[i - 1] + nums[i], nums[i], 0) 47 | return max(dp) 48 | 49 | 50 | # 别人的 51 | class Solution: 52 | def maxProfit(self, prices: List[int]) -> int: 53 | inf = int(1e9) 54 | minprice = inf 55 | maxprofit = 0 56 | for price in prices: 57 | maxprofit = max(price - minprice, maxprofit) 58 | minprice = min(price, minprice) 59 | return maxprofit 60 | -------------------------------------------------------------------------------- /1.动态规划/122-maxProfit.py: -------------------------------------------------------------------------------- 1 | # 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 2 | # 3 | # 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。 4 | # 5 | # 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 6 | # 7 | # 8 | # 9 | # 示例 1: 10 | # 11 | # 输入: [7,1,5,3,6,4] 12 | # 输出: 7 13 | # 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 14 | #   随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。 15 | # 16 | # 17 | # 示例 2: 18 | # 19 | # 输入: [1,2,3,4,5] 20 | # 输出: 4 21 | # 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 22 | #   注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。 23 | #   因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 24 | # 25 | # 26 | # 示例 3: 27 | # 28 | # 输入: [7,6,4,3,1] 29 | # 输出: 0 30 | # 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 31 | # 32 | # 33 | # 34 | # 提示: 35 | # 36 | # 37 | # 1 <= prices.length <= 3 * 10 ^ 4 38 | # 0 <= prices[i] <= 10 ^ 4 39 | # 40 | # Related Topics 贪心算法 数组 41 | 42 | 43 | # leetcode submit region begin(Prohibit modification and deletion) 44 | # 我的 45 | class Solution: 46 | def maxProfit(self, prices: List[int]) -> int: 47 | if len(prices) <= 1: 48 | return 0 49 | nums = [prices[i] - prices[i - 1] for i in range(1, len(prices))] 50 | dp = [0] * len(nums) 51 | for i in range(len(nums)): 52 | dp[i] = max(0, nums[i]) 53 | return sum(dp) 54 | 55 | 56 | # 别人的 57 | class Solution: 58 | def maxProfit(self, prices): 59 | """ 60 | :type prices: List[int] 61 | :rtype: int 62 | """ 63 | profit = 0 64 | for i in range(1, len(prices)): 65 | if prices[i] > prices[i - 1]: 66 | profit += prices[i] - prices[i - 1] 67 | return profit 68 | 69 | # leetcode submit region end(Prohibit modification and deletion) 70 | -------------------------------------------------------------------------------- /1.动态规划/1277-countSquares.py: -------------------------------------------------------------------------------- 1 | """ 2 | 1277. 统计全为 1 的正方形子矩阵 3 | 4 | 给你一个 m * n 的矩阵,矩阵中的元素不是 0 就是 1,请你统计并返回其中完全由 1 组成的 正方形 子矩阵的个数。 5 | 示例 1: 6 | 7 | 输入:matrix = 8 | [ 9 | [0,1,1,1], 10 | [1,1,1,1], 11 | [0,1,1,1] 12 | ] 13 | 输出:15 14 | 解释: 15 | 边长为 1 的正方形有 10 个。 16 | 边长为 2 的正方形有 4 个。 17 | 边长为 3 的正方形有 1 个。 18 | 正方形的总数 = 10 + 4 + 1 = 15. 19 | 示例 2: 20 | 21 | 输入:matrix = 22 | [ 23 | [1,0,1], 24 | [1,1,0], 25 | [1,1,0] 26 | ] 27 | 输出:7 28 | 解释: 29 | 边长为 1 的正方形有 6 个。 30 | 边长为 2 的正方形有 1 个。 31 | 正方形的总数 = 6 + 1 = 7. 32 | """ 33 | 34 | 35 | class Solution(object): 36 | def countSquares(self, matrix): 37 | """ 38 | :type matrix: List[List[int]] 39 | :rtype: int 40 | """ 41 | import numpy as np 42 | def is_matrix_all_one(nums): 43 | if (nums == 1).all(): 44 | return 1 45 | else: 46 | return 0 47 | # return (np.array(nums)==1).all() 48 | 49 | m, n = len(matrix), len(matrix[0]) 50 | dp = [[0] * n for _ in range(m)] 51 | matrix = np.array(matrix) 52 | for i in range(m): 53 | for j in range(n): 54 | if i == 0 and j == 0: 55 | dp[i][j] = matrix[i][j] 56 | print(i, j, dp) 57 | elif i == 0: 58 | dp[i][j] = dp[i][j - 1] + matrix[i][j] 59 | print(i, j, dp) 60 | elif j == 0: 61 | dp[i][j] = dp[i - 1][j] + matrix[i][j] 62 | print(i, j, dp) 63 | else: 64 | 65 | cur_num = 0 66 | for idx in range(min(i + 1, j + 1)): 67 | cur_num += is_matrix_all_one(matrix[i - idx:i + 1, j - idx:j + 1]) 68 | print(is_matrix_all_one(matrix[i - idx:i + 1, j - idx:j + 1])) 69 | print('--', i, j, idx, matrix[i - idx:i + 1, j - idx:j + 1], i - idx, i + 1, j - idx, j + 1) 70 | 71 | # print(dp) 72 | print(i, j, cur_num, dp[i - 1][j], dp[i][j - 1], dp) 73 | 74 | dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + cur_num 75 | # print(i,j,dp) 76 | 77 | print(dp) 78 | return dp[-1][-1] 79 | 80 | 81 | class Solution(object): 82 | def countSquares(self, matrix): 83 | """ 84 | :type matrix: List[List[int]] 85 | :rtype: int 86 | """ 87 | import numpy as np 88 | 89 | m, n = len(matrix), len(matrix[0]) 90 | dp = [[0] * n for _ in range(m)] 91 | matrix = np.array(matrix) 92 | ans = 0 93 | for i in range(m): 94 | dp[i][0] = matrix[i][0] 95 | ans += dp[i][0] 96 | for j in range(n): 97 | dp[0][j] = matrix[0][j] 98 | ans += dp[0][j] 99 | 100 | if matrix[0][0] == 1: 101 | ans -= 1 102 | for i in range(1, m): 103 | for j in range(1, n): 104 | if matrix[i][j] == 1: 105 | dp[i][j] = min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1 106 | ans += dp[i][j] 107 | 108 | return ans 109 | 110 | 111 | """ 112 | dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1; 113 | """ 114 | 115 | matrix = [[0, 1, 1, 1], 116 | [1, 1, 1, 1], 117 | [0, 1, 1, 1]] 118 | import numpy as np 119 | 120 | s = Solution() 121 | print(np.array(matrix)) 122 | 123 | s.countSquares(matrix) 124 | -------------------------------------------------------------------------------- /1.动态规划/139-wordBreak.py: -------------------------------------------------------------------------------- 1 | # 给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。 2 | # 3 | # 说明: 4 | # 5 | # 6 | # 拆分时可以重复使用字典中的单词。 7 | # 你可以假设字典中没有重复的单词。 8 | # 9 | # 10 | # 示例 1: 11 | # 12 | # 输入: s = "leetcode", wordDict = ["leet", "code"] 13 | # 输出: true 14 | # 解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。 15 | # 16 | # 17 | # 示例 2: 18 | # 19 | # 输入: s = "applepenapple", wordDict = ["apple", "pen"] 20 | # 输出: true 21 | # 解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。 22 | #   注意你可以重复使用字典中的单词。 23 | # 24 | # 25 | # 示例 3: 26 | # 27 | # 输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"] 28 | # 输出: false 29 | # 30 | # Related Topics 动态规划 31 | 32 | 33 | # leetcode submit region begin(Prohibit modification and deletion) 34 | class Solution: 35 | def wordBreak(self, s: str, wordDict: List[str]) -> bool: 36 | while s: 37 | for each in wordDict: 38 | s = s.replace(each, '') 39 | if not s: 40 | return True 41 | 42 | return False 43 | 44 | 45 | class Solution: 46 | def wordBreak(self, s: str, wordDict: List[str]) -> bool: 47 | # 初始化 dp 48 | dp = [False] * (len(s) + 1) 49 | dp[0] = True 50 | 51 | # 遍历字符串 52 | for i in range(1, len(s) + 1): 53 | # j 逐渐划分字符串 54 | for j in range(i, -1, -1): 55 | # 判断前缀部分是否为真以及后面剩余部分是否在单词表中 56 | dp[i] = dp[j] and (s[j:i] in wordDict) 57 | # 如果为真跳出循环,这里已经判定可以拆分,没必要继续 58 | if dp[i]: 59 | break 60 | 61 | return dp[len(s)] 62 | 63 | # leetcode submit region end(Prohibit modification and deletion) 64 | -------------------------------------------------------------------------------- /1.动态规划/221_maximalSquare.py: -------------------------------------------------------------------------------- 1 | """ 2 | 221. 最大正方形 3 | 在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。 4 | 示例: 5 | 6 | 输入: 7 | 1 0 1 0 0 8 | 1 0 1 1 1 9 | 1 1 1 1 1 10 | 1 0 0 1 0 11 | 12 | 输出: 4 13 | """ 14 | 15 | 16 | class Solution(object): 17 | def maximalSquare(self, matrix): 18 | """ 19 | :type matrix: List[List[str]] 20 | :rtype: int 21 | """ 22 | import numpy as np 23 | if len(matrix) == 0: 24 | return 0 25 | m, n = len(matrix), len(matrix[0]) 26 | dp = [[0] * n for _ in range(m)] 27 | matrix = np.array(matrix) 28 | ans_max = 0 29 | for i in range(m): 30 | dp[i][0] = int(matrix[i][0]) 31 | ans_max = max(ans_max, dp[i][0]) 32 | for j in range(n): 33 | dp[0][j] = int(matrix[0][j]) 34 | ans_max = max(ans_max, dp[0][j]) 35 | 36 | for i in range(1, m): 37 | for j in range(1, n): 38 | if matrix[i][j] == '1': 39 | dp[i][j] = min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1 40 | ans_max = max(ans_max, dp[i][j]) 41 | 42 | return ans_max ** 2 43 | 44 | 45 | matrix = [[0, 1, 1, 1], 46 | [1, 1, 1, 1], 47 | [0, 1, 1, 1]] 48 | matrix = [["1", "0", "1", "0", "0"], 49 | ["1", "0", "1", "1", "1"], 50 | ["1", "1", "1", "1", "1"], 51 | ["1", "0", "0", "1", "0"]] 52 | import numpy as np 53 | 54 | s = Solution() 55 | print(np.array(matrix)) 56 | 57 | s.maximalSquare(matrix) 58 | """ 59 | 要找到二维矩阵中,只包含 1 的最大正方形,我们可以用动态规划解决。用 dp[i][j] 表示直到 (i, j) 位置的可以包含最大正方形的边长, 60 | 注意这里的正方形需要包含 (i, j) 位置。 61 | 62 | 在矩形左侧和上边界,如果 matrix[i][j]=='1',有 dp[0][j]==dp[i][0]==1,即能构成的最大正方形的边长为 1。 63 | 64 | 而对于更一般的情况: 65 | 66 | 1、如果 matrix[i][j]=='0',显然 dp[i][j]==0。 67 | 2、如果 matrix[i][j]=='1',状态转移方程为:dp[i][j]=min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1])+1,即 (i, j) 位置的值被其左侧、上侧、左上侧的值所限制。 68 | """ 69 | -------------------------------------------------------------------------------- /1.动态规划/300-lengthOfLIS.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个无序的整数数组,找到其中最长上升子序列的长度。 3 | 4 | 示例: 5 | 6 | 输入: [10,9,2,5,3,7,101,18] 7 | 输出: 4 8 | """ 9 | from typing import List 10 | 11 | 12 | class Solution: 13 | def lengthOfLIS(self, nums: List[int]) -> int: 14 | size = len(nums) 15 | # 特判 16 | if size < 2: 17 | return size 18 | 19 | # 为了防止后序逻辑发生数组索引越界,先把第 1 个数放进去 20 | tail = [nums[0]] 21 | for i in range(1, size): 22 | # 【逻辑 1】比 tail 数组实际有效的末尾的那个元素还大 23 | # 先尝试是否可以接在末尾 24 | if nums[i] > tail[-1]: 25 | tail.append(nums[i]) 26 | continue 27 | 28 | # 使用二分查找法,在有序数组 tail 中 29 | # 找到第 1 个大于等于 nums[i] 的元素,尝试让那个元素更小 30 | left = 0 31 | right = len(tail) - 1 32 | while left < right: 33 | # 选左中位数不是偶然,而是有原因的,原因请见 LeetCode 第 35 题题解 34 | mid = left + (right - left) // 2 35 | # mid = (left + right) >> 1 36 | if tail[mid] < nums[i]: 37 | # 中位数肯定不是要找的数,把它写在分支的前面 38 | left = mid + 1 39 | else: 40 | right = mid 41 | # 走到这里是因为【逻辑 1】的反面,因此一定能找到第 1 个大于等于 nums[i] 的元素,因此无需再单独判断 42 | tail[left] = nums[i] 43 | return len(tail) 44 | 45 | 46 | nums = [10, 9, 2, 5, 3, 7, 101, 18] 47 | s = Solution() 48 | s.lengthOfLIS(nums) 49 | 50 | 51 | class Solution: 52 | def lengthOfLIS(self, nums: List[int]) -> int: 53 | # 动态规划 dp 代表以nums[i]结尾的 54 | # 递增子序列的长度 55 | if not nums: 56 | return 0 57 | dp = [1] 58 | for i in range(len(nums)): 59 | dp.append(1) 60 | for j in range(i): 61 | if nums[i] > nums[j]: 62 | dp[i] = max(dp[i], dp[j] + 1) 63 | return max(dp) 64 | -------------------------------------------------------------------------------- /1.动态规划/322-coinChange.py: -------------------------------------------------------------------------------- 1 | """ 2 | 322. 零钱兑换 3 | 4 | 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。 5 | 6 | 示例 1: 7 | 8 | 输入: coins = [1, 2, 5], amount = 11 9 | 输出: 3 10 | 解释: 11 = 5 + 5 + 1 11 | 示例 2: 12 | 13 | w v 14 | 1 1 15 | 2 1 16 | 5 1 17 | 18 | 输入: coins = [2], amount = 3 19 | 输出: -1 20 | 说明: 21 | 你可以认为每种硬币的数量是无限的。 22 | 23 | 总金额就是背包容量,硬币就是物品,可以表示为,物品(vi--coins[i], wi--1(价值就是数量,每个硬币的数量就是1)) 24 | 25 | 定义状态:dp[i][j]:当考虑放入第i枚硬币时,所凑成金额j时的最小硬币数。仿照上面,状态转移方程为:dp[i][j]=Min(dp[i-1][j],dp[i][j-conins[i]]+1) 26 | 27 | 现在需要思考初始条件了,求的是最小值,那么我们在最开始时,需要将值设为最大,同时dp[0] 28 | """ 29 | 30 | 31 | class Solution: 32 | def coninChange(self, coins, amount): 33 | v, w, c = coins, [1] * len(coins), amount 34 | dp = [0] + [c + 1] * c 35 | for i in range(0, len(v)): 36 | for j in range(0, c + 1): 37 | if j - v[i] >= 0: 38 | dp[j] = min(dp[j], dp[j - v[i]] + 1) 39 | if dp[c] == c + 1: 40 | return -1 41 | 42 | return dp[-1] 43 | 44 | 45 | coins = [1, 2, 5] 46 | amount = 11 47 | coins = [2] 48 | amount = 2 49 | coins = [3] 50 | amount = 2 51 | amount =2 52 | s = Solution() 53 | res = s.coninChange(coins, amount) 54 | print(res) 55 | -------------------------------------------------------------------------------- /1.动态规划/416-canPartition.py: -------------------------------------------------------------------------------- 1 | # 给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 2 | # 3 | # 注意: 4 | # 5 | # 6 | # 每个数组中的元素不会超过 100 7 | # 数组的大小不会超过 200 8 | # 9 | # 10 | # 示例 1: 11 | # 12 | # 输入: [1, 5, 11, 5] 13 | # 14 | # 输出: true 15 | # 16 | # 解释: 数组可以分割成 [1, 5, 5] 和 [11]. 17 | # 18 | # 19 | # 20 | # 21 | # 示例 2: 22 | # 23 | # 输入: [1, 2, 3, 5] 24 | # 25 | # 输出: false 26 | # 27 | # 解释: 数组不能分割成两个元素和相等的子集. 28 | # 29 | # 30 | # 31 | # Related Topics 动态规划 32 | from typing import List 33 | 34 | 35 | class Solution: 36 | def canPartition(self, nums: List[int]) -> bool: 37 | if sum(nums) % 2 == 1 or len(nums) < 2: 38 | return False 39 | 40 | half = sum(nums) // 2 41 | nums.sort(reverse=True) 42 | if nums[0] > half: 43 | return False 44 | 45 | @lru_cache(None) # 不加就超时了呢 46 | def dfs(target, i): 47 | if target == nums[i]: 48 | return True 49 | 50 | if target > nums[i]: 51 | for j in range(i + 1, len(nums)): 52 | if dfs(target - nums[i], j): 53 | return True 54 | return False 55 | 56 | return dfs(half, 0) 57 | 58 | 59 | from typing import List 60 | 61 | 62 | # leetcode submit region begin(Prohibit modification and deletion) 63 | # from collections import lru_cache 64 | class Solution: 65 | def canPartition(self, nums: List[int]) -> bool: 66 | nums.sort(reverse=True) 67 | sum_ = sum(nums) 68 | if sum_ % 2 != 0 or len(nums) < 2: 69 | return False 70 | 71 | per_sum = sum_ / 2 72 | if nums[0] > per_sum: 73 | return False 74 | n = len(nums) 75 | 76 | # TODO以下这种写法最大的问题在于会有重复计算 77 | # @lru_cache(None) # 不加就超时了呢 78 | # def dfs(cursum, loc): 79 | # # print(cursum, loc,per_sum, per_sum==cursum) 80 | # if cursum == per_sum: 81 | # return True 82 | # if loc>n: 83 | # return False 84 | # for i in range(loc, n): 85 | # # cursum=cursum + nums[i] 86 | # if cursum + nums[i]>per_sum: 87 | # if dfs(cursum, loc + 1): 88 | # return True 89 | # else: 90 | # if dfs(cursum+nums[i], loc+1): 91 | # return True 92 | # # if dfs(cursum, loc+1): 93 | # # return True 94 | # return False 95 | def dfs(cursum, loc): 96 | if cursum + nums[loc] == per_sum: 97 | return True 98 | if loc >= n: 99 | return False 100 | 101 | if cursum + nums[loc] < per_sum: 102 | for i in range(loc + 1, n): 103 | if dfs(cursum + nums[i], i): 104 | return True 105 | return False 106 | 107 | return dfs(cursum=0, loc=0) 108 | 109 | 110 | # nums=[1, 5, 11, 5] 111 | # nums=[2,2,1,1] 112 | nums = [2, 2, 3, 5] 113 | s = Solution() 114 | print(s.canPartition(nums)) 115 | 116 | # leetcode submit region end(Prohibit modification and deletion) 117 | -------------------------------------------------------------------------------- /1.动态规划/494-findTargetSumWays.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。 3 | 对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。 4 | 5 | 返回可以使最终数组和为目标数 S 的所有添加符号的方法数。 6 | 7 | 示例 1: 8 | 9 | 输入: nums: [1, 1, 1, 1, 1], S: 3 10 | 输出: 5 11 | 解释: 12 | 13 | -1+1+1+1+1 = 3 14 | +1-1+1+1+1 = 3 15 | +1+1-1+1+1 = 3 16 | +1+1+1-1+1 = 3 17 | +1+1+1+1-1 = 3 18 | 19 | 一共有5种方法让最终目标和为3。 20 | 21 | 题解: 22 | 转移方程: dp[i][j]=dp[i-1][j-nums[i]]+dp[i-1][j+nums[i]] 23 | 空间优化: dp[j]=dp[j-nums[i]]+dp[j+nums[i]] 24 | 25 | #todo 待自己完善 26 | """ 27 | 28 | 29 | class Solution(object): 30 | def findTargetSumWays(self, nums, S): 31 | """ 32 | :type nums: List[int] 33 | :type S: int 34 | :rtype: int 35 | """ 36 | # 如果 nums 的总和都小于 S,肯定是不行的 37 | # 如果求出的容量 V 是一个浮点数也不符合要求 38 | if (sum(nums) + S) % 2 != 0 or sum(nums) < S: 39 | return 0 40 | # 求背包容量 41 | V = (sum(nums) + S) // 2 42 | # dp[i] 表示此时背包容量为 i 43 | dp = [0] * (V + 1) 44 | # 为什么 dp[0] 取 1 ? 因为当背包容量为 0 时, 45 | # 显然我们不需要取任何数到背包即满足条件,所以可行解为 1 46 | dp[0] = 1 47 | for num in nums: 48 | i = V 49 | while i >= num: 50 | # dp[i] 的解法数为自身加上dp[i-num]的解法数 51 | # 这里为什么会如此,可以参考斐波那契数列 52 | # 这里我可以不放 num 进背包,解法数为 dp[i] 53 | # 将 num 放进背包,解法数为 dp[i-num] 54 | # 所以此时的总解法数为 二者和 55 | dp[i] = dp[i] + dp[i - num] 56 | i -= 1 # 背包容量减 1 57 | return dp[V] 58 | -------------------------------------------------------------------------------- /1.动态规划/5-longestPalindrome.py: -------------------------------------------------------------------------------- 1 | """给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。 2 | 3 | 输入: "babad" 4 | 输出: "bab" 5 | 注意: "aba" 也是一个有效答案。 6 | 7 | 输入: "cbbd" 8 | 输出: "bb" 9 | """ 10 | 11 | """方法二: 12 | """ 13 | 14 | 15 | class Solution: 16 | # def longestPalindrome(self, s: str) -> str: 17 | # 中心扩散法Spread From Center 18 | def spread(self, s, left, right): 19 | """ 20 | left = right 的时候,此时回文中心是一条线,回文串的长度是奇数 21 | right = left + 1 的时候,此时回文中心是任意一个字符,回文串的长度是偶数 22 | """ 23 | while left >= 0 and right < len(s) and s[left] == s[right]: 24 | left -= 1 25 | right += 1 26 | return s[left + 1:right] 27 | 28 | # 动态规划法-中心扩散法Spread From Center 29 | def spread_from_center(self, s: str) -> str: 30 | if s == s[::-1]: 31 | return s 32 | res = s[:1] 33 | 34 | for i in range(len(s)): 35 | palindrome_odd = self.spread(s=s, left=i, right=i) 36 | palindrome_even = self.spread(s=s, left=i, right=i + 1) 37 | # 当前找到的最长回文子串 38 | res = max(palindrome_odd, palindrome_even, res, key=len) 39 | print(i, res, palindrome_odd, palindrome_even) 40 | return res 41 | 42 | 43 | ## 动态规划: 44 | class Solution: 45 | def longestPalindrome(self, s: str) -> str: 46 | dp = [[False] * len(s) for i in range(len(s))] 47 | if not s or len(s) == 1: 48 | return s 49 | max_len = 1 50 | result = s[:1] 51 | 52 | for j in range(len(s)): 53 | for i in range(len(s)): 54 | if i <= j: 55 | if j == i: 56 | dp[i][j] = True 57 | elif j == i + 1: 58 | dp[i][j] = (s[i] == s[j]) 59 | else: 60 | dp[i][j] = dp[i + 1][j - 1] and s[i] == s[j] 61 | 62 | if dp[i][j]: 63 | if j - i + 1 > max_len: 64 | max_len = j - i + 1 65 | result = s[i:j + 1] 66 | return result 67 | 68 | 69 | if __name__ == "__main__": 70 | s = Solution() 71 | res = s.spread_from_center(s='babad') 72 | print(res) 73 | # print(longestPalindrome(s='babad')) 74 | print(s.spread_from_center(s='acccc')) 75 | 76 | # print('res',longestPalindrome(s='babadddede')) 77 | # print('res',longestPalindrome(s='cbbd')) 78 | -------------------------------------------------------------------------------- /1.动态规划/53-maxSubArray.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 3 | 4 | 示例: 5 | 6 | 输入: [-2,1,-3,4,-1,2,1,-5,4], 7 | 输出: 6 8 | 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 9 | 进阶: 10 | 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 11 | 12 | 13 | 思路: 14 | 设 f[x] 为以 a[x] 终止且包含 a[x] 的最大序列的和,有: 15 | f[1] = a[1]; 16 | f[x+1] = max(f[x] ,f[x] + a[x+1]) 17 | 那么最大子序列的和就是 f[1] .. f[n] 中最大的一个 18 | 19 | 首先对数组进行遍历,当前最大连续子序列和为sum,结果为results 20 | 如果sum > 0,则说明sum对结果有增益效果,则sum保留并加上当前遍历数字 21 | 如果sum <= 0,则说明sum对结果无增益效果,需要舍弃,则sum直接更新为当前遍历数字 22 | 每次比较sum 和 results的大小,将最大值置为results,遍历结束后返回结果results 23 | 时间复杂度为O(n) 24 | """ 25 | 26 | 27 | class Solution: 28 | def maxSubArray(self, nums: 'List[int]') -> 'int': 29 | n = len(nums) 30 | max_sum = nums[0] 31 | dp = [nums[0]] + [0] * (n - 1) 32 | for i in range(1, n): 33 | # 先求当前位置最大和, 34 | if dp[i - 1] < 0: 35 | dp[i] = nums[i] 36 | else: 37 | dp[i] = dp[i - 1] + nums[i] 38 | 39 | # 再求全局最大和 40 | max_sum = max(dp[i], max_sum) 41 | return max_sum 42 | 43 | 44 | s = Solution() 45 | print(s.maxSubArray([-2, 1, -3, 4, -1, 2, 1, -5, 4])) 46 | -------------------------------------------------------------------------------- /1.动态规划/62-uniquePaths.py: -------------------------------------------------------------------------------- 1 | """ 2 | 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 3 | 4 | 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 5 | 6 | 问总共有多少条不同的路径? 7 | 8 | 解析: 9 | 动态规划问题: 10 | (1)位于第一行第一列的时候数据为1,(2)其他情况的动态转移方程为:dp[i][j]=dp[i-1][j]+dp[i][j-1] 11 | """ 12 | 13 | 14 | class Solution: 15 | "时间复杂度:O(m*n)O(m∗n); 空间复杂度:O(m * n)O(m∗n)" 16 | 17 | def uniquePaths(self, m: int, n: int) -> int: 18 | "因为要根据第一行第一列推后面的,所以需要先设定第一行第一列的数值" 19 | dp = [[1] * n] + [[1] + [0] * (n - 1) for _ in range(m - 1)] 20 | for i in range(1, m): 21 | for j in range(1, n): 22 | dp[i][j] = dp[i - 1][j] + dp[i][j - 1] 23 | return dp[-1][-1] 24 | 25 | 26 | class Solution: 27 | "优化1:空间复杂度 O(2n)" 28 | 29 | def uniquePaths(self, m: int, n: int) -> int: 30 | pre = [1] * n 31 | cur = [1] * n 32 | for i in range(1, m): 33 | for j in range(1, n): 34 | cur[j] = pre[j] + cur[j - 1] 35 | pre = cur[:] 36 | return pre[-1] 37 | 38 | 39 | class Solution: 40 | "优化2:空间复杂度 O(n)" 41 | 42 | def uniquePaths(self, m: int, n: int) -> int: 43 | cur = [1] * n 44 | for i in range(1, m): 45 | for j in range(1, n): 46 | cur[j] += cur[j - 1] 47 | return cur[-1] 48 | 49 | 50 | s = Solution() 51 | res = s.uniquePaths(3, 4) 52 | print(res) 53 | -------------------------------------------------------------------------------- /1.动态规划/64-minPathSum.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 3 | 4 | 说明:每次只能向下或者向右移动一步。 5 | 6 | 示例: 7 | 8 | 输入: 9 | [ 10 |   [1,3,1], 11 | [1,5,1], 12 | [4,2,1] 13 | ] 14 | 输出: 7 15 | 解释: 因为路径 1→3→1→1→1 的总和最小。 16 | 17 | 来源:力扣(LeetCode) 18 | 链接:https://leetcode-cn.com/problems/minimum-path-sum 19 | 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 20 | 21 | 22 | 题解:动态规划 23 | """ 24 | from typing import List 25 | 26 | 27 | class Solution: 28 | def minPathSum(self, grid: List[List[int]]) -> int: 29 | if not grid or not grid[0]: 30 | return 0 31 | 32 | rows, columns = len(grid), len(grid[0]) 33 | dp = [[0] * columns for _ in range(rows)] 34 | dp[0][0] = grid[0][0] 35 | for i in range(1, rows): 36 | dp[i][0] = dp[i - 1][0] + grid[i][0] 37 | for j in range(1, columns): 38 | dp[0][j] = dp[0][j - 1] + grid[0][j] 39 | for i in range(1, rows): 40 | for j in range(1, columns): 41 | dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j] 42 | 43 | return dp[rows - 1][columns - 1] 44 | -------------------------------------------------------------------------------- /1.动态规划/647-countSubstrings.py: -------------------------------------------------------------------------------- 1 | # 给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。 2 | # 3 | # 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。 4 | # 5 | # 6 | # 7 | # 示例 1: 8 | # 9 | # 输入:"abc" 10 | # 输出:3 11 | # 解释:三个回文子串: "a", "b", "c" 12 | # 13 | # 14 | # 示例 2: 15 | # 16 | # 输入:"aaa" 17 | # 输出:6 18 | # 解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa" 19 | # 20 | # 21 | # 22 | # 提示: 23 | # 24 | # 25 | # 输入的字符串长度不会超过 1000 。 26 | # 27 | # Related Topics 字符串 动态规划 28 | """ 29 | 动态规划的思想是,我们先确定所有的回文,即 string[start:end]是回文. 当我们要确定string[i:j] 是不是回文的时候,要确定: 30 | 31 | string[i] 等于 string[j]吗? 32 | string[i+1:j-1]是回文吗? 33 | 单个字符是回文;两个连续字符如果相等是回文;如果有3个以上的字符,需要两头相等并且去掉首尾之后依然是回文。 34 | """ 35 | 36 | 37 | class Solution(object): 38 | def countSubstrings(self, s): 39 | """ 40 | :type s: str 41 | :rtype: int 42 | """ 43 | n = len(s) 44 | count = 0 45 | dp = [[0] * n for _ in range(n)] 46 | for i in range(n): 47 | for j in range(i): 48 | dp[j][i] = (s[j] == s[i]) & ((i - j < 2) | dp[j + 1][i - 1]) 49 | if dp[j][i]: 50 | count += 1 51 | dp[i][i] = 1 52 | count += 1 53 | return count 54 | 55 | 56 | class Solution(object): 57 | def countSubstrings(self, s): 58 | """ 59 | :type s: str 60 | :rtype: int 61 | """ 62 | count = 0 63 | for i in range(len(s)): 64 | count += 1 65 | # 回文长度是奇数的情况 66 | left = i - 1 67 | right = i + 1 68 | while left >= 0 and right < len(s) and s[left] == s[right]: 69 | count += 1 70 | left -= 1 71 | right += 1 72 | # 回文长度是偶数的情况 73 | left = i 74 | right = i + 1 75 | while left >= 0 and right < len(s) and s[left] == s[right]: 76 | count += 1 77 | left -= 1 78 | right += 1 79 | return count 80 | 81 | # leetcode submit region end(Prohibit modification and deletion) 82 | -------------------------------------------------------------------------------- /1.动态规划/70-climbStairs.py: -------------------------------------------------------------------------------- 1 | """ 2 | 爬楼梯: 3 | 4 | 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 5 | 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 6 | 注意:给定 n 是一个正整数。 7 | 示例 1: 8 | 9 | 输入: 2 10 | 输出: 2 11 | 解释: 有两种方法可以爬到楼顶。 12 | 1. 1 阶 + 1 阶 13 | 2. 2 阶 14 | 示例 2: 15 | 16 | 输入: 3 17 | 输出: 3 18 | 解释: 有三种方法可以爬到楼顶。 19 | 1. 1 阶 + 1 阶 + 1 阶 20 | 2. 1 阶 + 2 阶 21 | 3. 2 阶 + 1 阶 22 | 23 | 24 | 题解: 25 | 第 i 阶可以由以下两种方法得到: 26 | 在第 (i-1)阶后向上爬一阶。 27 | 在第 (i-2) 阶后向上爬 2 阶。 28 | 所以到达第 ii 阶的方法总数就是到第 (i-1)(i−1) 阶和第 (i-2)(i−2) 阶的方法数之和。 29 | 30 | """ 31 | 32 | 33 | class Solution: 34 | def climbStairs(self, n: int) -> int: 35 | if n == 1: 36 | return 1 37 | if n == 2: 38 | return 2 39 | dp = [1, 2] + [0] * (n - 2) 40 | for i in range(2, n): 41 | dp[i] = dp[i - 1] + dp[i - 2] 42 | return dp[-1] 43 | 44 | 45 | s = Solution() 46 | print(s.climbStairs(4)) 47 | -------------------------------------------------------------------------------- /1.动态规划/78-subsets.py: -------------------------------------------------------------------------------- 1 | # 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。 2 | # 3 | # 说明:解集不能包含重复的子集。 4 | # 5 | # 示例: 6 | # 7 | # 输入: nums = [1,2,3] 8 | # 输出: 9 | # [ 10 | # [3], 11 | #   [1], 12 | #   [2], 13 | #   [1,2,3], 14 | #   [1,3], 15 | #   [2,3], 16 | #   [1,2], 17 | #   [] 18 | # ] 19 | # Related Topics 位运算 数组 回溯算法 20 | 21 | 22 | """ 23 | 方法二:递归 24 | f([1,2]) = [[], [1,2], [2], [1]]; 25 | f([1,2,3]) = [[], [1], [2], [1,2], [3], [1,3], [2,3], [1,2,3]] 26 | f([1,2,3])是在f([1,2])的元素基础上,对每个元素多加一个3 27 | 28 | """ 29 | 30 | 31 | class Solution(object): 32 | def subsets(self, nums): 33 | if nums == []: 34 | return [[]] 35 | return self.subsets(nums[:-1]) + [i + [nums[-1]] for i in self.subsets(nums[:-1])] 36 | 37 | 38 | """动态规划 39 | 跟上面递归的想法类似,就是找出当前的结果跟前一个结果的关系: 40 | dp[i] = dp[i-1] + [each+[nums[i]] for each in dp[i-1]] 41 | """ 42 | 43 | 44 | class Solution(object): 45 | def subsets(self, nums): 46 | dp = [[]] 47 | for i in range(len(nums)): 48 | dp = dp + [each + [nums[i]] for each in dp] 49 | return dp 50 | 51 | 52 | s = Solution() 53 | s.subsets([1, 2, 3]) 54 | -------------------------------------------------------------------------------- /1.动态规划/983-mincostTickets.py: -------------------------------------------------------------------------------- 1 | """ 2 | 983. 3 | 在一个火车旅行很受欢迎的国度,你提前一年计划了一些火车旅行。在接下来的一年里,你要旅行的日子将以一个名为 days 的数组给出。每一项是一个从 1 到 365 的整数。 4 | 5 | 火车票有三种不同的销售方式: 6 | 7 | 一张为期一天的通行证售价为 costs[0] 美元; 8 | 一张为期七天的通行证售价为 costs[1] 美元; 9 | 一张为期三十天的通行证售价为 costs[2] 美元。 10 | 通行证允许数天无限制的旅行。 例如,如果我们在第 2 天获得一张为期 7 天的通行证,那么我们可以连着旅行 7 天:第 2 天、第 3 天、第 4 天、第 5 天、第 6 天、第 7 天和第 8 天。 11 | 12 | 返回你想要完成在给定的列表 days 中列出的每一天的旅行所需要的最低消费。 13 | 14 | 示例 1: 15 | 16 | 输入:days = [1,4,6,7,8,20], costs = [2,7,15] 17 | 输出:11 18 | 解释: 19 | 例如,这里有一种购买通行证的方法,可以让你完成你的旅行计划: 20 | 在第 1 天,你花了 costs[0] = $2 买了一张为期 1 天的通行证,它将在第 1 天生效。 21 | 在第 3 天,你花了 costs[1] = $7 买了一张为期 7 天的通行证,它将在第 3, 4, ..., 9 天生效。 22 | 在第 20 天,你花了 costs[0] = $2 买了一张为期 1 天的通行证,它将在第 20 天生效。 23 | 你总共花了 $11,并完成了你计划的每一天旅行。 24 | 25 | 示例 2: 26 | 27 | 输入:days = [1,2,3,4,5,6,7,8,9,10,30,31], costs = [2,7,15] 28 | 输出:17 29 | 解释: 30 | 例如,这里有一种购买通行证的方法,可以让你完成你的旅行计划: 31 | 在第 1 天,你花了 costs[2] = $15 买了一张为期 30 天的通行证,它将在第 1, 2, ..., 30 天生效。 32 | 在第 31 天,你花了 costs[0] = $2 买了一张为期 1 天的通行证,它将在第 31 天生效。 33 | 你总共花了 $17,并完成了你计划的每一天旅行。 34 | 35 | """ 36 | 37 | 38 | # 以下我的代码,问题贼多 39 | class Solution: 40 | def mincostTickets(self, days, costs): 41 | dp = [min(costs)] + [0 for i in range(1, len(days))] 42 | days_delta = [1] + [days[i + 1] - days[i] for i in range(len(days) - 1)] 43 | for i in range(1, len(days)): 44 | cost1 = dp[i - 1] + costs[0] 45 | 46 | n_2, idx_2 = 0, i 47 | for j in range(i, -1, -1): 48 | if n_2 > 7 or n_2 + days_delta[j] > 7: 49 | break 50 | else: 51 | n_2 += days_delta[j] 52 | idx_2 = j 53 | if idx_2 > 0: 54 | cost2 = dp[idx_2 - 1] + costs[1] 55 | else: 56 | cost2 = costs[1] 57 | 58 | n_3, idx_3 = 0, i 59 | for j in range(i, -1, -1): 60 | if n_3 > 30 or n_3 + days_delta[j] > 30: 61 | break 62 | else: 63 | n_3 += days_delta[j] 64 | idx_3 = j 65 | if idx_3 > 0: 66 | cost3 = dp[idx_3 - 1] + costs[2] 67 | else: 68 | cost3 = costs[2] 69 | 70 | dp[i] = min(cost1, cost2, cost3) 71 | return dp[-1] 72 | 73 | 74 | class Solution(object): 75 | def mincostTickets(self, days, costs): 76 | """ 77 | :type days: List[int] 78 | :type costs: List[int] 79 | :rtype: int 80 | """ 81 | dp = [float("inf")] * 366 82 | for day in days: 83 | dp[day] = 0 84 | dp[0] = 0 85 | for i in range(1, 366): 86 | if dp[i] == float("inf"): 87 | dp[i] = dp[i - 1] 88 | else: 89 | cur = dp[i - 1] + costs[0] 90 | cur = min(dp[max(0, i - 7)] + costs[1], cur) 91 | cur = min(dp[max(0, i - 30)] + costs[2], cur) 92 | dp[i] = cur 93 | return dp[days[-1]] 94 | 95 | 96 | days = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 30, 31] 97 | costs = [2, 7, 15] 98 | days = [1, 4, 6, 7, 8, 20] 99 | costs = [2, 7, 15] 100 | days = [1, 3, 7] 101 | costs = [1, 4, 20] 102 | days = [1, 4, 6, 7, 8, 20] 103 | costs = [7, 2, 15] 104 | days = [1, 5, 8, 9, 10, 12, 13, 16, 17, 18, 19, 20, 23, 24, 29] 105 | costs = [3, 12, 54] 106 | s = Solution() 107 | res = s.mincostTickets(days, costs) 108 | print(res) 109 | -------------------------------------------------------------------------------- /10.树/100-isSameTree.py: -------------------------------------------------------------------------------- 1 | """ 2 | 判断是否是同一颗二叉树 3 | 4 | 给定两个二叉树,编写一个函数来检验它们是否相同。 5 | 6 | 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。 7 | 8 | 示例 1: 9 | 10 | 输入: 1 1 11 | / \ / \ 12 | 2 3 2 3 13 | 14 | [1,2,3], [1,2,3] 15 | 16 | 输出: true 17 | 示例 2: 18 | 19 | 输入: 1 1 20 | / \ 21 | 2 2 22 | 23 | [1,2], [1,null,2] 24 | 25 | 输出: false 26 | 示例 3: 27 | 28 | 输入: 1 1 29 | / \ / \ 30 | 2 1 1 2 31 | 32 | [1,2,1], [1,1,2] 33 | 34 | 输出: false 35 | 36 | """ 37 | 38 | 39 | class TreeNode: 40 | def __init__(self, x): 41 | self.val = x 42 | self.left = None 43 | self.right = None 44 | 45 | 46 | # 我的解法 47 | class Solution: 48 | def isSameTree(self, p: TreeNode, q: TreeNode) -> bool: 49 | if not p and not q: 50 | return True 51 | if not (p and q): 52 | return False 53 | if p.val != q.val: 54 | return False 55 | left = self.isSameTree(p.left, q.left) 56 | right = self.isSameTree(p.right, q.right) 57 | if left and right: 58 | return True 59 | else: 60 | return False 61 | 62 | 63 | # 别人的解法 64 | class Solution: 65 | def isSameTree(self, p, q): 66 | """ 67 | :type p: TreeNode 68 | :type q: TreeNode 69 | :rtype: bool 70 | """ 71 | # p and q are both None 72 | if not p and not q: 73 | return True 74 | # one of p and q is None 75 | if not q or not p: 76 | return False 77 | if p.val != q.val: 78 | return False 79 | return self.isSameTree(p.right, q.right) and \ 80 | self.isSameTree(p.left, q.left) 81 | 82 | 83 | 84 | A, B, C, D, E, F, G = [TreeNode(x) for x in '4271369'] 85 | A.left, A.right = B, C 86 | B.left, B.right = D, E 87 | C.left, C.right = F, G 88 | root = A 89 | 90 | s = Solution() 91 | res = s.isSameTree(A, A) 92 | res = s.isSameTree(A, B) 93 | 94 | print(res) 95 | -------------------------------------------------------------------------------- /10.树/101_isSymmetric.py: -------------------------------------------------------------------------------- 1 | """堆成二叉树 2 | 给定一个二叉树,检查它是否是镜像对称的。 3 | 4 | 例如,二叉树 [1,2,2,3,4,4,3] 是对称的。 5 | 6 | 1 7 | / \ 8 | 2 2 9 | / \ / \ 10 | 3 4 4 3 11 | 但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的: 12 | 13 | 1 14 | / \ 15 | 2 2 16 | \ \ 17 | 3 3 18 | 说明: 19 | 20 | 如果你可以运用递归和迭代两种方法解决这个问题,会很加分。 21 | 22 | """ 23 | 24 | 25 | def a(root): 26 | if not root: 27 | return True 28 | 29 | def dfs(left, right): 30 | if not (left or right): 31 | return True 32 | if not (left and right): 33 | return False 34 | # if root.left or root.right: 35 | if left.val != right.val: 36 | return False 37 | return 38 | 39 | 40 | # Definition for a binary tree node. 41 | class TreeNode: 42 | def __init__(self, x): 43 | self.val = x 44 | self.left = None 45 | self.right = None 46 | 47 | 48 | class Solution(object): 49 | def isSymmetric(self, root): 50 | """ 51 | :type root: TreeNode 52 | :rtype: bool 53 | """ 54 | if not root: 55 | return True 56 | 57 | def dfs(left, right): 58 | # 递归的终止条件是两个节点都为空 59 | # 或者两个节点中有一个为空 60 | # 或者两个节点的值不相等 61 | if not (left or right): 62 | return True # todo 这是两个都为none 63 | if not (left and right): 64 | return False # todo 这是其中一个为none 65 | if left.val != right.val: 66 | return False 67 | return dfs(left.left, right.right) and dfs(left.right, right.left) 68 | 69 | # 用递归函数,比较左节点,右节点 70 | return dfs(root.left, root.right) 71 | -------------------------------------------------------------------------------- /10.树/102-levelOrder.py: -------------------------------------------------------------------------------- 1 | """ 2 | 二叉树层序遍历 3 | """ 4 | 5 | 6 | class Solution: 7 | def levelOrder(self, root): 8 | """ 9 | :type root: TreeNode 10 | :rtype: List[List[int]] 11 | """ 12 | levels = [] 13 | if not root: 14 | return levels 15 | 16 | def helper(node, level): 17 | # start the current level 18 | if len(levels) == level: 19 | levels.append([]) 20 | 21 | # append the current node value 22 | levels[level].append(node.val) 23 | 24 | # process child nodes for the next level 25 | if node.left: 26 | helper(node.left, level + 1) 27 | if node.right: 28 | helper(node.right, level + 1) 29 | 30 | helper(root, 0) 31 | return levels 32 | -------------------------------------------------------------------------------- /10.树/103-zigzagLevelOrder.py: -------------------------------------------------------------------------------- 1 | """ 2 | 103. 二叉树的锯齿形层次遍历 3 | 难度中等 4 | 给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。 5 | 6 | 例如: 7 | 给定二叉树 [3,9,20,null,null,15,7], 8 | 9 | 3 10 | / \ 11 | 9 20 12 | / \ 13 | 15 7 14 | 返回锯齿形层次遍历如下: 15 | 16 | [ 17 | [3], 18 | [20,9], 19 | [15,7] 20 | ] 21 | 22 | """ 23 | 24 | 25 | # leetcode submit region begin(Prohibit modification and deletion) 26 | # Definition for a binary tree node. 27 | class TreeNode: 28 | def __init__(self, x): 29 | self.val = x 30 | self.left = None 31 | self.right = None 32 | 33 | 34 | class Solution: 35 | def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]: 36 | res = [] 37 | if not root: 38 | return res 39 | 40 | def helper(root, level): 41 | if level == len(res): 42 | res.append([]) 43 | 44 | res[level].append(root.val) 45 | 46 | if root.left: 47 | helper(root.left, level + 1) 48 | if root.right: 49 | helper(root.right, level + 1) 50 | 51 | helper(root, level=0) 52 | 53 | temp = [] 54 | for i in range(len(res)): 55 | if i % 2 == 0: 56 | temp.append(res[i]) 57 | else: 58 | temp.append(res[i][::-1]) 59 | 60 | return temp 61 | -------------------------------------------------------------------------------- /10.树/104_maxDepth.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个二叉树,找出其最大深度。 3 | 4 | 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 5 | 6 | 说明: 叶子节点是指没有子节点的节点。 7 | 8 | 题解: 9 | 直观的方法是通过递归来解决问题。在这里,我们演示了 DFS(深度优先搜索)策略的示例。 10 | 11 | """ 12 | 13 | 14 | class Solution: 15 | def maxDepth(self, root): 16 | """ 17 | :type root: TreeNode 18 | :rtype: int 19 | """ 20 | if root is None: 21 | return 0 22 | else: 23 | return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right)) 24 | -------------------------------------------------------------------------------- /10.树/105-buildTree.py: -------------------------------------------------------------------------------- 1 | """ 2 | 105. 从前序与中序遍历序列构造二叉树 3 | 根据一棵树的前序遍历与中序遍历构造二叉树。 4 | 5 | 注意: 6 | 你可以假设树中没有重复的元素。 7 | 8 | 例如,给出 9 | 10 | 前序遍历 preorder = [3,9,20,15,7] # 中左右 11 | 中序遍历 inorder = [9,3,15,20,7] # 左右中 12 | 返回如下的二叉树: 13 | 14 | 3 15 | / \ 16 | 9 20 17 | / \ 18 | 15 7 19 | """ 20 | 21 | 22 | class TreeNode(object): 23 | def __init__(self, val=None, left=None, right=None): 24 | self.val = val 25 | self.left = left 26 | self.right = right 27 | 28 | 29 | 30 | class Solution: 31 | def buildTree(self, preorder, inorder): 32 | 33 | if len(preorder) == 0: 34 | return None 35 | if len(preorder) == 1: 36 | return TreeNode(preorder[0]) 37 | else: 38 | index = inorder.index(preorder[0]) 39 | root = TreeNode(preorder[0]) 40 | root.left = self.buildTree(preorder[1:index + 1], inorder[:index]) 41 | root.right = self.buildTree(preorder[index + 1:], inorder[index + 1:]) 42 | return root 43 | -------------------------------------------------------------------------------- /10.树/106-buildTree.py: -------------------------------------------------------------------------------- 1 | """ 2 | 根据一棵树的中序遍历与后序遍历构造二叉树。 3 | 4 | 注意:你可以假设树中没有重复的元素。 5 | 6 | 例如,给出 7 | 8 | 中序遍历 inorder = [9,3,15,20,7] #左中右 9 | 后序遍历 postorder = [9,15,7,20,3] #左右中 10 | 1 11 | 2 12 | 返回如下的二叉树: 13 | 14 | 3 15 | / \ 16 | 9 20 17 | / \ 18 | 15 7 19 | 1 20 | 2 21 | 3 22 | 4 23 | 5 24 | ———————————————— 25 | 原文链接:https://blog.csdn.net/Forlogen/article/details/105159853 26 | """ 27 | from typing import List 28 | 29 | 30 | class TreeNode(object): 31 | def __init__(self, val=None, left=None, right=None): 32 | self.val = val 33 | self.left = left 34 | self.right = right 35 | 36 | 37 | class Solution: 38 | def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode: 39 | if len(postorder) == 0: 40 | return None 41 | if len(postorder) == 1: 42 | return TreeNode(postorder[-1]) 43 | else: 44 | root = TreeNode(postorder[-1]) 45 | index = inorder.index(root.val) 46 | 47 | root.left = self.buildTree(inorder[:index], postorder[: index]) 48 | root.right = self.buildTree(inorder[index + 1:], postorder[index: -1]) 49 | 50 | return root 51 | 52 | 53 | inorder = [9, 3, 15, 20, 7] 54 | postorder = [9, 15, 7, 20, 3] 55 | s = Solution() 56 | res = s.buildTree(inorder, postorder) 57 | print(res) 58 | -------------------------------------------------------------------------------- /10.树/107-levelOrderBottom.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历) 3 | 4 | 例如: 5 | 给定二叉树 [3,9,20,null,null,15,7], 6 | 7 |     3 8 |    / \ 9 |   9  20 10 |     /  \ 11 |    15   7 12 | 返回其自底向上的层次遍历为: 13 | 14 | [ 15 |   [15,7], 16 |   [9,20], 17 |   [3] 18 | ] 19 | """ 20 | from base.tree import TreeNode 21 | 22 | 23 | class Solution: 24 | def levelOrder(self, root): 25 | """ 26 | :type root: TreeNode 27 | :rtype: List[List[int]] 28 | """ 29 | levels = [] 30 | if not root: 31 | return levels 32 | 33 | def helper(node, level): 34 | # start the current level 35 | if len(levels) == level: 36 | levels.append([]) 37 | 38 | # append the current node value 39 | levels[level].append(node.val) 40 | 41 | # process child nodes for the next level 42 | if node.left: 43 | helper(node.left, level + 1) 44 | if node.right: 45 | helper(node.right, level + 1) 46 | 47 | helper(root, 0) 48 | return levels 49 | 50 | def levelOrderBottom(self, root: TreeNode): 51 | levels = self.levelOrder(root) 52 | return levels[::-1] 53 | 54 | 55 | # 别人的解法 56 | class Solution: 57 | def levelOrderBottom(self, root): 58 | queue = [] # 结果列表 59 | cur = [root] # 接下来要循环的当前层节点,存的是节点 60 | while cur: # 当前层存在结点时 61 | cur_layer_val = [] # 初始化当前层结果列表为空,存的是val 62 | next_layer_node = [] # 初始化下一层结点列表为空 63 | for node in cur: # 遍历当前层的每一个结点 64 | if node: # 如果该结点不为空,则进行记录 65 | cur_layer_val.append(node.val) # 将该结点的值加入当前层结果列表的末尾 66 | next_layer_node.extend([node.left, node.right]) # 将该结点的左右孩子结点加入到下一层结点列表 67 | if cur_layer_val: # 只要当前层结果列表不为空 68 | queue.insert(0, cur_layer_val) # 则把当前层结果列表插入到队列首端 69 | cur = next_layer_node # 下一层的结点变成当前层,接着循环 70 | return queue # 返回结果队列 71 | -------------------------------------------------------------------------------- /10.树/108-sortedArrayToBST.py: -------------------------------------------------------------------------------- 1 | """ 2 | 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。 3 | 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。 4 | 5 | 示例: 6 | 7 | 给定有序数组: [-10,-3,0,5,9], 8 | 9 | 一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树: 10 | 11 | 0 12 | / \ 13 | -3 9 14 | / / 15 | -10 5 16 | """ 17 | 18 | 19 | # Definition for a binary tree node. 20 | class TreeNode: 21 | def __init__(self, x): 22 | self.val = x 23 | self.left = None 24 | self.right = None 25 | 26 | 27 | class Solution: 28 | def sortedArrayToBST(self, nums): 29 | """ 30 | :type nums: List[int] 31 | :rtype: TreeNode 32 | """ 33 | if not nums: 34 | return None 35 | l = len(nums) 36 | root = TreeNode(nums[l // 2]) 37 | root.left = self.sortedArrayToBST(nums[:l // 2]) 38 | root.right = self.sortedArrayToBST(nums[l // 2 + 1:]) 39 | -------------------------------------------------------------------------------- /10.树/109-sortedListToBST.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。 3 | 4 | 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。 5 | 6 | 示例: 7 | 8 | 给定的有序链表: [-10, -3, 0, 5, 9], 9 | 10 | 一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树: 11 | 12 |       0 13 |      / \ 14 |    -3   9 15 |    /   / 16 |  -10  5 17 | 18 | """ 19 | 20 | 21 | # Definition for singly-linked list. 22 | class ListNode(object): 23 | def __init__(self, x): 24 | self.val = x 25 | self.next = None 26 | 27 | 28 | # Definition for a binary tree node. 29 | class TreeNode(object): 30 | def __init__(self, x): 31 | self.val = x 32 | self.left = None 33 | self.right = None 34 | 35 | 36 | class Solution(object): 37 | def sortedListToBST(self, head): 38 | """ 39 | :type head: ListNode 40 | :rtype: TreeNode 41 | """ 42 | if not head: 43 | return None 44 | mid = self.findMiddleNode(head) 45 | node = TreeNode(mid.val) 46 | 47 | if head == mid: 48 | return node 49 | 50 | node.left = self.sortedListToBST(head) 51 | node.right = self.sortedListToBST(mid.next) 52 | return node 53 | 54 | def findMiddleNode(self, head): 55 | pre = None 56 | slow = head 57 | fast = head 58 | while fast and fast.next: 59 | pre = slow 60 | slow = slow.next 61 | fast = fast.next.next 62 | if pre: 63 | pre.next = None 64 | return slow 65 | 66 | 67 | # zhennan 的解法 68 | class Solution: 69 | def sortedListToBST(self, head: ListNode) -> TreeNode: 70 | def findMid(head: ListNode, tail: ListNode): 71 | slow = head 72 | fast = head 73 | while fast != tail and fast.next != tail: 74 | slow = slow.next 75 | fast = fast.next.next 76 | return slow 77 | 78 | def helper(head: ListNode, tail: ListNode): 79 | if head == tail: 80 | return None 81 | mid = findMid(head, tail) 82 | root = TreeNode(mid.val) 83 | root.left = helper(head, mid) 84 | root.right = helper(mid.next, tail) 85 | return root 86 | 87 | return helper(head, None) 88 | 89 | 90 | "1.放进list再放进tree. 2.如上" 91 | -------------------------------------------------------------------------------- /10.树/110-isBalanced.py: -------------------------------------------------------------------------------- 1 | """ 2 | 题目:平衡二叉树 simple 3 | 给定一个二叉树,判断它是否是高度平衡的二叉树。 4 | 本题中,一棵高度平衡二叉树定义为: 5 | 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。 6 | 示例 1: 7 | 给定二叉树 [3,9,20,null,null,15,7] 8 | 3 9 | / \ 10 | 9 20 11 | / \ 12 | 15 7 13 | 返回 true 。 14 | 示例 2: 15 | 给定二叉树 [1,2,2,3,3,null,null,4,4] 16 | 1 17 | / \ 18 | 2 2 19 | / \ 20 | 3 3 21 | / \ 22 | 4 4 23 | 返回 false 24 | """ 25 | 26 | 27 | # Definition for a binary tree node. 28 | class TreeNode(object): 29 | def __init__(self, x): 30 | self.val = x 31 | self.left = None 32 | self.right = None 33 | 34 | 35 | A, B, C = [TreeNode(x) for x in '123'] 36 | A.right = B 37 | B.right = C 38 | 39 | a, b, c, d, e, f, g = [TreeNode(x) for x in '1223344'] 40 | # [1,2,2,3,null,null,3,4,null,null,4] 41 | a.left, a.right = b, c 42 | b.left = d 43 | c.right = e 44 | e.left = f 45 | f.right = g 46 | root = a 47 | 48 | 49 | class Solution: 50 | # Compute the tree's height via recursion 51 | def height(self, root: TreeNode) -> int: 52 | # An empty tree has height -1 53 | if not root: 54 | return -1 55 | return 1 + max(self.height(root.left), self.height(root.right)) 56 | 57 | def isBalanced(self, root: TreeNode) -> bool: 58 | # An empty tree satisfies the definition of a balanced tree 59 | if not root: 60 | return True 61 | 62 | # Check if subtrees have height within 1. If they do, check if the 63 | # subtrees are balanced 64 | return abs(self.height(root.left) - self.height(root.right)) < 2 \ 65 | and self.isBalanced(root.left) \ 66 | and self.isBalanced(root.right) 67 | 68 | 69 | s = Solution() 70 | print(s.isBalanced(root=a)) 71 | -------------------------------------------------------------------------------- /10.树/113_pathSum.py: -------------------------------------------------------------------------------- 1 | # 给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。 2 | # 3 | # 说明: 叶子节点是指没有子节点的节点。 4 | # 5 | # 示例: 6 | # 给定如下二叉树,以及目标和 sum = 22, 7 | # 8 | # 5 9 | # / \ 10 | # 4 8 11 | # / / \ 12 | # 11 13 4 13 | # / \ / \ 14 | # 7 2 5 1 15 | # 16 | # 17 | # 返回: 18 | # 19 | # [ 20 | # [5,4,11,2], 21 | # [5,8,4,5] 22 | # ] 23 | # 24 | # Related Topics 树 深度优先搜索 25 | 26 | 27 | # leetcode submit region begin(Prohibit modification and deletion) 28 | # Definition for a binary tree node. 29 | # class TreeNode: 30 | # def __init__(self, x): 31 | # self.val = x 32 | # self.left = None 33 | # self.right = None 34 | from typing import List 35 | 36 | from post_pre_in_order_traversal import TreeNode 37 | 38 | 39 | class Solution: 40 | def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]: 41 | res = [] 42 | 43 | def dfs(cursum, node, l): 44 | if not node: 45 | return 46 | 47 | tmp_sum = cursum + node.val 48 | if tmp_sum == sum and not node.left and not node.right: 49 | res.append(l + [node.val]) 50 | return 51 | # elif tmp_sum != sum: # todo 不能加这句!超级重要 !!! 52 | dfs(tmp_sum, node.left, l + [node.val]) 53 | dfs(tmp_sum, node.right, l + [node.val]) 54 | 55 | dfs(cursum=0, node=root, l=[]) 56 | return res 57 | -------------------------------------------------------------------------------- /10.树/114-flatten.py: -------------------------------------------------------------------------------- 1 | # 给定一个二叉树,原地将它展开为一个单链表。 2 | # 3 | # 4 | # 5 | # 例如,给定二叉树 6 | # 7 | # 1 8 | # / \ 9 | # 2 5 10 | # / \ \ 11 | # 3 4 6 12 | # 13 | # 将其展开为: 14 | # 15 | # 1 16 | # \ 17 | # 2 18 | # \ 19 | # 3 20 | # \ 21 | # 4 22 | # \ 23 | # 5 24 | # \ 25 | # 6 26 | # Related Topics 树 深度优先搜索 27 | 28 | 29 | # leetcode submit region begin(Prohibit modification and deletion) 30 | # Definition for a binary tree node. 31 | # class TreeNode: 32 | # def __init__(self, val=0, left=None, right=None): 33 | # self.val = val 34 | # self.left = left 35 | # self.right = right 36 | class Solution: 37 | def flatten(self, root: TreeNode) -> None: 38 | """ 39 | Do not return anything, modify root in-place instead. 40 | """ 41 | 42 | preorderList, stack = [], [root] 43 | while stack: 44 | node = stack.pop() 45 | if isinstance(node, TreeNode): 46 | stack.extend([node.right, node.left, node.val]) 47 | else: 48 | preorderList.append(node) 49 | 50 | size = len(preorderList) 51 | for i in range(1, size): 52 | prev, curr = preorderList[i - 1], preorderList[i] 53 | prev.left = None 54 | prev.right = curr 55 | 56 | 57 | 58 | """ 59 | 使用方法一的前序遍历,由于将节点展开之后会破坏二叉树的结构而丢失子节点的信息,因此前序遍历和展开为单链表分成了两步。 60 | 能不能在不丢失子节点的信息的情况下,将前序遍历和展开为单链表同时进行? 61 | 62 | 之所以会在破坏二叉树的结构之后丢失子节点的信息,是因为在对左子树进行遍历时,没有存储右子节点的信息,在遍历完左子树之后才获得右子节点的信息。 63 | 只要对前序遍历进行修改,在遍历左子树之前就获得左右子节点的信息,并存入栈内,子节点的信息就不会丢失,就可以将前序遍历和展开为单链表同时进行。 64 | 65 | 该做法不适用于递归实现的前序遍历,只适用于迭代实现的前序遍历。修改后的前序遍历的具体做法是,每次从栈内弹出一个节点作为当前访问的节点, 66 | 获得该节点的子节点,如果子节点不为空,则依次将右子节点和左子节点压入栈内(注意入栈顺序)。 67 | 68 | 展开为单链表的做法是,维护上一个访问的节点 prev,每次访问一个节点时,令当前访问的节点为 curr,将 prev 的左子节点设为 null 以及将 69 | prev 的右子节点设为 curr,然后将 curr 赋值给 prev,进入下一个节点的访问,直到遍历结束。需要注意的是,初始时 prev 为 null, 70 | 只有在 prev 不为 null 时才能对 prev 的左右子节点进行更新。 71 | 72 | """ 73 | 74 | 75 | 76 | "O(1)" 77 | 78 | 79 | class Solution: 80 | def flatten(self, root: TreeNode) -> None: 81 | curr = root 82 | while curr: 83 | if curr.left: 84 | predecessor = nxt = curr.left 85 | while predecessor.right: 86 | predecessor = predecessor.right 87 | predecessor.right = curr.right 88 | curr.left = None 89 | curr.right = nxt 90 | curr = curr.right 91 | 92 | # leetcode submit region end(Prohibit modification and deletion) 93 | -------------------------------------------------------------------------------- /10.树/144-preorderTraversal.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个二叉树,返回它的 前序 遍历。 3 | 4 |  示例: 5 | 6 | 输入: [1,null,2,3] 7 | 1 8 | \ 9 | 2 10 | / 11 | 3 12 | 13 | 输出: [1,2,3] 14 | 进阶: 递归算法很简单,你可以通过迭代算法完成吗? 15 | 16 | 中左右 17 | """ 18 | 19 | 20 | # https://blog.csdn.net/weixin_36677127/article/details/82284909 21 | 22 | class TreeNode: 23 | def __init__(self, x): 24 | self.val = x 25 | self.left = None 26 | self.right = None 27 | 28 | 29 | # A, B, C, D = [TreeNode(x) for x in [1, None, 2, 3]] 30 | # A.left, A.right = B, C 31 | # C.left = D 32 | # root=A 33 | A, B, C, D, E, F, G, H, I = [TreeNode(x) for x in 'ABCDEFGHI'] 34 | A.left, A.right = B, C 35 | B.right = D 36 | C.left, C.right = E, F 37 | E.left = G 38 | F.left, F.right = H, I 39 | root = A 40 | res = ['A', 'B', 'D', 'C', 'E', 'G', 'F', 'H', 'I'] 41 | 42 | 43 | class Solution: 44 | def preorderTraversal(self, root): 45 | "先序遍历:中-》左-》右" 46 | res, stack = [], [root] 47 | while stack: 48 | node = stack.pop() 49 | if node: 50 | res.append(node.val) 51 | # 注意压入栈的顺序,先压入右孩子,再压入左孩子 52 | stack.append(node.right) 53 | stack.append(node.left) 54 | print([i.val for i in stack if i]) 55 | return res 56 | 57 | 58 | s = Solution() 59 | print(s.preorderTraversal(root)) 60 | -------------------------------------------------------------------------------- /10.树/145-postorderTraversal.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个二叉树,返回它的 后序 遍历。 3 | 4 | 示例: 5 | 6 | 输入: [1,null,2,3] 7 | 1 8 | \ 9 | 2 10 | / 11 | 3 12 | 13 | 输出: [3,2,1] 14 | 左右中 15 | """ 16 | 17 | 18 | class TreeNode: 19 | def __init__(self, x): 20 | self.val = x 21 | self.left = None 22 | self.right = None 23 | 24 | 25 | # A, B, C, D = [TreeNode(x) for x in [1, None, 2, 3]] 26 | # A.left, A.right = B, C 27 | # C.left = D 28 | # root=A 29 | A, B, C, D, E, F, G, H, I = [TreeNode(x) for x in 'ABCDEFGHI'] 30 | A.left, A.right = B, C 31 | B.right = D 32 | C.left, C.right = E, F 33 | E.left = G 34 | F.left, F.right = H, I 35 | root = A 36 | # 先序 37 | res = ['A', 'B', 'D', 'C', 'E', 'G', 'F', 'H', 'I'] 38 | # 后序 39 | res = ['D', 'B', 'G', 'E', 'H', 'I', 'F', 'C', 'A'] 40 | 41 | 42 | class Solution: 43 | """ 44 | 先序:根左右 45 | 后续:左右根 46 | 即把先序顺序中的 ‘根左右’转换为‘根右左’,然后反过来就变成了‘左右根’。 47 | """ 48 | 49 | def postorderTraversal(self, root): 50 | # write your code here 51 | if root is None: 52 | return [] 53 | stack = [] 54 | seq = [] 55 | output = [] 56 | while root or len(stack) != 0: 57 | if root: 58 | seq.append(root.val) 59 | stack.append(root) 60 | root = root.right # 这从left变成了 right 61 | else: 62 | root = stack.pop() 63 | root = root.left # 这从right变成了 left 64 | 65 | while seq: # 后序遍历 是 将先序遍历的反过来 66 | output.append(seq.pop()) 67 | 68 | return output 69 | 70 | 71 | s = Solution() 72 | print(s.postorderTraversal(root)) 73 | 74 | 75 | class Solution: 76 | """ 77 | 先序遍历 78 | """ 79 | 80 | def preorderTraversal(self, root): 81 | # write your code here 82 | if not root: 83 | return [] 84 | stack = [] 85 | seq = [] # 记录先序访问序列 86 | while root or len(stack) != 0: 87 | if root: 88 | seq.append(root.val) # 先访问根节点 89 | stack.append(root) 90 | root = root.left 91 | else: 92 | root = stack.pop() # 回溯至父节点 93 | root = root.right 94 | return seq 95 | 96 | 97 | class Solution: 98 | """ 99 | 中序遍历 100 | """ 101 | 102 | def inorderTraversal(self, root): 103 | # write your code here 104 | if root is None: 105 | return [] 106 | stack = [] 107 | seq = [] 108 | output = [] 109 | while root or len(stack) != 0: 110 | if root: 111 | stack.append(root) 112 | root = root.left 113 | else: 114 | root = stack.pop() 115 | seq.append(root.val) # 左孩子先pop出来,再pop根节点 116 | root = root.right 117 | 118 | return seq 119 | -------------------------------------------------------------------------------- /10.树/199-rightSideView.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。 3 | 4 | 示例: 5 | 6 | 输入: [1,2,3,null,5,null,4] 7 | 输出: [1, 3, 4] 8 | 解释: 9 | 10 | 1 <--- 11 | / \ 12 | 2 3 <--- 13 | \ \ 14 | 5 4 <--- 15 | 16 | 17 | """ 18 | 19 | 20 | class Solution: 21 | def levelOrder(self, root): 22 | result = [] 23 | if not root: 24 | return result 25 | 26 | def helper(root, level): 27 | if len(result) == level: 28 | result.append([]) 29 | result[level].append(root.val) 30 | if root.left: 31 | helper(root.left, level + 1) 32 | if root.right: 33 | helper(root.right, level + 1) 34 | 35 | helper(root, level=0) 36 | return result 37 | 38 | def rightSideView(self, root): 39 | level_result = self.levelOrder(root) 40 | return [i[-1] for i in level_result] 41 | -------------------------------------------------------------------------------- /10.树/222-countNodes.py: -------------------------------------------------------------------------------- 1 | """ 2 | # 给出一个完全二叉树,求出该树的节点个数。 3 | # 4 | # 说明: 5 | # 6 | # 7 | # 完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 8 | # h 层,则该层包含 1~ 2^h 个节点。 9 | # 10 | # 示例: 11 | # 12 | # 输入: 13 | # ⁠ 1 14 | # ⁠ / \ 15 | # ⁠ 2 3 16 | # ⁠/ \ / 17 | # 4 5 6 18 | # 19 | # 输出: 6 20 | """ 21 | 22 | 23 | # Definition for a binary tree node. 24 | class TreeNode: 25 | def __init__(self, x): 26 | self.val = x 27 | self.left = None 28 | self.right = None 29 | 30 | 31 | class Solution: 32 | def countNodes(self, root: TreeNode) -> int: 33 | if not root: 34 | return 0 35 | return 1 + self.countNodes(root.left) + self.countNodes(root.right) 36 | -------------------------------------------------------------------------------- /10.树/226-invertTree.py: -------------------------------------------------------------------------------- 1 | """翻转一棵二叉树。 2 | 3 | * 输入: 4 | * 4 5 | * / \ 6 | * 2 7 7 | * / \ / \ 8 | * 1 3 6 9 9 | * 输出: 10 | 11 | * 4 12 | * / \ 13 | * 7 2 14 | * / \ / \ 15 | * 9 6 3 1 16 | """ 17 | 18 | 19 | class TreeNode: 20 | def __init__(self, x): 21 | self.val = x 22 | self.left = None 23 | self.right = None 24 | 25 | 26 | 27 | class Solution(object): 28 | def invertTree(self, root): 29 | """ 30 | :type root: TreeNode 31 | :rtype: TreeNode 32 | """ 33 | # 递归函数的终止条件,节点为空时返回 34 | if not root: 35 | return None 36 | # 将当前节点的左右子树交换 37 | root.left, root.right = root.right, root.left 38 | # 递归交换当前节点的 左子树和右子树 39 | self.invertTree(root.left) 40 | self.invertTree(root.right) 41 | # 函数返回时就表示当前这个节点,以及它的左右子树 42 | # 都已经交换完了 43 | return root 44 | 45 | 46 | A, B, C, D, E, F, G = [TreeNode(x) for x in '4271369'] 47 | A.left, A.right = B, C 48 | B.left, B.right = D, E 49 | C.left, C.right = F, G 50 | root = A 51 | 52 | s = Solution() 53 | res = s.invertTree(root) 54 | -------------------------------------------------------------------------------- /10.树/230-kthSmallest.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。 3 | 4 | 说明: 5 | 你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数。 6 | 7 | 示例 1: 8 | 输入: root = [3,1,4,null,2], k = 1 9 | 3 10 | / \ 11 | 1 4 12 | \ 13 | 2 14 | 输出: 1 15 | 16 | 示例 2: 17 | 输入: root = [5,3,6,2,4,null,null,1], k = 3 18 | 5 19 | / \ 20 | 3 6 21 | / \ 22 | 2 4 23 | / 24 | 1 25 | 输出: 3 26 | 进阶: 27 | 如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k 小的值,你将如何优化 kthSmallest 函数? 28 | 29 | 先序遍历:在第一次遍历到节点时就执行操作,一般只是想遍历执行操作(或输出结果)可选用先序遍历; 30 | 中序遍历:对于二分搜索树,中序遍历的操作顺序(或输出结果顺序)是符合从小到大(或从大到小)顺序的,故要遍历输出排序好的结果需要使用中序遍历 31 | 后序遍历:后续遍历的特点是执行操作时,肯定已经遍历过该节点的左右子节点,故适用于要进行破坏性操作的情况,比如删除所有节点 32 | """ 33 | 34 | 35 | # from base.tree import Btree 36 | 37 | 38 | class TreeNode: 39 | def __init__(self, x): 40 | self.val = x 41 | self.left = None 42 | self.right = None 43 | 44 | 45 | class Solution: 46 | def kthSmallest(self, root, k): 47 | # TODO 中序遍历:对于二分搜索树,中序遍历的操作顺序(或输出结果顺序)是符合从小到大(或从大到小)顺序的,故要遍历输出排序好的结果需要使用中序遍历 48 | res, stack = [], [] 49 | while root or stack: 50 | # stack.append(root) 51 | if root: 52 | stack.append(root) 53 | root = root.left 54 | else: 55 | tmp = stack.pop() 56 | res.append(tmp.val) 57 | root = root.right 58 | 59 | return res[k - 1] 60 | 61 | 62 | class Solution: 63 | def kthSmallest(self, root: TreeNode, k): 64 | stack, res = [root], [] 65 | while stack: 66 | node = stack.pop() 67 | if isinstance(node, TreeNode): 68 | stack.extend([node.right, node.val, node.left]) 69 | elif isinstance(node, int): 70 | res.append(node) 71 | return res[k - 1] 72 | 73 | 74 | 75 | 76 | s = Solution() 77 | -------------------------------------------------------------------------------- /10.树/437-pathSum.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个二叉树,它的每个结点都存放着一个整数值。 3 | 4 | 找出路径和等于给定数值的路径总数。 5 | 6 | 路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。 7 | 8 | 二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。 9 | 10 | """ 11 | 12 | 13 | class TreeNode: 14 | def __init__(self, x): 15 | self.val = x 16 | self.left = None 17 | self.right = None 18 | 19 | 20 | # 暴力解法:双层递归,核心在于: 21 | # 每个node都要计算以它作为起点往下是否有path --> 这是一层递归 22 | # 在考虑当前点为起点往下有没有path的时候,它的path可以往左也可以往右,于是要综合考虑 --> 这是另一层递归 23 | 24 | class Solution: 25 | def pathSum(self, root: TreeNode, sum: int) -> int: 26 | '''如果没有根节点,整个返回值应该为0,没有路径''' 27 | if not root: 28 | return 0 29 | ''' 30 | self.dfs(root, sum):这个方法是判断以当前点为起点往下是否有path,也就是path的数量,返回值应该是0或1 31 | self.pathSum(root.left, sum):对于左节点我依然要考虑以它为起点往下判断 32 | self.pathSum(root.right, sum):同上,于是,此时的sum是不变化的,仍然为初始值 33 | ''' 34 | 35 | return self.dfs(root, sum) + self.pathSum(root.left, sum) + self.pathSum(root.right, sum) 36 | 37 | def dfs(self, root, curr_sum): 38 | if not root: 39 | return 0 40 | '''每一次都要减去当前层的节点值''' 41 | 42 | curr_sum -= root.val 43 | ''' 44 | (1 if path==0 else 0):说明找到了路径 45 | self.dfs(root.left, path) self.dfs(root.right, path): 46 | 此时path更新过,这是因为当前点既可以往左走找path,也可以往右走,是或的关系,只要有一边找到了路径,最终结果都会为1 47 | ''' 48 | return (1 if curr_sum == 0 else 0) + self.dfs(root.left, curr_sum) + \ 49 | self.dfs(root.right, curr_sum) 50 | 51 | 52 | a, b, c, d, e = [TreeNode(x) for x in [1, 2, 3, 4, 5]] 53 | a.right = b 54 | b.right = c 55 | c.right = d 56 | d.right = e 57 | 58 | # [1,null,2,null,3,null,4,null,5] 59 | s = Solution() 60 | print(s.pathSum(root=a, sum=3)) 61 | -------------------------------------------------------------------------------- /10.树/450-deleteNode.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | class TreeNode(object): 3 | def __init__(self, x): 4 | self.val = x 5 | self.left = None 6 | self.right = None 7 | 8 | 9 | # root = [5,3,6,2,4,null,7] 10 | a, b, c, d, e, f = [TreeNode(i) for i in [5, 3, 6, 2, 4, 7]] 11 | a.left, a.right = b, c 12 | b.left, b.right = d, e 13 | c.right = f 14 | 15 | root = a 16 | 17 | """ 18 | 算法: 19 | 20 | 如果 key > root.val,说明要删除的节点在右子树,root.right = deleteNode(root.right, key)。 21 | 如果 key < root.val,说明要删除的节点在左子树,root.left = deleteNode(root.left, key)。 22 | 如果 key == root.val,则该节点就是我们要删除的节点,则: 23 | 如果该节点是叶子节点,则直接删除它:root = null。 24 | 如果该节点不是叶子节点且有右节点,则用它的后继节点的值替代 root.val = successor.val,然后删除后继节点。 25 | 如果该节点不是叶子节点且只有左节点,则用它的前驱节点的值替代 root.val = predecessor.val,然后删除前驱节点。 26 | 返回 root。 27 | """ 28 | 29 | 30 | class Solution: 31 | def successor(self, root): 32 | """ 33 | One step right and then always left 34 | """ 35 | root = root.right 36 | while root.left: 37 | root = root.left 38 | return root.val 39 | 40 | def predecessor(self, root): 41 | """ 42 | One step left and then always right 43 | """ 44 | root = root.left 45 | while root.right: 46 | root = root.right 47 | return root.val 48 | 49 | def deleteNode(self, root, key: int): 50 | if not root: 51 | return None 52 | 53 | # delete from the right subtree 54 | if key > root.val: 55 | root.right = self.deleteNode(root.right, key) 56 | # delete from the left subtree 57 | elif key < root.val: 58 | root.left = self.deleteNode(root.left, key) 59 | # delete the current node 60 | else: 61 | # the node is a leaf 62 | if not (root.left or root.right): 63 | root = None 64 | # the node is not a leaf and has a right child 65 | elif root.right: 66 | root.val = self.successor(root) 67 | root.right = self.deleteNode(root.right, root.val) 68 | # the node is not a leaf, has no right child, and has a left child 69 | else: 70 | root.val = self.predecessor(root) 71 | root.left = self.deleteNode(root.left, root.val) 72 | 73 | return root 74 | -------------------------------------------------------------------------------- /10.树/538-convertBST.py: -------------------------------------------------------------------------------- 1 | # 给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree), 2 | # 使每个节点 node 的新值等于原树中大于或等于 3 | # node.val 的值之和。 4 | # 5 | # 提醒一下,二叉搜索树满足下列约束条件: 6 | # 7 | # 8 | # 节点的左子树仅包含键 小于 节点键的节点。 9 | # 节点的右子树仅包含键 大于 节点键的节点。 10 | # 左右子树也必须是二叉搜索树。 11 | # 12 | # 13 | # 注意:本题和 1038: https://leetcode-cn.com/problems/binary-search-tree-to-greater-s 14 | # um-tree/ 相同 15 | # 16 | # 17 | # 18 | # 示例 1: 19 | # 20 | # 21 | # 22 | # 输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8] 23 | # 输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8] 24 | # 25 | # 26 | # 示例 2: 27 | # 28 | # 输入:root = [0,null,1] 29 | # 输出:[1,null,1] 30 | # 31 | # 32 | # 示例 3: 33 | # 34 | # 输入:root = [1,0,2] 35 | # 输出:[3,3,2] 36 | # 37 | # 38 | # 示例 4: 39 | # 40 | # 输入:root = [3,2,4,1] 41 | # 输出:[7,9,4,10] 42 | # 43 | # 44 | # 45 | # 46 | # 提示: 47 | # 48 | # 49 | # 树中的节点数介于 0 和 104 之间。 50 | # 每个节点的值介于 -104 和 104 之间。 51 | # 树中的所有值 互不相同 。 52 | # 给定的树为二叉搜索树。 53 | # 54 | # Related Topics 树 55 | 56 | 57 | # leetcode submit region begin(Prohibit modification and deletion) 58 | # Definition for a binary tree node. 59 | class TreeNode: 60 | def __init__(self, val=0, left=None, right=None): 61 | self.val = val 62 | self.left = left 63 | self.right = right 64 | 65 | 66 | class Solution: 67 | def convertBST(self, root: TreeNode) -> TreeNode: 68 | # global total 69 | # total=0 70 | 71 | def dfs(root: TreeNode): 72 | nonlocal total # nonlocal声明的变量不是局部变量,也不是全局变量,而是外部嵌套函数内的变量。 73 | if root: 74 | dfs(root.right) 75 | total += root.val 76 | root.val = total 77 | dfs(root.left) 78 | 79 | dfs(root) 80 | return root 81 | 82 | # leetcode submit region end(Prohibit modification and deletion) 83 | -------------------------------------------------------------------------------- /10.树/543-diameterOfBinaryTree.py: -------------------------------------------------------------------------------- 1 | # 给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。 2 | # 这条路径可能穿过也可能不穿过根结点。 3 | # 4 | # 5 | # 6 | # 示例 : 7 | # 给定二叉树 8 | # 9 | # 1 10 | # / \ 11 | # 2 3 12 | # / \ 13 | # 4 5 14 | # 15 | # 16 | # 返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。 17 | # 18 | # 19 | # 20 | # 注意:两结点之间的路径长度是以它们之间边的数目表示。 21 | # Related Topics 树 22 | 23 | 24 | # leetcode submit region begin(Prohibit modification and deletion) 25 | # Definition for a binary tree node. 26 | # class TreeNode: 27 | # def __init__(self, x): 28 | # self.val = x 29 | # self.left = None 30 | # self.right = None 31 | 32 | 33 | 34 | class Solution(object): 35 | def diameterOfBinaryTree(self, root): 36 | """ 37 | :type root: TreeNode 38 | :rtype: int 39 | """ 40 | self.ans = 0 41 | 42 | def dfs(root): 43 | if not root: 44 | return 0 45 | left = dfs(root.left) 46 | right = dfs(root.right) 47 | self.ans = max(self.ans, left + right) 48 | print(left, right, self.ans, max(left, right) + 1) 49 | return max(left, right) + 1 50 | 51 | dfs(root) 52 | return self.ans 53 | 54 | # leetcode submit region end(Prohibit modification and deletion) 55 | -------------------------------------------------------------------------------- /10.树/572-isSubtree.py: -------------------------------------------------------------------------------- 1 | """ 2 | Subtree of Another Tree(判断一棵树是不是另外一棵树的子树) 3 | 给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。 4 | 5 | 示例 1: 6 | 7 | 给定的树 s: 8 | 9 | 3 10 | / \ 11 | 4 5 12 | / \ 13 | 1 2 14 | 给定的树 t: 15 | 16 | 4 17 | / \ 18 | 1 2 19 | 返回 true,因为 t 与 s 的一个子树拥有相同的结构和节点值 20 | 21 | """ 22 | from base.tree import TreeNode 23 | 24 | 25 | class Solution: 26 | def isSameTree(self, p: TreeNode, q: TreeNode) -> bool: 27 | # p and q are both None 28 | if not p and not q: 29 | return True 30 | # one of p and q is None 31 | if not q or not p: 32 | return False 33 | if p.val != q.val: 34 | return False 35 | return self.isSameTree(p.right, q.right) and \ 36 | self.isSameTree(p.left, q.left) 37 | 38 | def isSubtree(self, s, t): 39 | # print(self.isSameTree(s, t)) 40 | if self.isSameTree(s, t): 41 | return True 42 | if s.left: 43 | if self.isSubtree(s.left, t): 44 | return True 45 | 46 | if s.right: 47 | if self.isSubtree(s.right, t): 48 | return True 49 | return False 50 | -------------------------------------------------------------------------------- /10.树/617-mergeTrees.py: -------------------------------------------------------------------------------- 1 | # 给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。 2 | # 3 | # 你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点 4 | # 。 5 | # 6 | # 示例 1: 7 | # 8 | # 9 | # 输入: 10 | # Tree 1 Tree 2 11 | # 1 2 12 | # / \ / \ 13 | # 3 2 1 3 14 | # / \ \ 15 | # 5 4 7 16 | # 输出: 17 | # 合并后的树: 18 | # 3 19 | # / \ 20 | # 4 5 21 | # / \ \ 22 | # 5 4 7 23 | # 24 | # 25 | # 注意: 合并必须从两个树的根节点开始。 26 | # Related Topics 树 27 | 28 | 29 | # leetcode submit region begin(Prohibit modification and deletion) 30 | # Definition for a binary tree node. 31 | class TreeNode: 32 | def __init__(self, x): 33 | self.val = x 34 | self.left = None 35 | self.right = None 36 | 37 | 38 | # return t1 39 | 40 | class Solution: 41 | def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode: 42 | """ 43 | :type t1: TreeNode 44 | :type t2: TreeNode 45 | :rtype: TreeNode 46 | """ 47 | # 结点都为空时 48 | if not t1 and not t2: 49 | return 50 | # 只有一个结点为空时 51 | if not t1: 52 | return t2 53 | if not t2: 54 | return t1 55 | 56 | root = TreeNode(None) 57 | # 结点重叠时 58 | root.val = t1.val + t2.val 59 | # 进行迭代 60 | # root.right = self.mergeTrees(t1.right if t1, t2.right) 61 | # root.left = self.mergeTrees(t1.left, t2.left) 62 | root.left = self.mergeTrees(t1.left if (t1 and t1.left) else None, t2.left if (t2 and t2.left) else None) 63 | root.right = self.mergeTrees(t1.right if (t1 and t1.right) else None, t2.right if (t2 and t2.right) else None) 64 | 65 | return root 66 | 67 | # leetcode submit region end(Prohibit modification and deletion) 68 | -------------------------------------------------------------------------------- /10.树/654-constructMaximumBinaryTree.py: -------------------------------------------------------------------------------- 1 | """ 2 | 654 根据数组构造最大树 3 | 给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下: 4 | 二叉树的根是数组中的最大元素。 5 | 左子树是通过数组中最大值左边部分构造出的最大二叉树。 6 | 右子树是通过数组中最大值右边部分构造出的最大二叉树。 7 | 通过给定的数组构建最大二叉树,并且输出这个树的根节点。 8 | 9 | Example 1: 10 | 输入: [3,2,1,6,0,5] 11 | 输入: 返回下面这棵树的根节点: 12 | 13 | 6 14 | / \ 15 | 3 5 16 | \ / 17 | 2 0 18 | \ 19 | 1 20 | 21 | 给定的数组的大小在 [1, 1000] 之间。 22 | """ 23 | 24 | 25 | class TreeNode(object): 26 | def __init__(self, x): 27 | self.val = x 28 | self.left = None 29 | self.right = None 30 | 31 | 32 | class Solution: 33 | def constructMaximumBinaryTree(self, nums): 34 | if not nums: 35 | return 36 | if len(nums) == 1: 37 | return TreeNode(nums[0]) 38 | max_index = nums.index(max(nums)) 39 | root = TreeNode(nums[max_index]) 40 | root.left = self.constructMaximumBinaryTree(nums[:max_index]) 41 | root.right = self.constructMaximumBinaryTree(nums[max_index + 1:]) 42 | return root 43 | -------------------------------------------------------------------------------- /10.树/701-insertIntoBST.py: -------------------------------------------------------------------------------- 1 | """701. 二叉搜索树中的插入操作 2 | 给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 保证原始二叉搜索树中不存在新值。 3 | 注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回任意有效的结果。 4 | 例如, 给定二叉搜索树: 5 | 6 | 4 7 | / \ 8 | 2 7 9 | / \ 10 | 1 3 11 | 12 | 和 插入的值: 5 13 | 你可以返回这个二叉搜索树: 14 | 15 | 4 16 | / \ 17 | 2 7 18 | / \ / 19 | 1 3 5 20 | 或者这个树也是有效的: 21 | 22 | 5 23 | / \ 24 | 2 7 25 | / \ 26 | 1 3 27 | \ 28 | 4 29 | """ 30 | 31 | 32 | # Definition for a binary tree node. 33 | class TreeNode(object): 34 | def __init__(self, x): 35 | self.val = x 36 | self.left = None 37 | self.right = None 38 | 39 | 40 | class Solution: 41 | def insertIntoBST(self, root, val): 42 | if not root: 43 | return TreeNode(val) 44 | 45 | if val > root.val: 46 | # insert into the right subtree 47 | root.right = self.insertIntoBST(root.right, val) 48 | else: 49 | # insert into the left subtree 50 | root.left = self.insertIntoBST(root.left, val) 51 | return root 52 | -------------------------------------------------------------------------------- /10.树/94-inorderTraversal.py: -------------------------------------------------------------------------------- 1 | """ 2 | 中序遍历 3 | 4 | 给定一个二叉树,返回它的中序遍历。 5 | 6 | 示例: 7 | 8 | 输入: [1,null,2,3] 9 | 1 10 | \ 11 | 2 12 | / 13 | 3 14 | 15 | 输出: [1,3,2] 16 | 进阶: 递归算法很简单,你可以通过迭代算法完成吗? 17 | 18 | 递归,迭代,莫里斯算法 19 | 20 | 左中右 21 | """ 22 | 23 | 24 | class TreeNode: 25 | def __init__(self, x): 26 | self.val = x 27 | self.left = None 28 | self.right = None 29 | 30 | 31 | # A, B, C, D = [TreeNode(x) for x in [1, None, 2, 3]] 32 | # A.left, A.right = B, C 33 | # C.left = D 34 | # root=A 35 | A, B, C, D, E, F, G, H, I = [TreeNode(x) for x in 'ABCDEFGHI'] 36 | A.left, A.right = B, C 37 | B.right = D 38 | C.left, C.right = E, F 39 | E.left = G 40 | F.left, F.right = H, I 41 | root = A 42 | 43 | 44 | # 递归 # 没懂 45 | class Solution(object): 46 | def inorderTraversal(self, root): 47 | """ 48 | :type root: TreeNode 49 | :rtype: List[int] 50 | """ 51 | res = [] 52 | 53 | def dfs(root): 54 | if not root: 55 | return 56 | # 按照 左-打印-右的方式遍历 57 | dfs(root.left) 58 | res.append(root.val) 59 | dfs(root.right) 60 | 61 | dfs(root) 62 | return res 63 | 64 | 65 | # 迭代 66 | class Solution: 67 | def inorderTraversal(self, root): 68 | res = [] 69 | stack = [] 70 | if not root: 71 | return [] 72 | while stack or root: 73 | if root: 74 | stack.append(root) 75 | root = root.left 76 | else: 77 | tmp = stack.pop() 78 | res.append(tmp.val) 79 | root = tmp.right # todo 怎么办怎么都看不懂 80 | return res 81 | 82 | 83 | # 先序打印二叉树(递归) 84 | def preOrderTraverse(node): 85 | if node is None: 86 | return None 87 | print(node.val) 88 | preOrderTraverse(node.left) 89 | preOrderTraverse(node.right) 90 | 91 | 92 | # 先序打印二叉树(非递归) 93 | def preOrderTravese(node): 94 | stack = [node] 95 | while len(stack) > 0: 96 | print(node.val) 97 | if node.right is not None: 98 | stack.append(node.right) 99 | if node.left is not None: 100 | stack.append(node.left) 101 | node = stack.pop() 102 | 103 | 104 | s = Solution() 105 | print(s.inorderTraversal(root)) 106 | -------------------------------------------------------------------------------- /10.树/95_generateTrees.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个整数 n,生成所有由 1 ... n 为节点所组成的二叉搜索树。 3 | 4 | 示例: 5 | 6 | 输入: 3 7 | 输出: 8 | [ 9 |   [1,null,3,2], 10 |   [3,2,null,1], 11 |   [3,1,null,null,2], 12 |   [2,1,3], 13 |   [1,null,2,null,3] 14 | ] 15 | 解释: 16 | 以上的输出对应以下 5 种不同结构的二叉搜索树: 17 | 18 | 1 3 3 2 1 19 | \ / / / \ \ 20 | 3 2 1 1 3 2 21 | / / \ \ 22 | 2 1 2 3 23 | 24 | """ 25 | 26 | 27 | class TreeNode(object): 28 | def __init__(self, x): 29 | self.val = x 30 | self.left = None 31 | self.right = None 32 | 33 | 34 | class Solution: 35 | def generateTrees(self, n): 36 | 37 | if n == 0: 38 | return [] 39 | s = [_ + 1 for _ in range(n)] 40 | return self.helper(s) 41 | 42 | def helper(self, s): 43 | if len(s) == 1: 44 | return [TreeNode(s[0])] 45 | if s == []: 46 | return [None] 47 | l = [] 48 | for i in range(len(s)): 49 | for a in self.helper(s[:i]): 50 | for b in self.helper(s[i + 1:]): 51 | root = TreeNode(s[i]) 52 | root.left = a 53 | root.right = b 54 | l.append(root) 55 | 56 | return l 57 | -------------------------------------------------------------------------------- /10.树/96-numTrees.py: -------------------------------------------------------------------------------- 1 | # 给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种? 2 | # 3 | # 示例: 4 | # 5 | # 输入: 3 6 | # 输出: 5 7 | # 解释: 8 | # 给定 n = 3, 一共有 5 种不同结构的二叉搜索树: 9 | # 10 | # 1 3 3 2 1 11 | # \ / / / \ \ 12 | # 3 2 1 1 3 2 13 | # / / \ \ 14 | # 2 1 2 3 15 | # Related Topics 树 动态规划 16 | 17 | 18 | # leetcode submit region begin(Prohibit modification and deletion) 19 | class Solution(object): 20 | def numTrees(self, n): 21 | """ 22 | :type n: int 23 | :rtype: int 24 | """ 25 | dp = [0] * (n + 1) 26 | dp[0] = 1 27 | dp[1] = 1 28 | for i in range(2, n + 1): 29 | for j in range(1, i + 1): 30 | dp[i] += dp[j - 1] * dp[i - j] 31 | return dp[n] 32 | # leetcode submit region end(Prohibit modification and deletion) 33 | -------------------------------------------------------------------------------- /10.树/98-isValidBST.py: -------------------------------------------------------------------------------- 1 | """ 2 | 98. 验证二叉搜索树 3 | 4 | 给定一个二叉树,判断其是否是一个有效的二叉搜索树。 5 | 假设一个二叉搜索树具有如下特征: 6 | 节点的左子树只包含小于当前节点的数。 7 | 节点的右子树只包含大于当前节点的数。 8 | 所有左子树和右子树自身必须也是二叉搜索树。 9 | """ 10 | 11 | 12 | # Definition for a binary tree node. 13 | class TreeNode(object): 14 | def __init__(self, x): 15 | self.val = x 16 | self.left = None 17 | self.right = None 18 | 19 | 20 | class Solution(object): 21 | def isValidBST(self, root: TreeNode): 22 | """ 23 | :type root: TreeNode 24 | :rtype: bool 25 | """ 26 | 27 | def inorderTravelsal(root): 28 | res, stack = [], [root] 29 | while stack: 30 | node = stack.pop() 31 | if isinstance(node, TreeNode): 32 | stack.extend([node.right, node.val, node.left]) 33 | elif isinstance(node, int): 34 | res.append(node) 35 | return res 36 | 37 | inorder_res = inorderTravelsal(root) 38 | print(inorder_res) 39 | if len(inorder_res) <= 1: 40 | return True 41 | for i in range(1, len(inorder_res)): 42 | if inorder_res[i - 1] >= inorder_res[i]: 43 | return False 44 | return True 45 | 46 | 47 | class Solution(object): 48 | def isValidBST(self, root): 49 | """ 50 | :type root: TreeNode 51 | :rtype: bool 52 | """ 53 | 54 | def helper(node, lower=float("-inf"), upper=float("inf")): 55 | if not node: 56 | return True # 递归结束条件 57 | 58 | val = node.val 59 | if val <= lower or val >= upper: # 如果触犯了搜索树的原则直接返回False 60 | return False 61 | if not helper(node.right, val, upper): # 判断右子树是否都大于当前节点值 62 | return False 63 | if not helper(node.left, lower, val): # 判断左子树是否都小于当前节点值 64 | return False 65 | return True # 如果都通过了就代表没问题 66 | 67 | return helper(root) 68 | -------------------------------------------------------------------------------- /10.树/post_pre_in_order_traversal.py: -------------------------------------------------------------------------------- 1 | class TreeNode: 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | 8 | A, B, C, D, E, F, G, H, I = [TreeNode(x) for x in 'ABCDEFGHI'] 9 | A.left, A.right = B, C 10 | B.right = D 11 | C.left, C.right = E, F 12 | E.left = G 13 | F.left, F.right = H, I 14 | 15 | A = TreeNode(0) 16 | A.right = TreeNode(-1) 17 | root = A 18 | 19 | 20 | # 先序 21 | 22 | 23 | # 用栈不断压入根、左孩子,通过pop来回溯父节点,再访问右孩子。 24 | class Solution: 25 | """ 26 | 先序遍历 中左右 27 | """ 28 | 29 | def preorderTraversal(self, root): 30 | # write your code here 31 | if not root: 32 | return [] 33 | stack = [] 34 | seq = [] # 记录先序访问序列 35 | while root or len(stack) != 0: 36 | if root: 37 | seq.append(root.val) # 先访问根节点 38 | stack.append(root) 39 | root = root.left 40 | else: 41 | root = stack.pop() # 回溯至父节点 42 | root = root.right 43 | print('***', [i.val for i in stack]) 44 | print('---', seq) 45 | return seq 46 | 47 | 48 | class Solution: 49 | """ 50 | 中序遍历 左中右 51 | """ 52 | 53 | def inorderTraversal(self, root): 54 | # write your code here 55 | if root is None: 56 | return [] 57 | stack = [] 58 | seq = [] 59 | output = [] 60 | while root or len(stack) != 0: 61 | if root: 62 | stack.append(root) 63 | root = root.left 64 | else: 65 | root = stack.pop() 66 | seq.append(root.val) # 左孩子先pop出来,再pop根节点 67 | root = root.right 68 | 69 | return seq 70 | 71 | 72 | class Solution: 73 | """ 74 | 先序:中左右 75 | 后续:左右中 76 | 即把先序顺序中的 ‘根左右’转换为‘根右左’,然后反过来就变成了‘左右根’。 77 | """ 78 | 79 | def postorderTraversal(self, root): 80 | # write your code here 81 | if root is None: 82 | return [] 83 | stack = [] 84 | seq = [] 85 | output = [] 86 | while root or len(stack) != 0: 87 | if root: 88 | seq.append(root.val) 89 | stack.append(root) 90 | root = root.right # 这从left变成了 right 91 | else: 92 | root = stack.pop() 93 | root = root.left # 这从right变成了 left 94 | 95 | while seq: # 后序遍历 是 将先序遍历的反过来 96 | output.append(seq.pop()) 97 | 98 | return output 99 | 100 | 101 | # class Solution: 102 | # def inorderTraversal(self, root: TreeNode): 103 | # stack,rst = [root],[] 104 | # while stack: 105 | # i = stack.pop() 106 | # if isinstance(i,TreeNode): 107 | # # stack.extend([i.right,i.val,i.left]) ## 中序遍历 108 | # stack.extend([i.val,i.right,i.left]) ## 后序遍历 109 | # # stack.extend([i.right,i.left,i.val]) ## 先序遍历 110 | # elif isinstance(i,int): 111 | # rst.append(i) 112 | # return rst 113 | class Solution: 114 | """ 115 | 其核心思想如下: 116 | 使用颜色标记节点的状态,新节点为白色,已访问的节点为灰色。 117 | 如果遇到的节点为白色,则将其标记为灰色,然后将其右子节点、自身、左子节点依次入栈。 118 | 如果遇到的节点为灰色,则将节点的值输出。 119 | 120 | 步骤: 121 | 1.第一个所入节点为root 122 | 2.while stack: 123 | color, node = stack.pop() 124 | 此时,如果节点颜色为白色: 125 | 则按照前序、中序、后序的逆顺序将数据压入栈,并且node节点为灰色 126 | 否则: 127 | result.append(node.val) 128 | """ 129 | 130 | def inorderTraversal(self, root): 131 | WHITE, GRAY = 0, 1 132 | res, stack = [], [(WHITE, root)] 133 | while stack: 134 | color, node = stack.pop() 135 | if node is None: 136 | continue 137 | if color == WHITE: 138 | # 先序 139 | stack.append((WHITE, node.right)) 140 | stack.append((WHITE, node.left)) 141 | stack.append((GRAY, node)) 142 | # 中序 143 | # stack.append((WHITE, node.right)) 144 | # stack.append((GRAY, node)) 145 | # stack.append((WHITE, node.left)) 146 | # # 后序 147 | # stack.append((GRAY, node)) 148 | # stack.append((WHITE, node.right)) 149 | # stack.append((WHITE, node.left)) 150 | else: 151 | res.append(node.val) 152 | return res 153 | 154 | 155 | class Solution: 156 | def inorderTraversal(self, root: TreeNode): 157 | stack, res = [root], [] 158 | while stack: 159 | node = stack.pop() 160 | if isinstance(node, TreeNode): 161 | stack.extend([node.right, node.val, node.left]) 162 | elif isinstance(node, int): 163 | res.append(node) 164 | return res 165 | 166 | 167 | # 补充一个层序遍历 168 | class Solution: 169 | def levelTraversal(self, root: TreeNode) -> List[int]: 170 | queue,rst = [root],[] 171 | while queue: 172 | i = queue.pop(0) 173 | if isinstance(i,TreeNode): 174 | queue.extend([i.val,i.left,i.right]) 175 | elif isinstance(i,int): 176 | rst.append(i) 177 | return rst 178 | 179 | 180 | s = Solution() 181 | # print(s.preorderTraversal(root)) 182 | print(s.inorderTraversal(root)) 183 | # print(s.postorderTraversal(root)) 184 | # print(s.levelTraversal(root)) 185 | # 先序 186 | res = ['A', 'B', 'D', 'C', 'E', 'G', 'F', 'H', 'I'] 187 | # 中序 188 | res = ['B', 'D', 'A', 'G', 'E', 'C', 'H', 'F', 'I'] 189 | # 后序 190 | res = ['D', 'B', 'G', 'E', 'H', 'I', 'F', 'C', 'A'] 191 | -------------------------------------------------------------------------------- /11.图/207-canFinish.py: -------------------------------------------------------------------------------- 1 | """ 2 | 你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 。 3 | 在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1] 4 | 给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习? 5 | 6 |   7 | 示例 1: 8 | 9 | 输入: 2, [[1,0]] 10 | 输出: true 11 | 解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。 12 | 示例 2: 13 | 14 | 输入: 2, [[1,0],[0,1]] 15 | 输出: false 16 | 解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成​课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。 17 |   18 | 19 | 提示: 20 | 21 | 输入的先决条件是由 边缘列表 表示的图形,而不是 邻接矩阵 。详情请参见图的表示法。 22 | 你可以假定输入的先决条件中没有重复的边。 23 | 1 <= numCourses <= 10^5 24 | 25 | 拓扑排序 入度 邻接矩阵 :https://blog.csdn.net/HYbuery/article/details/82787646 26 | 步骤: 27 | 一、定义 28 | 29 | 1.入度就是有向图中指向这个点的边的数量。 30 | 2.出度就是从这个点出去的边的数量。 31 | 3.邻接矩阵{出发点:【到达点】} 32 | 二、 33 | 1.在有向图中找出(没有前驱)入度为零的点,并且输出。 34 | 2.从图中删除以它为弧尾的边(删除从它出发的边) 35 | 3.重复1、2两步直至所有顶点全部输出,或者图中不存在入度为零的顶点(剩下的就是环),说明有向图有环。 36 | """ 37 | from collections import deque 38 | from typing import List 39 | 40 | 41 | class Solution: 42 | def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: 43 | indegrees = [0 for _ in range(numCourses)] 44 | adjacency = [[] for _ in range(numCourses)] 45 | queue = deque() 46 | # Get the indegree and adjacency of every course. 47 | for cur, pre in prerequisites: 48 | indegrees[cur] += 1 49 | adjacency[pre].append(cur) 50 | # Get all the courses with the indegree of 0. 51 | for i in range(len(indegrees)): 52 | if not indegrees[i]: queue.append(i) 53 | # BFS TopSort. 54 | print(numCourses, indegrees) 55 | """209 56 | 有零入度点,是一张图是dag的必要条件。因为dag是有向树加向外的边。 57 | 这意味着:有0入度点的图不一定是dag,没有0入度的图一定不是dag,一张图不是dag必有环 58 | """ 59 | while queue: 60 | print(11) 61 | pre = queue.popleft() 62 | numCourses -= 1 63 | for cur in adjacency[pre]: 64 | indegrees[cur] -= 1 65 | if not indegrees[cur]: 66 | queue.append(cur) 67 | return not numCourses 68 | 69 | 70 | numCourses, prerequisites = 2, [[1, 0], [0, 1]] 71 | s = Solution() 72 | res = s.canFinish(numCourses, prerequisites) 73 | print(res) 74 | -------------------------------------------------------------------------------- /12.位运算/136-singleNumber.py: -------------------------------------------------------------------------------- 1 | # 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 2 | # 3 | # 说明: 4 | # 5 | # 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗? 6 | # 7 | # 示例 1: 8 | # 9 | # 输入: [2,2,1] 10 | # 输出: 1 11 | # 12 | # 13 | # 示例 2: 14 | # 15 | # 输入: [4,1,2,1,2] 16 | # 输出: 4 17 | # Related Topics 位运算 哈希表 18 | from functools import reduce 19 | from typing import List 20 | 21 | 22 | 23 | "reduce() 函数会对参数序列中元素进行累积。" 24 | 25 | 26 | class Solution: 27 | def singleNumber(self, nums: List[int]) -> int: 28 | # print(reduce(lambda x, y: x ^ y, nums)) 29 | return reduce(lambda x, y: x ^ y, nums) 30 | 31 | 32 | class Solution: 33 | def singleNumber(self, nums: List[int]) -> int: 34 | xor = 0 35 | for i in range(len(nums)): 36 | print(i, nums[i], xor, nums[i] ^ xor) 37 | xor ^= nums[i] 38 | return xor 39 | 40 | 41 | """ 42 | 异或这招实在是太妙了 43 | print(i, nums[i], xor, nums[i] ^ xor) 44 | 0 4 0 4 45 | 1 1 4 5 46 | 2 2 5 7 47 | 3 1 7 6 48 | 4 2 6 4 49 | """ 50 | 51 | s = Solution() 52 | s.singleNumber([1, 1, 2, 2, 4]) 53 | # # leetcode submit region end(Prohibit modification and deletion) 54 | -------------------------------------------------------------------------------- /12.位运算/461_hammingDistance.py: -------------------------------------------------------------------------------- 1 | """ 2 | 两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。 3 | 4 | 给出两个整数 x 和 y,计算它们之间的汉明距离。 5 | 6 | 注意: 7 | 0 ≤ x, y < 231. 8 | 9 | 示例: 10 | 输入: x = 1, y = 4 11 | 输出: 2 12 | 13 | 解释: 14 | 1 (0 0 0 1) 15 | 4 (0 1 0 0) 16 | """ 17 | 18 | 19 | class Solution: 20 | def hammingDistance(self, x: int, y: int) -> int: 21 | # print(bin(x), bin(y)) 22 | x_bin = bin(x)[2:] 23 | y_bin = bin(y)[2:] 24 | len_ = max(len(x_bin), len(y_bin)) 25 | x_bin = x_bin if len(x_bin) == len_ else '0' * (len_ - len(x_bin)) + x_bin 26 | y_bin = y_bin if len(y_bin) == len_ else '0' * (len_ - len(y_bin)) + y_bin 27 | dis = 0 28 | for i in range(len_): 29 | if x_bin[i] != y_bin[i]: 30 | dis += 1 31 | return dis 32 | 33 | 34 | if __name__ == "__main__": 35 | s = Solution() 36 | x, y = 1, 4 37 | print(s.hammingDistance(x, y)) 38 | -------------------------------------------------------------------------------- /2.递归+DFS+BFS+回溯/131_partition.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。 3 | 4 | 返回 s 所有可能的分割方案。 5 | 6 | 示例: 7 | 8 | 输入: "aab" 9 | 输出: 10 | [ 11 | ["aa","b"], 12 | ["a","a","b"] 13 | ] 14 | """ 15 | 16 | 17 | class Solution(object): 18 | def partition(self, s): 19 | """ 20 | :type s: str 21 | :rtype: List[List[str]] 22 | """ 23 | res = [] 24 | 25 | def ishuiwen(s): 26 | return s == s[::-1] 27 | 28 | def dfs(l, start, end): 29 | if len(''.join(l)) == len(s): 30 | res.append(l) 31 | return 32 | cur = start 33 | while cur < len(s): 34 | if ishuiwen(s[start:cur + 1]): 35 | dfs(l + [s[start:cur + 1]], cur + 1, end) 36 | cur += 1 37 | # else: 38 | # break 39 | 40 | dfs(l=[], start=0, end=len(s)) 41 | return res 42 | 43 | 44 | s = Solution() 45 | s_ = 'aab' 46 | s_ = 'efe' 47 | res = s.partition(s_) 48 | print(res) 49 | -------------------------------------------------------------------------------- /2.递归+DFS+BFS+回溯/17_letterCombinations.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。 3 | 4 | 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 5 | 6 | ![](./img/17.png) 7 | 8 | 示例: 9 | 10 | 输入:"23" 11 | 输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]. 12 | 说明: 13 | 尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。 14 | 15 | 题解: 16 | 明明是递归,哪里有回溯? 17 | """ 18 | 19 | 20 | def f(n): 21 | if n <= 2: 22 | return 2 23 | else: 24 | return f(n - 1) * n 25 | 26 | 27 | class Solution: 28 | """ 29 | 完全自己写出来的哦。 30 | """ 31 | 32 | def letterCombinations(self, str_): 33 | map_dict = { 34 | '0': [], 35 | '1': [], 36 | '2': ['a', 'b', 'c'], 37 | '3': ['d', 'e', 'f'], 38 | '4': ['g', 'h', 'i'], 39 | '5': ['j', 'k', 'l'], 40 | '6': ['m', 'n', 'o'], 41 | '7': ['p', 'q', 'r', 's'], 42 | '8': ['t', 'u', 'v'], 43 | '9': ['w', 'x', 'y', 'z'], 44 | } 45 | s_list = [map_dict[i] for i in str_] 46 | 47 | def combination_between_two(l1, l2): 48 | combin_list = [] 49 | for i in l1: 50 | for j in l2: 51 | combin_list.append(i + j) 52 | return combin_list 53 | 54 | def dfs(s_list): 55 | if len(s_list) == 0: 56 | return [] 57 | elif len(s_list) <= 1: 58 | return s_list[0] 59 | elif len(s_list) == 2: 60 | return combination_between_two(s_list[0], s_list[1]) 61 | else: 62 | return dfs([combination_between_two(s_list[0], s_list[1])] + s_list[2:]) 63 | 64 | return dfs(s_list) 65 | 66 | 67 | class Solution: 68 | def letterCombinations(self, digits): 69 | """ 70 | 71 | 官方题解! 72 | :type digits: str 73 | :rtype: List[str] 74 | """ 75 | phone = {'2': ['a', 'b', 'c'], 76 | '3': ['d', 'e', 'f'], 77 | '4': ['g', 'h', 'i'], 78 | '5': ['j', 'k', 'l'], 79 | '6': ['m', 'n', 'o'], 80 | '7': ['p', 'q', 'r', 's'], 81 | '8': ['t', 'u', 'v'], 82 | '9': ['w', 'x', 'y', 'z']} 83 | 84 | def backtrack(combination, next_digits): 85 | # if there is no more digits to check 86 | if len(next_digits) == 0: 87 | # the combination is done 88 | output.append(combination) 89 | # if there are still digits to check 90 | else: 91 | # iterate over all letters which map 92 | # the next available digit 93 | for letter in phone[next_digits[0]]: 94 | # append the current letter to the combination 95 | # and proceed to the next digits 96 | 97 | backtrack(combination + letter, next_digits[1:]) 98 | 99 | output = [] 100 | if digits: 101 | backtrack("", digits) 102 | return output 103 | 104 | 105 | s = Solution() 106 | str_ = '234' 107 | res = s.letterCombinations(str_) 108 | print(res) 109 | print(len(res)) 110 | print(len(set(res))) 111 | -------------------------------------------------------------------------------- /2.递归+DFS+BFS+回溯/200-numIsland.py: -------------------------------------------------------------------------------- 1 | """ 2 | 岛屿数量 3 | 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。 4 | 一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。 5 | 6 | 示例: 7 | 示例 1: 8 | 输入: 9 | 11110 10 | 11010 11 | 11000 12 | 00000 13 | 输出: 1 14 | 示例 2: 15 | 输入: 16 | 11000 17 | 11000 18 | 00100 19 | 00011 20 | 输出: 3 21 | 22 | """ 23 | from typing import List 24 | 25 | 26 | class Solution: 27 | "方法一:深度优先遍历" 28 | # x-1,y 29 | # x,y-1 x,y x,y+1 30 | # x+1,y 31 | # 方向数组,它表示了相对于当前位置的 4 个方向的横、纵坐标的偏移量,这是一个常见的技巧 32 | directions = [(-1, 0), (0, -1), (1, 0), (0, 1)] 33 | 34 | def numIslands(self, grid: List[List[str]]) -> int: 35 | m = len(grid) 36 | # 特判 37 | if m == 0: 38 | return 0 39 | n = len(grid[0]) 40 | marked = [[False for _ in range(n)] for _ in range(m)] 41 | count = 0 42 | # 从第 1 行、第 1 格开始,对每一格尝试进行一次 DFS 操作 43 | for i in range(m): 44 | for j in range(n): 45 | # 只要是陆地,且没有被访问过的,就可以使用 DFS 发现与之相连的陆地,并进行标记 46 | if not marked[i][j] and grid[i][j] == '1': 47 | # count 可以理解为连通分量,你可以在深度优先遍历完成以后,再计数, 48 | # 即这行代码放在【位置 1】也是可以的 49 | count += 1 50 | self.__dfs(grid, i, j, m, n, marked) 51 | # 【位置 1】 52 | return count 53 | 54 | def __dfs(self, grid, i, j, m, n, marked): 55 | marked[i][j] = True 56 | for direction in self.directions: 57 | new_i = i + direction[0] 58 | new_j = j + direction[1] 59 | if 0 <= new_i < m and 0 <= new_j < n and not marked[new_i][new_j] and grid[new_i][new_j] == '1': 60 | self.__dfs(grid, new_i, new_j, m, n, marked) 61 | 62 | 63 | from typing import List 64 | from collections import deque 65 | 66 | 67 | class Solution: 68 | # x-1,y 69 | # x,y-1 x,y x,y+1 70 | # x+1,y 71 | # 方向数组,它表示了相对于当前位置的 4 个方向的横、纵坐标的偏移量,这是一个常见的技巧 72 | directions = [(-1, 0), (0, -1), (1, 0), (0, 1)] 73 | 74 | def numIslands(self, grid: List[List[str]]) -> int: 75 | m = len(grid) 76 | # 特判 77 | if m == 0: 78 | return 0 79 | n = len(grid[0]) 80 | marked = [[False for _ in range(n)] for _ in range(m)] 81 | count = 0 82 | # 从第 1 行、第 1 格开始,对每一格尝试进行一次 bFS 操作 83 | for i in range(m): 84 | for j in range(n): 85 | # 只要是陆地,且没有被访问过的,就可以使用 BFS 发现与之相连的陆地,并进行标记 86 | if not marked[i][j] and grid[i][j] == '1': 87 | # count 可以理解为连通分量,你可以在广度优先遍历完成以后,再计数, 88 | # 即这行代码放在【位置 1】也是可以的 89 | count += 1 90 | queue = deque() 91 | queue.append((i, j)) 92 | # 注意:这里要标记上已经访问过 93 | marked[i][j] = True 94 | while queue: 95 | cur_x, cur_y = queue.popleft() 96 | # marked[new_i][new_j] = True # todo 1 不能放在这里 97 | # 得到 4 个方向的坐标 98 | for direction in self.directions: 99 | new_i = cur_x + direction[0] 100 | new_j = cur_y + direction[1] 101 | # 如果不越界、没有被访问过、并且还要是陆地,我就继续放入队列,放入队列的同时,要记得标记已经访问过 102 | if 0 <= new_i < m and 0 <= new_j < n and not marked[new_i][new_j] and grid[new_i][ 103 | new_j] == '1': 104 | queue.append((new_i, new_j)) 105 | # 【特别注意】在放入队列以后,要马上标记成已经访问过,语义也是十分清楚的:反正只要进入了队列,你迟早都会遍历到它 106 | # 而不是在出队列的时候再标记 107 | # todo 2 【特别注意】如果是出队列的时候再标记,会造成很多重复的结点进入队列,造成重复的操作,这句话如果你没有写对地方,代码会严重超时的 108 | marked[new_i][new_j] = True 109 | # 【位置 1】 110 | return count 111 | 112 | 113 | class Solution: 114 | def numIslands(self, grid: List[List[str]]) -> int: 115 | directions = [(-1, 0), (0, -1), (1, 0), (0, 1)] 116 | n = len(grid[0]) 117 | m = len(grid) 118 | marked = [[False for _ in range(n)] for _ in range(m)] 119 | 120 | if m == 0: 121 | return 0 122 | count = 0 123 | 124 | def dfs(i, j, marked, grid): 125 | marked[i][j] = True # todo 1 注意放在这三个地方的不同,放在1可以,放在2 3不可以,会溢出 126 | for d in directions: 127 | new_i = i + d[0] 128 | new_j = j + d[1] 129 | print('---', i, j, d, marked[new_i][new_i]) 130 | if 0 <= new_i < m and 0 <= new_j < n and not marked[new_i][new_j] and grid[new_i][new_j] == '1': 131 | print('***', i, j, d, marked[new_i][new_i]) 132 | # marked[new_i][new_i] = True # todo 2 注意放在这三个地方的不同 133 | '原因在于,如果在这里该次循环之后的都会被marked' 134 | dfs(new_i, new_j, marked, grid) 135 | # marked[new_i][new_i]=True # todo 3 注意放在这三个地方的不 136 | 137 | for i in range(m): 138 | for j in range(n): 139 | if not marked[i][j] and grid[i][j] == '1': 140 | count += 1 141 | dfs(i, j, marked, grid) 142 | 143 | return count 144 | 145 | 146 | if __name__ == '__main__': 147 | grid = [['1', '1', '1', '1', '0'], 148 | ['1', '1', '0', '1', '0'], 149 | ['1', '1', '0', '0', '0'], 150 | ['0', '0', '0', '0', '0']] 151 | solution = Solution() 152 | result = solution.numIslands(grid) 153 | print(result) 154 | -------------------------------------------------------------------------------- /2.递归+DFS+BFS+回溯/22-generateParenthesis.py: -------------------------------------------------------------------------------- 1 | """ 2 | 括号生成: 3 | 给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。 4 | 5 | 例如,给出 n = 3,生成结果为: 6 | 7 | [ 8 | "((()))", 9 | "(()())", 10 | "(())()", 11 | "()(())", 12 | "()()()" 13 | ] 14 | """ 15 | from typing import List 16 | 17 | 18 | class Solution: 19 | def generateParenthesis(self, n: int) -> List[str]: 20 | res = [] 21 | 22 | def dfs(p=0, q=0, path=''): 23 | if len(path) == 2 * n: 24 | res.append(path) 25 | return 26 | if p < n: 27 | """ 28 | 区别在于, 29 | """ 30 | dfs(p + 1, q, path + '(') 31 | 32 | if q < p: 33 | 34 | dfs(p, q + 1, path + ')') 35 | 36 | dfs() 37 | return res 38 | 39 | 40 | 41 | s = Solution() 42 | print(s.generateParenthesis(n=3)) 43 | -------------------------------------------------------------------------------- /2.递归+DFS+BFS+回溯/28-Permutation.py: -------------------------------------------------------------------------------- 1 | """ 2 | 题目描述 3 | 输入一个字符串,按字典序打印出该字符串中字符的所有排列。 4 | 例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 5 | """ 6 | 7 | 8 | class Solution: 9 | def Permutation(self, ss): 10 | result = [] 11 | 12 | def helper(ss, tmp): 13 | if len(tmp) == len(ss): 14 | result.append(tmp) 15 | return 16 | 17 | for i in range(len(ss)): 18 | if ss[i] not in tmp: 19 | helper(ss, tmp + [ss[i]]) 20 | 21 | helper(ss, []) 22 | return result 23 | 24 | 25 | s = Solution() 26 | ss = 'abc' 27 | res = s.Permutation(ss) 28 | print(res) 29 | -------------------------------------------------------------------------------- /2.递归+DFS+BFS+回溯/39-combinationSum.py: -------------------------------------------------------------------------------- 1 | """组合总数: 2 | 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。 3 | 4 | candidates 中的数字可以无限制重复被选取。 5 | 6 | 说明: 7 | 8 | 所有数字(包括 target)都是正整数。 9 | 解集不能包含重复的组合。 10 | 示例 1: 11 | 12 | 输入: candidates = [2,3,6,7], target = 7, 13 | 所求解集为: 14 | [ 15 | [7], 16 | [2,2,3] 17 | ] 18 | """ 19 | from typing import List 20 | 21 | 22 | class Solution: 23 | 24 | def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: 25 | # candidates = [] 26 | res = [] 27 | path = [] 28 | # candidates.sort() # todo 先排序,目的是结果比较稳定 29 | self.dfs(candidates, target, path, res, 0, size=len(candidates)) 30 | return res 31 | 32 | def dfs(self, candidates, target, path, res, begin, size): 33 | # path = [] 34 | # while target>0: 35 | if target == 0: 36 | res.append(path[:]) # TODO 一定是[:]或者。copy 37 | return 38 | for index in range(0, size): # todo 如果变成range(0, size) 就是最全结果:[[2, 2, 3], [2, 3, 2], [3, 2, 2], [7]] 39 | 40 | residue = target - candidates[index] 41 | # “剪枝”操作,不必递归到下一层,并且后面的分支也不必执行 42 | if residue < 0: 43 | break 44 | 45 | # # todo 注意前/后三行的区别! 不能是target-=candidates[index] 46 | # if target - candidates[index] < 0: 47 | # break 48 | # target -= candidates[index] 49 | path.append(candidates[index]) 50 | print(index, begin, target, path[:], res) 51 | 52 | # todo 这里怎么使得path在纵向传递而不是横向传递? 53 | self.dfs(candidates, residue, path, res, index, size) 54 | path.pop() 55 | 56 | 57 | class Solution: 58 | def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: 59 | size = len(candidates) 60 | if size == 0: 61 | return [] 62 | 63 | # 剪枝是为了提速,在本题非必需 64 | candidates.sort() # TODO 这里不加sort结果就变成[[7]] ?? 65 | # 在遍历的过程中记录路径,它是一个栈 66 | path = [] 67 | res = [] 68 | # 注意要传入 size ,在 range 中, size 取不到 69 | self.__dfs(candidates, 0, size, path, res, target) 70 | return res 71 | 72 | def __dfs(self, candidates, begin, size, path, res, target): 73 | # 先写递归终止的情况 74 | if target == 0: 75 | # Python 中可变对象是引用传递,因此需要将当前 path 里的值拷贝出来 76 | # 或者使用 path.copy() 77 | res.append(path[:]) 78 | print(target, path, res) 79 | 80 | return 81 | 82 | for index in range(begin, size): 83 | residue = target - candidates[index] 84 | # “剪枝”操作,不必递归到下一层,并且后面的分支也不必执行 85 | if residue < 0: 86 | break 87 | path.append(candidates[index]) 88 | # 因为下一层不能比上一层还小,起始索引还从 index 开始 89 | self.__dfs(candidates, index, size, path, res, residue) 90 | path.pop() 91 | 92 | 93 | class Solution: 94 | 95 | def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: 96 | res = [] 97 | tmp = [] 98 | 99 | def dfs(candidates, target, tmp): 100 | if target == 0: 101 | res.append(tmp[:]) 102 | return 103 | 104 | for each in candidates: 105 | if target - each < 0: 106 | continue 107 | remaind = target - each 108 | tmp.append(each) 109 | dfs(candidates, remaind, tmp) 110 | tmp.pop() 111 | 112 | dfs(candidates, target, tmp) 113 | return res 114 | 115 | 116 | if __name__ == '__main__': 117 | candidates = [2, 6, 3, 7] 118 | target = 7 119 | solution = Solution() 120 | result = solution.combinationSum(candidates, target) 121 | print(result) 122 | -------------------------------------------------------------------------------- /2.递归+DFS+BFS+回溯/4-findMedianSortedArrays.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。 3 | 4 | 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。 5 | 6 | 你可以假设 nums1 和 nums2 不会同时为空。 7 | 8 | 示例 1: 9 | 注意:n为偶数的时候,中位数是下标是(n/2+n/2+1)/2;奇数的时候下标为(n+1)/2 10 | 11 | nums1 = [1, 3] 12 | nums2 = [2] 13 | 则中位数是 2.0 14 | 15 | 困难难题! 16 | """ 17 | from typing import List 18 | 19 | 20 | class Solution: 21 | def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float: 22 | """ 23 | :type nums1: List[int] 24 | :type nums2: List[int] 25 | :rtype: float 26 | """ 27 | # print(nums1, nums2) 28 | m, n = len(nums1), len(nums2) 29 | mid = (m + n) // 2 # 这里要减去1哦! 30 | 31 | def get_k(nums1, nums2, k): 32 | """ 33 | - 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较 34 | - 这里的 "/" 表示整除 35 | - nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个 36 | - nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个 37 | - 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个 38 | - 这样 pivot 本身最大也只能是第 k-1 小的元素 39 | - 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组 40 | - 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组 41 | - 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数 42 | """ 43 | 44 | index1, index2 = 0, 0 45 | while True: 46 | # 特殊情况 47 | if index1 == m: 48 | return nums2[index2 + k - 1] 49 | if index2 == n: 50 | return nums1[index1 + k - 1] 51 | if k == 1: 52 | return min(nums1[index1], nums2[index2]) 53 | 54 | # 正常情况 55 | new_index1 = min(index1 + k // 2 - 1, m - 1) 56 | new_index2 = min(index2 + k // 2 - 1, n - 1) 57 | if nums1[new_index1] <= nums2[new_index2]: 58 | k = k - (new_index1 - index1 + 1) 59 | index1 = new_index1 + 1 60 | else: 61 | k = k - (new_index2 - index2 + 1) 62 | index2 = new_index2 + 1 63 | 64 | if (m + n) % 2 != 0: 65 | return get_k(nums1, nums2, mid + 1) 66 | else: 67 | return (get_k(nums1, nums2, mid) + get_k(nums1, nums2, mid + 1)) / 2 68 | 69 | 70 | s = Solution() 71 | nums1 = [1, 3] 72 | nums2 = [2] 73 | 74 | # nums1 = [1, 3] 75 | # nums2 = [2,2] 76 | nums1 = [1, 2] 77 | nums2 = [3, 4] 78 | # # 预期结果 2.5 ? 79 | res = s.findMedianSortedArrays(nums1, nums2) 80 | # print(res) 81 | # r=findMedianSortedArrays(nums1, nums2) 82 | # print(r) 83 | -------------------------------------------------------------------------------- /2.递归+DFS+BFS+回溯/416-canPartition.py: -------------------------------------------------------------------------------- 1 | # 给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 2 | # 3 | # 注意: 4 | # 5 | # 6 | # 每个数组中的元素不会超过 100 7 | # 数组的大小不会超过 200 8 | # 9 | # 10 | # 示例 1: 11 | # 12 | # 输入: [1, 5, 11, 5] 13 | # 14 | # 输出: true 15 | # 16 | # 解释: 数组可以分割成 [1, 5, 5] 和 [11]. 17 | # 18 | # 19 | # 20 | # 21 | # 示例 2: 22 | # 23 | # 输入: [1, 2, 3, 5] 24 | # 25 | # 输出: false 26 | # 27 | # 解释: 数组不能分割成两个元素和相等的子集. 28 | # 29 | # 30 | # 31 | # Related Topics 动态规划 32 | from typing import List 33 | 34 | 35 | class Solution: 36 | def canPartition(self, nums: List[int]) -> bool: 37 | if sum(nums) % 2 == 1 or len(nums) < 2: 38 | return False 39 | 40 | half = sum(nums) // 2 41 | nums.sort(reverse=True) 42 | if nums[0] > half: 43 | return False 44 | 45 | @lru_cache(None) # 不加就超时了呢 46 | def dfs(target, i): 47 | if target == nums[i]: 48 | return True 49 | 50 | if target > nums[i]: 51 | for j in range(i + 1, len(nums)): 52 | if dfs(target - nums[i], j): 53 | return True 54 | return False 55 | 56 | return dfs(half, 0) 57 | 58 | 59 | from typing import List 60 | 61 | 62 | # leetcode submit region begin(Prohibit modification and deletion) 63 | # from collections import lru_cache 64 | class Solution: 65 | def canPartition(self, nums: List[int]) -> bool: 66 | nums.sort(reverse=True) 67 | sum_ = sum(nums) 68 | if sum_ % 2 != 0 or len(nums) < 2: 69 | return False 70 | 71 | per_sum = sum_ / 2 72 | if nums[0] > per_sum: 73 | return False 74 | n = len(nums) 75 | 76 | def dfs(cursum, loc): 77 | if cursum + nums[loc] == per_sum: 78 | return True 79 | if loc >= n: 80 | return False 81 | 82 | if cursum + nums[loc] < per_sum: 83 | for i in range(loc + 1, n): 84 | if dfs(cursum + nums[i], i): 85 | return True 86 | return False 87 | 88 | return dfs(cursum=0, loc=0) 89 | 90 | 91 | # nums=[1, 5, 11, 5] 92 | # nums=[2,2,1,1] 93 | nums = [2, 2, 3, 5] 94 | s = Solution() 95 | print(s.canPartition(nums)) 96 | 97 | # leetcode submit region end(Prohibit modification and deletion) 98 | -------------------------------------------------------------------------------- /2.递归+DFS+BFS+回溯/46-permute.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个 没有重复 数字的序列,返回其所有可能的全排列。 3 | 4 | 示例: 5 | 6 | 输入: [1,2,3] 7 | 输出: 8 | [ 9 | [1,2,3], 10 | [1,3,2], 11 | [2,1,3], 12 | [2,3,1], 13 | [3,1,2], 14 | [3,2,1] 15 | ] 16 | 17 | 链接:https://leetcode-cn.com/problems/permutations 18 | """ 19 | from typing import List 20 | 21 | 22 | 23 | class Solution: 24 | def permute(self, nums: List[int]) -> List[List[int]]: 25 | res = [] 26 | tmp = [] 27 | 28 | def backtrack(nums, tmp): 29 | if len(tmp) == len(nums): 30 | res.append(tmp[:]) 31 | return # todo 32 | 33 | for i in nums: 34 | if i not in tmp: 35 | backtrack(nums, tmp + [i]) # 纵向 36 | 37 | # todo 更优雅的写法:append pop => backtrack(nums, tmp+[i]) 38 | 39 | backtrack(nums, tmp) 40 | return res 41 | 42 | 43 | if __name__ == "__main__": 44 | s = Solution() 45 | input = [1, 2, 3] 46 | print(s.permute(input)) 47 | -------------------------------------------------------------------------------- /2.递归+DFS+BFS+回溯/698-canPartitionKSubsets.py: -------------------------------------------------------------------------------- 1 | # 给定一个整数数组 nums 和一个正整数 k,找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。 2 | # 3 | # 示例 1: 4 | # 5 | # 输入: nums = [4, 3, 2, 3, 5, 2, 1], k = 4 6 | # 输出: True 7 | # 说明: 有可能将其分成 4 个子集(5),(1,4),(2,3),(2,3)等于总和。 8 | # 9 | # 10 | # 11 | # 提示: 12 | # 13 | # 14 | # 1 <= k <= len(nums) <= 16 15 | # 0 < nums[i] < 10000 16 | # 17 | # Related Topics 递归 动态规划 18 | 19 | 20 | from typing import List 21 | 22 | 23 | class Solution: 24 | # leetcode submit region begin(Prohibit modification and deletion) 25 | class Solution: 26 | def canPartitionKSubsets(self, nums: List[int], k: int) -> bool: 27 | per_sum = sum(nums) / k 28 | nums.sort(reverse=True) 29 | if sum(nums) % k != 0: 30 | return False 31 | 32 | visited = [0] * len(nums) 33 | 34 | def dfs(start, cursum, k, visited): 35 | if k == 0: 36 | return True 37 | 38 | if cursum == per_sum: 39 | return dfs(0, 0, k - 1, visited) # todo dfs(0,0, k-1, visited) vs return dfs(0,0, k-1, visited) 40 | elif cursum > per_sum: 41 | return False 42 | for i in range(start, len(nums)): 43 | if not visited[i] and cursum + nums[i] <= per_sum: 44 | visited[i] = 1 45 | if dfs(i + 1, cursum + nums[i], k, visited): # todo 46 | return True 47 | visited[i] = 0 # todo 回溯 48 | 49 | res = dfs(start=0, cursum=0, k=k, visited=visited) 50 | return res if res else False 51 | 52 | 53 | nums = [4, 3, 2, 3, 5, 2, 1] 54 | k = 4 55 | s = Solution() 56 | print(s.canPartitionKSubsets(nums, k)) 57 | -------------------------------------------------------------------------------- /2.递归+DFS+BFS+回溯/78-subsets.py: -------------------------------------------------------------------------------- 1 | # 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。 2 | # 3 | # 说明:解集不能包含重复的子集。 4 | # 5 | # 示例: 6 | # 7 | # 输入: nums = [1,2,3] 8 | # 输出: 9 | # [ 10 | # [3], 11 | #   [1], 12 | #   [2], 13 | #   [1,2,3], 14 | #   [1,3], 15 | #   [2,3], 16 | #   [1,2], 17 | #   [] 18 | # ] 19 | # Related Topics 位运算 数组 回溯算法 20 | 21 | 22 | """ 23 | 方法二:递归 24 | f([1,2]) = [[], [1,2], [2], [1]]; 25 | f([1,2,3]) = [[], [1], [2], [1,2], [3], [1,3], [2,3], [1,2,3]] 26 | f([1,2,3])是在f([1,2])的元素基础上,对每个元素多加一个3 27 | 28 | """ 29 | 30 | 31 | class Solution(object): 32 | def subsets(self, nums): 33 | if nums == []: 34 | return [[]] 35 | return self.subsets(nums[:-1]) + [i + [nums[-1]] for i in self.subsets(nums[:-1])] 36 | 37 | 38 | """动态规划 39 | 跟上面递归的想法类似,就是找出当前的结果跟前一个结果的关系: 40 | dp[i] = dp[i-1] + [each+[nums[i]] for each in dp[i-1]] 41 | """ 42 | 43 | 44 | class Solution(object): 45 | def subsets(self, nums): 46 | dp = [[]] 47 | for i in range(len(nums)): 48 | dp = dp + [each + [nums[i]] for each in dp] 49 | return dp 50 | 51 | 52 | s = Solution() 53 | s.subsets([1, 2, 3]) 54 | -------------------------------------------------------------------------------- /2.递归+DFS+BFS+回溯/79-exist.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个二维网格和一个单词,找出该单词是否存在于网格中。 3 | 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。 4 | 示例: 5 | 6 | board = 7 | [ 8 | ['A','B','C','E'], 9 | ['S','F','C','S'], 10 | ['A','D','E','E'] 11 | ] 12 | 给定 word = "ABCCED", 返回 true 13 | 给定 word = "SEE", 返回 true 14 | 给定 word = "ABCB", 返回 false 15 | 16 | 题解: 17 | 之前做过的华为机试有点像:https://www.nowcoder.com/practice/cf24906056f4488c9ddb132f317e03bc?tpId=37&tqId=21266&tPage=3&rp=&ru=/ta/huawei&qru=/ta/huawei/question-ranking 18 | """ 19 | from typing import List 20 | 21 | 22 | class Solution(object): 23 | # 定义上下左右四个行走方向 24 | directs = [(0, 1), (0, -1), (1, 0), (-1, 0)] 25 | 26 | def exist(self, board, word): 27 | """ 28 | :type board: List[List[str]] 29 | :type word: str 30 | :rtype: bool 31 | """ 32 | m = len(board) 33 | if m == 0: 34 | return False 35 | n = len(board[0]) 36 | mark = [[0 for _ in range(n)] for _ in range(m)] 37 | 38 | for i in range(len(board)): 39 | for j in range(len(board[0])): 40 | if board[i][j] == word[0]: 41 | # 将该元素标记为已使用 42 | mark[i][j] = 1 43 | if self.backtrack(i, j, mark, board, word[1:]) == True: 44 | return True 45 | else: 46 | # 回溯 47 | mark[i][j] = 0 48 | return False 49 | 50 | def backtrack(self, i, j, mark, board, word): 51 | if len(word) == 0: 52 | return True 53 | 54 | for direct in self.directs: 55 | cur_i = i + direct[0] 56 | cur_j = j + direct[1] 57 | 58 | if cur_i >= 0 and cur_i < len(board) and cur_j >= 0 and cur_j < len(board[0]) and board[cur_i][cur_j] == \ 59 | word[0]: 60 | # 如果是已经使用过的元素,忽略 61 | if mark[cur_i][cur_j] == 1: 62 | continue 63 | # 将该元素标记为已使用 64 | mark[cur_i][cur_j] = 1 65 | if self.backtrack(cur_i, cur_j, mark, board, word[1:]) == True: 66 | return True 67 | else: 68 | # 回溯 69 | mark[cur_i][cur_j] = 0 70 | return False 71 | 72 | 73 | board = [ 74 | ['A', 'B', 'C', 'E'], 75 | ['S', 'F', 'C', 'S'], 76 | ['A', 'D', 'E', 'S'] 77 | ] 78 | word = 'SSE' 79 | # board = [ 80 | # ["A", "B", "C", "E"], 81 | # ["S", "F", "C", "S"], 82 | # ["A", "D", "E", "E"] 83 | # ] 84 | # word = "ABCCED" 85 | 86 | board = [ 87 | ["A", "B", "C", "E"], 88 | ["S", "F", "C", "S"], 89 | ["A", "D", "E", "E"]] 90 | word = "ABCB" # TODO 怎么是在b已经被用过,不能再用 91 | # start_ids = [ for i in range(len(board) for j in range(len(board[0])))] 92 | 93 | # board=[["a"]] 94 | # word="a" 95 | 96 | board = [ 97 | ["C", "A", "A"], 98 | ["A", "A", "A"], 99 | ["B", "C", "D"]] 100 | word = "AAB" 101 | s = Solution() 102 | print(board) 103 | print(s.exist(board, word=word)) 104 | -------------------------------------------------------------------------------- /3.栈/155-stack_minist.py: -------------------------------------------------------------------------------- 1 | """ 2 | 设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。 3 | 4 | push(x) -- 将元素 x 推入栈中。 5 | pop() -- 删除栈顶的元素。 6 | top() -- 获取栈顶元素。 7 | getMin() -- 检索栈中的最小元素。 8 | """ 9 | 10 | 11 | class Node(object): 12 | def __init__(self, value): 13 | self.value = value 14 | self.next = None 15 | 16 | 17 | class MinStack: 18 | 19 | def __init__(self): 20 | """ 21 | initialize your data structure here. 22 | """ 23 | self.stack = [] 24 | 25 | def push(self, x: int) -> None: 26 | self.stack.append(x) 27 | 28 | def pop(self) -> None: 29 | return self.stack.pop(-1) 30 | 31 | def top(self) -> int: 32 | return self.stack.pop() 33 | 34 | def getMin(self) -> int: 35 | return min(self.stack) 36 | 37 | # Your MinStack object will be instantiated and called as such: 38 | # obj = MinStack() 39 | # obj.push(x) 40 | # obj.pop() 41 | # param_3 = obj.top() 42 | # param_4 = obj.getMin() 43 | -------------------------------------------------------------------------------- /3.栈/20-valid_brackets.py: -------------------------------------------------------------------------------- 1 | """ 2 | 题目描述: 3 | 4 | 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。 5 | 6 | 有效字符串需满足: 7 | 8 | 左括号必须用相同类型的右括号闭合。 9 | 左括号必须以正确的顺序闭合。 10 | 注意空字符串可被认为是有效字符串。 11 | 12 | 示例 1: 13 | 14 | 输入: "()" 15 | 输出: true 16 | 示例 2: 17 | 18 | 输入: "()[]{}" 19 | 输出: true 20 | 示例 3: 21 | 22 | 输入: "(]" 23 | 输出: false 24 | "({())(})" 25 | "({()})()" 26 | 27 | 考查:栈 28 | 1.拿到一个题首先要考虑有几种情况: 29 | 1)空和奇数个直接排除 30 | 2)剩下偶数个,若第一个就是右括号直接排除,不是右括号再入栈。 31 | 2.画个流程图辅助。 32 | 3.for else的用法。for循环完后执行else语句块,若for中有break,则跳过else。 33 | 4.l[-1]为l的最后一个元素。 34 | """ 35 | 36 | 37 | # 我的 38 | class Solution(object): 39 | def isValid(self, s): 40 | """ 41 | :type s: str 42 | :rtype: bool 43 | """ 44 | d = {'(': ')', '{': '}', '[': ']'} 45 | # 奇数个排除! 46 | if len(s) % 2 != 0: 47 | return False 48 | if len(s) == 0: 49 | return True 50 | if s[0] in [')', ']', '}']: 51 | return False 52 | stack = [] 53 | for i in s: 54 | if i in d: 55 | stack.append(i) 56 | else: 57 | if i != d[stack.pop()]: 58 | return False 59 | return len(stack) == 0 60 | 61 | 62 | class Solution: 63 | def isValid(self, s: str) -> bool: 64 | d = {'(': ')', '{': '}', '[': ']'} 65 | stack = [] 66 | # 奇数个排除! 67 | if len(s) % 2 != 0 or len(s) == 0: 68 | return False 69 | 70 | for i in s: 71 | if i in d: 72 | stack.append(i) 73 | else: 74 | # 对称的,对称的话,则第一次出现的反括号一定是最近的正括号 75 | if i != d[stack.pop()]: 76 | return False 77 | 78 | # return True 79 | return stack == [] 80 | 81 | 82 | in_ = "({()})()" 83 | # in_="({())(})" 84 | 85 | s = Solution() 86 | res = s.isValid(in_) 87 | print(res) 88 | -------------------------------------------------------------------------------- /3.栈/739-dailyTemperatures.py: -------------------------------------------------------------------------------- 1 | # 请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。 2 | # 如果气温在这之后都不会升高,请在该位置用 0 来代替。 3 | # 4 | # 5 | # 例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2 6 | # , 1, 1, 0, 0]。 7 | # 8 | # 提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。 9 | # Related Topics 栈 哈希表 10 | 11 | 12 | # leetcode submit region begin(Prohibit modification and deletion) 13 | from typing import List 14 | 15 | 16 | class Solution: 17 | def dailyTemperatures(self, T: List[int]) -> List[int]: 18 | 19 | res = [0] * len(T) 20 | stack = [0] 21 | for i in range(1, len(T)): 22 | while stack and T[i] > T[stack[-1]]: 23 | tmp = stack.pop() 24 | res[tmp] = i - tmp 25 | stack.append(i) 26 | return res 27 | 28 | # leetcode submit region end(Prohibit modification and deletion) 29 | -------------------------------------------------------------------------------- /4.HashDict/128-longestConsecutive.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个未排序的整数数组,找出最长连续序列的长度。 3 | 4 | 要求算法的时间复杂度为 O(n)。 5 | 6 | 示例: 7 | 8 | 输入: [100, 4, 200, 1, 3, 2] 9 | 输出: 4 10 | 解释: 最长连续序列是 [1, 2, 3, 4]。它的长度为 4。 11 | 12 | """ 13 | 14 | 15 | class Solution: 16 | def longestConsecutive(self, nums): 17 | if not nums: 18 | return 0 19 | 20 | nums.sort() 21 | 22 | longest_streak = 1 23 | current_streak = 1 24 | 25 | for i in range(1, len(nums)): 26 | if nums[i] != nums[i - 1]: 27 | if nums[i] == nums[i - 1] + 1: 28 | current_streak += 1 29 | else: 30 | longest_streak = max(longest_streak, current_streak) 31 | current_streak = 1 32 | print(longest_streak, current_streak) 33 | return max(longest_streak, current_streak) 34 | 35 | 36 | class Solution: 37 | # @param num, a list of integer 38 | # @return an integer 39 | def longestConsecutive(self, num): 40 | dict = {x: False for x in num} # False means not visited 41 | maxLen = -1 42 | for i in dict: 43 | if dict[i] == False: 44 | curr = i + 1 45 | lenright = 0 46 | while curr in dict: 47 | lenright += 1 48 | dict[curr] = True 49 | curr += 1 50 | curr = i - 1 51 | lenleft = 0 52 | while curr in dict: 53 | lenleft += 1 54 | dict[curr] = True; 55 | curr -= 1 56 | maxLen = max(maxLen, lenleft + 1 + lenright) 57 | return maxLen 58 | 59 | 60 | numCourses, prerequisites = 2, [[1, 0], [0, 1]] 61 | s = Solution() 62 | nums = [100, 4, 200, 1, 3, 2] 63 | res = s.longestConsecutive(nums) 64 | print(res) 65 | -------------------------------------------------------------------------------- /4.HashDict/1_twoSum.py: -------------------------------------------------------------------------------- 1 | """" 2 | 题目 3 | 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标 4 | 5 | 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素 6 | 7 | 示例: 8 | 给定 nums = [2, 7, 11, 15], target = 9 9 | 10 | 因为 nums[0] + nums[1] = 2 + 7 = 9 11 | 所以返回 [0, 1] 12 | """ 13 | 14 | 15 | # hash 16 | class Solution(object): 17 | def twoSum(self, nums, target): 18 | """ 19 | :type nums: List[int] 20 | :type target: int 21 | :rtype: List[int] 22 | """ 23 | n = len(nums) 24 | d = {} 25 | for i in range(0, n): 26 | reduction = target - nums[i] 27 | if i > 0 and nums[i] in d: 28 | return [d[nums[i]], i] 29 | d[reduction] = i # 这个放在这里哈 30 | 31 | 32 | # list_a = [1,2,3,4] 33 | # list_b = [3,1,2] 34 | # set_c = set(list_a) & set(list_b) 35 | # list_c = list(set_c) 36 | # print(list_c) 37 | 38 | class Solution(object): 39 | def threeSum(self, nums): 40 | """ 41 | :type nums: List[int] 42 | :rtype: List[List[int]] 43 | """ 44 | 45 | def twoSum(nums, target): 46 | n = len(nums) 47 | d = {} 48 | for i in range(0, n): 49 | reduction = target - nums[i] 50 | if i > 0 and nums[i] in d: 51 | return [d[nums[i]], i] 52 | d[reduction] = i # 这个放在这里哈 53 | 54 | def equal(list_a, list_b): 55 | list_c = set(list_a) & set(list_b) 56 | 57 | return len(list_c) == len(set(list_a)) 58 | 59 | def drop_suplicates(lists): 60 | new_lists = [] 61 | for idx, i in enumerate(lists): 62 | is_equal = False 63 | for j in lists[idx + 1:len(lists)]: 64 | print(i, j, equal(i, j)) 65 | if equal(i, j): 66 | is_equal = True 67 | break 68 | if not is_equal: 69 | new_lists.append(i) 70 | return new_lists 71 | 72 | m = len(nums) 73 | res = [] 74 | target = 0 75 | for j in range(0, m): 76 | cur_nums = nums[:j] + nums[j + 1:m] 77 | two_sum_res = twoSum(cur_nums, target - nums[j]) 78 | if two_sum_res: 79 | res.append([nums[j], cur_nums[two_sum_res[0]], cur_nums[two_sum_res[1]]]) 80 | # print(res, drop_suplicates(res)) 81 | # print(res) 82 | return drop_suplicates(res) 83 | 84 | 85 | s = Solution() 86 | nums = [-4, -2, -2, -2, 0, 1, 2, 2, 2, 3, 3, 4, 4, 6, 6] 87 | # [3,-4,1],,[6,-4,-2]] 88 | # [[-4,-2,6],[-4,0,4],[-4,1,3]] 89 | 90 | print(s.threeSum(nums=nums 91 | )) 92 | -------------------------------------------------------------------------------- /4.HashDict/236_publicTreeNode.py: -------------------------------------------------------------------------------- 1 | """ 2 | 236题 二叉树的最近公共祖先 3 | 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 4 | 5 | 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x, 6 | 满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” 7 | 8 | 例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4] 9 | 示例 1: 10 | 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 11 | 输出: 3 12 | 解释: 节点 5 和节点 1 的最近公共祖先是节点 3。 13 | 示例 2: 14 | 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4 15 | 输出: 5 16 | 解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。 17 | """ 18 | 19 | 20 | # Definition for a binary tree node. 21 | class TreeNode(object): 22 | def __init__(self, x): 23 | self.val = x 24 | self.left = None 25 | self.right = None 26 | 27 | 28 | A, B, C, D, E, F, G, H, I = [TreeNode(x) for x in 'ABCDEFGHI'] 29 | A.left, A.right = B, C 30 | B.right = D 31 | C.left, C.right = E, F 32 | E.left = G 33 | F.left, F.right = H, I 34 | 35 | A = TreeNode(0) 36 | A.right = TreeNode(-1) 37 | root = A 38 | 39 | 40 | class Solution: 41 | def publicTreeNode(self, root, p, q): 42 | def down(root, p): 43 | while root: 44 | pass 45 | 46 | 47 | class Solution: 48 | """ 49 | @param: root: The root of the binary search tree. 50 | @param: A: A TreeNode in a Binary. 51 | @param: B: A TreeNode in a Binary. 52 | @return: Return the least common ancestor(LCA) of the two nodes. 53 | """ 54 | 55 | def lowestCommonAncestor(self, root, A, B): 56 | # A&B=>LCA 57 | # !A&!B=>None 58 | # A&!B=>A 59 | # B&!A=>B 60 | if (root is None or root == A or root == B): 61 | return root # 若root为空或者root为A或者root为B,说明找到了A和B其中一个 62 | left = self.lowestCommonAncestor(root.left, A, B) 63 | right = self.lowestCommonAncestor(root.right, A, B) 64 | if left and right: 65 | return root # 若左子树找到了A,右子树找到了B,说明此时的root就是公共祖先 66 | if not left: # 若左子树是none右子树不是,说明右子树找到了A或B 67 | return right 68 | if not right: # 同理 69 | return left 70 | return None 71 | -------------------------------------------------------------------------------- /4.HashDict/560-subarraySum.py: -------------------------------------------------------------------------------- 1 | # 给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。 2 | # 3 | # 示例 1 : 4 | # 5 | # 6 | # 输入:nums = [1,1,1], k = 2 7 | # 输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。 8 | # 9 | # 10 | # 说明 : 11 | # 12 | # 13 | # 数组的长度为 [1, 20,000]。 14 | # 数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。 15 | # 16 | # Related Topics 数组 哈希表 17 | 18 | 19 | # leetcode submit region begin(Prohibit modification and deletion) 20 | 21 | import collections 22 | from typing import List 23 | 24 | 25 | class Solution: 26 | def subarraySum(self, nums: List[int], k: int) -> int: 27 | # num_times 存储某“前缀和”出现的次数,这里用collections.defaultdict来定义它 28 | # 如果某前缀不在此字典中,那么它对应的次数为0 29 | num_times = collections.defaultdict(int) 30 | num_times[0] = 1 # 先给定一个初始值,代表前缀和为0的出现了一次 31 | cur_sum = 0 # 记录到当前位置的前缀和 32 | res = 0 33 | for i in range(len(nums)): 34 | cur_sum += nums[i] # 计算当前前缀和 35 | if cur_sum - k in num_times: # 如果前缀和减去目标值k所得到的值在字典中出现,即当前位置前缀和减去之前某一位的前缀和等于目标值 36 | res += num_times[cur_sum - k] 37 | # 下面一句实际上对应两种情况,一种是某cur_sum之前出现过(直接在原来出现的次数上+1即可), 38 | # 另一种是某cur_sum没出现过(理论上应该设为1,但是因为此处用defaultdict存储,如果cur_sum这个key不存在将返回默认的int,也就是0) 39 | # 返回0加上1和直接将其置为1是一样的效果。所以这里统一用一句话包含上述两种情况 40 | num_times[cur_sum] += 1 41 | return res 42 | 43 | # s=Solution() 44 | # nums = [1,1,1] 45 | # k = 2 46 | # print(s.subarraySum(nums,k)) 47 | 48 | # leetcode submit region end(Prohibit modification and deletion) 49 | -------------------------------------------------------------------------------- /5.链表/141-hasCycle.py: -------------------------------------------------------------------------------- 1 | # 给定一个链表,判断链表中是否有环。 2 | # 3 | # 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的 4 | # 位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。 5 | # 6 | # 如果链表中存在环,则返回 true 。 否则,返回 false 。 7 | # 8 | # 9 | # 10 | # 进阶: 11 | # 12 | # 你能用 O(1)(即,常量)内存解决此问题吗? 13 | # 14 | # 15 | # 16 | # 示例 1: 17 | # 18 | # 19 | # 20 | # 输入:head = [3,2,0,-4], pos = 1 21 | # 输出:true 22 | # 解释:链表中有一个环,其尾部连接到第二个节点。 23 | # 24 | # 25 | # 示例 2: 26 | # 27 | # 28 | # 29 | # 输入:head = [1,2], pos = 0 30 | # 输出:true 31 | # 解释:链表中有一个环,其尾部连接到第一个节点。 32 | # 33 | # 34 | # 示例 3: 35 | # 36 | # 37 | # 38 | # 输入:head = [1], pos = -1 39 | # 输出:false 40 | # 解释:链表中没有环。 41 | # 42 | # 43 | # 44 | # 45 | # 提示: 46 | # 47 | # 48 | # 链表中节点的数目范围是 [0, 104] 49 | # -105 <= Node.val <= 105 50 | # pos 为 -1 或者链表中的一个 有效索引 。 51 | # 52 | # Related Topics 链表 双指针 53 | 54 | 55 | # leetcode submit region begin(Prohibit modification and deletion) 56 | # Definition for singly-linked list. 57 | # class ListNode: 58 | # def __init__(self, x): 59 | # self.val = x 60 | # self.next = None 61 | 62 | "方法一:哈希表:o(N)内存" 63 | 64 | 65 | class Solution: 66 | def hasCycle(self, head: ListNode) -> bool: 67 | head_set = set() 68 | while head: 69 | if head in head_set: # todo 这里不是head.val哈,有可能重复。 70 | return True 71 | head_set.add(head) 72 | head = head.next 73 | return False 74 | 75 | 76 | class Solution: 77 | def hasCycle(self, head: ListNode) -> bool: 78 | if not head or not head.next: 79 | return False 80 | 81 | slow = head 82 | fast = head.next 83 | 84 | while slow != fast: 85 | if not fast or not fast.next: 86 | return False 87 | slow = slow.next 88 | fast = fast.next.next 89 | 90 | return True 91 | 92 | 93 | # 判断环的入口点: 94 | 95 | """判断有环: 96 | slow每次向前走一步,fast向前追了两步,因此每一步操作后fast到slow的距离缩短了1步,这样继续下去就会使得 97 | 两者之间的距离逐渐缩小:...、5、4、3、2、1、0 -> 相遇。又因为在同一个环中fast和slow之间的距离不会大于换的长度,因此 98 | 到二者相遇的时候slow一定还没有走完一周(或者正好走完以后,这种情况出现在开始的时候fast和slow都在环的入口处)。 99 | 100 | # https://www.cnblogs.com/yorkyang/p/10876604.html 101 | # 可以看出,从链表起点head开始到入口点的距离a,与从slow和fast的相遇点(如图)到入口点的距离相等。 102 | # 因此我们就可以分别用一个指针(ptr1, prt2),同时从head与slow和fast的相遇点出发,每一次操作走一步,直到ptr1 == ptr2,此时的位置也就是入口点! 103 | 104 | """ 105 | 106 | 107 | # @param head ListNode类 108 | # @return ListNode类 109 | # # 110 | # class Solution: 111 | # def detectCycle(self , head ): 112 | # # write code here 113 | # a = [] 114 | # while head: 115 | # if head in a: 116 | # return head 117 | # else: 118 | # a.append(head) 119 | # head=head.next 120 | # return 121 | 122 | # write code here 123 | 124 | 125 | class Solution: 126 | def detectCycle(self, head): 127 | # 首先判断是否存在环 128 | fastHead = head 129 | slowHead = head 130 | 131 | while (fastHead and fastHead.next): 132 | fastHead = fastHead.next.next 133 | slowHead = slowHead.next 134 | if fastHead == slowHead: 135 | break 136 | # 本想用快慢指针是否相等作为判断条件,但初始时快慢指针是相等的 137 | if fastHead == None or fastHead.next == None: 138 | return None 139 | fastHead = head 140 | while (fastHead != slowHead): 141 | fastHead = fastHead.next 142 | slowHead = slowHead.next 143 | return fastHead 144 | 145 | # leetcode submit region end(Prohibit modification and deletion) 146 | -------------------------------------------------------------------------------- /5.链表/147-insertionSortList.py: -------------------------------------------------------------------------------- 1 | """ 2 | 对链表进行插入排序。 3 | 4 | 插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。 5 | 每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。 6 | 7 | 插入排序算法: 8 | 9 | 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。 10 | 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。 11 | 重复直到所有输入数据插入完为止。 12 |   13 | 示例 1: 14 | 15 | 输入: 4->2->1->3 16 | 输出: 1->2->3->4 17 | 示例 2: 18 | 19 | 输入: -1->5->3->4->0 20 | 输出: -1->0->3->4->5 21 | 22 | 链接:https://leetcode-cn.com/problems/insertion-sort-list 23 | """ 24 | 25 | 26 | class ListNode: 27 | def __init__(self, x): 28 | self.val = x 29 | self.next = None 30 | 31 | 32 | class Solution: 33 | def insertionSortList(self, head: ListNode) -> ListNode: 34 | # 找个排头 35 | dummy = ListNode(-1) 36 | pre = dummy 37 | # 依次拿head节点 38 | cur = head 39 | while cur: 40 | # 把下一次节点保持下来 41 | tmp = cur.next 42 | # 找到插入的位置 43 | while pre.next and pre.next.val < cur.val: 44 | pre = pre.next 45 | # 进行插入操作 46 | cur.next = pre.next 47 | pre.next = cur 48 | pre = dummy 49 | cur = tmp 50 | return dummy.next 51 | 52 | 53 | class Solution(object): 54 | def insertionSortList(self, head): 55 | """ 56 | :type head: ListNode 57 | :rtype: ListNode 58 | """ 59 | if not head or not head.next: return head 60 | root = TreeNode(0) 61 | root.next = head 62 | while head.next: 63 | if head.val <= head.next.val: 64 | head = head.next 65 | else: 66 | temp = head.next 67 | q = root 68 | head.next = head.next.next 69 | while q.next and q.next.val < temp.val: 70 | q = q.next 71 | temp.next = q.next 72 | q.next = temp 73 | return root.next 74 | -------------------------------------------------------------------------------- /5.链表/148-sortLIst.py: -------------------------------------------------------------------------------- 1 | # Definition for singly-linked list. 2 | # class ListNode(object): 3 | # def __init__(self, x): 4 | # self.val = x 5 | # self.next = None 6 | 7 | # Definition for singly-linked list. 8 | class ListNode(object): 9 | def __init__(self, x): 10 | self.val = x 11 | self.next = None 12 | 13 | 14 | class Solution(object): 15 | def sortList(self, head): 16 | """ 17 | :type head: ListNode 18 | :rtype: ListNode 19 | """ 20 | if head is None or head.next is None: 21 | return head 22 | mid = self.get_mid(head) 23 | r = mid.next 24 | mid.next = None 25 | l = head 26 | return self.merge(self.sortList(l), self.sortList(r)) 27 | 28 | def merge(self, p, q): 29 | tmp = ListNode(0) 30 | h = tmp 31 | while p and q: 32 | if p.val < q.val: 33 | h.next = p 34 | p = p.next 35 | else: 36 | h.next = q 37 | q = q.next 38 | h = h.next 39 | if p: 40 | h.next = p 41 | if q: 42 | h.next = q 43 | return tmp.next 44 | 45 | def get_mid(self, node): 46 | if node is None: 47 | return node 48 | fast = slow = node 49 | while fast.next and fast.next.next: 50 | slow = slow.next 51 | fast = fast.next.next 52 | return slow 53 | -------------------------------------------------------------------------------- /5.链表/160-getIntersectionNode.py: -------------------------------------------------------------------------------- 1 | "Write a program to find the node at which the intersection of two singly linked lists begins." 2 | from base.linklist import ListNode 3 | 4 | l = [9, 1, 2, 4] 5 | # l = [5] 6 | heada = ListNode(0) 7 | curr = heada 8 | for i in l: 9 | curr.next = ListNode(i) 10 | curr = curr.next 11 | 12 | l = [2, 4] 13 | headb = ListNode(3) 14 | curr = headb 15 | for i in l: 16 | curr.next = ListNode(i) 17 | curr = curr.next 18 | 19 | 20 | # Definition for singly-linked list. 21 | # class ListNode(object): 22 | # def __init__(self, x): 23 | # self.val = x 24 | # self.next = None 25 | 26 | class Solution(object): 27 | def getIntersectionNode(self, headA, headB): 28 | """ 29 | :type head1, head1: ListNode 30 | :rtype: ListNode 31 | """ 32 | p, q = headA, headB 33 | while p != q: 34 | q = headB if q is None else q.next 35 | p = headA if p is None else p.next 36 | return p 37 | 38 | 39 | s = Solution() 40 | res = s.getIntersectionNode(heada, headb) 41 | print(res) 42 | -------------------------------------------------------------------------------- /5.链表/19_removeNthFromEnd.py: -------------------------------------------------------------------------------- 1 | # Definition for singly-linked list. 2 | """ 3 | Remove Nth Node From End of List(删除链表倒数第n个节点) 4 | Given a linked list, remove the n-th node from the end of list and return its head. 5 | 6 | Example: 7 | 8 | Given linked list: 1->2->3->4->5, and n = 2. 9 | 10 | After removing the second node from the end, the linked list becomes 1->2->3->5. 11 | Note: 12 | 13 | Given n will always be valid. 14 | """ 15 | 16 | 17 | class ListNode(object): 18 | def __init__(self, x): 19 | self.val = x 20 | self.next = None 21 | 22 | 23 | class Solution(object): 24 | def removeNthFromEnd(self, head, n): 25 | """ 26 | :type head: ListNode 27 | :type n: int 28 | :rtype: ListNode 29 | 30 | 特殊情况两种:第一种是总长为1或者0,那么删掉以后就为空了;第二种情况是删除的是第一个数据,顺序数下来n为0,则遍历即可 31 | 常规情况:删除的不是第一个数字,则n为n-1.. 32 | """ 33 | m = 0 34 | root = head 35 | while root: 36 | root = root.next 37 | m += 1 38 | 39 | n = m - n 40 | i = 0 41 | res_list = [] 42 | if m == 1 or m == 0: 43 | return 44 | if n == 0: 45 | head = head.next 46 | n = m 47 | 48 | while i < m and head: 49 | res_list.append(head.val) 50 | if i == n - 1 and head.next: 51 | head = head.next.next 52 | i += 2 53 | else: 54 | 55 | head = head.next 56 | i += 1 57 | 58 | if len(res_list) < 1: 59 | return 60 | node = ListNode(res_list[0]) 61 | curr = node 62 | for i in res_list[1:]: 63 | curr.next = ListNode(i) 64 | curr = curr.next 65 | return node 66 | 67 | 68 | class Solution: 69 | """ 70 | 1.定义一个头结点,指向链表的第一个结点(不再像1.0版一样计算链表长度) 71 | 2.快慢指针指向头结点 72 | 3.快指针先走n步 73 | 4.快慢指针一起走,直到快指针走到链表尾 74 | 5.慢指针后一位连接为其后一位的后一位(实现截断连接) 75 | 6.返回头结点的后一位结点。 76 | # 总结:这个方法的优势在于,不需要知道长度m,先走了n步,继续走,走到结尾那就是m-n的步数 77 | """ 78 | 79 | def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode: 80 | node = ListNode(None) 81 | node.next = head 82 | first, slow = node, node 83 | for i in range(n): 84 | first = first.next 85 | while first.next != None: 86 | first = first.next 87 | slow = slow.next 88 | slow.next = slow.next.next 89 | return node.next 90 | 91 | 92 | n = 2 93 | # l=[2,3,4,5] 94 | l = [2, 3, 4, 5] 95 | # l = [5] 96 | # l=[] 97 | head = ListNode(1) 98 | curr = head 99 | for i in l: 100 | curr.next = ListNode(i) 101 | curr = curr.next 102 | curr.next = ListNode(None) 103 | 104 | s = Solution() 105 | s.removeNthFromEnd(head, n) 106 | -------------------------------------------------------------------------------- /5.链表/2-add_two_num.py: -------------------------------------------------------------------------------- 1 | # 给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。 2 | # 3 | # 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。 4 | # 5 | # 您可以假设除了数字 0 之外,这两个数都不会以 0 开头。 6 | # 7 | # 示例: 8 | # 9 | # 输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 10 | # 输出:7 -> 0 -> 8 11 | # 原因:342 + 465 = 807 12 | # 13 | # Related Topics 链表 数学 14 | 15 | 16 | # leetcode submit region begin(Prohibit modification and deletion) 17 | # Definition for singly-linked list. 18 | class ListNode: 19 | def __init__(self, val=0, next=None): 20 | self.val = val 21 | self.next = next 22 | 23 | 24 | class Solution: 25 | def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode: 26 | node = ListNode(0) 27 | ans = node # TODO ,为什么? 28 | """ 29 | 这里只能是ans = node; 结尾return ans.next; 30 | 如果我这里写ans=node.next;结尾return ans就不行. 31 | 为什么? 32 | """ 33 | 34 | flag = 0 35 | while l1 or l2: 36 | if l1 and l2: 37 | cursum = l1.val + l2.val + flag 38 | l1 = l1.next 39 | l2 = l2.next 40 | elif l1: 41 | cursum = l1.val + flag 42 | l1 = l1.next 43 | elif l2: 44 | cursum = l2.val + flag 45 | l2 = l2.next 46 | 47 | if cursum > 9: 48 | node.next = ListNode(cursum % 10) 49 | flag = 1 50 | else: 51 | node.next = ListNode(cursum) 52 | flag = 0 53 | node = node.next 54 | if flag: 55 | node.next = ListNode(flag) 56 | return ans.next 57 | 58 | # leetcode submit region end(Prohibit modification and deletion) 59 | -------------------------------------------------------------------------------- /5.链表/21-mergeTwoLists.py: -------------------------------------------------------------------------------- 1 | # 版权 2 | # 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 3 | # 4 | # 例: 5 | # 6 | # 输入:1->2->4, 1->3->4 7 | # 输出:1->1->2->3->4->4 8 | # 本题同样可以很单线条解决,代码如下 9 | # 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 10 | # 11 | # 示例: 12 | # 13 | # 输入:1->2->4, 1->3->4 14 | # 输出:1->1->2->3->4->4 15 | 16 | # Definition for singly-linked list. 17 | class ListNode: 18 | def __init__(self, x): 19 | self.val = x 20 | self.next = None 21 | 22 | 23 | class Solution: 24 | def mergeTwoLists(self, l1, l2): 25 | """ 26 | :type l1: ListNode 27 | :type l2: ListNode 28 | :rtype: ListNode 29 | """ 30 | head = ListNode(0) 31 | first = head 32 | while l1 != None and l2 != None: 33 | if l1.val > l2.val: 34 | head.next = l2 35 | l2 = l2.next 36 | else: 37 | head.next = l1 38 | l1 = l1.next 39 | head = head.next 40 | if l1 == None: 41 | head.next = l2 42 | elif l2 == None: 43 | head.next = l1 44 | return first.next 45 | -------------------------------------------------------------------------------- /5.链表/23-mergeKLists.py: -------------------------------------------------------------------------------- 1 | # 给你一个链表数组,每个链表都已经按升序排列。 2 | # 3 | # 请你将所有链表合并到一个升序链表中,返回合并后的链表。 4 | # 5 | # 6 | # 7 | # 示例 1: 8 | # 9 | # 输入:lists = [[1,4,5],[1,3,4],[2,6]] 10 | # 输出:[1,1,2,3,4,4,5,6] 11 | # 解释:链表数组如下: 12 | # [ 13 | # 1->4->5, 14 | # 1->3->4, 15 | # 2->6 16 | # ] 17 | # 将它们合并到一个有序链表中得到。 18 | # 1->1->2->3->4->4->5->6 19 | # 20 | # 21 | # 示例 2: 22 | # 23 | # 输入:lists = [] 24 | # 输出:[] 25 | # 26 | # 27 | # 示例 3: 28 | # 29 | # 输入:lists = [[]] 30 | # 输出:[] 31 | # 32 | # 33 | # 34 | # 35 | # 提示: 36 | # 37 | # 38 | # k == lists.length 39 | # 0 <= k <= 10^4 40 | # 0 <= lists[i].length <= 500 41 | # -10^4 <= lists[i][j] <= 10^4 42 | # lists[i] 按 升序 排列 43 | # lists[i].length 的总和不超过 10^4 44 | # 45 | # Related Topics 堆 链表 分治算法 46 | 47 | 48 | # leetcode submit region begin(Prohibit modification and deletion) 49 | # Definition for singly-linked list. 50 | # class ListNode: 51 | # def __init__(self, val=0, next=None): 52 | # self.val = val 53 | # self.next = next 54 | # class Solution: 55 | # '我的答案:执行耗时:7156 ms,击败了5.00% 的Python3用户;内存消耗:409.2 MB,击败了5.01% 的Python3用户' 56 | # def mergeKLists(self, lists: List[ListNode]) -> ListNode: 57 | # def helper(l1: ListNode, l2: ListNode) -> ListNode: 58 | # if not l1 and not l2: 59 | # return 60 | # 61 | # node = ListNode(None) 62 | # res = node 63 | # # node.next = l1 64 | # # node = node.next # todo 65 | # # res = node 66 | # 67 | # while l1 or l2: 68 | # if l1 and l2: 69 | # if l1.val <= l2.val: # todo 我仅仅是把 node.next = ListNode(l1.val)改成了 node.next = l1 70 | # node.next = l1 71 | # l1 = l1.next 72 | # elif l1.val > l2.val: 73 | # node.next = l2 74 | # l2 = l2.next 75 | # node = node.next 76 | # 77 | # elif l1: 78 | # node.next = l1 79 | # break 80 | # elif l2: 81 | # node.next = l2 82 | # break 83 | # 84 | # return res.next 85 | # 86 | # if len(lists)==0: 87 | # return 88 | # elif len(lists)==1: 89 | # return lists[0] 90 | # elif len(lists)==2: 91 | # return helper(lists[0],lists[1]) 92 | # else: 93 | # return self.mergeKLists([helper(lists[0],lists[1])]+lists[2:]) 94 | 95 | # def mergeKLists(self, lists): 96 | # q = [] 97 | # for i, head in enumerate(lists): 98 | # if head: 99 | # heappush(q, (head.val, i, head)) 100 | # 101 | # node = dummy = ListNode(0) 102 | # while q: 103 | # val, i, pop_node = heappop(q) 104 | # print(val) 105 | # node.next = ListNode(val) 106 | # node = node.next 107 | # next_node = pop_node.next 108 | # if next_node: 109 | # heappush(q, (next_node.val, i, next_node)) 110 | # return dummy.next 111 | 112 | 113 | class Solution(object): 114 | def mergeKLists(self, lists): 115 | """ 116 | :type lists: List[ListNode] 117 | :rtype: ListNode 118 | """ 119 | heap = [] 120 | for node in lists: 121 | while node: 122 | heapq.heappush(heap, node.val) 123 | node = node.next 124 | temp = ListNode(None) 125 | res = temp 126 | 127 | while heap: 128 | # temp.next = ListNode(heap.pop()) 129 | temp.next = ListNode(heapq.heappop(heap)) 130 | temp = temp.next 131 | return res.next 132 | 133 | # heap = [] 134 | # for node in lists: 135 | # while node: 136 | # heapq.heappush(heap, node.val) 137 | # node = node.next 138 | # 139 | # temp = ListNode(-1) 140 | # head = temp 141 | # while heap: 142 | # smallestNode_val = heapq.heappop(heap) 143 | # temp.next = ListNode(smallestNode_val) 144 | # temp = temp.next 145 | # 146 | # return head.next 147 | 148 | # leetcode submit region end(Prohibit modification and deletion) 149 | -------------------------------------------------------------------------------- /6.数组/26-removeDuplicates.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 3 | 4 | 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 5 | 6 | 示例 1: 7 | 8 | 给定数组 nums = [1,1,2], 9 | 10 | 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 11 | 1, 2。 12 | 你不需要考虑数组中超出新长度后面的元素。 13 | 示例 2: 14 | 15 | 给定 nums = [0,0,1,1,1,2,2,3,3,4], 16 | 17 | 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 18 | 0, 1, 2, 3, 4。 19 | 你不需要考虑数组中超出新长度后面的元素。 20 | 21 | 22 | 思路: 23 | 24 | 原地替换,将重复位置的元素替换为下个非重复元素。 25 | 26 | """ 27 | from typing import List 28 | 29 | 30 | class Solution: 31 | def removeDuplicates(self, nums: List[int]) -> int: 32 | # 第一个指针用于更改值 33 | first = 0 34 | # 第二个指针用于遍历 35 | for second in range(len(nums)): 36 | # 如果和之前记录的值不同 37 | if nums[first] != nums[second]: 38 | # 第一个指针先加1 39 | first += 1 40 | # 然后赋值 41 | nums[first] = nums[second] 42 | return first + 1 43 | -------------------------------------------------------------------------------- /6.数组/289-gameOfLife.py: -------------------------------------------------------------------------------- 1 | """ 2 | 这道题是有名的 康威生命游戏, 而我又是第一次听说这个东东,这是一种细胞自动机,每一个位置有两种状态,1为活细胞,0为死细胞,对于每个位置都满足如下的条件: 3 | 1. 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡 4 | 2. 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活 5 | 3. 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡 6 | 4. 如果死细胞周围正好有三个活细胞,则该位置死细胞复活 7 | input_cell = [ 8 | [0,1,0], 9 | [0,0,1], 10 | [1,1,1], 11 | [0,0,0] 12 | ] 13 | 14 | """ 15 | import copy 16 | 17 | 18 | class Solution(object): 19 | def gameOfLife(self, board): 20 | eight_anchor_index = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)] 21 | a, b = len(board), len(board[0]) 22 | output_cell = copy.deepcopy(board) # 不能用output_cell = input_cell[:],元素会被改掉 23 | # copy.copy()是浅拷贝,但是python内置a.copy()是深拷贝 24 | for i in range(a): 25 | for j in range(b): 26 | live_cell_count = 0 27 | for m, n in eight_anchor_index: 28 | if i + m < 0 or j + n < 0 or i + m >= a or j + n >= b: 29 | continue 30 | 31 | if board[i + m][j + n] == 1: 32 | live_cell_count += 1 33 | 34 | if board[i][j] == 0 and live_cell_count == 3: 35 | output_cell[i][j] = 1 36 | elif board[i][j] == 1 and live_cell_count < 2: 37 | output_cell[i][j] = 0 38 | elif board[i][j] == 1 and 2 <= live_cell_count <= 3: 39 | output_cell[i][j] = 1 40 | elif board[i][j] == 1 and live_cell_count > 3: 41 | output_cell[i][j] = 0 42 | 43 | return output_cell 44 | 45 | 46 | board = [ 47 | [0, 1, 0], 48 | [0, 0, 1], 49 | [1, 1, 1], 50 | [0, 0, 0] 51 | ] 52 | board = [[0, 1, 0], [0, 0, 1], [1, 1, 1], [0, 0, 0]] 53 | # [[0, 0, 0], [1, 0, 1], [0, 1, 1], [0, 1, 0]] 54 | s = Solution() 55 | res = s.gameOfLife(board) 56 | print(res) 57 | 58 | # def get_next_state(input): 59 | # kernel = [1, 1, 1, 1, 0, 1, 1, 1, 1] 60 | # 61 | # padding = np.array([[0 for i in range(len(input[0])+2)] for j in range(len(input)+2)]) 62 | # 63 | # padding[1:-1, 1:-1] = input 64 | # 65 | # input_col = [a for col in padding for a in col] 66 | # 67 | # out_size_x = len(input) 68 | # out_size_y = len(input[0]) 69 | # convertW = len(kernel) 70 | # kernel_size = 3 71 | # 72 | # 73 | # temp_matrix = [0 for i in range(out_size_x * out_size_y * convertW)] 74 | # 75 | # feature_size_x = padding.shape[1] 76 | # feature_size_y = padding.shape[0] 77 | # 78 | # for i in range(0, out_size_x): 79 | # for j in range(0, out_size_y): 80 | # for k in range(convertW): 81 | # row = int(k / kernel_size) 82 | # col = int(k % kernel_size) 83 | # start = int(i * feature_size_x + j) 84 | # value = input_col[int(start + row*feature_size_x + col)] 85 | # index = int((i * out_size_y + j) * convertW + k) 86 | # temp_matrix[index] = value 87 | # 88 | # result = input.copy() 89 | # for i in range(len(input)): 90 | # for j in range(len(input[i])): 91 | # temp = 0 92 | # for k in range(len(kernel)): 93 | # temp += temp_matrix[(i*out_size_y + j)*convertW + k] * kernel[k] 94 | # if temp < 2: 95 | # result[i][j] = 0 96 | # elif (temp == 2 or temp == 3) and input[i][j] == 1: 97 | # result[i][j] = 1 98 | # elif temp > 3: 99 | # result[i][j] = 0 100 | # elif temp == 3 and input[i][j] == 0: 101 | # result[i][j] = 1 102 | # return result 103 | -------------------------------------------------------------------------------- /6.数组/4-findMedianSortedArrays.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。 3 | 4 | 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。 5 | 6 | 你可以假设 nums1 和 nums2 不会同时为空。 7 | 8 | 示例 1: 9 | 注意:n为偶数的时候,中位数是下标是(n/2+n/2+1)/2;奇数的时候下标为(n+1)/2 10 | 11 | nums1 = [1, 3] 12 | nums2 = [2] 13 | 则中位数是 2.0 14 | 15 | 困难难题! 16 | """ 17 | from typing import List 18 | 19 | 20 | class Solution: 21 | def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float: 22 | """ 23 | :type nums1: List[int] 24 | :type nums2: List[int] 25 | :rtype: float 26 | """ 27 | # print(nums1, nums2) 28 | m, n = len(nums1), len(nums2) 29 | mid = (m + n) // 2 # 这里要减去1哦! 30 | 31 | def get_k(nums1, nums2, k): 32 | """ 33 | - 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较 34 | - 这里的 "/" 表示整除 35 | - nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个 36 | - nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个 37 | - 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个 38 | - 这样 pivot 本身最大也只能是第 k-1 小的元素 39 | - 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组 40 | - 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组 41 | - 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数 42 | """ 43 | 44 | index1, index2 = 0, 0 45 | while True: 46 | # 特殊情况 47 | if index1 == m: 48 | return nums2[index2 + k - 1] 49 | if index2 == n: 50 | return nums1[index1 + k - 1] 51 | if k == 1: 52 | return min(nums1[index1], nums2[index2]) 53 | 54 | # 正常情况 55 | new_index1 = min(index1 + k // 2 - 1, m - 1) 56 | new_index2 = min(index2 + k // 2 - 1, n - 1) 57 | if nums1[new_index1] <= nums2[new_index2]: 58 | k = k - (new_index1 - index1 + 1) 59 | index1 = new_index1 + 1 60 | else: 61 | k = k - (new_index2 - index2 + 1) 62 | index2 = new_index2 + 1 63 | 64 | if (m + n) % 2 != 0: 65 | return get_k(nums1, nums2, mid + 1) 66 | else: 67 | return (get_k(nums1, nums2, mid) + get_k(nums1, nums2, mid + 1)) / 2 68 | 69 | 70 | s = Solution() 71 | nums1 = [1, 3] 72 | nums2 = [2] 73 | 74 | # nums1 = [1, 3] 75 | # nums2 = [2,2] 76 | nums1 = [1, 2] 77 | nums2 = [3, 4] 78 | # # 预期结果 2.5 ? 79 | res = s.findMedianSortedArrays(nums1, nums2) 80 | # print(res) 81 | # r=findMedianSortedArrays(nums1, nums2) 82 | # print(r) 83 | -------------------------------------------------------------------------------- /6.数组/406-reconstructQueue.py: -------------------------------------------------------------------------------- 1 | # 假设有打乱顺序的一群人站成一个队列。 每个人由一个整数对(h, k)表示, 2 | # 其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数。 编写一个算法来 3 | # 重建这个队列。 4 | # 5 | # 注意: 6 | # 总人数少于1100人。 7 | # 8 | # 示例 9 | # 10 | # 11 | # 输入: 12 | # [[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]] 13 | # 14 | # 输出: 15 | # [[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]] 16 | # 17 | # Related Topics 贪心算法 18 | 19 | from typing import List 20 | 21 | 22 | # leetcode submit region begin(Prohibit modification and deletion) 23 | 24 | class Solution: 25 | def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]: 26 | people.sort(key=lambda x: (-x[0], x[1])) 27 | output = [] 28 | for p in people: 29 | output.insert(p[1], p) 30 | return output 31 | 32 | # 33 | # people = [[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]] 34 | # # people.sort(key=lambda k:k[0]) 35 | # # print(people) 36 | # s=Solution() 37 | # s.reconstructQueue(people) 38 | # # leetcode submit region end(Prohibit modification and deletion) 39 | -------------------------------------------------------------------------------- /6.数组/448-findDisappearedNumbers.py: -------------------------------------------------------------------------------- 1 | # 给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。 2 | # 3 | # 找到所有在 [1, n] 范围之间没有出现在数组中的数字。 4 | # 5 | # 您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。 6 | # 7 | # 示例: 8 | # 9 | # 10 | # 输入: 11 | # [4,3,2,7,8,2,3,1] 12 | # 13 | # 输出: 14 | # [5,6] 15 | # 16 | # Related Topics 数组 17 | 18 | from typing import List 19 | 20 | # leetcode submit region begin(Prohibit modification and deletion) 21 | "我的解法:哈希表" 22 | 23 | 24 | class Solution: 25 | def findDisappearedNumbers(self, nums: List[int]) -> List[int]: 26 | res = [] 27 | tmp = set(nums) 28 | for i in range(1, len(nums) + 1): 29 | if i not in tmp: 30 | res.append(i) 31 | return res 32 | 33 | 34 | "原地修改" 35 | 36 | 37 | # todo 38 | 39 | class Solution(object): 40 | def findDisappearedNumbers(self, nums): 41 | """ 42 | :type nums: List[int] 43 | :rtype: List[int] 44 | """ 45 | 46 | for i in range(len(nums)): 47 | new_index = abs(nums[i]) - 1 48 | print(i, nums[i], new_index, nums[new_index]) 49 | if nums[new_index] > 0: 50 | nums[new_index] *= -1 51 | else: 52 | print('***', i, nums[i], new_index, nums[new_index]) 53 | print(nums[new_index]) 54 | print(nums) 55 | # Response array that would contain the missing numbers 56 | result = [] 57 | 58 | # Iterate over the numbers from 1 to N and add all those 59 | # that have positive magnitude in the array 60 | for i in range(1, len(nums) + 1): 61 | if nums[i - 1] > 0: 62 | result.append(i) 63 | 64 | return result 65 | 66 | 67 | s = Solution() 68 | s.findDisappearedNumbers([4, 3, 2, 7, 8, 2, 3, 1]) 69 | # leetcode submit region end(Prohibit modification and deletion) 70 | -------------------------------------------------------------------------------- /6.数组/54-spiralOrder.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。 3 | 4 | 示例 1: 5 | 6 | 输入: 7 | [ 8 | [ 1, 2, 3 ], 9 | [ 4, 5, 6 ], 10 | [ 7, 8, 9 ] 11 | ] 12 | 输出: [1,2,3,6,9,8,7,4,5] 13 | 14 | 示例 2: 15 | 16 | 输入: 17 | [ 18 | [1, 2, 3, 4], 19 | [5, 6, 7, 8], 20 | [9,10,11,12] 21 | ] 22 | 输出: [1,2,3,4,8,12,11,10,9,5,6,7] 23 | 24 | """ 25 | from typing import List 26 | 27 | 28 | class Solution: 29 | def spiralOrder(self, matrix: List[List[int]]) -> List[int]: 30 | rel, i, j, di, dj = [], 0, 0, 0, 1 31 | if matrix != []: 32 | for _ in range(len(matrix) * len(matrix[0])): 33 | rel.append(matrix[i][j]) 34 | matrix[i][j] = 0 35 | if matrix[(i + di) % len(matrix)][(j + dj) % len(matrix[0])] == 0: 36 | di, dj = dj, -di 37 | i += di 38 | j += dj 39 | print(i, j, di, dj) 40 | return rel 41 | -------------------------------------------------------------------------------- /6.数组/56-merge.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给出一个区间的集合,请合并所有重叠的区间。 3 | 4 | 示例 1: 5 | 6 | 输入: [[1,3],[2,6],[8,10],[15,18]] 7 | 输出: [[1,6],[8,10],[15,18]] 8 | 解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]. 9 | 示例 2: 10 | 11 | 输入: [[1,4],[4,5]] 12 | 输出: [[1,5]] 13 | 解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。 14 | 15 | 来源:力扣(LeetCode) 16 | 链接:https://leetcode-cn.com/problems/merge-intervals 17 | 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 18 | """ 19 | from typing import List 20 | 21 | 22 | # 我的解法 23 | class Solution: 24 | def merge(self, intervals: List[List[int]]) -> List[List[int]]: 25 | # todo 如果我们按照区间的 start 大小排序,那么在这个排序的列表中可以合并的区间一定是连续的。 26 | intervals = sorted(intervals, key=lambda x: x[0]) 27 | while True: 28 | if len(intervals) < 2: 29 | return intervals 30 | is_outer_continue = False 31 | is_finished = False 32 | for i in range(len(intervals) - 1): 33 | if intervals[i][-1] >= intervals[i + 1][0]: 34 | intervals[i] = [min(intervals[i] + intervals[i + 1]), max(intervals[i] + intervals[i + 1])] 35 | intervals = intervals[:i + 1] + intervals[i + 2:] 36 | is_outer_continue = True 37 | break 38 | if i == len(intervals) - 2: 39 | is_finished = True 40 | if is_outer_continue: 41 | continue 42 | 43 | if is_finished: 44 | break 45 | 46 | return intervals 47 | 48 | 49 | # 比人的解法 50 | class Solution: 51 | def merge(self, intervals): 52 | intervals.sort(key=lambda x: x[0]) 53 | 54 | merged = [] 55 | for interval in intervals: 56 | # if the list of merged intervals is empty or if the current 57 | # interval does not overlap with the previous, simply append it. 58 | if not merged or merged[-1][-1] < interval[0]: 59 | merged.append(interval) 60 | else: 61 | # otherwise, there is overlap, so we merge the current and previous 62 | # intervals. 63 | merged[-1][-1] = max(merged[-1][-1], interval[-1]) 64 | 65 | return merged 66 | 67 | 68 | if __name__ == "__main__": 69 | a = [[1, 3], [2, 6], [8, 10], [15, 18]] 70 | a = [[1, 4], [4, 5]] 71 | a = [[1, 4], [0, 4]] 72 | a = [[1, 4], [2, 3]] 73 | a = [[1, 4], [0, 0]] 74 | s = Solution() 75 | print(s.merge(intervals=a)) 76 | -------------------------------------------------------------------------------- /6.数组/75-sortColors.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | 给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 4 | 5 | 此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 6 | 7 | 注意: 8 | 不能使用代码库中的排序函数来解决这道题。 9 | 10 | 示例: 11 | 12 | 输入: [2,0,2,1,1,0] 13 | 输出: [0,0,1,1,2,2] 14 | 进阶: 15 | 16 | 一个直观的解决方案是使用计数排序的两趟扫描算法。 17 | 首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。 18 | 你能想出一个仅使用常数空间的一趟扫描算法吗? 19 | """ 20 | from typing import List 21 | 22 | 23 | class Solution: 24 | def sortColors(self, nums: List[int]) -> None: 25 | ''' 26 | 荷兰三色旗问题解 27 | ''' 28 | # 对于所有 idx < p0 : nums[idx < p0] = 0 29 | # curr是当前考虑元素的下标 30 | p0 = curr = 0 31 | # 对于所有 idx > p2 : nums[idx > p2] = 2 32 | # 我们用三个指针(p0, p2 和curr)来分别追踪0的最右边界,2的最左边界和当前考虑的元素。 33 | p2 = len(nums) - 1 34 | 35 | while curr <= p2: 36 | if nums[curr] == 0: 37 | nums[p0], nums[curr] = nums[curr], nums[p0] 38 | p0 += 1 39 | curr += 1 40 | elif nums[curr] == 2: 41 | nums[curr], nums[p2] = nums[p2], nums[curr] 42 | p2 -= 1 43 | else: 44 | curr += 1 45 | return nums 46 | 47 | 48 | """ 49 | 时间复杂度 :由于对长度 N的数组进行了一次遍历,时间复杂度为O(N) 。 50 | 空间复杂度 :由于只使用了常数空间,空间复杂度为O(1) 。 51 | """ 52 | if __name__ == "__main__": 53 | s = Solution() 54 | a = [2, 0, 2, 1, 1, 0] 55 | print(s.sortColors(a)) 56 | -------------------------------------------------------------------------------- /7.堆/215_findKthLargest.py: -------------------------------------------------------------------------------- 1 | """数组中的第K个最大元素 2 | 在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 3 | 4 | 示例 1: 5 | 6 | 输入: [3,2,1,5,6,4] 和 k = 2 7 | 输出: 5 8 | 示例 2: 9 | 10 | 输入: [3,2,3,1,2,4,5,5,6] 和 k = 4 11 | 输出: 4 12 | 说明: 13 | 14 | 你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。 15 | 16 | 17 | """ 18 | 19 | 20 | # 堆 21 | class Solution: 22 | """ 23 | 方法一:堆 24 | 思路是创建一个大顶堆,将所有数组中的元素加入堆中,并保持堆的大小小于等于 k。这样,堆中就保留了前 k 个最大的元素。这样,堆顶的元素就是正确答案。 25 | 像大小为 k 的堆中添加元素的时间复杂度为 {O}(\log k)O(logk),我们将重复该操作 N 次,故总时间复杂度为 {O}(N \log k)O(Nlogk)。 26 | 在 Python 的 heapq 库中有一个 nlargest 方法,具有同样的时间复杂度,能将代码简化到只有一行。 27 | 本方法优化了时间复杂度,但需要 {O}(k)O(k) 的空间复杂度。 28 | """ 29 | 30 | def findKthLargest(self, nums, k): 31 | import heapq 32 | return heapq.nlargest(k, nums)[-1] 33 | 34 | 35 | # 排序 36 | class Solution(object): 37 | def findKthLargest(self, nums, k): 38 | """ 39 | :type nums: List[int] 40 | :type k: int 41 | :rtype: int 42 | """ 43 | sort_nums = sorted(nums, reverse=True) 44 | idx = 0 45 | for i in sort_nums: 46 | 47 | idx += 1 48 | if idx == k: 49 | return i 50 | -------------------------------------------------------------------------------- /7.堆/23-mergeKLists.py: -------------------------------------------------------------------------------- 1 | # 给你一个链表数组,每个链表都已经按升序排列。 2 | # 3 | # 请你将所有链表合并到一个升序链表中,返回合并后的链表。 4 | # 5 | # 6 | # 7 | # 示例 1: 8 | # 9 | # 输入:lists = [[1,4,5],[1,3,4],[2,6]] 10 | # 输出:[1,1,2,3,4,4,5,6] 11 | # 解释:链表数组如下: 12 | # [ 13 | # 1->4->5, 14 | # 1->3->4, 15 | # 2->6 16 | # ] 17 | # 将它们合并到一个有序链表中得到。 18 | # 1->1->2->3->4->4->5->6 19 | # 20 | # 21 | # 示例 2: 22 | # 23 | # 输入:lists = [] 24 | # 输出:[] 25 | # 26 | # 27 | # 示例 3: 28 | # 29 | # 输入:lists = [[]] 30 | # 输出:[] 31 | # 32 | # 33 | # 34 | # 35 | # 提示: 36 | # 37 | # 38 | # k == lists.length 39 | # 0 <= k <= 10^4 40 | # 0 <= lists[i].length <= 500 41 | # -10^4 <= lists[i][j] <= 10^4 42 | # lists[i] 按 升序 排列 43 | # lists[i].length 的总和不超过 10^4 44 | # 45 | # Related Topics 堆 链表 分治算法 46 | 47 | 48 | # leetcode submit region begin(Prohibit modification and deletion) 49 | # Definition for singly-linked list. 50 | class ListNode: 51 | def __init__(self, val=0, next=None): 52 | self.val = val 53 | self.next = next 54 | 55 | 56 | class Solution(object): 57 | def mergeKLists(self, lists): 58 | """ 59 | :type lists: List[ListNode] 60 | :rtype: ListNode 61 | """ 62 | heap = [] 63 | for node in lists: 64 | while node: 65 | heapq.heappush(heap, node.val) 66 | node = node.next 67 | temp = ListNode(None) 68 | res = temp 69 | 70 | while heap: 71 | # temp.next = ListNode(heap.pop()) 72 | temp.next = ListNode(heapq.heappop(heap)) 73 | temp = temp.next 74 | return res.next 75 | 76 | # heap = [] 77 | # for node in lists: 78 | # while node: 79 | # heapq.heappush(heap, node.val) 80 | # node = node.next 81 | # 82 | # temp = ListNode(-1) 83 | # head = temp 84 | # while heap: 85 | # smallestNode_val = heapq.heappop(heap) 86 | # temp.next = ListNode(smallestNode_val) 87 | # temp = temp.next 88 | # 89 | # return head.next 90 | 91 | # leetcode submit region end(Prohibit modification and deletion) 92 | -------------------------------------------------------------------------------- /7.堆/347_topKFrequent.py: -------------------------------------------------------------------------------- 1 | """347. 前 K 个高频元素 2 | 3 | 给定一个非空的整数数组,返回其中出现频率前 k 高的元素。 4 | 5 | 示例 1: 6 | 输入: nums = [1,1,1,2,2,3], k = 2 7 | 输出: [1,2] 8 | 示例 2: 9 | 10 | 输入: nums = [1], k = 1 11 | 输出: [1] 12 | 说明: 13 | 14 | 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。 15 | 你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。 16 | """ 17 | 18 | 19 | # 排序 20 | class Solution(object): 21 | def topKFrequent(self, nums, k): 22 | """ 23 | :type nums: List[int] 24 | :type k: int 25 | :rtype: List[int] 26 | """ 27 | from collections import Counter # todo Counter 28 | count = Counter(nums) 29 | res = sorted(count.items(), key=lambda x: x[1], reverse=True) # todo items(), key= 30 | print(res) 31 | return [i[0] for i in res[:k]] 32 | 33 | 34 | # 堆 35 | class Solution(object): 36 | def topKFrequent(self, nums, k): 37 | from collections import Counter 38 | import heapq 39 | count = Counter(nums) 40 | 41 | return heapq.nlargest(k, count.keys(), key=count.get) 42 | 43 | 44 | c 45 | 46 | 47 | class Solution(object): 48 | def topKFrequent(self, words, k): 49 | import collections 50 | import heapq 51 | count = collections.Counter(words) 52 | 53 | heap = [(-freq, word) for word, freq in count.items()] 54 | heapq.heapify(heap) 55 | print(heap) 56 | return [heapq.heappop(heap)[1] for _ in range(k)] 57 | 58 | 59 | nums = [4, 1, -1, 2, -1, 2, 3] 60 | k = 2 61 | a = Solution() 62 | print(a.topKFrequent(nums, k)) 63 | -------------------------------------------------------------------------------- /7.堆/692-topKFrequent.py: -------------------------------------------------------------------------------- 1 | """前K个高频词 2 | 3 | 给一非空的单词列表,返回前 k 个出现次数最多的单词。 4 | 5 | 返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。 6 | 7 | 示例 1: 8 | 9 | 输入: ["i", "love", "leetcode", "i", "love", "coding"], k = 2 10 | 输出: ["i", "love"] 11 | 解析: "i" 和 "love" 为出现次数最多的两个单词,均为2次。 12 | 注意,按字母顺序 "i" 在 "love" 之前。 13 | 14 | 示例 2: 15 | 16 | 输入: ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"], k = 4 17 | 输出: ["the", "is", "sunny", "day"] 18 | 解析: "the", "is", "sunny" 和 "day" 是出现次数最多的四个单词, 19 | 出现次数依次为 4, 3, 2 和 1 次。 20 |   21 | 注意: 22 | 23 | 假定 k 总为有效值, 1 ≤ k ≤ 集合元素数。 24 | 输入的单词均由小写字母组成。 25 |   26 | 扩展练习: 27 | 尝试以 O(n log k) 时间复杂度和 O(n) 空间复杂度解决。 28 | """ 29 | import collections 30 | import heapq 31 | 32 | 33 | # 堆 nlogk 34 | class Solution(object): 35 | def topKFrequent(self, words, k): 36 | count = collections.Counter(words) 37 | heap = [(-freq, word) for word, freq in count.items()] 38 | heapq.heapify(heap) 39 | print(heap) 40 | return [heapq.heappop(heap)[1] for _ in range(k)] 41 | 42 | 43 | if __name__ == '__main__': 44 | l = ["i", "love", "leetcode", "i", "love", "coding"] 45 | print(Solution().topKFrequent(l, 2)) 46 | -------------------------------------------------------------------------------- /8.二分+分治/287-findDuplicate.py: -------------------------------------------------------------------------------- 1 | # 给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n), 2 | # 可知至少存在一个重复的整数。假设只有一个重复的整数,找出 3 | # 这个重复的数。 4 | # 5 | # 示例 1: 6 | # 7 | # 输入: [1,3,4,2,2] 8 | # 输出: 2 9 | # 10 | # 11 | # 示例 2: 12 | # 13 | # 输入: [3,1,3,4,2] 14 | # 输出: 3 15 | # 16 | # 17 | # 说明: 18 | # 19 | # 20 | # 不能更改原数组(假设数组是只读的)。 21 | # 只能使用额外的 O(1) 的空间。 22 | # 时间复杂度小于 O(n2) 。 23 | # 数组中只有一个重复的数字,但它可能不止重复出现一次。 24 | # 25 | # Related Topics 数组 双指针 二分查找 26 | """ 27 | 既然是对某个数的定位,我们想到是不是可以用二分法,但是和传统二分不一样的是,我们对要定位的“数”做二分, 28 | 而不是对数组的索引做二分。要定位的“数”根据题意在 1 和 n 之间,每一次二分都可以将搜索区间缩小一半。 29 | 30 | 以 [1, 2, 2, 3, 4, 5, 6, 7] 为例,一共有 8 个数,每个数都在 1 和 7 之间。1 和 7 的中位数是 4, 31 | 遍历整个数组,统计小于 4 的整数的个数,至多应该为 3 个,如果超过 3 个就说明重复的数存在于区间 [1,4) (注意:左闭右开)中; 32 | 33 | 否则,重复的数存在于区间 [4,7](注意:左右都是闭)中。这里小于 4 的整数有 4 个(它们是 1, 2, 2, 3), 34 | 因此砍掉右半区间,连中位数也砍掉。 35 | 36 | 以此类推,最后区间越来越小,直到变成 1 个整数,这个整数就是我们要找的重复的数,时间复杂度是 [公式] 。 37 | 38 | 39 | """ 40 | 41 | from typing import List 42 | 43 | 44 | # leetcode submit region begin(Prohibit modification and deletion) 45 | # 我的方法。先排序,已经更改了原数组,pass 46 | class Solution: 47 | def findDuplicate(self, nums: List[int]) -> int: 48 | nums.sort() 49 | for i in range(1, len(nums)): 50 | if nums[i] == nums[i - 1]: 51 | return nums[i] 52 | 53 | 54 | # o(n2) 超时 55 | class Solution: 56 | def findDuplicate(self, nums: List[int]) -> int: 57 | for i in range(len(nums)): 58 | for j in range(len(nums)): 59 | if i == j: 60 | continue 61 | else: 62 | if nums[i] == nums[j]: 63 | return nums[i] 64 | 65 | 66 | class Solution: 67 | def findDuplicate(self, nums: List[int]) -> int: 68 | def count(nums, start, end): 69 | c = 0 70 | for i in nums: 71 | if i > start and i <= end: 72 | c += 1 73 | return c 74 | 75 | def helper(nums, start, end): 76 | # 这里比较重要的点在于:如果end-start<=1, 77 | # 那 mid = start + (end - start) // 2这个方法得到的结果会是0.那start和mid就是同一个值了 78 | if end - start <= 1: 79 | mid = end 80 | else: 81 | mid = start + (end - start) // 2 82 | 83 | c = count(nums, start, mid) 84 | # print("**",start, end, mid, c) 85 | if mid - start <= 1 and c >= 2: 86 | return mid 87 | elif c > mid - start: # 说明重复再0-mid(包尾不包头) 88 | start, end = start, mid 89 | return helper(nums, start, end) 90 | else: # 否则说明在Mid到n(包尾不包头)这个区间 91 | start, end = mid, end 92 | return helper(nums, start, end) 93 | 94 | n = len(nums) 95 | start, end = 0, n 96 | return helper(nums, start, end) 97 | 98 | 99 | s = Solution() 100 | nums = [3, 1, 3, 4, 2] 101 | # nums=[1,3,4,2,2] 102 | 103 | print(s.findDuplicate(nums=nums)) 104 | # leetcode submit region end(Prohibit modification and deletion) 105 | -------------------------------------------------------------------------------- /8.二分+分治/300-lengthOfLIS.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个无序的整数数组,找到其中最长上升子序列的长度。 3 | 4 | 示例: 5 | 6 | 输入: [10,9,2,5,3,7,101,18] 7 | 输出: 4 8 | """ 9 | from typing import List 10 | 11 | 12 | class Solution: 13 | def lengthOfLIS(self, nums: List[int]) -> int: 14 | size = len(nums) 15 | # 特判 16 | if size < 2: 17 | return size 18 | 19 | # 为了防止后序逻辑发生数组索引越界,先把第 1 个数放进去 20 | tail = [nums[0]] 21 | for i in range(1, size): 22 | # 【逻辑 1】比 tail 数组实际有效的末尾的那个元素还大 23 | # 先尝试是否可以接在末尾 24 | if nums[i] > tail[-1]: 25 | tail.append(nums[i]) 26 | continue 27 | 28 | # 使用二分查找法,在有序数组 tail 中 29 | # 找到第 1 个大于等于 nums[i] 的元素,尝试让那个元素更小 30 | left = 0 31 | right = len(tail) - 1 32 | while left < right: 33 | # 选左中位数不是偶然,而是有原因的,原因请见 LeetCode 第 35 题题解 34 | mid = left + (right - left) // 2 35 | # mid = (left + right) >> 1 36 | if tail[mid] < nums[i]: 37 | # 中位数肯定不是要找的数,把它写在分支的前面 38 | left = mid + 1 39 | else: 40 | right = mid 41 | # 走到这里是因为【逻辑 1】的反面,因此一定能找到第 1 个大于等于 nums[i] 的元素,因此无需再单独判断 42 | tail[left] = nums[i] 43 | return len(tail) 44 | 45 | 46 | nums = [10, 9, 2, 5, 3, 7, 101, 18] 47 | s = Solution() 48 | s.lengthOfLIS(nums) 49 | 50 | 51 | class Solution: 52 | def lengthOfLIS(self, nums: List[int]) -> int: 53 | # 动态规划 dp 代表以nums[i]结尾的 54 | # 递增子序列的长度 55 | if not nums: 56 | return 0 57 | dp = [1] 58 | for i in range(len(nums)): 59 | dp.append(1) 60 | for j in range(i): 61 | if nums[i] > nums[j]: 62 | dp[i] = max(dp[i], dp[j] + 1) 63 | return max(dp) 64 | -------------------------------------------------------------------------------- /8.二分+分治/33-search.py: -------------------------------------------------------------------------------- 1 | """ 2 | 33. 搜索旋转排序数组 3 | 4 | 假设按照升序排序的数组在预先未知的某个点上进行了旋转。 5 | ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 6 | 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。 7 | 你可以假设数组中不存在重复的元素。 8 | 9 | 你的算法时间复杂度必须是 O(log n) 级别。 10 | 11 | 示例 1: 12 | 输入: nums = [4,5,6,7,0,1,2], target = 0 13 | 输出: 4 14 | 示例 2: 15 | 输入: nums = [4,5,6,7,0,1,2], target = 3 16 | 输出: -1 17 | """ 18 | 19 | 20 | class Solution(object): 21 | def search(self, nums, target): 22 | """ 23 | :type nums: List[int] 24 | :type target: int 25 | :rtype: int 26 | """ 27 | 28 | # 二分搜索查找旋转的节点:即寻找最小值 29 | def find_minimum_idx(nums): 30 | min_, max_ = 0, len(nums) 31 | while True: 32 | mid_ = (min_ + max_) // 2 33 | if mid_ - 1 >= 0 and min_ + 1 < len(nums): 34 | if nums[mid_ - 1] > nums[mid_] and nums[mid_ + 1] > nums[mid_]: 35 | return mid_ 36 | elif nums[0] < nums[mid_]: 37 | min_ = mid_ + 1 38 | elif nums[-1] > nums[mid_]: 39 | max_ = mid_ - 1 40 | else: 41 | return mid_ 42 | 43 | # 二分查找寻找值 44 | def binary_search(nums, target): 45 | min_, max_ = 0, len(nums) 46 | while True: 47 | mid_ = (min_ + max_) // 2 48 | if nums[mid_] == target: 49 | return mid_ 50 | elif target > nums[mid_]: 51 | min_ = mid_ + 1 52 | elif target < nums[mid_]: 53 | max_ = mid_ - 1 54 | if max_ < 0 or min_ > len(nums) - 1 or max_ < min_: 55 | return 56 | # print(min_,max_,mid_) 57 | 58 | mid_idx = find_minimum_idx(nums) 59 | new_nums = nums[-mid_idx + 1:] + nums[:mid_idx] 60 | 61 | idx = binary_search(new_nums, target) 62 | if not idx: 63 | return -1 64 | if idx > len(nums) - mid_idx + 1: 65 | return idx - (len(nums) - mid_idx + 1) 66 | else: 67 | return mid_idx + idx 68 | 69 | 70 | class Solution: 71 | def search(self, nums, target): 72 | if not nums: 73 | return -1 74 | 75 | i = 0 76 | j = len(nums) - 1 77 | while i <= j: 78 | mid = (i + j) // 2 79 | if nums[mid] == target: 80 | return mid 81 | if nums[i] <= nums[mid]: 82 | if nums[i] <= target < nums[mid]: 83 | j = mid - 1 84 | else: 85 | i = mid + 1 86 | else: 87 | if nums[mid] < target <= nums[j]: 88 | i = mid + 1 89 | else: 90 | j = mid - 1 91 | return -1 92 | 93 | 94 | nums = [4, 5, 6, 7, 0, 1, 2] 95 | target = 0 96 | 97 | # target=3 98 | s = Solution() 99 | print(s.search(nums, target)) 100 | -------------------------------------------------------------------------------- /8.二分+分治/34_searchRange.py: -------------------------------------------------------------------------------- 1 | """34. 在排序数组中查找元素的第一个和最后一个位置 2 | 给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。 3 | 你的算法时间复杂度必须是 O(log n) 级别。 4 | 如果数组中不存在目标值,返回 [-1, -1]。 5 | 6 | 示例 1: 7 | 输入: nums = [5,7,7,8,8,10], target = 8 8 | 输出: [3,4] 9 | 示例 2: 10 | 输入: nums = [5,7,7,8,8,10], target = 6 11 | 输出: [-1,-1] 12 | """ 13 | 14 | 15 | # 自己写的:超时 16 | class Solution(object): 17 | def searchRange(self, nums, target): 18 | """ 19 | :type nums: List[int] 20 | :type target: int 21 | :rtype: List[int] 22 | """ 23 | min_, max_ = 0, len(nums) 24 | 25 | while True: 26 | mid_ = (min_ + max_) // 2 27 | print(mid_) 28 | if nums[mid_] == target: 29 | start = mid_ 30 | end = mid_ 31 | while start - 1 >= 0 and nums[start - 1] == target: 32 | start -= 1 33 | while end + 1 < len(nums) and nums[end + 1] == target: 34 | end += 1 35 | return start, end 36 | elif target > nums[mid_]: 37 | min_ = mid_ + 1 38 | elif target < nums[mid_]: 39 | max_ = mid_ - 1 40 | if max_ < 0 or min_ > len(nums) - 1: 41 | return [-1, -1] 42 | 43 | 44 | class Solution: 45 | # returns leftmost (or rightmost) index at which `target` should be inserted in sorted 46 | # array `nums` via binary search. 47 | def extreme_insertion_index(self, nums, target, left): 48 | low = 0 49 | high = len(nums) 50 | 51 | while low < high: 52 | mid = (low + high) // 2 53 | 54 | if nums[mid] > target or (left and target == nums[mid]): 55 | high = mid 56 | else: 57 | low = mid + 1 58 | return low 59 | 60 | def searchRange(self, nums, target): 61 | left_idx = self.extreme_insertion_index(nums, target, True) 62 | print('--------') 63 | # assert that `left_idx` is within the array bounds and that `target` 64 | # is actually in `nums`. 65 | if left_idx == len(nums) or nums[left_idx] != target: 66 | return [-1, -1] 67 | 68 | return [left_idx, self.extreme_insertion_index(nums, target, False) - 1] 69 | 70 | 71 | nums = [5, 7, 7, 8, 8, 10] 72 | target = 8 73 | s = Solution() 74 | print(s.searchRange(nums, target)) 75 | -------------------------------------------------------------------------------- /8.二分+分治/53-maxSubArray.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 3 | 4 | 示例: 5 | 6 | 输入: [-2,1,-3,4,-1,2,1,-5,4], 7 | 输出: 6 8 | 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 9 | 进阶: 10 | 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 11 | 12 | 13 | 思路: 14 | 设 f[x] 为以 a[x] 终止且包含 a[x] 的最大序列的和,有: 15 | f[1] = a[1]; 16 | f[x+1] = max(f[x] ,f[x] + a[x+1]) 17 | 那么最大子序列的和就是 f[1] .. f[n] 中最大的一个 18 | 19 | 首先对数组进行遍历,当前最大连续子序列和为sum,结果为results 20 | 如果sum > 0,则说明sum对结果有增益效果,则sum保留并加上当前遍历数字 21 | 如果sum <= 0,则说明sum对结果无增益效果,需要舍弃,则sum直接更新为当前遍历数字 22 | 每次比较sum 和 results的大小,将最大值置为results,遍历结束后返回结果results 23 | 时间复杂度为O(n) 24 | """ 25 | 26 | 27 | class Solution: 28 | def maxSubArray(self, nums: 'List[int]') -> 'int': 29 | n = len(nums) 30 | max_sum = nums[0] 31 | dp = [nums[0]] + [0] * (n - 1) 32 | for i in range(1, n): 33 | # 先求当前位置最大和, 34 | if dp[i - 1] < 0: 35 | dp[i] = nums[i] 36 | else: 37 | dp[i] = dp[i - 1] + nums[i] 38 | 39 | # 再求全局最大和 40 | max_sum = max(dp[i], max_sum) 41 | return max_sum 42 | 43 | 44 | s = Solution() 45 | print(s.maxSubArray([-2, 1, -3, 4, -1, 2, 1, -5, 4])) 46 | -------------------------------------------------------------------------------- /9.双指针+滑动窗口/11-maxArea.py: -------------------------------------------------------------------------------- 1 | # 给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, 2 | # ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 3 | # 4 | # 说明:你不能倾斜容器,且 n 的值至少为 2。 5 | # 6 | # 7 | # 8 | # 9 | # 10 | # 图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。 11 | # 12 | # 13 | # 14 | # 示例: 15 | # 16 | # 输入:[1,8,6,2,5,4,8,3,7] 17 | # 输出:49 18 | # Related Topics 数组 双指针 19 | 20 | 21 | # leetcode submit region begin(Prohibit modification and deletion) 22 | class Solution: 23 | def maxArea(self, height: List[int]) -> int: 24 | left = 0 25 | right = len(height) - 1 26 | max_val = 0 27 | while left < len(height) and right >= 0 and left < right: 28 | max_val = max(max_val, (right - left) * min(height[left], height[right])) 29 | if height[left] < height[right]: 30 | left += 1 31 | else: 32 | right -= 1 33 | 34 | return max_val 35 | -------------------------------------------------------------------------------- /9.双指针+滑动窗口/141-hasCycle.py: -------------------------------------------------------------------------------- 1 | # 给定一个链表,判断链表中是否有环。 2 | # 3 | # 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的 4 | # 位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。 5 | # 6 | # 如果链表中存在环,则返回 true 。 否则,返回 false 。 7 | # 8 | # 9 | # 10 | # 进阶: 11 | # 12 | # 你能用 O(1)(即,常量)内存解决此问题吗? 13 | # 14 | # 15 | # 16 | # 示例 1: 17 | # 18 | # 19 | # 20 | # 输入:head = [3,2,0,-4], pos = 1 21 | # 输出:true 22 | # 解释:链表中有一个环,其尾部连接到第二个节点。 23 | # 24 | # 25 | # 示例 2: 26 | # 27 | # 28 | # 29 | # 输入:head = [1,2], pos = 0 30 | # 输出:true 31 | # 解释:链表中有一个环,其尾部连接到第一个节点。 32 | # 33 | # 34 | # 示例 3: 35 | # 36 | # 37 | # 38 | # 输入:head = [1], pos = -1 39 | # 输出:false 40 | # 解释:链表中没有环。 41 | # 42 | # 43 | # 44 | # 45 | # 提示: 46 | # 47 | # 48 | # 链表中节点的数目范围是 [0, 104] 49 | # -105 <= Node.val <= 105 50 | # pos 为 -1 或者链表中的一个 有效索引 。 51 | # 52 | # Related Topics 链表 双指针 53 | 54 | 55 | # leetcode submit region begin(Prohibit modification and deletion) 56 | # Definition for singly-linked list. 57 | class ListNode: 58 | def __init__(self, x): 59 | self.val = x 60 | self.next = None 61 | 62 | "方法一:哈希表:o(N)内存" 63 | 64 | 65 | class Solution: 66 | def hasCycle(self, head: ListNode) -> bool: 67 | head_set = set() 68 | while head: 69 | if head in head_set: # todo 这里不是head.val哈,有可能重复。 70 | return True 71 | head_set.add(head) 72 | head = head.next 73 | return False 74 | 75 | 76 | class Solution: 77 | def hasCycle(self, head: ListNode) -> bool: 78 | if not head or not head.next: 79 | return False 80 | 81 | slow = head 82 | fast = head.next 83 | 84 | while slow != fast: 85 | if not fast or not fast.next: 86 | return False 87 | slow = slow.next 88 | fast = fast.next.next 89 | 90 | return True 91 | 92 | 93 | # 判断环的入口点: 94 | 95 | """判断有环: 96 | slow每次向前走一步,fast向前追了两步,因此每一步操作后fast到slow的距离缩短了1步,这样继续下去就会使得 97 | 两者之间的距离逐渐缩小:...、5、4、3、2、1、0 -> 相遇。又因为在同一个环中fast和slow之间的距离不会大于换的长度,因此 98 | 到二者相遇的时候slow一定还没有走完一周(或者正好走完以后,这种情况出现在开始的时候fast和slow都在环的入口处)。 99 | 100 | # https://www.cnblogs.com/yorkyang/p/10876604.html 101 | # 可以看出,从链表起点head开始到入口点的距离a,与从slow和fast的相遇点(如图)到入口点的距离相等。 102 | # 因此我们就可以分别用一个指针(ptr1, prt2),同时从head与slow和fast的相遇点出发,每一次操作走一步,直到ptr1 == ptr2,此时的位置也就是入口点! 103 | 104 | """ 105 | 106 | 107 | class Solution: 108 | def detectCycle(self, head): 109 | # 首先判断是否存在环 110 | fastHead = head 111 | slowHead = head 112 | 113 | while (fastHead and fastHead.next): 114 | fastHead = fastHead.next.next 115 | slowHead = slowHead.next 116 | if fastHead == slowHead: 117 | break 118 | # 本想用快慢指针是否相等作为判断条件,但初始时快慢指针是相等的 119 | if fastHead == None or fastHead.next == None: 120 | return None 121 | fastHead = head 122 | while (fastHead != slowHead): 123 | fastHead = fastHead.next 124 | slowHead = slowHead.next 125 | return fastHead 126 | 127 | # leetcode submit region end(Prohibit modification and deletion) 128 | -------------------------------------------------------------------------------- /9.双指针+滑动窗口/15-threeSum.py: -------------------------------------------------------------------------------- 1 | """ 2 | 三数之和: 3 | 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。 4 | 注意:答案中不可以包含重复的三元组。 5 | 例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4], 6 | 7 | 满足要求的三元组集合为: 8 | [[-1, 0, 1], [-1, -1, 2]] 9 | """ 10 | from typing import List 11 | 12 | 13 | # 这样会超时 14 | class Solution: 15 | def twoSum(self, nums, target): 16 | "给定 nums = [2, 7, 11, 15], target = 9, return [0, 1]" 17 | d = {} 18 | result = [] 19 | for i in range(len(nums)): 20 | # if nums[i]==cur: 21 | # continue 22 | # print(nums[i],target-nums[i], d,target-nums[i] in d) 23 | if target - nums[i] in d: 24 | result.append([nums[i], nums[d[target - nums[i]]]]) 25 | d[nums[i]] = i 26 | 27 | return result 28 | 29 | def drop_duplicates(self, res): 30 | res_set = [set(i) for i in res] 31 | new_res_idx = [] 32 | new_res = [] 33 | for i, each in enumerate(res_set): 34 | if each not in new_res_idx: 35 | new_res_idx.append(each) 36 | new_res.append(res[i]) 37 | 38 | return new_res 39 | 40 | def threeSum(self, nums): 41 | result = [] 42 | for i in range(len(nums)): 43 | # new_nums = [i for i in nums[i+1:] if i not in nums[:i]] 44 | new_nums = nums[i + 1:] 45 | tmp = self.twoSum(new_nums, -nums[i]) 46 | 47 | # print(i, -nums[i],new_nums,nums[i+1:] ) 48 | if tmp: 49 | for each in tmp: 50 | result.append([nums[i], each[0], each[1]]) 51 | 52 | result = self.drop_duplicates(result) 53 | return result 54 | 55 | 56 | class Solution: 57 | def threeSum(self, nums: List[int]) -> List[List[int]]: 58 | nums.sort() # 方便完成不重复的任务,也便于决定两个指针如何移动 59 | n = len(nums) 60 | result = [] 61 | for i in range(n): 62 | if i > 0 and nums[i] == nums[i - 1]: # 使无重复 63 | continue 64 | left = i + 1 65 | right = n - 1 66 | while left < right: 67 | cur_sum = nums[i] + nums[left] + nums[right] 68 | if cur_sum == 0: 69 | result.append([nums[i], nums[left], nums[right]]) 70 | while right > left and nums[left] == nums[left + 1]: # 使无重复 71 | left += 1 72 | while right > left and nums[right] == nums[right - 1]: # 使无重复 73 | right -= 1 74 | left += 1 75 | right -= 1 76 | elif cur_sum > 0: 77 | right -= 1 78 | else: 79 | left += 1 80 | return result 81 | 82 | 83 | nums = [2, 7, 11, 15] 84 | nums = [-1, 0, 1, 2, -1, -4] 85 | # target = 9 86 | s = Solution() 87 | [[-1, 0, 1], [-1, -1, 2]] 88 | res = s.threeSum(nums) 89 | print(res) 90 | -------------------------------------------------------------------------------- /9.双指针+滑动窗口/16-threeSumClosest.py: -------------------------------------------------------------------------------- 1 | # 给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和 2 | # 。假定每组输入只存在唯一答案。 3 | # 4 | # 5 | # 6 | # 示例: 7 | # 8 | # 输入:nums = [-1,2,1,-4], target = 1 9 | # 输出:2 10 | # 解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。 11 | # 12 | # 13 | # 14 | # 15 | # 提示: 16 | # 17 | # 18 | # 3 <= nums.length <= 10^3 19 | # -10^3 <= nums[i] <= 10^3 20 | # -10^4 <= target <= 10^4 21 | # 22 | # Related Topics 数组 双指针 23 | class Solution: 24 | def threeSumClosest(self, nums, target): 25 | """ 26 | :type nums: List[int] 27 | :type target: int 28 | :rtype: int 29 | """ 30 | nums.sort() 31 | res = sum(nums[:3]) 32 | m = abs(res - target) 33 | for i in range(len(nums)): 34 | l = i + 1 35 | r = len(nums) - 1 36 | while l < r: 37 | temp = nums[i] + nums[l] + nums[r] 38 | if abs(res - target) > abs(temp - target): 39 | res = temp 40 | elif target < temp: 41 | r -= 1 42 | else: 43 | l += 1 44 | return res 45 | -------------------------------------------------------------------------------- /9.双指针+滑动窗口/209_minSubArrayLen.py: -------------------------------------------------------------------------------- 1 | """长度最小的子数组 2 | 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。 3 | 如果不存在符合条件的连续子数组,返回 0。 4 | 5 | 示例:  6 | 7 | 输入: s = 7, nums = [2,3,1,2,4,3] 8 | 输出: 2 9 | 解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。 10 | 进阶: 11 | 12 | 如果你已经完成了O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。 13 | 14 | https://zhuanlan.zhihu.com/p/61564531 15 | """ 16 | 17 | 18 | # 第一个版本:超时 19 | class Solution(object): 20 | def minSubArrayLen(self, s, nums): 21 | """ 22 | :type s: int 23 | :type nums: List[int] 24 | :rtype: int 25 | """ 26 | import copy 27 | left, right = 0, 0 28 | optim = len(nums) + 1 29 | if len(nums) == 0: 30 | return 0 31 | while left < len(nums): 32 | while right < len(nums): 33 | right += 1 34 | if sum(nums[left:right]) >= s: 35 | optim = min(optim, len(nums[left:right])) 36 | break 37 | if len(nums[left:right]) > optim - 1: 38 | break 39 | 40 | left += 1 41 | right = copy.deepcopy(left) 42 | if optim > len(nums): 43 | optim = 0 44 | 45 | return optim 46 | 47 | 48 | class Solution(object): 49 | """ 50 | 初始化窗口端点L,R,一般L为0,R为1 51 | 初始化最优值 52 | while R < len(Array): #TODO 怎么不是L< len(Array) 53 | while R < len(Array): 54 | R += 1 #移动右端点 55 | if R < len(Array): 56 | 更新状态 57 | if 状态满足条件: 58 | 可选的更新最优值的位置 59 | break #一旦满足条件即跳出 60 | if R == len(Array): # 若循环是由于移动到数组末尾结束,则停止整个程序。因为之后已经不再有可能的解 61 | break 62 | while L < R: 63 | 更新状态 # 移动左端点,需要更新状态 64 | L += 1 65 | if 状态满足条件: 66 | 可选的更新最优值的位置 67 | else: # 一旦窗口所在区间不再满足条件即跳出,去移动右端点 68 | break 69 | 可选的对于L,R端点的后续处理 70 | return 最优值 71 | """ 72 | 73 | pass 74 | 75 | 76 | class Solution(object): 77 | def minSubArrayLen(self, s: int, nums): 78 | summation = 0 79 | left, right = 0, -1 # WHY -1 80 | optim = len(nums) + 1 81 | while left < len(nums): # todo why while right < len(nums): 82 | while right < len(nums): 83 | right += 1 84 | if right < len(nums): 85 | summation += nums[right] # 因为是-1所以才能summation += nums[right] 86 | if summation >= s: 87 | optim = min(optim, right - left + 1) 88 | break 89 | 90 | if right == len(nums): 91 | break 92 | 93 | while left < right: 94 | summation -= nums[left] 95 | left += 1 96 | print(left, right) 97 | if summation >= s: 98 | optim = min(optim, right - left + 1) 99 | else: 100 | break 101 | return optim if optim != len(nums) + 1 else 0 102 | 103 | 104 | class Solution(object): 105 | def minSubArrayLen(self, s: int, nums): 106 | if len(nums) == 0 or sum(nums) < s: 107 | return 0 108 | 109 | left, right = 0, -1 110 | optim = len(nums) + 1 111 | summation = 0 112 | while left < len(nums): 113 | while right < len(nums): 114 | if summation < s and right + 1 < len(nums): 115 | right += 1 116 | summation += nums[right] 117 | elif summation >= s: 118 | # print('-' * 3, left, right, summation, optim, right - left, min(optim, right - left + 1)) 119 | optim = min(optim, right - left + 1) 120 | break 121 | else: 122 | break 123 | 124 | summation -= nums[left] 125 | left += 1 126 | return optim 127 | 128 | 129 | s = 7 130 | nums = [2, 3, 1, 2, 4, 3] 131 | # s = 100 132 | # nums = [] 133 | # s = 4 134 | # nums = [1, 4, 4] 135 | # s = 3 136 | # nums = [1, 1] 137 | # print(nums) 138 | a = Solution() 139 | print(a.minSubArrayLen(s, nums)) 140 | -------------------------------------------------------------------------------- /9.双指针+滑动窗口/239-maxSlidingWindow.py: -------------------------------------------------------------------------------- 1 | """滑动窗口最大值 2 | 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。 3 | 4 | 返回滑动窗口最大值。 5 | 6 | 示例: 7 | 8 | 输入: nums = 9 | [1,3,-1,-3,5,3,6,7] 10 | , 和 k = 3 11 | 输出:[3,3,5,5,6,7] 12 | 13 | # todo 哪里有双端队列? 14 | """ 15 | 16 | 17 | class Solution: 18 | def maxSlidingWindow(self, nums, k): 19 | res = [] 20 | if len(nums) <= k: 21 | res.append(max(nums)) 22 | return res 23 | 24 | for i in range(len(nums) - k + 1): 25 | print(nums[i:i + k]) 26 | res.append(max(nums[i:i + k])) 27 | return res 28 | 29 | 30 | nums = [1, 3, -1, -3, 5, 3, 6, 7] 31 | k = 3 32 | s = Solution() 33 | res = s.maxSlidingWindow(nums, k) 34 | print(res) 35 | -------------------------------------------------------------------------------- /9.双指针+滑动窗口/26-removeDuplicates.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 3 | 4 | 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 5 | 6 | 示例 1: 7 | 8 | 给定数组 nums = [1,1,2], 9 | 10 | 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 11 | 1, 2。 12 | 你不需要考虑数组中超出新长度后面的元素。 13 | 示例 2: 14 | 15 | 给定 nums = [0,0,1,1,1,2,2,3,3,4], 16 | 17 | 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 18 | 0, 1, 2, 3, 4。 19 | 你不需要考虑数组中超出新长度后面的元素。 20 | 21 | 22 | 思路: 23 | 24 | 原地替换,将重复位置的元素替换为下个非重复元素。 25 | 26 | """ 27 | from typing import List 28 | 29 | 30 | class Solution: 31 | def removeDuplicates(self, nums: List[int]) -> int: 32 | # 第一个指针用于更改值 33 | first = 0 34 | # 第二个指针用于遍历 35 | for second in range(len(nums)): 36 | # 如果和之前记录的值不同 37 | if nums[first] != nums[second]: 38 | # 第一个指针先加1 39 | first += 1 40 | # 然后赋值 41 | nums[first] = nums[second] 42 | return first + 1 43 | -------------------------------------------------------------------------------- /9.双指针+滑动窗口/287-findDuplicate.py: -------------------------------------------------------------------------------- 1 | # 给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n), 2 | # 可知至少存在一个重复的整数。假设只有一个重复的整数,找出 3 | # 这个重复的数。 4 | # 5 | # 示例 1: 6 | # 7 | # 输入: [1,3,4,2,2] 8 | # 输出: 2 9 | # 10 | # 11 | # 示例 2: 12 | # 13 | # 输入: [3,1,3,4,2] 14 | # 输出: 3 15 | # 16 | # 17 | # 说明: 18 | # 19 | # 20 | # 不能更改原数组(假设数组是只读的)。 21 | # 只能使用额外的 O(1) 的空间。 22 | # 时间复杂度小于 O(n2) 。 23 | # 数组中只有一个重复的数字,但它可能不止重复出现一次。 24 | # 25 | # Related Topics 数组 双指针 二分查找 26 | """ 27 | 既然是对某个数的定位,我们想到是不是可以用二分法,但是和传统二分不一样的是,我们对要定位的“数”做二分, 28 | 而不是对数组的索引做二分。要定位的“数”根据题意在 1 和 n 之间,每一次二分都可以将搜索区间缩小一半。 29 | 30 | 以 [1, 2, 2, 3, 4, 5, 6, 7] 为例,一共有 8 个数,每个数都在 1 和 7 之间。1 和 7 的中位数是 4, 31 | 遍历整个数组,统计小于 4 的整数的个数,至多应该为 3 个,如果超过 3 个就说明重复的数存在于区间 [1,4) (注意:左闭右开)中; 32 | 33 | 否则,重复的数存在于区间 [4,7](注意:左右都是闭)中。这里小于 4 的整数有 4 个(它们是 1, 2, 2, 3), 34 | 因此砍掉右半区间,连中位数也砍掉。 35 | 36 | 以此类推,最后区间越来越小,直到变成 1 个整数,这个整数就是我们要找的重复的数,时间复杂度是 [公式] 。 37 | 38 | 39 | """ 40 | 41 | from typing import List 42 | 43 | 44 | # leetcode submit region begin(Prohibit modification and deletion) 45 | # 我的方法。先排序,已经更改了原数组,pass 46 | class Solution: 47 | def findDuplicate(self, nums: List[int]) -> int: 48 | nums.sort() 49 | for i in range(1, len(nums)): 50 | if nums[i] == nums[i - 1]: 51 | return nums[i] 52 | 53 | 54 | # o(n2) 超时 55 | class Solution: 56 | def findDuplicate(self, nums: List[int]) -> int: 57 | for i in range(len(nums)): 58 | for j in range(len(nums)): 59 | if i == j: 60 | continue 61 | else: 62 | if nums[i] == nums[j]: 63 | return nums[i] 64 | 65 | 66 | class Solution: 67 | def findDuplicate(self, nums: List[int]) -> int: 68 | def count(nums, start, end): 69 | c = 0 70 | for i in nums: 71 | if i > start and i <= end: 72 | c += 1 73 | return c 74 | 75 | def helper(nums, start, end): 76 | # 这里比较重要的点在于:如果end-start<=1, 77 | # 那 mid = start + (end - start) // 2这个方法得到的结果会是0.那start和mid就是同一个值了 78 | if end - start <= 1: 79 | mid = end 80 | else: 81 | mid = start + (end - start) // 2 82 | 83 | c = count(nums, start, mid) 84 | # print("**",start, end, mid, c) 85 | if mid - start <= 1 and c >= 2: 86 | return mid 87 | elif c > mid - start: # 说明重复再0-mid(包尾不包头) 88 | start, end = start, mid 89 | return helper(nums, start, end) 90 | else: # 否则说明在Mid到n(包尾不包头)这个区间 91 | start, end = mid, end 92 | return helper(nums, start, end) 93 | 94 | n = len(nums) 95 | start, end = 0, n 96 | return helper(nums, start, end) 97 | 98 | 99 | s = Solution() 100 | nums = [3, 1, 3, 4, 2] 101 | # nums=[1,3,4,2,2] 102 | 103 | print(s.findDuplicate(nums=nums)) 104 | # leetcode submit region end(Prohibit modification and deletion) 105 | -------------------------------------------------------------------------------- /9.双指针+滑动窗口/300-lengthOfLIS.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个无序的整数数组,找到其中最长上升子序列的长度。 3 | 4 | 示例: 5 | 6 | 输入: [10,9,2,5,3,7,101,18] 7 | 输出: 4 8 | """ 9 | from typing import List 10 | 11 | 12 | class Solution: 13 | def lengthOfLIS(self, nums: List[int]) -> int: 14 | size = len(nums) 15 | # 特判 16 | if size < 2: 17 | return size 18 | 19 | # 为了防止后序逻辑发生数组索引越界,先把第 1 个数放进去 20 | tail = [nums[0]] 21 | for i in range(1, size): 22 | # 【逻辑 1】比 tail 数组实际有效的末尾的那个元素还大 23 | # 先尝试是否可以接在末尾 24 | if nums[i] > tail[-1]: 25 | tail.append(nums[i]) 26 | continue 27 | 28 | # 使用二分查找法,在有序数组 tail 中 29 | # 找到第 1 个大于等于 nums[i] 的元素,尝试让那个元素更小 30 | left = 0 31 | right = len(tail) - 1 32 | while left < right: 33 | # 选左中位数不是偶然,而是有原因的,原因请见 LeetCode 第 35 题题解 34 | mid = left + (right - left) // 2 35 | # mid = (left + right) >> 1 36 | if tail[mid] < nums[i]: 37 | # 中位数肯定不是要找的数,把它写在分支的前面 38 | left = mid + 1 39 | else: 40 | right = mid 41 | # 走到这里是因为【逻辑 1】的反面,因此一定能找到第 1 个大于等于 nums[i] 的元素,因此无需再单独判断 42 | tail[left] = nums[i] 43 | return len(tail) 44 | 45 | 46 | nums = [10, 9, 2, 5, 3, 7, 101, 18] 47 | s = Solution() 48 | s.lengthOfLIS(nums) 49 | 50 | 51 | class Solution: 52 | def lengthOfLIS(self, nums: List[int]) -> int: 53 | # 动态规划 dp 代表以nums[i]结尾的 54 | # 递增子序列的长度 55 | if not nums: 56 | return 0 57 | dp = [1] 58 | for i in range(len(nums)): 59 | dp.append(1) 60 | for j in range(i): 61 | if nums[i] > nums[j]: 62 | dp[i] = max(dp[i], dp[j] + 1) 63 | return max(dp) 64 | -------------------------------------------------------------------------------- /9.双指针+滑动窗口/3_lengthOfLongestSubstring.py: -------------------------------------------------------------------------------- 1 | """3. 无重复字符的最长子串 2 | 难度 3 | 中等 4 | 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。 5 | 6 | 示例 1: 7 | 8 | 输入: "abcabcbb" 9 | 输出: 3 10 | 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。 11 | 示例 2: 12 | 13 | 输入: "bbbbb" 14 | 输出: 1 15 | 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。 16 | 示例 3: 17 | 18 | 输入: "pwwkew" 19 | 输出: 3 20 | 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 21 | 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。""" 22 | 23 | 24 | class Solution: 25 | def lengthOfLongestSubstring(self, s: str) -> int: 26 | if not s: 27 | return 0 28 | 29 | left = 0 30 | right = 0 31 | optim = -1 32 | s_set = set() 33 | while left < len(s): 34 | while right < len(s): 35 | if s[right] not in s_set: 36 | s_set.add(s[right]) 37 | optim = max(optim, right - left + 1) 38 | right += 1 39 | else: 40 | break 41 | s_set.remove(s[left]) 42 | left += 1 43 | return optim 44 | 45 | """ 46 | 模板就是: 47 | while left list: 47 | ''' 48 | 解法1:滑动窗口 49 | ''' 50 | res = [] 51 | window = {} # 记录窗口中各个字符数量的字典 52 | needs = {} # 记录目标字符串中各个字符数量的字典 53 | for c in p: 54 | needs[c] = needs.get(c, 0) + 1 # 统计目标字符串的信息 55 | print(needs) 56 | 57 | length, limit = len(p), len(s) 58 | left = right = 0 # 定理两个指针,分别表示窗口的左、右界限 59 | 60 | while right < limit: 61 | c = s[right] 62 | if c not in needs: # 当遇到不需要的字符时 63 | window.clear() # 将之前统计的信息全部放弃 64 | left = right = right + 1 # 从下一位置开始重新统计 65 | else: 66 | window[c] = window.get(c, 0) + 1 # 统计窗口内各种字符出现的次数 67 | if right - left + 1 == length: # 当窗口大小与目标字符串长度一致时 68 | if window == needs: 69 | res.append(left) # 如果窗口内的各字符数量与目标字符串一致就将left添加到结果中 70 | window[s[left]] -= 1 # 并将移除的字符数量减一 71 | left += 1 # left右移 72 | right += 1 # right右移 73 | return res 74 | 75 | # leetcode submit region end(Prohibit modification and deletion) 76 | -------------------------------------------------------------------------------- /9.双指针+滑动窗口/5-longestPalindrome.py: -------------------------------------------------------------------------------- 1 | """给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。 2 | 3 | 输入: "babad" 4 | 输出: "bab" 5 | 注意: "aba" 也是一个有效答案。 6 | 7 | 输入: "cbbd" 8 | 输出: "bb" 9 | """ 10 | 11 | class Solution: 12 | # def longestPalindrome(self, s: str) -> str: 13 | # 中心扩散法Spread From Center 14 | def spread(self, s, left, right): 15 | """ 16 | left = right 的时候,此时回文中心是一条线,回文串的长度是奇数 17 | right = left + 1 的时候,此时回文中心是任意一个字符,回文串的长度是偶数 18 | """ 19 | while left >= 0 and right < len(s) and s[left] == s[right]: 20 | left -= 1 21 | right += 1 22 | return s[left + 1:right] 23 | 24 | # 动态规划法-中心扩散法Spread From Center 25 | def spread_from_center(self, s: str) -> str: 26 | if s == s[::-1]: 27 | return s 28 | res = s[:1] 29 | 30 | for i in range(len(s)): 31 | palindrome_odd = self.spread(s=s, left=i, right=i) 32 | palindrome_even = self.spread(s=s, left=i, right=i + 1) 33 | # 当前找到的最长回文子串 34 | res = max(palindrome_odd, palindrome_even, res, key=len) 35 | print(i, res, palindrome_odd, palindrome_even) 36 | return res 37 | 38 | 39 | ## 动态规划: 40 | class Solution: 41 | def longestPalindrome(self, s: str) -> str: 42 | dp = [[False] * len(s) for i in range(len(s))] 43 | if not s or len(s) == 1: 44 | return s 45 | max_len = 1 46 | result = s[:1] 47 | 48 | for j in range(len(s)): 49 | for i in range(len(s)): 50 | if i <= j: 51 | if j == i: 52 | dp[i][j] = True 53 | elif j == i + 1: 54 | dp[i][j] = (s[i] == s[j]) 55 | else: 56 | dp[i][j] = dp[i + 1][j - 1] and s[i] == s[j] 57 | 58 | if dp[i][j]: 59 | if j - i + 1 > max_len: 60 | max_len = j - i + 1 61 | result = s[i:j + 1] 62 | return result 63 | 64 | 65 | if __name__ == "__main__": 66 | s = Solution() 67 | res = s.spread_from_center(s='babad') 68 | print(res) 69 | # print(longestPalindrome(s='babad')) 70 | print(s.spread_from_center(s='acccc')) 71 | 72 | # print('res',longestPalindrome(s='babadddede')) 73 | # print('res',longestPalindrome(s='cbbd')) 74 | -------------------------------------------------------------------------------- /9.双指针+滑动窗口/581-findUnsortedSubarray.py: -------------------------------------------------------------------------------- 1 | # 给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。 2 | # 3 | # 你找到的子数组应是最短的,请输出它的长度。 4 | # 5 | # 示例 1: 6 | # 7 | # 8 | # 输入: [2, 6, 4, 8, 10, 9, 15] 9 | # 输出: 5 10 | # 解释: 你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。 11 | # 12 | # 13 | # 说明 : 14 | # 15 | # 16 | # 输入的数组长度范围在 [1, 10,000]。 17 | # 输入的数组可能包含重复元素 ,所以升序的意思是<=。 18 | # 19 | # Related Topics 数组 20 | 21 | 22 | # leetcode submit region begin(Prohibit modification and deletion) 23 | class Solution: 24 | def findUnsortedSubarray(self, nums: List[int]) -> int: 25 | num = sorted(nums) 26 | begin, end = 0, 0 27 | j = len(nums) - 1 28 | for i in range(len(nums)): 29 | if num[i] != nums[i]: 30 | begin = i 31 | break 32 | while j >= 0: 33 | if nums[j] != num[j]: 34 | end = j 35 | break 36 | j -= 1 37 | if begin == end: 38 | return 0 39 | return end - begin + 1 40 | 41 | # leetcode submit region end(Prohibit modification and deletion) 42 | -------------------------------------------------------------------------------- /9.双指针+滑动窗口/647-countSubstrings.py: -------------------------------------------------------------------------------- 1 | # 给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。 2 | # 3 | # 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。 4 | # 5 | # 6 | # 7 | # 示例 1: 8 | # 9 | # 输入:"abc" 10 | # 输出:3 11 | # 解释:三个回文子串: "a", "b", "c" 12 | # 13 | # 14 | # 示例 2: 15 | # 16 | # 输入:"aaa" 17 | # 输出:6 18 | # 解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa" 19 | # 20 | # 21 | # 22 | # 提示: 23 | # 24 | # 25 | # 输入的字符串长度不会超过 1000 。 26 | # 27 | # Related Topics 字符串 动态规划 28 | """ 29 | 动态规划的思想是,我们先确定所有的回文,即 string[start:end]是回文. 当我们要确定string[i:j] 是不是回文的时候,要确定: 30 | 31 | string[i] 等于 string[j]吗? 32 | string[i+1:j-1]是回文吗? 33 | 单个字符是回文;两个连续字符如果相等是回文;如果有3个以上的字符,需要两头相等并且去掉首尾之后依然是回文。 34 | """ 35 | 36 | 37 | class Solution(object): 38 | def countSubstrings(self, s): 39 | """ 40 | :type s: str 41 | :rtype: int 42 | """ 43 | n = len(s) 44 | count = 0 45 | dp = [[0] * n for _ in range(n)] 46 | for i in range(n): 47 | for j in range(i): 48 | dp[j][i] = (s[j] == s[i]) & ((i - j < 2) | dp[j + 1][i - 1]) 49 | if dp[j][i]: 50 | count += 1 51 | dp[i][i] = 1 52 | count += 1 53 | return count 54 | 55 | 56 | class Solution(object): 57 | def countSubstrings(self, s): 58 | """ 59 | :type s: str 60 | :rtype: int 61 | """ 62 | count = 0 63 | for i in range(len(s)): 64 | count += 1 65 | # 回文长度是奇数的情况 66 | left = i - 1 67 | right = i + 1 68 | while left >= 0 and right < len(s) and s[left] == s[right]: 69 | count += 1 70 | left -= 1 71 | right += 1 72 | # 回文长度是偶数的情况 73 | left = i 74 | right = i + 1 75 | while left >= 0 and right < len(s) and s[left] == s[right]: 76 | count += 1 77 | left -= 1 78 | right += 1 79 | return count 80 | 81 | # leetcode submit region end(Prohibit modification and deletion) 82 | -------------------------------------------------------------------------------- /9.双指针+滑动窗口/76-minWindow.py: -------------------------------------------------------------------------------- 1 | # 给你一个字符串 S、一个字符串 T 。请你设计一种算法,可以在 O(n) 的时间复杂度内,从字符串 S 里面找出:包含 T 所有字符的最小子串。 2 | # 3 | # 4 | # 5 | # 示例: 6 | # 7 | # 输入:S = "ADOBECODEBANC", T = "ABC" 8 | # 输出:"BANC" 9 | # 10 | # 11 | # 12 | # 提示: 13 | # 14 | # 15 | # 如果 S 中不存这样的子串,则返回空字符串 ""。 16 | # 如果 S 中存在这样的子串,我们保证它是唯一的答案。 17 | # 18 | # Related Topics 哈希表 双指针 字符串 Sliding Window 19 | 20 | 21 | # leetcode submit region begin(Prohibit modification and deletion) 22 | import collections 23 | 24 | 25 | class Solution: 26 | 27 | def minWindow(self, s: str, t: str) -> str: 28 | need = collections.defaultdict(int) 29 | for c in t: 30 | need[c] += 1 31 | needCnt = len(t) 32 | i = 0 33 | res = (0, float('inf')) 34 | for j, c in enumerate(s): 35 | if need[c] > 0: # 虽然D不在need但是默认为0 36 | needCnt -= 1 37 | need[c] -= 1 38 | if needCnt == 0: # 步骤一:滑动窗口包含了所有T元素 39 | while True: # 步骤二:增加i,排除多余元素 40 | c = s[i] 41 | if need[c] == 0: 42 | break 43 | need[c] += 1 44 | i += 1 45 | if j - i < res[1] - res[0]: # 记录结果 46 | res = (i, j) 47 | need[s[i]] += 1 # 步骤三:i增加一个位置,寻找新的满足条件滑动窗口 48 | needCnt += 1 49 | i += 1 50 | return '' if res[1] > len(s) else s[res[0]:res[1] + 1] # 如果res始终没被更新过,代表无满足条件的结果 51 | 52 | 53 | s = Solution() 54 | S = "ADOBECODEBANC" 55 | T = "ABC" 56 | s.minWindow(S, T) 57 | 58 | # leetcode submit region end(Prohibit modification and deletion) 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 118-classic-Questions-of-LeetCode 2 | 118个按照题目类型分门别类的LeetCode题目,刷完这个再找工作容易多啦! 3 | 4 | 如果想从事算法相关,LeetCode必刷,没有捷径。 5 | 6 | 我从2020.04月开始刷LeetCode,花了些时间找了118道经典题目,前后反反复复刷了三遍之后,到2020下半年面对大厂的coding才开始不怯场。 7 | 在此之前,几乎没刷过LeetCode,数据结构掌握也不是很深刻。 8 | 9 | LeetCode是我面试准备中跨越的第一个难题,当我刷完三遍这118道经典题目以后,LC在面试中逐渐从失分项变成了加分项。 10 | 11 | 在此祭出我的血和泪实践出来的经验,希望各位找工作的人能少走弯路,直达大厂。 12 | 13 | ### 划重点:大家刷题的时候集中刷一个类别的题,因为一个类别的题目套路很相似,一起刷会很容易把握其中规律,这一点很重要哦~主要有以下的类别,按照个人认为的优先级顺序排了个序 14 | 15 | 0.排序(**这里有一些非LC的题目,自己整理的比较基础的冒泡、堆排、合并排、快排等,大家需要先学会写这些哦**) 16 | 1.动态规划 17 | 2.递归+DFS+BFS+回溯 18 | 3.栈 19 | 4.HashDict 20 | 5.链表 21 | 6.数组 22 | 7.堆 23 | 8.二分+分治 24 | 9.双指针+滑动窗口 25 | 10.树 26 | 11.图 27 | 12.位运算 28 | 29 | 这里说明一下,**树排在比较后面,不代表树问题不重要,而是树问题会包含很多算法思想(如递归、动归、回溯等),所以可以先把算法思想刷了再刷树的题目,上手比较容易**。再次声明,树很重要哦~: 30 | 31 | 32 | --------------------------------------------------------------------------------