"The Road to Algorithm Passing" study notes, record your own process of solving questions, please purchase the author's book for detailed content.
Circular shift problem
Rotate array
Question 189:
Given an integer array nums, rotate the elements in the array k positions to the right, where k is a non-negative number.
'''
方法一:复制相同的数组
时间复杂度:O(n)
空间复杂度:O(n)
'''
class Solution:
def rotate(self, nums: list[int], k: int) -> None:
copy = nums.copy()
n = len(nums)
for i in range(n):
nums[(k+i)%n] = copy[i]
nums, k = [1,2,3,4,5,6,7], 3
print(nums)
solu = Solution()
solu.rotate(nums, k)
print(nums)
[1, 2, 3, 4, 5, 6, 7]
[5, 6, 7, 1, 2, 3, 4]
'''
方法二:时间换空间(超时)
时间复杂度:O(nk)
空间复杂度:O(1)
'''
class Solution:
def rotate(self, nums: list[int], k: int) -> None:
n = len(nums)
t = None
offset = n - k % n # 右移变左移
if offset == 0:
return
while offset:
t = nums[0]
offset -= 1
for i in range(n-1):
nums[i] = nums[i+1]
nums[n-1] = t
nums, k = [1,2,3,4,5,6,7], 3
print(nums)
solu = Solution()
solu.rotate(nums, k)
print(nums)
[1, 2, 3, 4, 5, 6, 7]
[5, 6, 7, 1, 2, 3, 4]
'''
方法三:空间换时间(不符合题意)
时间复杂度:O(n)
空间复杂度:O(n)
'''
class Solution:
def rotate(self, nums: list[int], k: int) -> None:
n = len(nums)
offset = k % n
nums = nums + nums.copy()
return nums[n - offset : n * 2 - offset]
nums, k = [1,2,3,4,5,6,7], 3
print(nums)
solu = Solution()
solu.rotate(nums, k)
[1, 2, 3, 4, 5, 6, 7]
[5, 6, 7, 1, 2, 3, 4]
'''
方法四:利用python迭代器
时间复杂度:O(k)
空间复杂度:O(1)
'''
class Solution:
def rotate(self, nums: list[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
for i in range(0, -k, -1):
nums.insert(0,nums.pop())
nums, k = [1,2,3,4,5,6,7], 3
print(nums)
solu = Solution()
solu.rotate(nums, k)
print(nums)
[1, 2, 3, 4, 5, 6, 7]
[5, 6, 7, 1, 2, 3, 4]
'''
方法五:三次翻转法
时间复杂度:O(k)
空间复杂度:O(1)
'''
class Solution:
def rotate(self, nums: list[int], k: int) -> None:
def reverse(list: list[int], start: int, end: int) -> None:
while start < end:
t = list[start]
list[start] = list[end]
list[end] = t
start += 1
end -= 1
n = len(nums)
offset = k % n
if offset == 0:
return
reverse(nums, 0, n - offset - 1)
reverse(nums, n - offset, n - 1)
reverse(nums, 0, n - 1)
nums, k = [1,2,3,4,5,6,7], 3
print(nums)
solu = Solution()
solu.rotate(nums, k)
print(nums)
[1, 2, 3, 4, 5, 6, 7]
[5, 6, 7, 1, 2, 3, 4]
rotating linked list
Likou Question 61:
You are given the head node of a linked list, rotate the linked list, and move each node of the linked list k positions to the right.
'''
方法一:右移变左移
时间复杂度:O(n)
空间复杂度:O(1)
'''
from typing import Optional
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
if not head or k == 0 or not head.next:
return head
p1 = head
res = None
n = 1
while p1 and p1.next:
p1 = p1.next
n += 1
cur = 1 # 通过cur计数来寻找断点
p2 = head
while cur < n - k % n:
p2 = p2.next
cur += 1
p1.next = head
res = p2.next
p2.next = None
return res
edit distance
edit distance
Question 72:
Given two words, word1 and word2, please return the minimum number of operations used to convert word1 into word2.
You can perform the following three operations on a word:
insert a character,
delete a character,
replace a character
'''
方法一:记忆化递归
空间复杂度:O(mn)
'''
class Solution:
def minDistance(self, s1: str, s2: str) -> int:
def find_min(s1, i, s2, j, memo):
if i == -1:
return j + 1
if j == -1:
return i + 1
if memo[i][j] != -1:
return memo[i][j]
if s1[i] == s2[j]:
memo[i][j] = find_min(s1, i - 1, s2, j - 1, memo)
else:
memo[i][j] = min(
find_min(s1, i, s2, j - 1, memo) + 1, # 插入
find_min(s1, i - 1, s2, j, memo) + 1, # 删除
find_min(s1, i - 1, s2, j - 1, memo) + 1 # 替换
)
return memo[i][j]
m, n = len(s1), len(s2)
memo = [[-1] * n for _ in range(m)]
res = find_min(s1, m - 1, s2, n - 1, memo)
return res
word1, word2 = "horse", "ros"
solu = Solution()
solu.minDistance(word1, word2)
3
'''
方法二:动态规划
时间复杂度:O(mn)
空间复杂度:O(mn)
'''
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
m, n = len(word1), len(word2)
# dp[i][j]表示字符串word[:i]和word[:j]的最小编辑距离
dp = [[0 for j in range(n + 1)] for i in range(m + 1)]
for i in range(1, m + 1):
dp[i][0] = i
for j in range(1, n + 1):
dp[0][j] = j
for i in range(1, m + 1):
for j in range(1, n + 1):
if word1[i - 1] == word2[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = min(dp[i - 1][j - 1], dp[i][j - 1], dp[i - 1][j]) + 1
return dp[m][n]
word1, word2 = "horse", "ros"
solu = Solution()
solu.minDistance(word1, word2)
3
'''
方法三:动态规划(空间优化)
时间复杂度:O(mn)
空间复杂度:O(n)
'''
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
m, n = len(word1), len(word2)
pre, cur = [0] * (n + 1), [0] * (n + 1)
for i in range(1, n + 1):
pre[i] = i
for i in range(1, m + 1):
cur[0] = i
for j in range(1, n + 1):
if word1[i - 1] == word2[j - 1]:
cur[j] = pre[j - 1]
else:
cur[j] = min(pre[j], pre[j - 1], cur[j - 1]) + 1
pre = cur.copy()
return pre[n] # 解决没有进入循环的情况
word1, word2 = "horse", "ros"
solu = Solution()
solu.minDistance(word1, word2)
3
'''
方法四:动态规划(继续优化空间)
时间复杂度:O(mn)
空间复杂度:O(n)
'''
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
m, n = len(word1), len(word2)
cur = [0] * (n + 1)
pre = None
for i in range(1, n + 1):
cur[i] = i
for i in range(1, m + 1):
pre = cur[0]
cur[0] = i
for j in range(1, n + 1):
tmp = cur[j]
if word1[i - 1] == word2[j - 1]:
cur[j] = pre
else:
cur[j] = min(cur[j], cur[j - 1], pre) + 1
pre = tmp
return cur[n]
word1, word2 = "horse", "ros"
solu = Solution()
solu.minDistance(word1, word2)
3
kth question
The Kth largest element in the array
Question 215:
Given an integer array nums and an integer k, please return the kth largest element in the array.
Please note that you need to find the k-th largest element in the sorted array, not the k-th distinct element.
You must design and implement an algorithm with time complexity O(n) to solve this problem.
'''
方法一:堆排序(调包)
时间复杂度:O(nlogk)
空间复杂度:O(k)
'''
from heapq import nlargest
class Solution:
def findKthLargest(self, nums: list[int], k: int) -> int:
# [3,2,1,5,6,4]
# [6, 5]
return nlargest(k, nums)[-1]
nums, k = [3,2,1,5,6,4], 2
solu = Solution()
solu.findKthLargest(nums, k)
5
For other solutions, please refer to:
"The Road to Algorithm Clearance" - Chapter 13 Divide and Conquer Method
The Kth largest element in the data stream
Focus on question 703.
Design a class that finds the k-th largest element in the data stream. Note that it is the k-th largest element after sorting, not the k-th different element.
Please implement the KthLargest class:
KthLargest(int k, int[] nums) Initializes an object with an integer k and an integer stream nums.
int add(int val) After inserting val into the data stream nums, return the k-th largest element in the current data stream.
'''
方法一:堆排序(调包)
时间复杂度:O(nlogk)
空间复杂度:O(k)
'''
import heapq
class KthLargest:
def __init__(self, k: int, nums: list[int]):
# len(nums) >= k-1,通过加一个元素保证查找k大元素时,数组中至少有k个元素
self.nums = heapq.nlargest(k, nums + [float('-inf')])
self.k = k
heapq.heapify(self.nums)
def add(self, val: int) -> int:
heapq.heappushpop(self.nums, val)
return self.nums[0]
kth_largest = KthLargest(3, [4, 5, 8, 2])
kth_largest.add(3) # return 4
kth_largest.add(5) # return 5
kth_largest.add(10) # return 5
kth_largest.add(9) # return 8
kth_largest.add(4) # return 8
8
Kth smallest element in binary search tree
Question 230:
Given the root node root of a binary search tree and an integer k, please design an algorithm to find the k-th smallest element (counting from 1).
'''
方法一:二分查找
时间复杂度:O(n),最坏O(n2)
空间复杂度:O(n),最坏O(n2)
'''
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
# 返回该节点及所有子节点的个数
def countNodes(node) -> int:
if node == None:
return 0
l = countNodes(node.left)
r = countNodes(node.right)
return l + r + 1
cnt = countNodes(root.left)
if cnt == k - 1:
return root.val
elif cnt > k - 1:
return self.kthSmallest(root.left, k)
return self.kthSmallest(root.right, k - cnt - 1)
The Kth smallest element in the ordered matrix
Question 378:
Given an nxn matrix matrix, in which the elements in each row and column are sorted in ascending order, find the kth smallest element in the matrix.
Note that it is the kth smallest element after sorting, not the kth distinct element.
You have to find a solution with memory complexity better than O(n2).
'''
方法一:二分查找
时间复杂度:O(nlog(hi-lo))
空间复杂度:O(1)
'''
class Solution:
def kthSmallest(self, matrix: list[list[int]], k: int) -> int:
n = len(matrix)
lo, hi = matrix[0][0], matrix[n - 1][n - 1]
def countNotGreater(target: int) -> int:
i, j = 0, n - 1
cnt = 0
while i < n and j >= 0:
if matrix[i][j] <= target:
cnt += j + 1
i += 1
else:
j -= 1
return cnt
while lo < hi:
mid = (lo + hi) // 2
cnt = countNotGreater(mid)
if cnt < k:
lo = mid + 1
else:
hi = mid
return lo
matrix, k = [[1,5,9],[10,11,13],[12,13,15]], 8
solu = Solution()
solu.kthSmallest(matrix, k)
13
The kth smallest number in the multiplication table
Close question 668:
Almost everyone uses the multiplication table. But can you quickly find the kth smallest number in the multiplication table?
'''
方法一:二分查找
时间复杂度:O(mlogmn)
空间复杂度:O(1)
'''
class Solution:
def findKthNumber(self, m: int, n: int, k: int) -> int:
lo, hi = 1, m * n
def countNotGreater(mid: int, m: int, n: int) -> int:
cnt = 0
for i in range(1, m + 1):
cnt += min(mid // i, n)
return cnt
while lo < hi:
mid = (lo + hi) // 2
if countNotGreater(mid, m, n) < k:
lo = mid + 1
else:
hi = mid
return lo
m, n, k = 3, 3, 5
solu = Solution()
solu.findKthNumber(m, n, k)
3