leecode128,最长连续子序列,哈希表枚举,要注意技巧,判断num-1是否在哈希表中,可以降低时间复杂度。
class Solution: def longestConsecutive(self, nums: List[int]) -> int: nums_set = set(nums) long_path = 0 for num in nums_set: if num-1 not in nums_set: current_num = num current_path = 1 while current_num + 1 in nums_set: current_num += 1 current_path += 1 long_path = max(long_path,current_path) return long_path
旋转数组
leecode33,搜索旋转数组,二分后两边子数组一个有序一个无序,在有序的那一边继续搜索。但是要考虑重复数字的存在。
class Solution: def search(self, nums,target: int) -> int: if len(nums) == 0: return -1 if len(nums) == 1: if nums[0] == target: return 0 else: return -1 l = 0 r = len(nums) while l < r: mid = (l+r)//2 if nums[mid] == target: return mid if nums[l] == nums[mid] and nums[r-1] == nums[mid]: l += 1 r -= 1 elif nums[l]<=nums[mid]: if nums[l]<=target<=nums[mid]: r = mid else: l = mid + 1 else: if nums[mid]<=target<=nums[r-1]: l = mid + 1 else: r = mid return -1
leecode81,旋转数组,leecode38加强版,有重复数字,难以判断子数组哪个有序,所以碰到nums[l],nums[mid],nums[r-1]三数字相等时,l和r分别加减1
class Solution: def search(self, nums,target: int) -> int: if len(nums) == 0: return -1 if len(nums) == 1: return nums[0] == target l = 0 r = len(nums) while l < r: mid = (l+r)//2 if nums[mid] == target: return True if nums[l] == nums[mid] and nums[r-1] == nums[mid]: l += 1 r -= 1 elif nums[l]<=nums[mid]: if nums[l]<=target<=nums[mid]: r = mid else: l = mid + 1 else: if nums[mid]<=target<=nums[r-1]: l = mid + 1 else: r = mid return False
leecode153,查找旋转数组的最小值,二分法。注意起始区间l = 0,r = len(nums)-1,当nums[mid]>nums[r]时,说明mid在左边子区间,那么不考虑左边区间;当nums[mid]<nums[r]时,说明mid在右边区间,那么不考虑右边区间;当nums[mid] == nums[r]的时候,如果有重复元素,则无法区分,此时只能让r -= 1。返回的索引要考虑实际问题,本题就是最小元素的索引,即右边区间的最左边的点。
class Solution: def findMin(self, nums): l,r = 0,len(nums)-1 while l < r: mid = (l+r)//2 if nums[mid]>nums[r]: l = mid + 1 elif nums[mid] < nums[r]: r = mid else: r -= 1 return nums[l]#或者nums[r] if __name__ == "__main__": solution = Solution() nums = [3,4,5,1,2] ans = solution.findMin(nums) print(ans)
leecode154,寻找旋转数组的最小值,leecode153加强版本,包含重复元素,二分法。注意起始区间l = 0,r = len(nums)-1,当nums[mid]>nums[r]时,说明mid在左边子区间,那么不考虑左边区间;当nums[mid]<nums[r]时,说明mid在右边区间,那么不考虑右边区间;当nums[mid] == nums[r]的时候,如果有重复元素,则无法区分,此时只能让r -= 1
class Solution: def findMin(self, nums): l,r = 0,len(nums) while l < r: mid = (l+r)//2 if nums[mid]>nums[r-1]: l = mid + 1 elif nums[mid] < nums[r-1]: r = mid else: r -= 1 return nums[l]#或者nums[r]
dfs
leecode695,岛屿的最大面积,dfs,要注意变量、边界条件和递归表达式三要素
class Solution: def dfs(self, grid, cur_i, cur_j) -> int: if cur_i < 0 or cur_j < 0 or cur_i>=len(grid) or cur_j>=len(grid[0]) or grid[cur_i][cur_j] != 1: return 0 grid[cur_i][cur_j] = 0 ans = 1 for next_i,next_j in [0,1],[0,-1],[1,0],[-1,0]: di,dj = cur_i+next_i,cur_j+next_j ans += self.dfs(grid,di,dj) return ans def maxAreaOfIsland(self, grid: List[List[int]]) -> int: ans = 0 for i in range(len(grid)): for j in range(len(grid[0])): ans = max(ans,self.dfs(grid,i,j)) return ans
leecode200,岛屿数量,dfs,具体代码思路和leecode695岛屿最大面积很相似
class Solution: def dfs(self, grid, cur_i, cur_j) -> int: if cur_i < 0 or cur_j < 0 or cur_i == len(grid) or cur_j == len(grid[0]) or grid[cur_i][cur_j] != "1": return grid[cur_i][cur_j] = "0" for di, dj in [[0, 1], [0, -1], [1, 0], [-1, 0]]: next_i, next_j = cur_i + di, cur_j + dj self.dfs(grid, next_i, next_j) def numIslands(self, grid: List[List[str]]) -> int: ans = 0 for i in range(len(grid)): for j in range(len(grid[0])): if grid[i][j] == "1": ans += 1 self.dfs(grid,i,j) return ans
leecode 463,岛屿的周长,遍历判断即可
class Solution: def islandPerimeter(self, grid: List[List[int]]) -> int: ans = 0 for i in range(len(grid)): for j in range(len(grid[0])): tmp = 4 if grid[i][j] == 1: for next_i,next_j in [1,0],[-1,0],[0,1],[0,-1]: di,dj = i + next_i,j+ next_j if 0<=di<len(grid) and 0<=dj<len(grid[0]) and grid[di][dj] == 1: tmp -= 1 ans += tmp return ans
反转链表
offer24、leecode206反转链表,双指针的迭代写法+递归写法,注意递归写法中值传递和结束过程。递归写法是不断递归修改节点的next,实质是从尾部节点开始修改。
# Definition for singly-linked list. class ListNode: def __init__(self, x): self.val = x self.next = None class Solution: def reverseList(self, head: ListNode) -> ListNode: if head == None: return head pre = None while head != None: tmp = head.next head.next = pre pre = head head = tmp return pre class Solution: def reverseList(self, head: ListNode) -> ListNode: def dfs(head,pre): if head == None: return pre res = dfs(head.next,head) head.next = pre return res pre = None return dfs(head,pre) def print_node(node): while node != None: print(node.val) node = node.next if __name__ == "__main__": head = ListNode(1) head.next = ListNode(2) head.next.next = ListNode(3) solution = Solution() ans = solution.reverseList(head) print_node(ans) # [1,2,3,4,5]
leecode92,反转链表2,反转链表的加强版本,反转部分链表,要处理好left=1的边界条件
# Definition for singly-linked list. class Solution: def reverseBetween(self, head, left: int, right: int): if head ==None or head.next ==None: return head i = 1 pre = None start = head while i < left: pre = start start = start.next i += 1 j = i cur_pre = None cur = start # 反转 while j <= right: tmp = cur.next cur.next = cur_pre cur_pre = cur cur = tmp j += 1 # 接起来,要考虑从链表开始就反转的情况,这个时候表头变了 if pre !=None: pre.next = cur_pre start.next = cur if pre == None: head = cur_pre return head
打家劫舍
leecode198,打家劫舍,优化空间的动态规划
class Solution: def rob(self, nums): if len(nums) == 0: return 0 if len(nums) == 1: return nums[0] dp_0 = nums[0] dp_1 = max(nums[0],nums[1]) for i in range(2,len(nums)): tmp = dp_0 dp_0 = dp_1 dp_1 = max(nums[i]+tmp,dp_1) return dp_1
leecode213,打家劫舍,环形数组,对两个子数组进行动态规划,取结果最大的值
class Solution: def robrange(self,nums): dp_0 = nums[0] dp_1 = max(nums[0],nums[1]) for i in range(2,len(nums)): tmp = dp_0 dp_0 = dp_1 dp_1 = max(nums[i]+tmp,dp_1) return dp_1 def rob(self, nums): if len(nums) == 0: return 0 if len(nums) == 1: return nums[0] if len(nums) == 2: return max(nums[0],nums[1]) return max(self.robrange(nums[:len(nums)-1]),self.robrange(nums[1:]))
leecode337,打家劫舍,树形dp,注意模版
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def rob(self, root: Optional[TreeNode]) -> int: def dfs(root): if root == None: return 0,0 l_rob,l_not_rob = dfs(root.left) r_rob,r_not_rob = dfs(root.right) rob = l_not_rob + r_not_rob + root.val not_rob = max(l_rob,l_not_rob) + max(r_rob,r_not_rob) return rob,not_rob return max(dfs(root))
最大化最小值/最小化最大值
leecode2569,打家劫舍,最小化最大值,二分法+dp
class Solution: def minCapability(self, nums: List[int], k: int) -> int: n = len(nums) if n == 1: return nums[0] left, right = 1, 1e9+1 # 求最小最大值,所以将初始最大值设置为极大的正数,方便求最小最大值 ans = right while left < right: mid = (left + right) // 2 # 窃取能力mid # mid = (left + right) >> 1 # dp[i],表示前 i 个(i从0算起)房间中可窃取金额不超过窃取能力mid的最大房间数 dp = [0] * n # base case:第1个房屋dp[0],如果房屋金额<=窃取能力mid,选择窃取; # 第2个房屋dp[1],如果1、2间房屋其中一个满足条件,则可偷盗房间数为1,否则为0 if nums[0] <= mid: dp[0] = 1 dp[1] = 1 if min(nums[0], nums[1]) <= mid else 0 for i in range(2, n): # 此房屋的金额nums[i]大于窃取能力mid,无法窃取,只能顺延dp[i-1]保证尽量大 if nums[i] > mid: dp[i] = dp[i - 1] # 此房屋可以窃取, 可选择窃取(dp[i-2]+1)和不窃取(dp[i-1])的最优值 else: dp[i] = max(dp[i - 1], dp[i - 2] + 1) # 只要窃取的房屋数量>=k即可成功,保存答案,继续寻找更小的符合条件的窃取能力 if dp[n - 1] >= k: ans = mid right = mid else: left = mid + 1 return int(right)