• 2017-3-13 leetcode 4 11 15


    ji那天居然早起了,惊呆我了,眼睛有点儿疼,一直流泪。。。。继续保持

    ==========================================================

    leetcode4 Median of Two Sorted Arrays

    leetcode11 Container With Most Water

    leetcode15 3Sum

    ==========================================================

    4讲的是
    给你n个有序数字,再给你m个有序数字,让你找到所有数里面的中位数,要求复杂度O(log(n+m))

    我的思路
    同时二分两个数组。。。。。

    莫名其妙的一写二分就想打人。。。。

    好吧,我没什么成型的想法,于是我看了讨论版,看到了一个很不错的算法,是这样的,首先看一下第一个数组是否比第二个短,如果短就交换一下。在第二个数组中随便找个点i,把nums2划分为两部分,[0...i-1],[i...m-1],可以看到前一部分长度为i,后一部分是m-i,这时在nums1里面有个分割点j,让j=(n+m)/2-i,nums1前半部分长度是j,后半部分长度是n-j,且这时候两个数组的前半部分和后半部分元素数目是相同的(或者后半部分比前半部分多一个),如果我们可以找到一个i,切nums1[j-1]<=nums2[i]&&nums1[j]>=nums2[i-1](保证所有前半部分的数据都大于等于后半部分),那么我们就顺利的找到了分割点,中位数自然就出来了。这里边界处理需要稍微注意下,i的取值范围是[0...m],因为nums[m-1]是有效的。

     1 class Solution {
     2 public:
     3     double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
     4         int n=nums1.size(),m=nums2.size();
     5         if(n<m)return findMedianSortedArrays(nums2,nums1);
     6         if(m==0){
     7             if(n&1)return nums1[n>>1];
     8             else return 1.0*(nums1[n>>1]+nums1[(n>>1)-1])/2;
     9         }
    10         int m_left=0,m_right=m,mid=0,i=0,j=(n+m)/2-i;
    11         while(m_left<=m_right){
    12             mid=m_left+(m_right-m_left)>>1;
    13             i=mid,j=(n+m)/2-i;
    14             if(i<m&&j>0&&nums2[i]<nums1[j-1])m_left=mid+1;
    15             else if(j<n&&i>0&&nums1[j]<nums2[i-1]&&mid!=0)m_right=mid-1;
    16             else{
    17                 int max_of_left=max(i==0?INT_MIN:nums2[i-1],j==0?INT_MIN:nums1[j-1]);
    18                 int min_of_right=min(i==m?INT_MAX:nums2[i],j==n?INT_MAX:nums1[j]);
    19                 if((n+m)&1){
    20                     return min_of_right;
    21                 }else{
    22                     return 1.0*(max_of_left+min_of_right)/2;
    23                 }
    24             }
    25         }
    26         return 0;
    27     }
    28 };
    WA

    这段代码理论AC就是过不了,居然会超时。。。你们可以看出来问题在哪儿吗?(AC代码见下)

     1 class Solution {
     2 public:
     3     double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
     4         int n=nums1.size(),m=nums2.size();
     5         if(n<m)return findMedianSortedArrays(nums2,nums1);
     6         if(m==0){
     7             if(n&1)return nums1[n>>1];
     8             else return 1.0*(nums1[n>>1]+nums1[(n>>1)-1])/2;
     9         }
    10         int m_left=0,m_right=m,mid=0,i=0,j=(n+m)/2-i;
    11         while(m_left<=m_right){
    12             mid=m_left+((m_right-m_left)>>1);//注意右移的优先级低于加号。。。不加括号会爆炸。。。
    13             i=mid,j=(n+m)/2-i;
    14             if(i<m&&j>0&&nums2[i]<nums1[j-1])m_left=mid+1;
    15             else if(j<n&&i>0&&nums1[j]<nums2[i-1]&&mid!=0)m_right=mid-1;
    16             else{
    17                 int max_of_left=max(i==0?INT_MIN:nums2[i-1],j==0?INT_MIN:nums1[j-1]);
    18                 int min_of_right=min(i==m?INT_MAX:nums2[i],j==n?INT_MAX:nums1[j]);
    19                 if((n+m)&1){
    20                     return min_of_right;
    21                 }else{
    22                     return 1.0*(max_of_left+min_of_right)/2;
    23                 }
    24             }
    25         }
    26         return 0;
    27     }
    28 };
    AC

    醉了醉了。。。。。

    刚刚算法的复杂度是O(log(min(n,m)))的,这道题还有另一种思路,复杂度是O(log(n+m))的。

    我们只需要在两个数组中找到大小为第(n+m)/2的元素就可以,于是我们成块的舍弃数据,让两个数组的容量和二分逼近(n+m)/2,看代码。

     1 class Solution {
     2 public:
     3     int getkth(int s[], int m, int l[], int n, int k){
     4         // let m <= n
     5         if (m > n) 
     6             return getkth(l, n, s, m, k);
     7         if (m == 0)
     8             return l[k - 1];
     9         if (k == 1)
    10             return min(s[0], l[0]);
    11 
    12         int i = min(m, k / 2), j = min(n, k / 2);
    13         if (s[i - 1] > l[j - 1])
    14             return getkth(s, m, l + j, n - j, k - j);
    15         else
    16             return getkth(s + i, m - i, l, n, k - i);
    17         return 0;
    18     }
    19     
    20     double findMedianSortedArrays(int A[], int m, int B[], int n) {
    21         int l = (m + n + 1) >> 1;
    22         int r = (m + n + 2) >> 1;
    23         return (getkth(A, m ,B, n, l) + getkth(A, m, B, n, r)) / 2.0;
    24     }
    25 };
    View Code

    这段代码不能直接提交,因为C++要求传入的参数是vector<int>,但是他传入的是数组,不太清楚vector有没有O(1)舍弃大块连续数据的方法,否则没有意义,因为只是舍弃数据就会达到O(n)23333

    先抛开这道题目的思路不说,当时如果静心拿笔写写很快就可以发现诀窍,没什么好说的。
    这里说说二分的写法,十个二分九个错,不知道别人如何,我个人是十分恐惧写二分的,今天看到一句挺好的话“永远保证一端是可行解,另一端是不可行解,就不会出问题”(其实这个是保证答案一定在区间[l,r)中)

    另一种叫循环不变式,保证答案一定在区间[l,r]中

     1 int l = 0,r = n-1;    // x 必然存在于区间 [0,n-1] 内。(这里使用闭区间表示)
     2 while( r-l+1 > 1 )     // 只要区间的长度大于1,就继续细分这个区间
     3 {
     4     // 循环不变式 : x 在区间 [l,r] 内。
     5     int m = (l+r+1)/2;    // 这里涉及到应该上取整还是下取整的问题
     6     if( x < v[m] )
     7         r = m-1;    // x 在区间 [l,m-1] 内
     8     else
     9         l = m;        // x 在区间 [r,m] 内
    10 }
    11 
    12 //作者:匿名用户
    13 //链接:https://www.zhihu.com/question/36132386/answer/66207613
    14 //来源:知乎
    15 //著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    BS

    ==========================================================

     11讲的是
    给你n个数字作为高度,顺序的排在坐标轴上,取其中两个高度建墙,问你最多能储存多少水。

    我的思路
    一开始以为是DP之类的,不过感觉没什么规律,后来想到一个贪心的策略,就是从两边往中间逼近,哪边低哪边前进,证明也不难,如果l比较低,但是l不前进,r不管怎么前进,都不可能取到比当时更大的面积了,r作为一个端点限制了水面的高度。

     1 class Solution {
     2 public:
     3     int maxArea(vector<int>& height) {
     4         int l=0,r=height.size()-1,ans=0;
     5         while(l!=r){
     6             ans=max(ans,(r-l)*min(height[l],height[r]));
     7             if(height[l]<height[r])l++;
     8             else r--;
     9         }
    10         return ans;
    11     }
    12 };
    11

    ==========================================================

    15讲的是
    给你n个数,输出所有任选3个之和为0的组合。

    我的思路
    hash记录每个元素,然后双重循环枚举两个元素,判断第三个元素是否存在,O(n^2)
    为了避免重复计数,先O(nlogn)排序一下好了。。。

     1 class Solution {
     2 public:
     3     vector<vector<int> > threeSum(vector<int>& nums) {
     4         int n=nums.size();
     5         vector<vector<int> > aim;
     6         sort(nums.begin(),nums.end(),less<int>());
     7         unordered_map<int,int> m;
     8         for(int i=0;i<n;i++)
     9             m[nums[i]]=i;
    10         for(int i=0;i<n;i++){
    11             for(int j=i+1;j<n;j++){
    12                 if(m.find(-(nums[i]+nums[j]))!=m.end()){
    13                     if(m[-(nums[i]+nums[j])]>j){
    14                         vector<int> temp;
    15                         temp.push_back(nums[i]);
    16                         temp.push_back(nums[j]);
    17                         temp.push_back(-(nums[i]+nums[j]));
    18                         aim.push_back(temp);
    19                     }
    20                 }
    21                 while(j<n-1&&nums[j+1]==nums[j])j++;
    22             }
    23             while(i<n-1&&nums[i+1]==nums[i])i++;
    24         }
    25         return aim;
    26     }
    27 };
    View Code

     发现自己只击败了4%。。。。。。

  • 相关阅读:
    HTML 相关面试题
    h5简写时钟效果
    软件工程结对作业二
    软件工程结对作业一
    软件工程第三次作业
    软件工程第二次作业
    软件工程第一次作业
    软件工程第四次作业
    软件工程第三次作业
    2019软件工程第二次作业(VS2017中对C++的单元测试)
  • 原文地址:https://www.cnblogs.com/xuwangzihao/p/6541829.html
Copyright © 2020-2023  润新知