4.18
136. 只出现一次的数字 - 力扣(LeetCode)
利用异或运算 a⊕a=0 的性质,我们可以用异或来「消除」所有出现了两次的元素,最后剩下的一定是只出现一次的元素。
例如 nums=[4,1,2,1,2],把所有元素异或:
4⊕1⊕2⊕1⊕2 = 4⊕(1⊕1)⊕(2⊕2) = 4⊕0⊕0 = 4
其中用到了异或运算的结合律,即 (a⊕b)⊕c=a⊕(b⊕c)(类比加法)。
代码中,初始化
ans = 0是因为 0⊕a=a,相当于我们从第一个数开始,和其它数异或。class Solution { public: int singleNumber(vector<int>& nums) { int ans = 0; for(int i : nums) ans ^= i; return ans; } };复杂度分析
- 时间复杂度:O(n),其中 n 为 nums 的长度。
- 空间复杂度:O(1)。
//自己的写法:两次遍历
class Solution {
public:
int singleNumber(vector<int>& nums) {
unordered_map<int , int> mp;
int res = -1;
for(int i : nums){
mp[i] ++;
}
for(auto& x : mp) {
if(x.second == 1) {
res = x.first;
break;
}
}
return res;
}
};
异或题:1720. 解码异或后的数组 - 力扣(LeetCode)
class Solution { public: vector<int> decode(vector<int>& encoded, int first) { vector<int> res; res[0] = first; for (int i = 0; i < encoded.size(); i++) { res[i + 1] = encoded[i] ^ res[i]; } return res; } };这种写法是错误的:根本原因在于
vector未初始化空间时直接通过下标访问元素。以下是详细分析和修复方案:
空
vector直接下标访问vector<int> res; res[0] = first; // 错误!此时 res 是空容器,res[0] 是非法访问
vector默认构造时是空的,size() == 0。- 直接通过
res[0]访问元素会触发越界,导致未定义行为(UB)。循环中的越界访问
for (int i = 0; i < encoded.size(); i++) { res[i + 1] = encoded[i] ^ res[i]; // 错误!i+1 超出当前 res 的大小 }
res初始为空,循环中i+1的索引必然越界。
修复后的代码
方案1:预先分配空间
class Solution { public: vector<int> decode(vector<int>& encoded, int first) { int n = encoded.size() + 1; vector<int> res(n); // 直接分配足够空间 res[0] = first; // 合法访问 for (int i = 0; i < encoded.size(); i++) { res[i + 1] = encoded[i] ^ res[i]; } return res; } };方案2:使用
push_back动态扩展class Solution { public: vector<int> decode(vector<int>& encoded, int first) { vector<int> res; res.push_back(first); // 正确初始化第一个元素 for (int i = 0; i < encoded.size(); i++) { res.push_back(encoded[i] ^ res[i]); // 动态添加 } return res; } };
34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)
二分法,细节:
- ‘=’加在
if(nums[mid] <= target) ll = mid;那么ll就是最后一个等于target的数(如果存在)- ‘=’加在
if(nums[mid] >= target) r = mid;那么r就是第一个等于target的数(如果存在)- 第一次二分之后判断是否存在target的条件是:
if(r == nums.size() || nums[r] != target)
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int l = -1 , r = nums.size();
while(l + 1 < r){
int mid = l + ((r - l) >> 1);
if(nums[mid] < target) l = mid;
else r = mid;//第一次出现的位置
}
if(r == nums.size() || nums[r] != target) return {-1 , -1};
int ll = -1 , rr = nums.size();
while(ll + 1 < rr){
int mid = ll + ((rr - ll) >> 1);
if(nums[mid] <= target) ll = mid;
else rr = mid;
}
return {r , ll};
}
};
611. 有效三角形的个数 - 力扣(LeetCode)
[!WARNING]
回溯法深搜会TLE,用户当前的代码思路是使用回溯法生成所有可能的三元组,然后判断是否满足条件。但显然,这种方法在时间效率上会有问题,因为数组长度可能很大,回溯的时间复杂度是O(n³),当n较大时会超时。比如,如果数组长度是1000,那么时间复杂度是10^9级别,显然无法通过。

//回溯超时代码
class Solution {
public:
int triangleNumber(vector<int>& nums) {
int res = 0;
vector<int> path;
ranges::sort(nums);
auto dfs = [&](this auto&& dfs , int startIndex)->void{
if(path.size() == 3 ){
if(path[0] + path[1] > path[2]) res ++;
return;
}
for (int i = startIndex; i < nums.size(); i++) {
if(path.size() < 3) {
path.push_back(nums[i]);
dfs(i + 1);
path.pop_back();
}
}
};
dfs(0);
return res;
}
};
分析
首先明确计算规则:从示例 1 可以知道,对于三元组 (2,3,4) 和 (4,3,2),我们只统计了其中的 (2,3,4),并没有把 (4,3,2) 也统计到答案中,所以题目意思是把这两个三元组当成是同一个三元组,我们不能重复统计。
既然有这样的规则,那么不妨规定三角形的三条边 a,b,c 满足:1≤a≤b≤c
这可以保证我们在统计合法三元组 (a,b,c) 的个数时,不会把 (c,b,a) 这样的三元组也统计进去。
那么问题变成,从 nums 中选三个数,满足 1≤a≤b≤c 且 a+b>c 的方案数。
方法一:枚举最长边 + 相向双指针
为了能够使用相向双指针,先对数组从小到大排序。
外层循环枚举最长边 c=nums[k],内层循环用相向双指针枚举 a=nums[i] 和 b=nums[j],具体如下:
- 初始化左右指针 i=0, j=k−1。
- 如果 nums[i]+nums[j]>c,由于数组是有序的,nums[j] 与下标 i′在 [i,j−1] 中的任何 nums[i′] 相加,都是 >c 的,因此直接找到了 j−i 个合法方案,加到答案中,然后将 j 减一。
- 如果 nums[i]+nums[j]≤c,由于数组是有序的,nums[i] 与下标 j′ 在 [i+1,j] 中的任何 nums[j′] 相加,都是 ≤c 的,因此后面无需考虑 nums[i],将 i 加一。
- 重复上述过程直到 i≥j 为止。
优化前
class Solution { public: int triangleNumber(vector<int>& nums) { ranges::sort(nums); int n = nums.size(), res = 0; for (int k = n - 1; k >= 2; k--) { int i = 0, j = k - 1; while (i < j) { if (nums[i] + nums[j] > nums[k]) { res += j - i; // [i, j-1] 均满足条件 j--; } else { i++; } } } return res; } };优化
类似 15. 三数之和的做法,本题也有两个优化。
首先把循环改成倒序枚举 k。可以剪枝优化:
第一个优化:在执行双指针之前,如果发现最小的 a 和 b 相加大于 c,也就是
nums\[0\]+nums\[1\]\>nums\[k\]说明从 nums[0] 到 nums[k] 中任选三个数 a,b,c 都满足 a+b>c,那么直接把 \(C_{k+1}^3 =\frac{(k+1)k(k−1)}{6}\) 加入答案,退出外层循环。这是为什么要倒序枚举 k 的原因(正序枚举没法退出外层循环)。
第二个优化:在执行双指针之前,如果发现最大的 a 和 b 相加小于等于 c,也就是
nums[k−2]+nums[k−1]≤nums[k]
说明不存在 a+b>c,不执行双指针,继续外层循环。
注:由于数据原因,上述优化在本题可能并不明显。
class Solution { public: int triangleNumber(vector<int>& nums) { ranges::sort(nums); int ans = 0; for (int k = nums.size() - 1; k > 1; k--) { int c = nums[k]; if (nums[0] + nums[1] > c) { // 优化一 ans += (k + 1) * k * (k - 1) / 6; break; } if (nums[k - 2] + nums[k - 1] <= c) { // 优化二 continue; } int i = 0; // a=nums[i] int j = k - 1; // b=nums[j] while (i < j) { if (nums[i] + nums[j] > c) { ans += j - i; j--; } else { i++; } } } return ans; } };复杂度分析
- 时间复杂度:O(n^2),其中 n 为 nums 的长度。
- 空间复杂度:O(1)。不计入排序的栈开销,仅用到若干额外变量。
方法二:枚举最短边 + 同向双指针
枚举最短边 a,问题变成计算满足 c−b < a 的 (b,c) 个数。
这个条件意味着,当 a 固定不变时,b 和 c 不能隔太远。
这可以用同向双指针解决,原理见 滑动窗口【基础算法精讲 03】。
- 枚举 a=nums[i],其中 i=0,1,2,…,n−3。
- 如果 a=0,则跳过。
- 现在计算,对于 k=i+2,i+3,…,n−1,有多少个符合要求的 j?。
- 枚举 k 的同时,维护指针 j,初始值为 i+1。
- 如果发现 nums[k]−nums[j]≥a,说明 b 和 c 隔太远了,那么把 j 不断加一,直到 c−b<a,也就是 nums[k]−nums[j] < a 为止。
- 此时,对于固定的 a=nums[i] 和固定的 c=nums[k],nums[j] , nums[j+1] ,…, nums[k−1] 都可以作为 b,这一共有 k−j 个,加入答案。
class Solution { public: int triangleNumber(vector<int>& nums) { ranges::sort(nums); int n = nums.size(), ans = 0; for (int i = 0; i < n - 2; i++) { int a = nums[i]; if (a == 0) { // 三角形的边不能是 0 continue; } int j = i + 1; for (int k = i + 2; k < n; k++) { while (nums[k] - nums[j] >= a) { j++; } // 如果 a=nums[i] 和 c=nums[k] 固定不变 // 那么 b 可以是 nums[j],nums[j+1],...,nums[k-1],一共有 k-j 个 ans += k - j; } } return ans; } };复杂度分析
- 时间复杂度:O(n^2),其中 n 为 nums 的长度。里面的二重循环,由于 j 一直在增大,所以里面的二重循环的时间复杂度是 O(n) 的。
- 空间复杂度:O(1)。不计入排序的栈开销,仅用到若干额外变量。
673. 最长递增子序列的个数 - 力扣(LeetCode)
前置题:300. 最长递增子序列 - 力扣(LeetCode)
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
if(n == 0 || n == 1) return n;
int res = 0;
vector<int> dp(n , 1);
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if(nums[i] > nums[j])
{
dp[i] = max(dp[i] , dp[j] + 1);
}
}
res = max(res , dp[i]);
}
return res;
}
};
思路
这道题可以说是 300.最长上升子序列 的进阶版本
1. 确定dp数组(dp table)以及下标的含义
这道题目我们要一起维护两个数组。
dp[i]:i之前(包括i)最长递增子序列的长度为dp[i]
count[i]:以nums[i]为结尾的字符串,最长递增子序列的个数为count[i]
2. 确定递推公式
在
300.最长上升子序列中,我们给出的状态转移是:if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);
即:位置i的最长递增子序列长度 等于j从0到i-1各个位置的最长升序子序列 + 1的最大值。
本题就没那么简单了,我们要考虑两个维度,一个是dp[i]的更新,一个是count[i]的更新。
那么如何更新count[i]呢?
以nums[i]为结尾的字符串,最长递增子序列的个数为count[i]。
那么在nums[i] > nums[j]前提下
如果在[0, i-1]的范围内,找到了j,使得dp[j] + 1 > dp[i],说明找到了一个更长的递增子序列。
那么以j为结尾的子串的最长递增子序列的个数,就是最新的以i为结尾的子串的最长递增子序列的个数,相当于在所有的递增子序列后加入一个nums[i] , 长度变了,个数不变。
即:count[i] = count[j] , dp[i] = dp[j] + 1;
在nums[i] > nums[j]前提下,如果在[0, i-1]的范围内,找到了j,使得dp[j] + 1 == dp[i],说明找到了两个相同长度的递增子序列。
那么以i为结尾的子串的最长递增子序列的个数 就应该加上以j为结尾的子串的最长递增子序列的个数,即:count[i] += count[j];
[!NOTE]
如何理解? 相当于把子序列
0 , 1 ,2 ···nums[j]换成了0 , 1 ,2···nums[i],这样子序列长度是相同的,那么count[i] 就应该加上count[j]的个数。if (nums[i] > nums[j]) { if (dp[j] + 1 > dp[i]) { dp[i] = dp[j] + 1; // 更新dp[i]放在这里,就不用max了 count[i] = count[j]; } else if (dp[j] + 1 == dp[i]) { count[i] += count[j]; } }题目要求最长递增序列的长度的个数,我们应该把最长长度记录下来。
代码如下:
for (int i = 1; i < nums.size(); i++) { for (int j = 0; j < i; j++) { if (nums[i] > nums[j]) { if (dp[j] + 1 > dp[i]) { count[i] = count[j]; dp[i] = dp[j] + 1; } else if (dp[j] + 1 == dp[i]) { count[i] += count[j]; } } if (dp[i] > maxCount) maxCount = dp[i]; // 记录最长长度 } }3. dp数组如何初始化
再回顾一下dp[i]和count[i]的定义
count[i]记录了以nums[i]为结尾的字符串,最长递增子序列的个数。那么最少也就是1个,所以count[i]初始为1。
dp[i]记录了i之前(包括i)最长递增序列的长度。
最小的长度也是1,所以dp[i]初始为1。
代码如下:
vector<int> dp(nums.size(), 1); vector<int> count(nums.size(), 1);其实动规的题目中,初始化很有讲究,也很考察对dp数组定义的理解。
4. 确定遍历顺序
dp[i] 是由0到i-1各个位置的最长升序子序列推导而来,那么遍历i一定是从前向后遍历。
j其实就是0到i-1,遍历i的循环里外层,遍历j则在内层,代码如下:
for (int i = 1; i < nums.size(); i++) { for (int j = 0; j < i; j++) { if (nums[i] > nums[j]) { if (dp[j] + 1 > dp[i]) { count[i] = count[j]; dp[i] = dp[j] + 1; } else if (dp[j] + 1 == dp[i]) { count[i] += count[j]; } } if (dp[i] > maxCount) maxCount = dp[i]; } }最后还有再遍历一遍dp[i],把最长递增序列长度对应的count[i]累计下来就是结果了。
代码如下:
for (int i = 1; i < nums.size(); i++) { for (int j = 0; j < i; j++) { if (nums[i] > nums[j]) { if (dp[j] + 1 > dp[i]) { count[i] = count[j]; } else if (dp[j] + 1 == dp[i]) { count[i] += count[j]; } dp[i] = max(dp[i], dp[j] + 1); } if (dp[i] > maxCount) maxCount = dp[i]; } } int result = 0; // 统计结果 for (int i = 0; i < nums.size(); i++) { if (maxCount == dp[i]) result += count[i]; }5. 举例推导dp数组
输入:[1,3,5,4,7]
如果代码写出来了,怎么改都通过不了,那么把dp和count打印出来看看对不对!
以上分析完毕,C++整体代码如下:
class Solution { public: int findNumberOfLIS(vector<int>& nums) { if (nums.size() <= 1) return nums.size(); vector<int> dp(nums.size(), 1); vector<int> count(nums.size(), 1); int maxCount = 0; for (int i = 1; i < nums.size(); i++) { for (int j = 0; j < i; j++) { if (nums[i] > nums[j]) { if (dp[j] + 1 > dp[i]) { dp[i] = dp[j] + 1; count[i] = count[j]; } else if (dp[j] + 1 == dp[i]) { count[i] += count[j]; } } if (dp[i] > maxCount) maxCount = dp[i]; } } int result = 0; for (int i = 0; i < nums.size(); i++) { if (maxCount == dp[i]) result += count[i]; } return result; } };
- 时间复杂度O(n^2)
- 空间复杂度O(n)
还有O(nlogn)的解法,使用树状数组,今天有点忙就先不写了,感兴趣的同学可以自行学习一下,这里有我之前写的树状数组系列博客 (十年前的陈年老文了)
class Solution {
public:
int findNumberOfLIS(vector<int>& nums) {
int n = nums.size();
if(n <= 1) return n;
vector<int> dp(n , 1);
vector<int> count(n , 1);
int maxCount = 0;
for (int i = 1; i < n; i++) {
for (int j = 0; j < i ; j++) {
if(nums[i] > nums[j]){
if(dp[j] + 1 > dp[i]){
dp[i] = dp[j] + 1;
count[i] = count[j];
}else if(dp[j] + 1 == dp[i]){
count[i] += count[j];
}
}
if(dp[i] > maxCount) maxCount = dp[i];
}
}
int res = 0;
for (int i = 0; i < n; i++) {
if(dp[i] == maxCount) res += count[i];
}
return res;
}
};
子序列系列力扣题目
按照如下顺序将力扣题目做完,相信会帮助你对动态规划之子序列问题有一个深刻的理解。以下每道题目在力扣题解区都有「代码随想录」的题解。

4. 寻找两个正序数组的中位数 - 力扣(LeetCode)
二、枚举:双指针做法
目标:找第一组的最大值 < 第二组的最小值,即\(max(a_i,b_j)< min(a_{i+1},b_{j+1})\)。
由于 \(a_i ≤ a_{i+1}\) 且 \(b_j < b_{j+1}\),所以只需判断\(a_i ≤ b_{j+1}\) 和\(b_j< a_{i+1}\)是否同时成立。下面来说具体做法。
设 a 的 b 的长度分别为 m 和 n,且 m≤n(如果不满足则交换两个数组)。
- 为方便处理 i=−1,即 a 有 0 个数在第一组的情况,我们可以往 a 的最左边插入一个哨兵 −∞,这可以保证数组仍然是有序的。对于 j=−1 的情况也同理,往b的最左边插入一个−∞。
- 为方便处理 i+1=m,即 a 有 m 个数在第一组的情况,我们可以往 a 的最右边插入一个哨兵 ∞,这可以保证数组仍然是有序的。对于 j+1=n 的情况也同理,往 b 的最右边插入一个 ∞。这可以避免 \(a_{i+1}\) 和 \(b_ {j+1}\)下标越界。
- 插入 −∞ 和 ∞ 后,便可保证无论 a 和 b 是什么样的,一定存在一个 i,满足 \(a_i≤b_ {j+1}\)且 \(a_{i+1}>=b_j\) 。
- m 和 n 的值不变。
如此修改后,i 的含义变成了 a 有 i 个数在第一组,j 的含义变成了 b 有 j 个数在第一组。
初始化 i=0,那么 j 应该初始化成多少?
- 如果 m+n 是偶数,那么每组的大小为\(\frac{m+n}{2}\),j 应当初始化成\(\frac{m+n}{2}\)
- 如果 m+n 是奇数,我们规定第一组比第二组多一个数,第一组的大小为 \(\frac{m+n + 1}{2}\) ,j 应当初始化成 \(\frac{m+n + 1}{2}\)。
两种情况可以合并为:j 初始化成\(⌊\frac{m+n + 1}{2}⌋\) , i + j = \(\frac{m+n + 1}{2}\)
为了保证组的大小不变,i 每增加 1,j 就要减少 1(i + + , j - -)。
根据图片中的结论,只要发现 \(a_i≤b_{ j+1}\)且 \(a_{i+1}>b_j\) ,那么:
- 如果 m+n 是偶数,中位数为 \(max(a_i ,b_j)\) 和$ min(a_{i+1} ,b_{j+1})$ 的平均值。
- 如果 m+n 是奇数,中位数为 \(max(a_i ,b_j)\)。
双指针法:
class Solution {
public:
double findMedianSortedArrays(vector<int>& a, vector<int>& b) {
if (a.size() > b.size()) {
swap(a, b); // 保证下面的 i 可以从 0 开始枚举
}
int m = a.size(), n = b.size();
a.insert(a.begin(), INT_MIN); // 最左边插入 -∞
b.insert(b.begin(), INT_MIN);
a.push_back(INT_MAX); // 最右边插入 ∞
b.push_back(INT_MAX);
// 枚举 nums1 有 i 个数在第一组
// 那么 nums2 有 (m + n + 1) / 2 - i 个数在第一组
int i = 0, j = (m + n + 1) / 2;
while (true) {
if (a[i] <= b[j + 1] && a[i + 1] > b[j]) { // 写 >= 也可以
int max1 = max(a[i], b[j]); // 第一组的最大值
int min2 = min(a[i + 1], b[j + 1]); // 第二组的最小值
return (m + n) % 2 ? max1 : (max1 + min2) / 2.0;
}
i++; // 继续枚举
j--;
}
}
};
复杂度分析
- 时间复杂度:O(m+n),其中 m 是 a 的长度,n 是 b 的长度。往 a 前面插入一个元素的时间复杂度是 O(m),往 b 前面插入一个元素的时间复杂度是 O(n),加起来是 O(m+n)。
- 空间复杂度:O(m+n)。
三、优化:二分做法
由于 a 和 b 是有序数组,i 越小,\(a_i ≤b_{j+1}\) 越能成立;i 越大,\(a_i ≤b_{j+1}\)
越不能成立。所以可以二分最大的满足 \(a_i ≤b_{j+1}\) 的 i。二分结束后,我们有 a
\(a_i ≤b_{j+1}\)且 \(a _{i+1}>b_j\)。最后,讨论二分的上下界。本文用开区间二分,其他二分写法也是可以的。
- 开区间二分左边界:0。在插入 −∞ 后, \(a_i ≤b_{j+1}\)在 i=0 时一定成立。
- 开区间二分右边界:m+1。在插入 ∞ 后, \(a_i ≤b_{j+1}\) 在 i=m+1 时一定不成立。
答疑
问:能否二分红色折线图的最小值?
答:这种做法会在有重复元素时失效。试想一下,如果我们在折线图上二分,碰巧遇到了相邻且相同的元素,你要更新 left 还是更新 right 呢?
写法一
注意在数组前面插入元素的时间复杂度是线性的,所以和上面的复杂度分析一样,都是 O(n+m)。
真正满足题目时间复杂度要求的是后面的写法二。
class Solution { public: double findMedianSortedArrays(vector<int>& a, vector<int>& b) { if (a.size() > b.size()) { swap(a, b); // 保证下面的 i 可以从 0 开始枚举 } int m = a.size(), n = b.size(); a.insert(a.begin(), INT_MIN); b.insert(b.begin(), INT_MIN); a.push_back(INT_MAX); b.push_back(INT_MAX); // 循环不变量:a[left] <= b[j+1] // 循环不变量:a[right] > b[j+1] int left = 0, right = m + 1; while (left + 1 < right) { // 开区间 (left, right) 不为空 int i = (left + right) / 2; int j = (m + n + 1) / 2 - i; if (a[i] <= b[j + 1]) { left = i; // 缩小二分区间为 (i, right) } else { right = i; // 缩小二分区间为 (left, i) } } // 此时 left 等于 right-1 // a[left] <= b[j+1] 且 a[right] > b[j'+1] = b[j],所以答案是 i=left int i = left; int j = (m + n + 1) / 2 - i; int max1 = max(a[i], b[j]); int min2 = min(a[i + 1], b[j + 1]); return (m + n) % 2 ? max1 : (max1 + min2) / 2.0; } };写法二
去掉插入的 −∞ 和 ∞,所有下标都减一。
开区间二分的左右边界改成 −1 和 m。
i + j = ⌊(m+n+1)/2⌋−2
i=−1 时,j 的值为 ⌊(m+n+1)/2⌋−1。
i=0 时,j 的值为 ⌊(m+n+1)/2⌋−2。
一般地,j 和 i 的关系为 j = ⌊(m+n+1)/2⌋−2−i = ⌊(m+n-3)/2⌋−i。
答疑
问:当 m=0 时,是否会算出 i=0?
答:不会,m=0 不会进入二分循环,i=left=−1。
class Solution { public: double findMedianSortedArrays(vector<int>& a, vector<int>& b) { if (a.size() > b.size()) { swap(a, b); } int m = a.size(), n = b.size(); // 循环不变量:a[left] <= b[j+1] // 循环不变量:a[right] > b[j+1] int left = -1, right = m; while (left + 1 < right) { // 开区间 (left, right) 不为空 int i = (left + right) / 2; int j = (m + n + 1) / 2 - 2 - i; if (a[i] <= b[j + 1]) { left = i; // 缩小二分区间为 (i, right) } else { right = i; // 缩小二分区间为 (left, i) } } // 此时 left 等于 right-1 // a[left] <= b[j+1] 且 a[right] > b[j'+1] = b[j],所以答案是 i=left int i = left; int j = (m + n + 1) / 2 - 2 - i; int ai = i >= 0 ? a[i] : INT_MIN; int bj = j >= 0 ? b[j] : INT_MIN; int ai1 = i + 1 < m ? a[i + 1] : INT_MAX; int bj1 = j + 1 < n ? b[j + 1] : INT_MAX; int max1 = max(ai, bj); int min2 = min(ai1, bj1); return (m + n) % 2 ? max1 : (max1 + min2) / 2.0; } };
复杂度分析
- 时间复杂度:O(log(min(m,n))),其中 m 是 a 的长度,n 是 b 的长度。注:这个复杂度比题目所要求的 O(log(m+n)) 更优。
- 空间复杂度:O(1)。



浙公网安备 33010602011771号