4. 寻找两个正序数组的中位数
困难
给定两个大小分别为 m
和 n
的正序(从小到大)数组 nums1
和 nums2
。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n))
。
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
提示:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106
维护最大堆最小堆法
import heapq
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
max_heap = [] # 存储较小的那一半(用负数模拟最大堆)
min_heap = [] # 存储较大的那一半(直接是最小堆)
# 合并两个有序数组(可以用双指针优化,但这里简单起见直接合并)
nums = nums1 + nums2
nums.sort() # 先排序(如果 nums1 和 nums2 已排序,可以用双指针优化)
for num in nums:
# 先尝试放入 max_heap
if not max_heap or num <= -max_heap[0]:
heapq.heappush(max_heap, -num)
else:
heapq.heappush(min_heap, num)
# 平衡两个堆的大小
if len(max_heap) > len(min_heap) + 1:
heapq.heappush(min_heap, -heapq.heappop(max_heap))
elif len(min_heap) > len(max_heap):
heapq.heappush(max_heap, -heapq.heappop(min_heap))
# 计算中位数
if len(max_heap) > len(min_heap):
return -max_heap[0]
else:
return (-max_heap[0] + min_heap[0]) / 2
nextpage
104. 二叉树的最大深度
简单
给定一个二叉树 root
,返回其最大深度。
二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:3
示例 2:
输入:root = [1,null,2]
输出:2
提示:
- 树中节点的数量在
[0, 104]
区间内。 -100 <= Node.val <= 100
python做法
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0 # 空树深度为 0
left_depth = self.maxDepth(root.left) if root.left else 0
right_depth = self.maxDepth(root.right) if root.right else 0
return 1 + max(left_depth, right_depth)
C++做法
class Solution {
public:
int maxDepth(TreeNode* root) {
if (!root) return 0;
int left_depth = root->left ? maxDepth(root->left) : 0;
int right_depth = root->right ? maxDepth(root->right) : 0;
return 1 + max(left_depth, right_depth);
}
};
nextpage
111. 二叉树的最小深度
简单
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
**说明:**叶子节点是指没有子节点的节点。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:2
示例 2:
输入:root = [2,null,3,null,4,null,5,null,6]
输出:5
提示:
- 树中节点数的范围在
[0, 105]
内 -1000 <= Node.val <= 1000
python做法
class Solution:
def minDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0 # 空树深度为 0
left_depth = self.minDepth(root.left) if root.left else float("inf")
right_depth = self.minDepth(root.right) if root.right else float("inf")
return 1 + min(left_depth, right_depth)
C++做法
#include <climits> // 引入 INT_MAX
class Solution {
public:
int minDepth(TreeNode* root) {
if (!root) return 0;
int left_depth = root->left ? minDepth(root->left) : INT_MAX;
int right_depth = root->right ? minDepth(root->right) : INT_MAX;
return 1 + min(left_depth, right_depth);
}
};
nextpage
405. 数字转换为十六进制数
简单
给定一个整数,编写一个算法将这个数转换为十六进制数。对于负整数,我们通常使用 补码运算 方法。
答案字符串中的所有字母都应该是小写字符,并且除了 0 本身之外,答案中不应该有任何前置零。
注意: 不允许使用任何由库提供的将数字直接转换或格式化为十六进制的方法来解决这个问题。
示例 1:
输入:num = 26
输出:"1a"
示例 2:
输入:num = -1
输出:"ffffffff"
提示:
-231 <= num <= 231 - 1
python两行做法
class Solution:
def toHex(self, num: int) -> str:
# 确保负数以32位二进制补码的形式表示
num &= 0xFFFFFFFF
return hex(num)[2:]
python正常做法
hex_str = "0123456789abcdef"
class Solution:
def toHex(self, num: int) -> str:
if num == 0:
return "0"
# 确保负数以32位二进制补码的形式表示
# 方法一 #######################################
if num < 0:
num = num + 2**32
# 方法二 #######################################
num &= 0xFFFFFFFF
# 这两种方法在本题中效果是一样的 ###################
ans = []
while num:
num, md = divmod(num, 16)
ans.append(hex_str[md])
return ("".join(ans))[::-1]
nextpage
541. 反转字符串 II
简单
给定一个字符串 s
和一个整数 k
,从字符串开头算起,每计数至 2k
个字符,就反转这 2k
字符中的前 k
个字符。
- 如果剩余字符少于
k
个,则将剩余字符全部反转。 - 如果剩余字符小于
2k
但大于或等于k
个,则反转前k
个字符,其余字符保持原样。
示例 1:
输入:s = "abcdefg", k = 2
输出:"bacdfeg"
示例 2:
输入:s = "abcd", k = 2
输出:"bacd"
提示:
1 <= s.length <= 104
s
仅由小写英文组成1 <= k <= 104
C++解法
#include <algorithm> // for std::swap and std::min
class Solution {
public:
void reverse(string &s, int begin, int end) { // 引用传递
while (begin < end) {
swap(s[begin++], s[end--]); // 直接交换
}
}
string reverseStr(string s, int k) {
for (int i = 0; i < s.size(); i += 2 * k){
reverse(s, i, min(i + k - 1, (int)s.size() - 1));
}
return s;
}
};
python递归解法
class Solution:
def reverseStr(self, s: str, k: int) -> str:
return s[:k][::-1] + s[k:2 * k] + self.reverseStr(s[2 * k:], k) if s else ''
nextpage
1277. 统计全为 1 的正方形子矩阵
1613
给你一个 m * n
的矩阵,矩阵中的元素不是 0
就是 1
,请你统计并返回其中完全由 1
组成的 正方形 子矩阵的个数。
示例 1:
输入:matrix =
[
[0,1,1,1],
[1,1,1,1],
[0,1,1,1]
]
输出:15
解释:
边长为 1 的正方形有 10 个。
边长为 2 的正方形有 4 个。
边长为 3 的正方形有 1 个。
正方形的总数 = 10 + 4 + 1 = 15.
示例 2:
输入:matrix =
[
[1,0,1],
[1,1,0],
[1,1,0]
]
输出:7
解释:
边长为 1 的正方形有 6 个。
边长为 2 的正方形有 1 个。
正方形的总数 = 6 + 1 = 7.
提示:
1 <= arr.length <= 300
1 <= arr[0].length <= 300
0 <= arr[i][j] <= 1
动态规划解法
class Solution:
def countSquares(self, matrix: List[List[int]]) -> int:
m, n = len(matrix), len(matrix[0])
f = [[0 for _ in range(n+1)] for _ in range(m+1)]
ans = 0
for i in range(1,m+1):
for j in range(1,n+1):
if matrix[i-1][j-1] == 0:
f[i][j] = 0
else:
f[i][j] = min(f[i][j - 1], f[i - 1][j], f[i - 1][j - 1]) + 1
ans += f[i][j]
return f
暴力解法
class Solution:
def countSquares(self, matrix: List[List[int]]) -> int:
# 构造二维前缀和
n, m = len(matrix), len(matrix[0])
pre_sum = [[0 for _ in range(m+1)] for _ in range(n+1)]
for i in range(1, n+1):
for j in range(1, m+1):
pre_sum[i][j] = matrix[i-1][j-1] + pre_sum[i][j-1] + pre_sum[i-1][j] - pre_sum[i-1][j-1]
ans = 0
for i in range(1, n+1):
for j in range(1, m+1):
for k in range(1, min(i,j)+1):
tmp = pre_sum[i][j] - pre_sum[i][j - k] - pre_sum[i - k][j] + pre_sum[i - k][j - k]
ans += int(tmp == k**2)
return ans
nextpage
1823. 找出游戏的获胜者(约瑟夫环)
1412
共有 n
名小伙伴一起做游戏。小伙伴们围成一圈,按 顺时针顺序 从 1
到 n
编号。确切地说,从第 i
名小伙伴顺时针移动一位会到达第 (i+1)
名小伙伴的位置,其中 1 <= i < n
,从第 n
名小伙伴顺时针移动一位会回到第 1
名小伙伴的位置。
游戏遵循如下规则:
- 从第
1
名小伙伴所在位置 开始 。 - 沿着顺时针方向数
k
名小伙伴,计数时需要 包含 起始时的那位小伙伴。逐个绕圈进行计数,一些小伙伴可能会被数过不止一次。 - 你数到的最后一名小伙伴需要离开圈子,并视作输掉游戏。
- 如果圈子中仍然有不止一名小伙伴,从刚刚输掉的小伙伴的 顺时针下一位 小伙伴 开始,回到步骤
2
继续执行。 - 否则,圈子中最后一名小伙伴赢得游戏。
给你参与游戏的小伙伴总数 n
,和一个整数 k
,返回游戏的获胜者。
示例 1:
输入:n = 5, k = 2
输出:3
解释:游戏运行步骤如下:
1) 从小伙伴 1 开始。
2) 顺时针数 2 名小伙伴,也就是小伙伴 1 和 2 。
3) 小伙伴 2 离开圈子。下一次从小伙伴 3 开始。
4) 顺时针数 2 名小伙伴,也就是小伙伴 3 和 4 。
5) 小伙伴 4 离开圈子。下一次从小伙伴 5 开始。
6) 顺时针数 2 名小伙伴,也就是小伙伴 5 和 1 。
7) 小伙伴 1 离开圈子。下一次从小伙伴 3 开始。
8) 顺时针数 2 名小伙伴,也就是小伙伴 3 和 5 。
9) 小伙伴 5 离开圈子。只剩下小伙伴 3 。所以小伙伴 3 是游戏的获胜者。
示例 2:
输入:n = 6, k = 5
输出:1
解释:小伙伴离开圈子的顺序:5、4、6、2、3 。小伙伴 1 是游戏的获胜者。
提示:
1 <= k <= n <= 500
**进阶:**你能否使用线性时间复杂度和常数空间复杂度解决此问题?
模拟解法
class Solution:
def findTheWinner(self, n: int, k: int) -> int:
arr = [i+1 for i in range(n)]
restmen = 1
counter = 0
iterator = 0
while len(arr) > restmen:
counter += 1
if counter == k: # 数到第m个人淘汰
arr.pop(iterator)
counter = 0
# 注意:淘汰后不需要移动iterator,因为下一个元素已经占据当前位置
else:
iterator += 1
if iterator >= len(arr):
iterator = 0
return arr[0]
数学解法
class Solution:
def findTheWinner(self, n: int, k: int) -> int:
winner = 1
for i in range(2, n + 1):
winner = (k + winner - 1) % i + 1
return winner
nextpage
3335. 字符串转换后的长度 I
1806
给你一个字符串 s
和一个整数 t
,表示要执行的 转换 次数。每次 转换 需要根据以下规则替换字符串 s
中的每个字符:
- 如果字符是
'z'
,则将其替换为字符串"ab"
。 - 否则,将其替换为字母表中的下一个字符。例如,
'a'
替换为'b'
,'b'
替换为'c'
,依此类推。
返回 恰好 执行 t
次转换后得到的字符串的 长度。
由于答案可能非常大,返回其对 109 + 7
取余的结果。
示例 1:
输入: s = "abcyy", t = 2
输出: 7
解释:
- 第一次转换 (t = 1)
'a'
变为'b'
'b'
变为'c'
'c'
变为'd'
'y'
变为'z'
'y'
变为'z'
- 第一次转换后的字符串为:
"bcdzz"
- 第二次转换 (t = 2)
'b'
变为'c'
'c'
变为'd'
'd'
变为'e'
'z'
变为"ab"
'z'
变为"ab"
- 第二次转换后的字符串为:
"cdeabab"
- 最终字符串长度:字符串为
"cdeabab"
,长度为 7 个字符。
示例 2:
输入: s = "azbk", t = 1
输出: 5
解释:
- 第一次转换 (t = 1)
'a'
变为'b'
'z'
变为"ab"
'b'
变为'c'
'k'
变为'l'
- 第一次转换后的字符串为:
"babcl"
- 最终字符串长度:字符串为
"babcl"
,长度为 5 个字符。
提示:
1 <= s.length <= 105
s
仅由小写英文字母组成。1 <= t <= 105
记忆化存储
class Solution:
def lengthAfterTransformations(self, s: str, t: int) -> int:
@cache
def process(num):
if num >= 26:
num -= 26
return process(num) + process(num+1)
return 1
s = list(s)
s = list(map(lambda x:(ord(x) - ord('a')), s))
return sum(process(num+t) for num in s)%(10**9 + 7)
动态规划
class Solution:
def lengthAfterTransformations(self, s: str, t: int) -> int:
MOD = 10**9 + 7
max_num = 26 + t # 最大可能的 num 值
dp = [1] * (max_num + 2) # dp[num] = process(num)
for num in range(26, max_num + 1):
dp[num] = (dp[num - 26] + dp[num - 25]) % MOD
s = [ord(c) - ord('a') for c in s]
return sum(dp[num + t] for num in s) % MOD
模拟
from collections import Counter
mod=10**9+7
class Solution:
def lengthAfterTransformations(self, s: str, t: int) -> int:
count=Counter(s)
number=[0]*26
for value,number_of_value in count.items():
number[ord(value)-ord("a")]+=number_of_value
number[ord(value)-ord("a")]%=mod
for _ in range(0,t):
temp=number[-1]
for i in range(25,0,-1):
number[i]=number[i-1]
number[0]=temp
number[1]+=temp
number[1]%=mod
return sum(number)%mod
nextpage
面试题 02.05. 链表求和
中等
给定两个用链表表示的整数,每个节点包含一个数位。
这些数位是反向存放的,也就是个位排在链表首部。
编写函数对这两个整数求和,并用链表形式返回结果。
示例:
输入:(7 -> 1 -> 6) + (5 -> 9 -> 2),即617 + 295
输出:2 -> 1 -> 9,即912
**进阶:**思考一下,假设这些数位是正向存放的,又该如何解决呢?
示例:
输入:(6 -> 1 -> 7) + (2 -> 9 -> 5),即617 + 295
输出:9 -> 1 -> 2,即912
python做法
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
carry = 0
dummy = ListNode()
cur = dummy
while l1 or l2 or carry:
if l1: carry += l1.val
if l2: carry += l2.val
cur.next = ListNode(carry%10)
carry//=10
if l1: l1 = l1.next
if l2: l2 = l2.next
cur = cur.next
return dummy.next
C++做法
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* dummy = new ListNode();
auto cur = dummy;
int carry = 0;
while(l1 || l2 || carry){
if(l1 != NULL) carry += l1->val;
if(l2 != NULL) carry += l2->val;
cur->next = new ListNode(carry%10);
if(l1 != NULL) l1 = l1->next;
if(l2 != NULL) l2 = l2->next;
carry /= 10;
cur = cur->next;
}
return dummy->next;
}
};