首次刷leetcode。写完两题easy和一题medium后感觉题目偏容易,准备挑战一下hard,选择的题目如下:
There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
Example 1:
nums1 = [1, 3] nums2 = [2] The median is 2.0
Example 2:
nums1 = [1, 2] nums2 = [3, 4] The median is (2 + 3)/2 = 2.5
第一反应是,这题不是重排序成一个数组然后取下标中间值就行了么,so easy。
然而注意到时间复杂度要求在O(log(m+n))以内,这题就有意思了。因为重新排序至少也要花费O(m+n)的时间,而要求的时间复杂度就限制了重新排序的可能。
想了一个多小时,发现同时丢掉中位数左边和右边的N个数对中位数是没有影响的,于是决定用二分法递归地来解决这个问题。思路是不停的丢弃中位数左右边的n个数,直到有一个数组里只有一个数,开始计算中位数,运算结束。
另外在跑测试用例的时候遇到了一些特殊情况,又增加了一些特殊情况的判断。最终得到代码如下:
double median2c(int* nums, int numsSize)
{
if(numsSize%2==0)
{
return (nums[numsSize/2-1]+nums[numsSize/2])/2.0;
}
else if(numsSize%2==1)
{
return (double)nums[numsSize/2];
}
return -1;
}
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {
if(nums1Size==0) return median2c(nums2, nums2Size);
if(nums2Size==0) return median2c(nums1, nums1Size);
int cutlen;
if(nums1Size%2 == 0 && nums2Size%2 == 0)
{
if(nums1[nums1Size/2-1]<nums2[nums2Size/2-1] && nums1[nums1Size/2]>nums2[nums2Size/2])
return (nums2[nums2Size/2-1]+nums2[nums2Size/2])/2.0;
if(nums1[nums1Size/2-1]>nums2[nums2Size/2-1] && nums1[nums1Size/2]<nums2[nums2Size/2])
return (nums1[nums1Size/2-1]+nums1[nums1Size/2])/2.0;
}
if(nums1Size==1 && nums2Size==1)
{
return (nums1[0]+nums2[0])/2.0;
}
else if(nums1Size==1)
{
if(nums2Size%2 == 0)
{
if(nums1[0]<=nums2[nums2Size/2-1]) return nums2[nums2Size/2-1];
else if(nums1[0]>=nums2[nums2Size/2]) return nums2[nums2Size/2];
else if(nums1[0]>nums2[nums2Size/2-1] && nums1[0]<nums2[nums2Size/2]) return nums1[0];
}
else if(nums2Size%2 == 1)
{
if(nums1[0]<=nums2[nums2Size/2-1]) return (nums2[nums2Size/2-1]+nums2[nums2Size/2])/2.0;
else if(nums1[0]>=nums2[nums2Size/2+1]) return (nums2[nums2Size/2]+nums2[nums2Size/2+1])/2.0;
else if(nums1[0]>nums2[nums2Size/2-1] && nums1[0]<nums2[nums2Size/2+1]) return (nums1[0]+nums2[nums2Size/2])/2.0;
}
}
else if(nums2Size==1)
{
return findMedianSortedArrays(nums2, nums2Size, nums1, nums1Size);
}
else
{
cutlen = (nums1Size/2 > nums2Size/2) ? nums2Size/2 : nums1Size/2;
if(median2c(nums1,nums1Size) > median2c(nums2,nums2Size))
{
nums2 = nums2 + cutlen;
}
else if(median2c(nums1,nums1Size) < median2c(nums2,nums2Size))
{
nums1 = nums1 + cutlen;
}
else if(median2c(nums1,nums1Size) == median2c(nums2,nums2Size))
{
return median2c(nums1,nums1Size);
}
nums1Size = nums1Size - cutlen;
nums2Size = nums2Size - cutlen;
// return findMedianSortedArrays(nums1, nums1Size, nums2, nums2Size);
}
return findMedianSortedArrays(nums1, nums1Size, nums2, nums2Size);
}
编写过程耗时超过3个小时。但毕竟是我第一次在实际问题中用递归思想将一个O(n)复杂度的算法降为了O(log(n)),心里还是挺美滋滋的。毕竟几年前在算法导论上看到分治法、快速排序等算法时还只能感叹前人的智慧。提交成功之后点击more details,发现自己只超过了77%的人,也就是说还有23%的人有更快的算法。于是我抱着学习的心态点进了速度最快的代码——下面转折来了。“最快“的算法使用的居然是O(m+n)复杂度的重新排序方法!也就是本博客一开始说的so easy的方法。
当时就有点崩溃,点开其他各个运行速度的代码查看,无一例外全部是O(m+n)。我转念一想,返回了提交页面重新提交了我的代码。提交的第二次居然只超过了44%的人。而提交到第五次的时候,也就是写这篇博客之前,居然获得了这样的结果:
??WTF?这算是leetcode颁发的安慰奖吗?
不管怎么样,通过这次刷题,得到了如下结论:
1.leetcode是一个适合练习编程语言而不是算法的地方。毕竟不合算法要求的代码提交也能通过,并且也有一定几率获得较好的运行时间结果。
2.代码的运行时间是在一定分布内随机波动的。不要太在意这个扯淡的submission detail。
3.有没有人能推荐个更靠谱点算法练习学习的网站?