【JS】4.寻找两个正序数组的中位数
作者:互联网
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
解法一:暴力(啰嗦的写法)
就两个数组合并成一个新的有序数组,然后取中间值
var findMedianSortedArrays = function(nums1, nums2) {
var m=nums1.length,n=nums2.length;
//要判断有长为0的情况
if(m===0){
return n%2===0 ? (nums2[n/2] + nums2[n/2 -1])/2 : nums2[Math.floor(n/2)];
}
if(n===0){
return m%2===0 ? (nums1[m/2] + nums1[m/2 -1])/2 : nums1[Math.floor(m/2)];
}
//两个数组根据大小比较来排序合并,数组可以用push或者直接开个固定的空间
var nums=[],i=0,j=0;
//两个都可以比的时候
while(i<m && j<n){
if(nums1[i]<nums2[j]){
nums.push(nums1[i]);
i++;
}else{
nums.push(nums2[j]);
j++;
}
}
//任意一个数组比完了,那么另一个一直加下去
while(i<m){
nums.push(nums1[i++]);
}
while(j<n){
nums.push(nums2[j++]);
}
//原来合并后的数组有长度这个属性的
const {length} = nums;
return length % 2 === 1 ? nums[Math.floor(length/2)] : (nums[length/2] + nums[length/2 -1])/2;
};
复杂度分析
- 时间复杂度:\(O(max(m, n))\),\(m\)为\(nums1\)的长度,\(n\)为\(nums2\)的长度
- 空间复杂度:\(O(m + n)\)
解法一:暴力(简洁的写法)
大佬的链接:4. 寻找两个正序数组的中位数 - 寻找两个正序数组的中位数 - 力扣(LeetCode)
用数组自带的concat函数把两个数组合并到一起,接着使用sort()函数进行排序。另外根据B战的蛋老师的一期视频,在JS的sort()跟我所学的C++或python都不太一样,不能直接用。
var findMedianSortedArrays = function(nums1, nums2) {
let len = nums1.length + nums2.length;
let nums = nums1.concat(nums2).sort((a,b) => a-b);
return len % 2 === 1 ? nums[Math.floor(len/2)] : (nums[len/2] + nums[len/2 -1])/2;
};
复杂度分析
- 时间复杂度:\(O((m+n)log(m+n))\),简洁带来的坏处就是时间效率下降
- 空间复杂度 \(O(m+n)\)
解法二:双指针比较
就没必要合并两个数组,本质就是两个指针进行对比。
var findMedianSortedArrays = function(nums1, nums2) {
let m=nums1.length,n=nums2.length,len=m+n;
//创建两个指针,指针的作用除了移动到指定的中位数位置
//还要辅助判断数组的大小才行,所以还要添加两个辅助变量
//进一步判断返回最终偶数或奇数时对应的中位数的大小
let point1=0,point2=0;
let preValue=-1,curValue=-1;
//最多就变量(m+n/2)次
for(let i = 0;i<=Math.floor(len/2);i++){
preValue=curValue;
//除了正常的比较大小外,还要考虑任意一方没法再比较的情况
if(point1<m && (point2 >= n || nums1[point1] < nums2[point2])){
curValue = nums1[point1++];
}else{
curValue = nums2[point2++];
}
}
//Math.floor(是向下取数的)
return len%2===0 ? (preValue + curValue)/2 : curValue;
};
复杂度分析
- 时间复杂度\(O(m+n)\)
- 空间复杂度\(O(1)\)
解法三:二分查找
观看Leetcode的视频:寻找两个有序数组的中位数 - 寻找两个正序数组的中位数 - 力扣(LeetCode)
其实就是两个都弄个分段,结合数组有序的性质与在最短长的数组中二分查找出分隔线的方式,总可以找到一个符合要求的中间值。
var findMedianSortedArrays = function(nums1, nums2) {
//保证nums1长度小于nums2,因为他们的分隔线位置互相影响
if(nums1.length > nums2.length){
[nums1,nums2] = [nums2,nums1];
}
//取长度
let m=nums1.length,n=nums2.length;
//在0~m区域
let left=0,right=m;
//暂存左半段的最大值,右半段的最小值
let median1=0,median2=0;
while(left<=right){
//找nums1这里的中位线作为分隔线
const i = left +Math.floor((right-left)/2);
//想象两个数组合并成一个新数组的总长度取中位线 - 左半段的分隔线,就是右半段分隔线的初始位置
const j = Math.floor((m+n+1)/2)-i;
//判断特殊情况,比如num1的中位线就是在下标0处,那么这分隔线也就缺乏意义了。
const maxLeft1 = i === 0 ? -Infinity : nums1[i-1];
const minRight1 = i === m ? Infinity : nums1[i];
const maxLeft2 = j === 0 ? -Infinity : nums2[j-1];
const minRight2 = j === n ? Infinity : nums2[j];
//不停通过二分查找调整分隔线的位置,直到找到对应的数组,然后取中间值
if(maxLeft1<=minRight2){
median1=Math.max(maxLeft1,maxLeft2);
median2=Math.min(minRight1,minRight2);
left = i+1;
}else{
right = i-1;
}
}
return (m+n) % 2 == 0 ? (median1 + median2) /2 : median1;
};
复杂度分析
- 时间复杂度:\(O(\log\min(m,n)))\),其中 \(m\) 和 \(n\) 分别是数组 \(\textit{nums}_1\) 和 \(\textit{nums}_2\) 的长度。查找的区间是 \([0, m]\) ,而该区间的长度在每次循环之后都会减少为原来的一半。所以,只需要执行 \(\log m\) 次循环。由于每次循环中的操作次数是常数,所以时间复杂度为 \(O(\log m)\)。由于我们可能需要交换 \(\textit{nums}_1\) 和 \(\textit{nums}_2\) 使得 \(m \leq n\),因此时间复杂度是 \(O(\log\min(m,n)))\)。
- 空间复杂度:\(O(1)\)。
标签:正序,复杂度,中位数,JS,length,数组,nums1,nums2 来源: https://www.cnblogs.com/PaturNax/p/16439921.html