LeetCode Notes in Python
1 Array & Hashing
- 217. Contains Duplicate
- 242. Valid Anagram
- 1. Two Sum
- 49. Group Anagrams
- 347. Top K Frequent Elements
Approach
Code
class Solution:
def containsDuplicate(self, nums):
record = set()
for i in nums:
if i in record:
return True
record.add(i)
return False
# Time complexity:
# Space complexity:
Approach
Code
class Solution:
def isAnagram(self, s, t):
if len(s) != len(t):
return False
hashmap = collections.defaultdict(list)
count_s = [0]*26
count_t = [0]*26
for l in s:
count_s[ord(l)-ord("a")] += 1
for l in t:
count_t[ord(l)-ord("a")] += 1
return count_s == count_t
# Time complexity:
# Space complexity:
1. Two Sum
Approach
Complexity
- Time complexity:
- Space complexity:
Code
class Solution:
def twoSum(self, nums, target):
record = {}
for index, value in enumerate(nums):
diff = target - value
if diff in record:
return record[diff],index
record[value] = index
return [] # 如果无解,返回[]
49. Group Anagrams
Approach
Complexity
- Time complexity: O(m * n), where m is length of the strs, n is the length of the s
- Space complexity:
Code
class Solution:
def groupAnagrams(self, strs):
res = defaultdict(list)
for s in strs:
count = [0]*26
for c in s:
count[ord(c)-ord("a")] += 1
res[tuple(count)].append(s)
return res.values()
347. Top K Frequent Elements
Approach
Complexity
- Time complexity:
- Space complexity:
Code
class Solution:
def topKFrequent(self, nums, k):
count = {}
freq = [[] for i in range(len(nums) + 1)]
# step1: fill your hashmap
for n in nums:
count[n] = 1 + count.get(n, 0)
for n, c in count.items():
freq[c].append(n)
# step2: output via the result array
res = []
for i in reversed(range(0, len(freq))): # go through index reversely
for n in freq[i]: # fill the result array
res.append(n)
if len(res) == k: # Top K
return res
238. Product of Array Except Self
Approach
- Brute Force
- optimization
Complexity
- Time complexity: O(n)
- Space complexity: O(1)
Code
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
res = [1] * len(nums)
# from left to right
prefix = 1
for i in range(len(nums)):
res[i] = prefix
prefix *= nums[i]
# from right to left
postfix = 1
for i in range(len(nums) - 1, -1, -1):
res[i] *= postfix
postfix *= nums[i]
return res
Two Pointers
- 125. Valid Palindrome
- 167. Two Sum II - Input Array Is Sorted
- 15. 3Sum
- 11. Container With Most Water
125. Valid Palindrome
Approach
- initialize
l
andr
while l < r
, which is outer loop, controls the two pointers move, point to the next letter.- skip constantly until
l
>=r
, which means going through the end of string. Usingwhile
rather thanif
which only make a step forward, due to more than one punctuaction and space.
Complexity
- Time complexity:
- Space complexity:
Code
class Solution:
def isPalindrome(self, s):
l, r = 0, len(s) - 1
while l < r:
while l < r and not self.alphanum(s[l]): # skip constantly from the left of string until l >= r
l += 1
while l < r and not self.alphanum(s[r]): # skip constantly from the right of string until l >= r
r -= 1
if s[l].lower() != s[r].lower():
return False
l += 1
r -= 1
return True
def alphanum(self, c): # when judgment condition statement is too long to type
return (
ord("A") <= ord(c) <= ord("Z")
or ord("a") <= ord(c) <= ord("z")
or ord("0") <= ord(c) <= ord("9")
)
167. Two Sum II - Input Array Is Sorted
Approach
- Given a sorted array --> Two pointers
Complexity
- Time complexity:
- Space complexity:
Code
class Solution:
def twoSum(self, numbers, target):
l, r = 0, len(numbers) - 1
while l < r:
if numbers[l] + numbers[r] == target:
return [l + 1,r + 1]
elif numbers[l] + numbers[r] > target:
r -= 1
else:
l += 1
return -1
15. 3Sum
Approach
-
首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。
-
依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]。
-
接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。
-
如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
Complexity
- Time complexity:
- Space complexity:
Code
class Solution:
def threeSum(self, nums):
res = []
nums.sort()
for i, a in enumerate(nums):
if a > 0: # return [] if the first element of 3 sum is positive
break
if i > 0 and nums[i] == nums[i - 1]: # skip the same i, from i == 1
continue
l, r = i + 1, len(nums) - 1
while l < r:
if a + nums[l] + nums[r] > 0:
r -= 1
elif a + nums[l] + nums[r] < 0:
l += 1
else:
res.append([a, nums[l], nums[r]]) # fill in the result array
# skip the same nums[r] and nums[l] towards left and right
while l < r and nums[r] == nums[r - 1]:
r -= 1
while l < r and nums[l] == nums[l + 1]:
l += 1
r -= 1
l += 1
return res
11. Container With Most Water
Approach
Complexity
- Time complexity:
- Space complexity:
Code
class Solution:
def maxArea(self, height):
l, r = 0, len(height) - 1
res = 0
while l < r:
res = max(res, min(height[l], height[r]) * (r - l))
if height[l] < height[r]:
l += 1
else:
r -= 1
return res
Sliding Window
Stack
Binary Search
- 704. Binary Search
- 74. Search a 2D Matrix
- 875. Koko Eating Bananas
- 153. Find Minimum in Rotated Sorted Array
- 33. Search in Rotated Sorted Array
704. Binary Search
Approach
Complexity
- Time complexity: O(log(n))
- Space complexity: O(1)
Code
class Solution:
def search(self, nums, target):
l = 0
r = len(nums)
while l < r:
mid = l + (r - l) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
l = mid + 1
elif nums[mid] > target:
r = mid
return -1
74. Search a 2D Matrix
Approach
- Convert 2D into 1D
mid = l + (r - l) // 2
--- 1D
cur = matrix[mid // n][mid % n]
--- 2D
Complexity
- Time complexity: O(log(m * n))
- Space complexity: O(1)
Code
class Solution:
def searchMatrix(self, matrix, target):
import numpy as np
m, n = np.shape(matrix)[0], np.shape(matrix)[1]
l, r = 0, m * n - 1
while l <= r:
mid = l + (r - l) // 2
cur = matrix[mid // n][mid % n]
if cur == target:
return True
elif cur > target:
r = mid - 1
else:
l = mid + 1
return False
875. Koko Eating Bananas
Approach
Code
class Solution:
def minEatingSpeed(self, piles, h):
l = 1
r = max(piles)
while l <= r:
mid = l + (r - l) // 2
total_hours = 0
for p in piles:
total_hours += math.ceil(p / mid)
if total_hours > h: # increase the speed
l = mid + 1
else: # if meet the requirement, find the minimum speed
r = mid - 1
return l # the reason that return l not mid is the question's object which is the minimum eating speed (sounds like sliding window)
153. Find Minimum in Rotated Sorted Array
Approach
Code
class Solution:
def findMin(self, nums):
l = 0
r = len(nums) - 1
while l <= r:
mid = l + (r - l) // 2
if nums[mid] < nums[r]: # min has not passed the mid
r = mid
else: # min has passed the mid
l = mid + 1
return nums[r]
33. Search in Rotated Sorted Array
Approach
- Binary Search is only useful for the sorted array.
mid
can partition thenums
into two sides, one of two must be sorted.- How to verify the list is sorted?
- It can be sure that there must be one side is sorted.
if nums[l] <= nums[mid]
, then the left side is sorted, another side is unsorted.else
, the situation is reversed.
Complexity
- Time complexity: O(logn)
- Space complexity: O(1)
Code
class Solution:
def search(self, nums, target):
l, r = 0, len(nums) - 1
while l <= r:
mid = l + (r - l) // 2
if nums[mid] == target:
return mid
# the left side partitioned by mid is sorted
if nums[l] <= nums[mid]:
# 不能写成target <= nums[mid - 1],mid = 0时,mid - 1 = -1
# in the sorted left side
if nums[l] <= target < nums[mid]:
r = mid - 1
# in the unsorted right side
else:
l = mid + 1
# the right side partitioned by mid is sorted
else:
# in the sorted right side
if nums[mid + 1] <= target <= nums[r]:
l = mid + 1
# in the unsorted left side
else:
r = mid - 1
return -1
Linked List
Trees
Tries
Heap / Priority Queue
Backtracking
77.Combinations
Approach
Complexity
- Time complexity:
- Space complexity:
Code
class Solution:
def combine(self, n, k):
result = []
path = []
self.backtracking(k, n, 1, path, result)
return result
def backtracking(self, k, n, startindex, path, result):
# promising function
if len(path) == k:
result.append(path[:])
return
# single layer search logic
for i in range(startindex, n + 1):
path.append(i)
self.backtracking(k, n, i + 1, path, result) # recursion
path.pop() # backtracking
-
Table of Content
-
Body
40. Combination Sum II
Approach
Complexity
- Time complexity:
- Space complexity:
Code
class Solution:
def combinationSum2(self, candidates, target):
result = []
path = []
used = [False] * len(candidates)
startindex = 0
candidates.sort()
self.backtracking(startindex, candidates, target, used, path, result)
return result
def backtracking(self, startindex, candidates, target, used, path, result):
if target == 0:
result.append(path[:])
return
if target < 0:
return
for i in range(startindex, len(candidates)):
if target - candidates[i] < 0:
break
if i > startindex and candidates[i] == candidates[i - 1] and used[i - 1] == False:
continue
used[i] = True
path.append(candidates[i])
self.backtracking(i + 1, candidates, target - candidates[i], used, path, result)
path.pop()
used[i] = False
79. Word Search
Approach
Complexity
- Time complexity:
- Space complexity:
Code
216. Combination Sum III
Approach
Complexity
- Time complexity:
- Space complexity:
Code
class Solution:
def combinationSum3(self, k, n):
result = []
path =[]
self.backtracking(k, n, 1, path, result)
return result
def backtracking(self, k, n, startindex, path, result):
# promisong function
if len(path) == k and sum(path) == n:
result.append(path[:])
# single layer search logic
for i in range(startindex, 10):
path.append(i)
self.backtracking(k, n, i + 1, path, result)
path.pop()
17. Letter Combinations of a Phone Number
Approach
Complexity
- Time complexity:
- Space complexity:
Code
class Solution:
def __init__(self):
self.letterMap = [
"", # 0
"", # 1
"abc", # 2
"def", # 3
"ghi", # 4
"jkl", # 5
"mno", # 6
"pqrs", # 7
"tuv", # 8
"wxyz" # 9
]
self.result = []
self.s = ""
def backtracking(self, digits, index):
if index == len(digits):
self.result.append(self.s)
return
digit = int(digits[index]) # 将索引处的数字转换为整数
letters = self.letterMap[digit] # 获取对应的字符集
for i in range(len(letters)):
self.s += letters[i] # 处理字符
self.backtracking(digits, index + 1) # 递归调用,注意索引加1,处理下一个数字
self.s = self.s[:-1] # 回溯,删除最后添加的字符
def letterCombinations(self, digits):
if len(digits) == 0:
return self.result
self.backtracking(digits, 0)
return self.result
78. Subsets
Approach
Complexity
- Time complexity:
- Space complexity:
Code
class Solution:
def subsets(self, nums):
res = []
path = []
nums.sort()
self.backtracking(nums, 0, path, res)
return res
def backtracking(self, nums, startindex, path, res):
res.append(path[:])
for i in range(startindex, len(nums)):
path.append(nums[i])
self.backtracking(nums, i + 1, path, res)
path.pop()
90. Subsets II
Approach
Complexity
- Time complexity:
- Space complexity:
Code
class Solution:
def subsetsWithDup(self, nums):
res = []
path = []
nums.sort()
self.backtracking(nums, 0, path, res)
return res
def backtracking(self, nums, startindex, path, res):
uset = set() # define uset set
res.append(path[:])
for i in range(startindex, len(nums)):
if nums[i] in uset: # skip the same subset
continue
uset.add(nums[i]) # fill in uset set
path.append(nums[i])
self.backtracking(nums, i + 1, path, res)
path.pop()
39. Combination Sums
Approach
纵向为递归(backtracking),横向为迭代(for循环)
图1 - Backtracking树形图
图2 - 简洁结果图
图3 - Backtracking动态流程图
Backtracking three elements
- Backtracking function parameters:
- 排序之后的
candidates
数组,target
,startIndex
,path
数组,res
数组
- 排序之后的
- Recursion end condition:
target == 0
,path[:]
记入res
数组target - candidates[i] < 0
,回溯(退出该层)
- Single-layer search condition:
- 设置
startIndex
- 设置
Complexity
- Time complexity:
- Space complexity:
NeetCode 150
III. LeetCode Problem List
二分查找
移除元素
滑动窗口
螺旋矩阵
Solutions
704. 二分查找
思路
二分查找法Binary Search
- 使用条件:有序数组,无重复元素
- 两种写法:左闭右闭写法、左闭右开写法
- 边界更新原则:使上一个的middle不在更新后的[left, right]内或[left, right)内
- 终止条件原则:[1,1]等价于[1],但是[1,1)无意义
代码
# Binary Search 左闭右闭写法 [left, right]
class Solution:
def search(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums)-1
while left <= right: #[1, 1]是合法的,相当于[1]
middle = left + (right-left) // 2
# (left + right) // 2 若left+right过大,可能会溢出
if nums[middle] > target:
right = middle - 1
elif nums[middle] < target:
left = middle + 1
else:
return middle
return -1
# Binary Search 左闭右开写法 [left, right)
class Solution:
def search(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums)
while left < right: #[1, 1]是合法的,相当于[1]
middle = left + (right-left) // 2
# (left + right) // 2 若left+right过大,可能会溢出
if nums[middle] > target:
right = middle
elif nums[middle] < target:
left = middle + 1
else:
return middle
return -1
35. 搜索插入位置
思路
代码
34. 在排序数组中查找元素的第一个和最后一个位置
思路
代码
69. x的平方根
思路
代码
367. 有效完全平方数
思路
代码
27. 移除元素
思路
数组的元素是不能删的,只能覆盖。
此题可使用快慢指针提高效率
- 快指针:向右寻找新元素
- 慢指针:慢慢覆盖更新数组
代码
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
fast = 0 # 快指针
slow = 0 # 慢指针
while fast < len(nums):
# 如果fast寻找的元素不等于val, 则slow照抄元素, slow += 1, fast += 1
# 如果fast寻找的元素等于val, 则跳过slow, fast += 1
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow
26. 删除排序数组中的重复项
思路
代码
283. 移动零
思路
代码
844. 比较含退格的字符串
思路
代码
977. 有序数组的平方
思路
代码
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
# 将数组元素平方
for i in range(len(nums)):
nums[i] = nums[i]**2
# 初始化
left = 0
right = len(nums) - 1
a = len(nums) - 1 # 结果数组下标,从右向左
result = [-1]*len(nums) # 结果数组
# 开始遍历
while left <= right:
if nums[left] > nums[left]:
result[a] = nums[left]
left = left + 1
else:
result[a] = nums[right]
right = right - 1
a = a - 1
return result
209. 长度最小的子数组
思路
代码
59. 螺旋矩阵II
思路
代码
Table of Contents
Solutions
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: Optional[ListNode], val: int) -> Optional[ListNode]:
# 设置虚拟头节点
dummy_head = ListNode(next = head)
pre = dummy_head
while pre.next:
if pre.next.val == val:
pre.next = pre.next.next # pre指针的next域指向下下个节点,即跳过一个节点
else:
pre = pre.next # 头节点向右移动
return dummy_head.next
707. 设计链表
思路
代码
# 单链表
class ListNode:
def __init__(self, val = 0, next = None): # 初始化链表节点
self.val = val
self.next = next
class MyLinkedList:
def __init__(self): # 初始化
self.head = ListNode(0) # 虚拟头节点
self.size = 0 # 设置一个链表长度的属性,便于后续操作,注意每次增和删的时候都要更新
def get(self, index: int) -> int:
# while (a)表示:当a!=0时执行循环,当a==0时跳出循环
if index < 0 or index > self.size - 1: # step1: 判断输入参数是否有效
return -1
cur = self.head.next # 第一个节点(头节点的下一个节点)
while (index): # step2:搜索index所在位置
cur = cur.next
index -= 1 # index递减,直到为0时到达相应位置
return cur.val
def addAtHead(self, val: int) -> None:
pre = self.head
new_node = ListNode(val)
new_node.next = pre.next
pre.next = new_node
self.size += 1
def addAtTail(self, val: int) -> None:
pre = self.head
new_node = ListNode(val)
while (pre.next):
pre = pre.next
new_node.next = pre.next
pre.next = new_node
self.size += 1
def addAtIndex(self, index: int, val: int) -> None:
if index < 0:
self.addAtHead(val)
return
elif index == self.size:
self.addAtTail(val)
return
elif index > self.size:
return
new_node = ListNode(val)
pre = self.head
while (index):
pre = pre.next
index -= 1
new_node.next = pre.next
pre.next = new_node
self.size += 1
def deleteAtIndex(self, index: int) -> None:
if index < 0 or index > self.size - 1:
return
pre = self.head
while (index):
pre = pre.next
index -= 1
pre.next = pre.next.next
self.size -= 1
# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)
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:
# 初始化指针
pre = None
cur = head
# 开始遍历,终止条件为cur指向最右端的下一个空节点None
while cur:
temp = cur.next # 存放当前cur.next
cur.next = pre # 元素2指向元素1
pre = cur # pre向右移动一位
cur = temp # cur向右移动一位
return pre
24. 两两交换链表中的节点
思路
代码
19. 删除链表的倒数第N个节点
思路
代码
160. 相交链表
思路
代码
142. 环形链表II
思路
代码
Table of Contents
Solutions
242. 有效的字母异位词
思路
代码
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
record = [0]*26
for i in s:
record[ord(i)-ord('a')] += 1
for j in t:
record[ord(j)-ord('a')] -= 1
return record == [0]*26
349. 两个数组的交集
思路
代码
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
# 要点:python利用字典Dictionary实现集合HashSet
# step1: 初始化空字典和空结果列表
val_dict = {}
ans = []
# step2:
# 遍历nums1,出现就记为1,[1,2,2,1]
# nums1 = [1,2,2,1], val_dict = {'1':1, '2':1}
for num in nums1:
val_dict[num] = 1
# val_dict与nums2比较,
# 如果key值能在nums2中找到,且value值为1,ans.append(num),相应的value值归0
# ans = [1,2]
for num in nums2:
if num in val_dict.keys() and val_dict[num] == 1:
ans.append(num)
val_dict[num] = 0
return ans
202. 快乐数
思路
代码
class Solution:
def isHappy(self, n: int) -> bool:
def calculate(num: int) -> int:
sum = 0
# 82 % 10 = 2,8 % 10 = 8
while num:
sum += (num % 10) ** 2 # %表示取模,返回余数
num = num // 10 # //表示向下取整,返回整数
return sum
# 记录中间结果
record = set()
while True:
n = calculate(n)
if n == 1:
return True
# 如果中间结果重复出现,说明陷入死循环了,该数不是快乐数
if n in record:
return False
else:
record.add(n)
1. 两数之和
思路
代码
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
record = {}
for index,number in enumerate(nums):
if target - number in record:
return [index,record[target - number]]
else:
record[number] = index
454. 四数相加II
思路
代码
383.赎金信
思路
代码
15. 三数之和
思路
代码
'''
step1: 排序,为双指针法作准备并进行第一道去重,如[-1,0,1,-1]
step2:
'''
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
result = []
nums.sort() # 排序,为双指针法作准备并进行第一道去重,如[-1,0,1,-1]
for i in range(len(nums)-2):
left = i + 1
right = len(nums) - 1
# 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
if nums[i] > 0:
break
if i >= 1 and nums[i] == nums[i - 1]: # 去重a,注意是nums[i] == nums[i-1]
continue
while left < right:
if nums[i] + nums[left] + nums[right] > 0:
right -= 1
elif nums[i] + nums[left] + nums[right] < 0:
left += 1
else:
result.append([nums[i],nums[left],nums[right]])
while left < right and nums[left] == nums[left+1]: left += 1
while left < right and nums[right] == nums[right-1]: right -= 1
left += 1
right -= 1
return result
18. 四数之和
思路
代码
Table of Contents
双指针
KMP
Solutions
344. 反转字符串
思路
代码
541. 反转字符串II
思路
代码
剑指Offer05. 替换空格
思路
代码
class Solution:
def replaceSpace(self, s: str) -> str:
# 统计空格数量
count = s.count(' ')
# 将字符串转换为数组方便操作,扩大数组大小
result = list(s)
result.extend([' '] * count * 2)
# 初始化左右指针
left = len(s) - 1
right = len(result) - 1
# 双指针开始从后向前移动
while left >= 0:
if result[left] != ' ':
result[right] = result[left]
right -= 1
else:
result[right-2:right+1] = '%20'
right -= 3
left -= 1
return ''.join(result) # 'sep'.join() 用新的分隔符连接任意数量的字符串,返回新的字符串
151. 翻转字符串里的单词
思路
代码
剑指Offer58-II. 左旋转字符串
思路
代码
28. 实现strStr
思路
代码
459. 重复的子字符串
思路
代码
Table of Contents
Solutions
232. 用栈实现队列
思路
代码
225. 用队列实现栈
思路
代码
20. 有效的括号
思路
代码
1047. 删除字符串中的所有相邻重复项
思路
代码
150. 逆波兰表达式求值
思路
代码
239. 滑动窗口最大值
思路
代码
347. 前K个高频元素
思路
代码
Table of Contents
二叉树的遍历方式
深度优先搜索DFS:前中后序遍历
广度优先搜索BFS:层序遍历
- 102. 二叉树的层序遍历
- 107. 二叉树的层次遍历II
- 199. 二叉树的右视图
- 637. 二叉树的层平均值
- 429. N叉树的层序遍历
- 515. 在每个树行中找最大值
- 116. 填充每个节点的下一个右侧节点指针
- 117. 填充每个节点的下一个右侧节点指针II
求二叉树的属性
- 101. 对称二叉树
- 104. 二叉树的最大深度
- 111. 二叉树的最小深度
- 222. 完全二叉树的节点个数
- 110. 平衡二叉树
- 257. 二叉树的所有路径
- 404. 左叶子之和
- 513. 找树左下角的值
- 112. 路径总和
二叉树的修改与构造
求二叉搜索树的属性
二叉树公共祖先问题
二叉搜索树的修改与构造
Solutions
144. 二叉树的前序遍历
思路
代码
145. 二叉树的后序遍历
思路
代码
94. 二叉树的中序遍历
思路
代码
102. 二叉树的层序遍历
思路
代码
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
"二叉树层序遍历之迭代法"
# 检查根节点是否为空:若为空,返回[]
if root == None:
return []
# 从模块collections中导入deque
from collections import deque
# 初始化
q = deque([root]) # 将根节点存入队列
results = [] # 结果数组
# 开始迭代
# 终止条件:队列为空
while q:
size = len(q) # 获取长度,控制每一层的弹出数目
result = [] # 当前层的结果数组,每一层初始化归零
for _ in range(size):
cur = q.popleft() # deque.popleft():移除第一个元素并返回它的值
result.append(cur.val) # 存入当前层的结果数组
# 每次当前节点弹出队列后,将其左右孩子保存至队列
# 由于获取了size,不需要考虑当前层会弹出孩子的故障
if cur.left:
q.append(cur.left)
if cur.right:
q.append(cur.right)
# 将当前层存入结果数组
# 使用append保留了[],可以做到实现二维数组。(区分append和extend)
results.append(result)
return results
107. 二叉树的层次遍历II
思路
对上题的结果进行倒序处理
# 返回结果
return results[::-1]
代码
class Solution:
def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
"二叉树层序遍历之迭代法"
# 检查根节点是否为空:若为空,返回[]
if root == None:
return []
# 从模块collections中导入deque
from collections import deque
# 初始化
q = deque([root]) # 将根节点存入队列
results = [] # 结果数组
# 开始迭代
# 终止条件:队列为空
while q:
size = len(q) # 获取长度,控制每一层的弹出数目
result = [] # 当前层的结果数组,每一层初始化归零
for _ in range(size):
cur = q.popleft() # deque.popleft():移除第一个元素并返回它的值
result.append(cur.val) # 存入当前层的结果数组
# 每次当前节点弹出队列后,将其左右孩子保存至队列
# 由于获取了size,不需要考虑当前层会弹出孩子的故障
if cur.left:
q.append(cur.left)
if cur.right:
q.append(cur.right)
# 将当前层存入结果数组
# 使用append保留了[],可以做到实现二维数组。(区分append和extend)
results.append(result)
# 返回结果
return results[::-1]
199. 二叉树的右视图
思路
代码
class Solution:
def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
# 检查根节点是否为空:若为空,返回[]
if root == None:
return []
# 从模块collections中导入deque
from collections import deque
# 初始化
queue = deque([root]) # 将根节点存入队列
result = [] # 结果数组
# 开始遍历
# 终止条件:队列为空
while queue:
size = len(queue) # 获取长度,控制每一层的弹出数目
cur = queue[-1] # 取当前层的最后一个元素
result.append(cur.val)
for _ in range(size):
cur = queue.popleft() # 弹出当前层的第一个元素并返回它的值
# 每次当前节点弹出队列后,将其左右孩子保存至队列
# 由于获取了size,不需要考虑当前层会弹出孩子的故障
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
# 返回结果
return result
637. 二叉树的层平均值
思路
代码
429. N叉树的层序遍历
思路
代码
515. 在每个树行中找最大值
思路
代码
116. 填充每个节点的下一个右侧节点指针
思路
代码
117. 填充每个节点的下一个右侧节点指针II
思路
代码
101. 对称二叉树
思路
代码
104. 二叉树的最大深度
思路
代码
111. 二叉树的最小深度
思路
代码
222. 完全二叉树的节点个数
思路
代码
110. 平衡二叉树
思路
代码
257. 二叉树的所有路径
思路
代码
404. 左叶子之和
思路
代码
513. 找树左下角的值
思路
代码
112. 路径总和
思路
代码
226. 翻转二叉树
思路
代码
106. 从中序与后序遍历序列构造二叉树
思路
代码
105. 从前序与中序遍历序列构造二叉树
思路
代码
654. 最大二叉树
思路
代码
617. 合并二叉树
思路
代码
700. 二叉搜索树中的搜索
思路
代码
98. 验证二叉搜索树
思路
代码
530. 二叉搜索树的最小绝对差
思路
代码
501. 二叉搜索树中的众数
思路
代码
538. 把二叉搜索树转换为累加树
思路
代码
236. 二叉树的最近公共祖先
思路
代码
235. 二叉搜索树的最近公共祖先
思路
代码
701. 二叉搜索树中的插入操作
思路
代码
450. 删除二叉搜索树中的节点
思路
代码
669. 修剪二叉搜索树
思路
代码
108. 将有序数组转换为二叉搜索树
思路
代码
Code
class Solution:
def combinationSum(self, candidates, target):
res = []
path = []
candidates.sort()
self.backtracking(candidates, target, 0, path, res)
return res
def backtracking(self, candidates, target, startIndex, path, res):
if target == 0:
res.append(path[:])
return
for i in range(startIndex, len(candidates)):
if target - candidates[i] < 0:
break
path.append(candidates[i])
self.backtracking(candidates, target - candidates[i], i, path, res)
path.pop()
51. N-Queens
Approach
Complexity
- Time complexity:
- Space complexity:
Code
class Solution:
def solveNQueens(self, n):
result = []
chessboard = ['.' * n for _ in range(n)]
self.backtracking(n, 0, chessboard, result)
return [[''.join(row) for row in solution] for solution in result]
def backtracking(self, n, row, chessboard, result):
# promising function
if row == n:
result.append(chessboard[:])
return
# single layer search logic
for col in range(n):
if self.is_valid(row, col, chessboard, n):
chessboard[row] = chessboard[row][:col] + 'Q' + chessboard[row][col + 1:]
self.backtracking(n, row + 1, chessboard, result)
chessboard[row] = chessboard[row][:col] + '.' + chessboard[row][col + 1:]
def is_valid(self, row, col, chessboard, n):
directions = [(dx, dy) for dx in [-1, 0, 1] for dy in [-1, 0, 1] if (dx, dy) != (0,0)]
for (dx, dy) in directions:
i, j = row, col
while 0 <= i < n and 0 <= j < n:
if chessboard[i][j] == 'Q':
return False
i += dx
j += dy
return True
Graphs
Advanced Graphs
1-D Dynamic Programming
- 70. Climbing Stairs
- 416. Partition Equal Subset Sum
- 1049. Last Stone Weight II
- 494. Target Sum
- 474. Ones and Zeroes
- 322. Coin Change
70. Climbing Stairs
Approach
Complexity
- Time complexity:
- Space complexity:
Code
# recursion
class Solution:
# recursion
def climbStairs_recursion(self, n):
if n in {0,1}:
return 1
return self.climbStairs_recursion(n - 1) + self.climbStairs_recursion(n - 2)
# recursion memoization
def __init__(self):
self.memo = {}
def climbStairs_memoization(self, n):
if n in {0, 1}:
return 1
if n in self.memo:
return self.memo[n]
else:
self.memo[n] = self.climbStairs_memoization(n - 1) + self.climbStairs_memoization(n - 2)
return self.memo[n]
# iteration tabulation
def climbStairs_tabulation(self, n):
dp = [0] * (n + 1)
dp[1], dp[2] = 1, 2
for i in range(3, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
# iteration space optimization
def climbStairs_iteration(self, n):
# 1 2 3 5 8 13 21
a, b = 1, 2
for _ in range(3, n + 1):
a, b = b, a + b
return b
if __name__ == '__main__':
# Test case
n = 7
# Build the model
solution = Solution()
result_recursion = solution.climbStairs_recursion(n)
result_memoization = solution.climbStairs_memoization(n)
result_tabulation = solution.climbStairs_tabulation(n)
result_iteration = solution.climbStairs_iteration(n)
# Print
print(f'{result_recursion}(using the recursion)')
print(f'{result_memoization}(using the memoization(recursion time optimization)')
print(f'{result_tabulation}(using the iteration tabulation)')
print(f'{result_iteration}(using the iteration space optimization')
416. Partition Equal Subset Sum
Approach
Complexity
- Time complexity:
- Space complexity:
Code
class Solution:
def canPartition(self, nums: List[int]) -> bool:
if sum(nums) % 2 != 0:
return False
# initializaiton
target = sum(nums) // 2
dp = [0] * (target + 1)
# 0-1 knapsack
for num in nums:
for j in range(target, num - 1, -1):
dp[j] = max(dp[j], dp[j - num] + num)
return dp[target] == target
1049. Last Stone Weight II
Approach
Complexity
- Time complexity:
- Space complexity:
Code
class Solution:
def lastStoneWeightII(self, stones: List[int]) -> int:
# initialization
total_sum = sum(stones)
target = total_sum // 2
dp = [0] * (target + 1)
# 0/1 knapsack
for stone in stones:
for j in range(target, stone - 1, -1):
dp[j] = max(dp[j], dp[j - stone] + stone)
return (total_sum - dp[target]) - dp[target]
494. Target Sum
Approach
Complexity
- Time complexity:
- Space complexity:
Code
474. Ones and Zeroes
Approach
Complexity
- Time complexity:
- Space complexity:
Code
322. Coin Change
Approach
Complexity
- Time complexity:
- Space complexity:
Code
class Solution:
def coinChange(self, coins, amount):
dp = [float('inf')] * (amount + 1)
dp[0] = 0
for coin in coins:
for j in range(coin, amount + 1):
dp[j] = min(dp[j], dp[j - coin] + 1)
return dp[amount] if dp[amount] != float('inf') else -1
2-D Dynamic Programming
Greedy
Intervals
Math & Geometry
Bit Manipulation
6 Sort
6.1 Quick Sort

class Solution:
def quick_sort(self, nums: list[int], left: int, right: int):
"""快速排序"""
if left >= right: # 子数组长度为 1 时终止递归
return
pivot = self.partition(nums, left, right) # 哨兵划分
self.quick_sort(nums, left, pivot - 1) # 递归左子数组
self.quick_sort(nums, pivot + 1, right) # 递归右子数组
def partition(self, nums: list[int], left: int, right: int) -> int:
"""哨兵划分"""
i, j = left, right
pivot = nums[left] # 以 nums[left] 为基准数
while i < j:
while i < j and nums[j] >= pivot: # 从右向左找首个小于基准数的元素
j -= 1
while i < j and nums[i] <= pivot: # 从左向右找首个大于基准数的元素
i += 1
nums[i], nums[j] = nums[j], nums[i] # 元素交换
nums[i], nums[left] = nums[left], nums[i] # 将基准数交换至两子数组的分界线
return i
if __name__ == "__main__":
# 快速排序
nums = [2, 4, 1, 0, 3, 5]
k = 3
solution = Solution()
solution.quick_sort(nums, 0, len(nums) - 1)
print("快速排序完成后 nums =", nums)
LeetCode 215.KthLargest
Solution 1:
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
return sorted(nums)[len(nums)-k]
- Time Complexity : 其中 为数组元素数量。
- Space Complexity: 取决于内置排序算法的具体设计。
Solution 2:
class Solution:
def findKthLargest(self, nums, k):
def quick_select(nums, k):
pivot = random.choice(nums) # 随机选择基准数
big, equal, small = [], [], [] # 将大于、小于、等于 pivot 的元素划分至 big, small, equal 中
for num in nums:
if num > pivot:
big.append(num)
elif num < pivot:
small.append(num)
else:
equal.append(num)
if k <= len(big): # 第 k 大元素在 big 中,递归划分
return quick_select(big, k)
if len(nums) - len(small) < k: # 第 k 大元素在 small 中,递归划分
return quick_select(small, k - len(nums) + len(small))
return pivot # 第 k 大元素在 equal 中,直接返回 pivot
return quick_select(nums, k)
if __name__ == "__main__":
import random
nums = [2, 4, 1, 0, 3, 5]
k = 3
solution = Solution()
num = solution.findKthLargest(nums, k)
print(f"The kth largest number is {num}")
- Time Complexity : 其中 为数组元素数量。
- 对于长度为 的数组执行哨兵划分操作的时间复杂度为 。
- 每轮哨兵划分后,向下递归子数组的平均长度为 。
- 因此平均情况下,哨兵划分操作一共有 (等比数列求和), 复杂度为
- Space Complexity : The worst case is n-1 elements are divided into one of the lists(big, equal, small)
6.2 Merge Sort

观察发现,归并排序与二叉树后序遍历的递归顺序是一致的。
- 后序遍历:先递归左子树,再递归右子树,最后处理根节点。
- 归并排序:先递归左子数组,再递归右子数组,最后处理合并。
def merge_sort(nums: list[int], left: int, right: int):
"""
divide stage:
[9, 7, 5, 6, 4]
[9, 7, 5],[6, 4]
[9, 7],[5],[6, 4]
[9],[7],[5],[6, 4]
[9],[7],[5],[6],[4]
merge stage:
[7, 9],[5],[6],[4]
[5, 7, 9],[6],[4]
[5, 7, 9],[4, 6]
[4, 5, 6, 7, 9]
"""
# base case: when subarray length is 1, recursion terminates
if left >= right:
return
# divide stage
mid = (left + right) // 2 # calculate midpoint
merge_sort(nums, left, mid) # recursion over left subarray
merge_sort(nums, mid + 1, right) # recursion over right subarray
# merge stage
merge(nums, left, mid, right)
def merge(nums: list[int], left: int, mid: int, right: int):
tmp = [0] * (right - left + 1) # store the merged subarray for update
i, j = left, mid + 1 # The left subarray interval is [left, mid]; The right subarray interval is [mid+1, right]
k = 0 # for tmp array
while i <= mid and j <= right: # 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
if nums[i] <= nums[j]:
tmp[k] = nums[i]
i += 1
else:
tmp[k] = nums[j]
j += 1
k += 1
while i <= mid: # 将左子数组和右子数组的剩余元素复制到临时数组中
tmp[k] = nums[i]
i += 1
k += 1
while j <= right:
tmp[k] = nums[j]
j += 1
k += 1
for k in range(0, len(tmp)): # 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
nums[left + k] = tmp[k]
if __name__=='__main__':
record = [9, 7, 5, 6, 4]
merge_sort(record, 0, len(record)-1)
print(record)
剑指Offer51.逆序对

class Solution:
def reversePairs(self, record):
return self.merge_sort(record, 0, len(record) - 1)
def merge_sort(self, nums, left, right):
if left >= right:
return 0
mid = (left + right) // 2
cnt_l = self.merge_sort(nums, left, mid)
cnt_r = self.merge_sort(nums, mid + 1, right)
cnt_c = self.merge(nums, left, mid, right)
return cnt_l + cnt_r + cnt_c
def merge(self, nums, left, mid, right):
tmp = [0] * (right - left + 1)
cnt = 0
i, j, k = left, mid + 1, 0
while i <= mid and j <= right:
if nums[i] <= nums[j]:
tmp[k] = nums[i]
i += 1
else:
tmp[k] = nums[j]
j += 1
cnt += (mid - i + 1) # count the reverse pair
k += 1
while i <= mid:
tmp[k] = nums[i]
i += 1
k += 1
while j <= right:
tmp[k] = nums[j]
j += 1
k += 1
for k in range(len(tmp)):
nums[left + k] = tmp[k]
return cnt
if __name__ == '__main__':
record = [9, 7, 5, 6, 4]
solution = Solution()
cnt = solution.reversePairs(record)
print(cnt)
print(record)
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 我与微信审核的“相爱相杀”看个人小程序副业
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~