Python数据结构及算法刷题记录
Array数组
485. 最大连续 1 的个数
class Solution: def findMaxConsecutiveOnes(self, nums: List[int]) -> int: # 如果数组为空或长度为0,则直接返回0 if nums is None or len(nums) == 0: return 0 # count为当前连续的1的个数,res是上一个连续的1的个数 count = 0 res = 0 for num in nums: if num == 1: count += 1 # 遍历到的num为1则count+1 else: res = max(res, count) # 遇到0后,将res和count中的最大值赋值给res count = 0 # count重新为0 return max(res, count) # 因为最后一个数字为1时,并没有进行res=max(res,count),所以返回时需要再max一下
283. 移动零
1、两次遍历
class Solution: def moveZeroes(self, nums: List[int]) -> None: """ Do not return anything, modify nums in-place instead. """ # 两次遍历 index = 0 # 第一次遍历,将所有非0的元素均赋值给nums[index] for i in range(len(nums)): if nums[i] != 0: nums[i], nums[index] = nums[index], nums[i] index += 1 # 第二次遍历,将索引index之后的元素全赋值为0 for i in range(index,len(nums)): nums[i] = 0 return nums
2、一次遍历
class Solution: def moveZeroes(self, nums: List[int]) -> None: """ Do not return anything, modify nums in-place instead. """ # 一次遍历 # 两个指针i和j j = 0 for i in range(len(nums)): # 当前元素!=0,就把其交换到左边,等于0的交换到右边 if nums[i] != 0: nums[j],nums[i] = nums[i],nums[j] j += 1
27. 移除元素
1、首尾双指针
class Solution: def removeElement(self, nums: List[int], val: int) -> int: # 首尾双指针 if nums is None or len(nums) == 0: return 0 l = 0 r = len(nums) - 1 while(l < r): # l从头到尾找等于val的元素的索引 while(l < r and nums[l] != val): l += 1 # r从尾到头找不等于val的元素的索引 while(l < r and nums[r] == val): r -= 1 nums[l], nums[r] = nums[r], nums[l] # 因为跳出循环时,i,j可能都指向val或不指向val if nums[l] == val: return l else: return l+1
2、快慢双指针
class Solution: def removeElement(self, nums: List[int], val: int) -> int: # 快慢指针 if not nums: return 0 n = len(nums) fast = slow = 0 #两个指针初始位置都为0,fast指针用来遍历数组 while fast < n: if nums[fast] != val: #当fast指向的元素不等于val时,将fast的值赋予slow nums[slow] = nums[fast] slow += 1 fast += 1 return slow
1. 两数之和
1、暴力解法:时间复杂度为N(N*N)
class Solution: def twoSum(self, nums: List[int], target: int) -> List[int]: for i in range(0, len(nums)): for j in range(i + 1, len(nums)): if nums[i] + nums[j] == target: return [i,j]
2、哈希字典
class Solution: def twoSum(self, nums: List[int], target: int) -> List[int]: hashtable = {} # hashmap = dict(),创建一个空字典 for i, num in enumerate(nums): # enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for循环当中。 if target - num in hashtable: # 循环遍历num时,如果hashtable中已经有另一个元素(target-num)在哈希表中,则直接返回这两个元素的索引 return [hashtable[target - num], i] hashtable[num] = i # 否则将该元素和索引添加到字典中 return []
268. 丢失的数字
1、排序
class Solution: def missingNumber(self, nums: List[int]) -> int: # 将数组排序之后,即可根据数组中每个下标处的元素是否和下标相等,得到丢失的数字。 nums.sort() for i, num in enumerate(nums): if num != i: return i return len(nums)
2、哈希集合
class Solution: def missingNumber(self, nums: List[int]) -> int: # 哈希集合:首先遍历数组nums,将数组中的每个元素加入哈希集合,然后依次检查从0到n的每个整数是否在哈希集合中,不在哈希集合中的数字即为丢失的数字。 s = set(nums) for i in range(len(nums) + 1): if i not in s: return i
78. 子集
1、使用库函数
class Solution: def subsets(self, nums: List[int]) -> List[List[int]]: # itertools模块combinations(iterable, r)方法可以创建一个迭代器,返回iterable中所有长度为r的子序列,返回的子序列中的项按输入iterable中的顺序排序。 res = [] for i in range(len(nums) + 1): for num in itertools.combinations(nums, i): # 子序列默认是元组,所以将其转化为list res.append(list(num)) return res
Linked List链表
203. 移除链表元素
# Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution: def removeElements(self, head: ListNode, val: int) -> ListNode: # 初始化一个伪节点dummy,和双指针prev、cur dummy = ListNode(0) dummy.next = head prev, cur = dummy, head while cur: # 当head不为空时,进入循环 if cur.val == val: # 当head指向的值等于val时,进行删除操作 prev.next = cur.next cur = cur.next else: # 当head指向的值不等于val时,prev和head依次向后移 prev = cur cur = cur.next return dummy.next
206. 反转链表
1、双指针
# 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: # 双指针 pre, cur = None, head # 初始化两个指针,一个指向头节点,一个指向None while cur: # 当cur指向None时,跳出循环 tmp = cur.next # 临时指针指向cur的后继节点 cur.next = pre # 断开原来cur的后继节点,使其指向pre pre = cur # pre指向cur cur = tmp # cur指向tmp,即一次循环内cur的后继节点 return pre
2、头插法
# 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: # 头插法,往cur节点前插入节点 cur = None while head: newNode = ListNode(head.val) #将原始列表的节点的val形成一个节点 head = head.next # head指向下一个节点 newNode.next = cur # newNode.next指向cur cur = newNode # cur指向新的节点 return cur
Queue队列
933. 最近的请求次数
class RecentCounter: def __init__(self): # 使用列表初始化一个队列 self.lst = [] def ping(self, t: int) -> int: # 将t入队 self.lst.append(t) # 如果t与队列头元素差值大于3000,需要将对头出队,因为题目解释需要返回过去 3000 毫秒内发生的所有请求数(包括新请求) while t - self.lst[0] > 3000: self.lst.pop(0) # 最后返回队列长度即可 return len(self.lst) # Your RecentCounter object will be instantiated and called as such: # obj = RecentCounter() # param_1 = obj.ping(t)
Stack栈
20. 有效的括号
class Solution: def isValid(self, s: str) -> bool: dict = {')':'(',']':'[','}':'{'} stack = [] for c in s: if stack and c in dict: # 如果栈不为空并且c为dict中的某一个键,为什么要加一个stack是否为空的判断:因为如果为空stack[-1]会报错 if stack[-1] == dict[c]: # 判断栈顶元素和键c的值相等 stack.pop() else: return False else: # 如果栈为空或c不在dict中,说明c为左括号,将其入栈 stack.append(c) return not stack # 为什么返回not stack:因为如果最后stack中还存在元素,则说明s中元素为奇数个,并未完全匹配,则返回False;stack为空则说明都匹配完了,not stack = True
496. 下一个更大元素 I
1、暴力遍历
class Solution: def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]: len1, len2 = len(nums1), len(nums2) res = [0] * len1 # 构造一个和数组nums1等长的数组 for i in range(len1): # 依次遍历nums1中的元素 j = nums2.index(nums1[i]) # 找到本次目标元素在nums2中和nums1相等的元素的坐标 k = j + 1 # k:往右边找 while k < len2 and nums2[k] < nums2[j]: # k小于nums2的长度且小于该目标元素,则k继续加1,继续往右找 k += 1 if k < len2: # 加这个判断是因为如果遍历到nums2中的最后一个元素比目标元素小,也会进行k自加1,这时表明未找到下一个更大的元素 res[i] = nums2[k] else: res[i] = -1 return res
2、单调栈+哈希表
class Solution: def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]: # 单调栈+哈希表解法 res = {} # 新建一个哈希字典 stack = [] # 新建一个栈 lst = [] # 存储最后结果 for num in nums2: # 依次遍历nums2中的元素 while stack and num > stack[-1]: # 判断stack不为空且num大于栈顶元素,则找到了num为目前栈顶元素的下一个更大元素,将栈顶元素作为key,num作为value添加到哈希字典中 res[stack.pop()] = num # 否则将num入栈 stack.append(num) # 然后依次遍历nums1中的元素 for num in nums1: lst.append(res.get(num, -1)) return lst ''' 语法 get()方法语法: dict.get(key, default=None) 参数 key -- 字典中要查找的键。 default -- 如果指定键的值不存在时,返回该默认值。 返回值 返回指定键的值,如果键不在字典中返回默认值 None 或者设置的默认值。 '''
Hash Table 哈希表
217. 存在重复元素
1、排序
class Solution: def containsDuplicate(self, nums: List[int]) -> bool: # 排序:在对数字从小到大排序之后,数组的重复元素一定出现在相邻位置中。因此,我们可以扫描已排序的数组,每次判断相邻的两个元素是否相等,如果相等则说明存在重复的元素。 nums.sort() for i in range(1,len(nums)): if nums[i] == nums[i - 1]: return True return False
2、哈希表
class Solution: def containsDuplicate(self, nums: List[int]) -> bool: # 哈希表(集合):遍历数组,数字放到 set 中。如果数字已经存在于 set 中,直接返回 true。如果成功遍历完数组,则表示没有重复元素,返回 false。 hashset = set() for num in nums: if num not in hashset: hashset.add(num) else: return True return False
389. 找不同
1、使用哈希表
class Solution: def findTheDifference(self, s: str, t: str) -> str: # 如果s为空,则直接返回t if len(s) == 0: return t # 初始化一个hashtable hashtable = [0] * 26 # 遍历s,遍历到s中的每个元素,对应索引下hashtable中的值-1 for i in s: tmp = ord(i) - 97 hashtable[tmp] = hashtable[tmp] - 1 # 遍历t,遍历到t中的每个元素,对应索引下hashtable中的值+1 for j in t: tmp = ord(j) - 97 hashtable[tmp] = hashtable[tmp] + 1 # 最后遍历hashtable,值为1的元素就是我们要找的元素 for k in range(len(hashtable)): if hashtable[k] == 1: ascii = k + 97 return chr(ascii)
2、取巧,使用ascii
class Solution: def findTheDifference(self, s: str, t: str) -> str: # sum1为s中字母ascii的和 sum1 = sum([ord(i) for i in s]) # sum2为t中字母ascii的和 sum2 = sum([ord(i) for i in t]) # sum2-sum1就是添加的那个字母的ascii return chr(sum2 - sum1)
Set 集合
217. 存在重复元素
class Solution: def containsDuplicate(self, nums: List[int]) -> bool: # 集合是无序可变元素不能重复。实际上集合底层是字典实现,集合的所有元素都是字典中的“键对象”,因此是不能重复的且唯一 hashset = set(nums) if len(nums) != len(hashset): return True else: return False
class MyHashSet: def __init__(self): # 因为0 <= key <= 10^6,所以可以设计一个超大数组,如果没有范围,则不能使用该方法 self.set = [False] * 1000001 def add(self, key: int) -> None: '''添加,将对应索引的值置为True''' self.set[key] = True def remove(self, key: int) -> None: '''删除,将对应索引的值置为False''' self.set[key] = False def contains(self, key: int) -> bool: '''是否存在,直接返回对应索引的值True or False''' return self.set[key] # Your MyHashSet object will be instantiated and called as such: # obj = MyHashSet() # obj.add(key) # obj.remove(key) # param_3 = obj.contains(key)
Tree 树
102. 二叉树的层序遍历
class Solution(object): def levelOrder(self, root): """ :type root: TreeNode :rtype: List[List[int]] """ # 如果树为空,直接返回[] if not root: return [] # 初始化一个res数组存储最后结果 res = [] # 初始化一个的队列用于存储每层的遍历结果,初始存储根节点 queue = [root] while queue: # 获取当前队列的长度,这个长度相当于 当前这一层的节点个数 size = len(queue) tmp = [] # 将队列中的元素都拿出来(也就是获取这一层的节点),放到tmp中 # 如果节点的左/右子树不为空,也放入队列中 for _ in range(size): r = queue.pop(0) tmp.append(r.val) if r.left: queue.append(r.left) if r.right: queue.append(r.right) # 将临时list加入最终返回结果中 res.append(tmp) return res
144. 二叉树的前序遍历
1、递归
# 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 preorderTraversal(self, root: TreeNode) -> List[int]: # 递归解法,前序遍历:根-左-右 res = [] def preorder(root: TreeNode): if not root: return [] res.append(root.val) preorder(root.left) preorder(root.right) preorder(root) return res
2、迭代
# 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 preorderTraversal(self, root: TreeNode) -> List[int]: # 递归,使用栈(先进后出) if not root: return [] cur, stack, res = root, [], [] while cur or stack: while cur: # 该循环的目的是将根节点和左孩子全部入栈 res.append(cur.val) # 将节点元素存储在res中 stack.append(cur) cur = cur.left # 每弹出一个元素,cur重新执行该元素的右孩子 tmp = stack.pop() cur = tmp.right return res
94. 二叉树的中序遍历
1、递归(左根右)
# 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 inorderTraversal(self, root: Optional[TreeNode]) -> List[int]: res = [] def inorder(root: TreeNode): if not root: return [] inorder(root.left) res.append(root.val) inorder(root.right) inorder(root) return res
2、迭代
# 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 inorderTraversal(self, root: Optional[TreeNode]) -> List[int]: # 递归,使用栈(先进后出) if not root: return [] cur, stack, res = root, [], [] while cur or stack: while cur: # cur入栈,并到达最左端的叶子节点 stack.append(cur) cur = cur.left tmp = stack.pop() res.append(tmp.val) # 出栈时加入res cur = tmp.right return res
145. 二叉树的后序遍历
1、递归(左右根)
# 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 postorderTraversal(self, root: TreeNode) -> List[int]: #前序遍历:根-左-右 #中序遍历:左-根-右 #后序遍历:左-右-根 res = [] def postorder(root: TreeNode): if not root: return postorder(root.left) postorder(root.right) res.append(root.val) postorder(root) return res
2、迭代
# 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 postorderTraversal(self, root: Optional[TreeNode]) -> List[int]: if not root: return [] cur, stack, res = root, [], [] while cur or stack: while cur: # 将右子节点存进stack res.append(cur.val) stack.append(cur) cur = cur.right tmp = stack.pop() cur = tmp.left return res[::-1]
Heap 堆
215. 数组中的第K个最大元素
import heapq class Solution: def findKthLargest(self, nums: List[int], k: int) -> int: # 初始化一个堆 heap = [] heapq.heapify(heap) # 遍历nums中的元素,加入到堆中,因为python没有最大堆,所以使每个元素*-1来构造绝对值大的元素在堆顶 for num in nums: heapq.heappush(heap, num * -1) # 让数组中第 k 个最大的元素到堆顶 while k > 1: heapq.heappop(heap) k = k - 1 #返回数组中第 k 个最大的元素,返回时重新*-1 return heapq.heappop(heap) * -1
692. 前K个高频单词
import heapq class Solution: def topKFrequent(self, words: List[str], k: int) -> List[str]: # 将每个单词和其出现的频次加入到哈希表中 dict = {} for word in words: if word not in dict: dict[word] = 1 dict[word] = dict[word] + 1 heap, ans = [], [] # 因为求前 K 个高频元素,python 默认最小堆,则将频次取负再入堆 # 堆的元素可以是元组类型 for i in dict: heapq.heappush(heap, (-dict[i], i)) # 取出前K个元素 for _ in range(k): ans.append(heapq.heappop(heap)[1]) return ans
Graph 图
略
Trie 字典树/前缀树
class Trie: def __init__(self): self.child = [None] * 26 # 孩子节点 self.isEnd = False # 结束标志 def insert(self, word: str) -> None: rt= self # 从根开始 for w in word: ID = ord(w) - ord('a') # 字母的ASCII值 if rt.child[ID] == None: #没有,就新建 rt.child[ID] = Trie() rt = rt.child[ID] #往下走 rt.isEnd = True #标记位 def search(self, word: str) -> bool: rt= self # 从根开始 for w in word: ID = ord(w) - ord('a') if rt.child[ID] == None: #有字母不在这条path上,断了 return False rt = rt.child[ID] #沿着path往下走 return rt.isEnd == True #看isEnd位 def startsWith(self, prefix: str) -> bool: rt = self # 从根开始 for w in prefix: ID = ord(w) - ord('a') if rt.child[ID] == None: #path断了 return False rt = rt.child[ID] return True #无论是否是单词,path没断就ok # Your Trie object will be instantiated and called as such: # obj = Trie() # obj.insert(word) # param_2 = obj.search(word) # param_3 = obj.startsWith(prefix)
class Solution: def longestWord(self, words: List[str]) -> str: res = '' trie = Trie() for word in words: # 将每个单词依次插入到前缀树中 trie.insert(word) for word in words: if trie.search(word): if len(word) > len(res): res = word elif len(word) == len(res) and word < res: res = word return res # 前缀树 class Trie: def __init__(self): self.child = [None] * 26 # 孩子节点 self.isEnd = False # 结束标志 def insert(self, word: str) -> None: rt= self # 从根开始 for w in word: ID = ord(w) - ord('a') # 字母的ASCII值 if rt.child[ID] == None: #没有,就新建 rt.child[ID] = Trie() rt = rt.child[ID] #往下走 rt.isEnd = True #标记位 def search(self, word: str) -> bool: rt= self # 从根开始 for w in word: # 检索该单词的前n项子字符串是否都在树中,如果是则返回True ID = ord(w) - ord('a') rt = rt.child[ID] if rt is None or not rt.isEnd: return False return True
TWO Pointers 双指针
141. 环形链表
# Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: def hasCycle(self, head: ListNode) -> bool: # 快慢双指针 slow = fast = head # 初始化两个快慢指针,指向头节点 while fast and fast.next: # 防止head为空和出现空指针的next的情况 slow = slow.next fast = fast.next.next if slow == fast: return True return False
881. 救生艇
class Solution: def numRescueBoats(self, people: List[int], limit: int) -> int: # 对撞双指针 if people is None or len(people) == 0: return 0 people.sort() # 使用对撞双指针必须是有序的 i = 0 j = len(people) - 1 res = 0 while i <= j: # i大于j时跳出循环 if people[i] + people[j] <= limit: # 如果i和j指向的元素和大于limit,i不能走,所以不会进入循环 i = i + 1 j = j - 1 # 每次进入while,j指向的元素肯定能走,所以在if外 res = res + 1 return res
Binary Search 二分查找法
704. 二分查找
class Solution: def search(self, nums: List[int], target: int) -> int: # 二分查找法,定义target在左闭右闭的区间里,[l, r] l = 0 r = len(nums) - 1 while l <= r: mid = l + ((r - l) // 2) # 防止溢出 等同于(l + r)//2 if nums[mid] > target: r = mid - 1 # target在[l, mid-1]区间 elif nums[mid] < target: l = mid + 1 # target在[mid+1, r]区间 else: return mid return -1
35. 搜索插入位置
class Solution: def searchInsert(self, nums: List[int], target: int) -> int: # 定义target在左闭右闭的区间里,[left, right] left = 0 right = len(nums) - 1 while left <= right: mid = left + ((right - left) // 2) # 防止溢出 等同于(left + right)/2 if nums[mid] > target: right = mid - 1 #target 在左区间,所以[left, mid - 1] elif nums[mid] < target: left = mid + 1 # target 在右区间,所以[mid + 1, right] else: return mid return right + 1 ''' 分别处理如下四种情况: 目标值在数组所有元素之前:return right + 1;--若有此情况,此时left和right都指向第一个元素,索引为0,mid也为0,满足nums[mid] > target,此时right=mid-1=-1,返回right+1,则添加到数组的第一个位置 目标值等于数组中某一个元素 return middle; 目标值插入数组中的位置 [left, right],return right + 1; 目标值在数组所有元素之后的情况 [left, right], return right + 1;
162. 寻找峰值
class Solution: def findPeakElement(self, nums: List[int]) -> int: # 二分查找最大值 left, right = 0, len(nums) - 1 while left < right: # 注意循环退出条件,若加上=号,会有死循环 mid = (left + right) // 2 if nums[mid] > nums[mid + 1]: # 若中点值比右方值大,说明极值点在中点左侧(包括中点) right = mid else: left = mid + 1 # 若中点值比右方小,说明极值点在中点右侧(不包括中点) return left
74. 搜索二维矩阵
class Solution: def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: # 将二位数组转化为一维数组进行整体二分 M, N = len(matrix), len(matrix[0]) # M:行,N:列 left, right = 0, M * N - 1 # 分别指向首尾 while left <= right: mid = left + (right - left) // 2 # 将一维又转化为二维中的元素cur表示:(x,y)的元素索引=x*N+y cur = matrix[mid // N][mid % N] # 以下和正常的二分法一样的流程 if cur == target: return True elif cur < target: left = mid + 1 else: right = mid - 1 return False
Sliding Windows 滑动窗口
209. 长度最小的子数组
class Solution: def minSubArrayLen(self, target: int, nums: List[int]) -> int: # 滑动窗口 left = right = 0 #初始化两个指针,分别为窗口的左边界和右边界,窗口大小right-left+1 min_len = float("inf") # 初始化一个最小长度,默认值为无穷大 sum = 0 # 子数组和 while right < len(nums): sum += nums[right] # 加上右边一个元素 while sum >= target: # sum大于等于目标值,进入循环 min_len = min(min_len, right-left+1) # 取之前记录的窗口小大min_len和当前的窗口大小right-left+1中的最小值 # sum减去左边界的数再左边界右移一位 sum -= nums[left] left = left + 1 # sum小于target right = right + 1 # 如果不存在符合条件的子数组,返回 0 if min_len == float('inf'): return 0 else: return min_len
1456. 定长子串中元音的最大数目
class Solution: def maxVowels(self, s: str, k: int) -> int: # 滑动窗口+哈希 if s is None or len(s) == 0 or len(s) < k: return 0 hashset = {'a','e','i','o','u'} count = res = 0 # 算出第一个窗口中的元音字母数 for i in range(k): if s[i] in hashset: count += 1 res = max(res, count) # 处理后面的窗口 for i in range(k, len(s)): if s[i - k] in hashset: # 滑出窗口的字母是元音,count-1 count = count - 1 if s[i] in hashset: # 滑进窗口的字母是元音,count+1 count = count + 1 # 最后取res和count中最大值 res = max(res, count) return res
Recursion 递归
509. 斐波那契数
class Solution: def fib(self, n: int) -> int: # 递归,接受的参数:n if n == 0: return 0 if n == 1: return 1 # 终止条件为n=0/1 m = self.fib(n - 1) + self.fib(n - 2) # 拆解 return m # 返回值
206. 反转链表
# Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution: def reverseList(self, head: ListNode) -> ListNode: # 递归终止条件是当前为空,或者下一个节点为空 if head==None or head.next==None: return head # 这里的cur就是最后一个节点 cur = self.reverseList(head.next) # 如果链表是 1->2->3->4->5,那么此时的cur就是5,head是4,head的下一个是5,下下一个是空 # head.next.next 就是5->4 head.next.next = head # 防止链表循环,需要将head.next设置为空 head.next = None # 每层递归函数都返回cur,也就是最后一个节点 return cur
344. 反转字符串
class Solution: def reverseString(self, s: List[str]) -> None: """ Do not return anything, modify s in-place instead. """ self.recursion(s, 0, len(s)-1) def recursion(self, s, left, right): if left >= right: return self.recursion(s,left+1,right-1) s[left],s[right] = s[right], s[left]
Dicide & Conquer 分治法
169. 多数元素
class Solution: def majorityElement(self, nums: List[int]) -> int: # 强行分治法 return self.getMajority(nums, 0, len(nums)-1) def getMajority(self, nums, left, right): if left == right: # 只有一个元素时 return nums[left] mid = left + (right-left) // 2 leftMajority = self.getMajority(nums, left, mid) rightMajority = self.getMajority(nums, mid+1, right) if leftMajority == rightMajority: # 如果最后合并后的左右的多数元素一样,可以直接返回 return leftMajority # 如果不一样 leftCount = 0 rightCount = 0 for i in range(left, right+1): if nums[i] == leftMajority: leftCount += 1 elif nums[i] == rightMajority: rightCount += 1 if leftCount > rightCount: return leftMajority else: return rightMajority
53. 最大子数组和
class Solution: def maxSubArray(self, nums: List[int]) -> int: # 分治法 return self.getMax(nums, 0, len(nums)-1) ''' 连续子序列的最大和主要由这三部分子区间里元素的最大和得到: 第 1 部分:子区间 [left, mid]; 第 2 部分:子区间 [mid + 1, right]; 第 3 部分:包含子区间 [mid , mid + 1] 的子区间,即 nums[mid] 与 nums[mid + 1] 一定会被选取。 ''' def getMax(self, nums, l, r): if l == r: # 分到最后只有一个元素 return nums[l] mid = l + (r-l) // 2 leftSum = self.getMax(nums, l, mid) rightSum = self.getMax(nums, mid+1, r) crossSum = self.crossSum(nums, l, r) return max(leftSum, rightSum, crossSum) def crossSum(self, nums, l, r): mid = l + (r-l) // 2 # 左边到mid leftSum = nums[mid] leftMax = leftSum for i in range(mid-1, l-1, -1): leftSum += nums[i] leftMax = max(leftMax, leftSum) # mid到右边 rightSum = nums[mid+1] rightMax = rightSum for i in range(mid+2, r+1): rightSum += nums[i] rightMax = max(rightMax, rightSum) return leftMax + rightMax
Backtracking 回溯法
22. 括号生成
class Solution: def generateParenthesis(self, n: int) -> List[str]: if n <= 0: return [] res = [] # 初始化结果集 # paths表示当前的括号组合,left表示当前左括号已使用个数,right表示当前右括号已使用个数 def dfs(paths, left, right): if left > n or right > left: # 如果左括号个数大于n,或者右括号大于左括号,当前的paths肯定不符合,直接返回 return if len(paths) == n * 2: # 因为括号都是成对出现的 res.append(paths) return dfs(paths + '(', left+1, right) # 左括号生成一个就加一个 dfs(paths + ')', left, right+1) # 左括号生成一个就加一个 dfs('', 0, 0) return res
78. 子集
class Solution: def subsets(self, nums: List[int]) -> List[List[int]]: def backtrack(index, tmp): # 枚举长度为0,1,2,3的子集 res.append(tmp) for i in range(index, n): backtrack(i + 1, tmp + [nums[i]]) n = len(nums) # 数组nums的元素个数 res = [] # 结果集 backtrack(0, []) return res ''' 枚举过程: [1] ---> [1,2] ---> [1,2,3] [1,3] [] ---> [2] ---> [2,3] [3] '''
77. 组合
class Solution: def combine(self, n: int, k: int) -> List[List[int]]: res = [] def backtrace(i,tmp): if len(tmp) == k: # 长度等于k就将tmp添加到结果集 res.append(tmp) return for j in range(i,n+1): backtrace(j+1,tmp+[j]) backtrace(1,[]) return res
46. 全排列
class Solution: def permute(self, nums: List[int]) -> List[List[int]]: res = [] def backtrack(nums, tmp): if not nums: res.append(tmp) return for i in range(len(nums)): backtrack(nums[:i] + nums[i+1:], tmp + [nums[i]]) backtrack(nums, []) return res
DFS 深度优先搜索算法
938. 二叉搜索树的范围和
# 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 rangeSumBST(self, root: TreeNode, low: int, high: int) -> int: # 普通二叉树 res = 0 if not root: return res res += self.rangeSumBST(root.left, low, high) # 左子树满足条件的和 if low <= root.val <= high: # 根节点 res += root.val res += self.rangeSumBST(root.right, low, high) # 右子树满足条件的和 return res # # 二叉搜索树,最重要性质:二叉搜索树的中序遍历是有序的 # res = 0 # if not root: # return res # if root.val > low: # 二叉搜索树的左子树一定比 root 小,因此如果 root.val <= low,那么不用继续搜索左子树 # res += self.rangeSumBST(root.left, low, high) # if low <= root.val <= high: # res += root.val # if root.val < high: # 二叉搜索树的右子树一定比 root 大,因此如果 root.val >= high,那么不用继续搜索右子树 # res += self.rangeSumBST(root.right, low, high) # return res
class Solution: def numIslands(self, grid: [[str]]) -> int: def dfs(grid, i, j): if not 0 <= i < len(grid) or not 0 <= j < len(grid[0]) or grid[i][j] == '0': # 若遍历的网格i,j超过边界或为0,直接返回 return grid[i][j] = '0' # 执行 grid[i][j] = '0',即将岛屿所有节点删除,以免之后重复搜索相同岛 # 搜索(i,j)点的上下左右 dfs(grid, i + 1, j) dfs(grid, i, j + 1) dfs(grid, i - 1, j) dfs(grid, i, j - 1) count = 0 for i in range(len(grid)): for j in range(len(grid[0])): if grid[i][j] == '1': dfs(grid, i, j) count += 1 return count
广度优先算法 BFS
102. 二叉树的层序遍历
# 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 levelOrder(self, root: TreeNode) -> List[List[int]]: # 如果树为空,直接返回[] if not root: return [] # 初始化一个res数组存储最后结果 res = [] # 初始化一个的队列用于存储每层的遍历结果,初始存储根节点 queue = [root] while queue: # 获取当前队列的长度,这个长度相当于 当前这一层的节点个数 size = len(queue) tmp = [] # 将队列中的元素都拿出来(也就是获取这一层的节点),放到tmp中 # 如果节点的左/右子树不为空,也放入队列中 for _ in range(size): cur = queue.pop(0) # 从队列头部删除一个节点 tmp.append(cur.val) # 需要添加的是cur的val值,而不是cur,切记!!! if cur.left: queue.append(cur.left) if cur.right: queue.append(cur.right) # 将临时list加入最终返回结果中 res.append(tmp) return res
107. 二叉树的层序遍历 II
将102题的res[::-1]
class Solution: def numIslands(self, grid: [[str]]) -> int: def bfs(grid, i, j): queue = [[i, j]] while queue: [i, j] = queue.pop(0) if 0 <= i < len(grid) and 0 <= j < len(grid[0]) and grid[i][j] == '1': grid[i][j] = '0' queue += [[i + 1, j], [i - 1, j], [i, j - 1], [i, j + 1]] count = 0 for i in range(len(grid)): for j in range(len(grid[0])): if grid[i][j] == '0': continue bfs(grid, i, j) count += 1 return count
并查集 Union Find
200. 岛屿数量
class Solution: def numIslands(self, grid: List[List[str]]) -> int: # 并查集:使用横坐标*列数+纵坐标作为元素在并查集中的唯一标识 # 岛屿数量 = (总元素个数 - 水的个数)中的连通块的个数 class UnionFind: def __init__(self, n): # 总数 self.size = n self.p = [i for i in range(n)] def find(self, x): # 查找根节点,即当前元素所属的集合 if self.p[x] != x: self.p[x] = self.find(self.p[x]) return self.p[x] def union(self, a, b): ar, br = self.find(a), self.find(b) # 两个元素位于同一个集合,跳过 if ar == br: return # 不在同一个集合,合并 else: self.p[ar] = br self.size -= 1 n, m = len(grid), len(grid[0]) ocean = 0 # 统计水的个数 uf = UnionFind(n*m) for i in range(n): for j in range(m): # 统计水的个数 if grid[i][j] == "0": ocean += 1 else: # 只需向右和向下查看 if i+1 < n and grid[i+1][j]=="1": uf.union(i*m+j, (i+1)*m+j) if j+1 < m and grid[i][j+1]=="1": uf.union(i*m+j, i*m+(j+1)) return uf.size - ocean
547. 省份数量
class Solution: def findCircleNum(self, isConnected: List[List[int]]) -> int: def find(index: int) -> int: if parent[index] != index: parent[index] = find(parent[index]) return parent[index] def union(index1: int, index2: int): parent[find(index1)] = find(index2) provinces = len(isConnected) parent = list(range(provinces)) for i in range(provinces): for j in range(i + 1, provinces): if isConnected[i][j] == 1: union(i, j) circles = sum(parent[i] == i for i in range(provinces)) return circles
贪心算法 Greedy
1217. 玩筹码
class Solution: def minCostToMoveChips(self, position: List[int]) -> int: # 所有偶数位的筹码可以没有代价的转移到任意偶数位,奇数同理 # 此时假设移动后,所有奇数都在位置1,所有偶数都在位置2,目前是无代价的 # 奇数和偶数是相邻的,不管谁移动代价都为各自的个数,为了使代价最小,题目就转变为求奇数和偶数的个数,并返回它们两者中的最小值 odd, even = 0, 0 for i in position: if i % 2 == 0: even += 1 else: odd += 1 return min(odd, even)
class Solution: def canJump(self, nums: List[int]) -> bool: max_i = 0 #初始化当前能到达最远的位置 for i, jump in enumerate(nums): #i为当前位置,jump是当前位置的跳数 if max_i >= i and i + jump > max_i: #如果当前位置能到达,并且当前位置+跳数>最远位置 max_i = i + jump #更新最远能到达位置 return max_i >= i
记忆化搜索/备忘录 Memorzation
class Solution: def fib(self, n: int) -> int: # 记忆化搜索/备忘录 self.memo = [-1 for _ in range(n+1)] return self.__fibo(n) def __fibo(self, n): if n==0: return 0 if n==1: return 1 if self.memo[n] == -1: self.memo[n] = self.__fibo(n-1)+self.__fibo(n-2) return self.memo[n]
动态规划
class Solution: def uniquePaths(self, m: int, n: int) -> int: dp = [[0 for _ in range(n)] for _ in range(m)] # 创建一个默认值全为0的m*n的二维数组 # 第一行都赋予 1 for j in range(n): dp[0][j] = 1 # 第一列都赋予 1 for i in range(m): dp[i][0] = 1 # 两个for循环推导,对于(i,j)来说,只能由上方或者左方转移过来 for i in range(1, m): for j in range(1, n): dp[i][j] = dp[i - 1][j] + dp[i][j - 1] return dp[-1][-1]

浙公网安备 33010602011771号