zoukankan      html  css  js  c++  java
  • k-th smallest 问题总结

    k-th smallest/biggest 问题大约有这几道:

    373. Find K Pairs with Smallest Sums 从两个list里各取一个数求和,求所有可能的sum里第k小的
    378. Kth Smallest Element in a Sorted Matrix 给一个每一横排/每一纵列都有序的matrix,求其中第k小的元素
    668. Kth Smallest Number in Multiplication Table 给一个乘法表(类似378的matrix),求其中第k小的元素
    719. Find K-th Smallest Pair Distance 从一个list里取两个元素求和,求所有可能的sum里第k小的
    786. K-th Smallest Prime Fraction 从一个list里取两个元素相除,求所有可能的sum里第k小的

    可以看出,其实373 719 786是同一题,378 668是同一题

    这种题大致有两种思路:

    1. 用heap

    关于堆的介绍网上有一大把......这里只划重点

    堆是一种是一种特殊的完全二叉树,其中每个根节点一定比它的左、右儿子节点都大/小(大根堆or小根堆)。每次增/删元素时,需要移动二叉树节点来保持这一性质。

    时间复杂度:

    从一个长度为N的乱序数组建堆  O(NlogN)

    向长度为N的堆插入/删除一个元素  O(logN)

    实现:

    C++里可以用priority_queue<int> pq。常用操作有push(), pop(), top(), empty(), size()

    Python 里可以用import heapq,然后heapq.heapify(list_a)就在原list上建好堆啦,a[0]就是堆顶。不过它只能建大根堆  

     1 import heapq
     2 
     3 #heapq implemented min heap only. Thus we save -nums[i] into the heap
     4 class Solution:
     5     def maxSlidingWindow(self, nums, k):
     6         """
     7         :type nums: List[int]
     8         :type k: int
     9         :rtype: List[int]
    10         """
    11         a=[]
    12         res=[]
    13         for i in range(len(nums)):
    14             a.append(-nums[i])
    15             if(i>=k):
    16                 a.remove(-nums[i-k])
    17             heapq.heapify(a)
    18             if(i>=k-1):
    19                 res.append(-a[0])
    20         return res
    21 
    22 sl=Solution()
    23 nn=[1,3,-1,-3,5,3,6,7]
    24 rr=sl.maxSlidingWindow(nn,3)
    25 print(rr)
    Python的implementation实例(LeetCode 239的弱鸡解法)

    解题思路:

    把所有要比较的东西都塞到heap里,最后做k次取堆顶--弹出堆顶的操作

    时间复杂度是 O(N^2 * log(N^2)) 有些medium题能过,但hard题肯定不行

    根据题目具体情况,一开始不一定所有的东西都要塞进heap

    例题:

    373. Find K Pairs with Smallest Sums

    反正过是能过...它的加强版就是719 786啦

    priority_queue默认是大根堆,要是想要小根堆需要自定义比较函数。这里我直接重载了运算符(因为是pair嘛),推荐一下这种写法(骄傲

     1 class Solution {
     2 public:
     3     struct Node
     4     {
     5         pair<int, int> val;
     6         Node(int x,int y) {val=pair<int, int>(x,y);}
     7         bool operator <(Node a) const {return(val.first+val.second < a.val.first+a.val.second);}
     8         bool operator >(Node a) const {return(val.first+val.second > a.val.first+a.val.second);}
     9     };
    10 
    11     vector<pair<int, int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) 
    12     {
    13         vector< pair<int, int> > ans;
    14         int lx=nums1.size(),ly=nums2.size();
    15         if(lx==0 || ly==0) return ans;
    16         k=min(k,lx*ly);
    17         priority_queue<Node, vector<Node>, greater<Node> > pq;
    18         for(int i=0;i<min(lx,k);i++)
    19             for(int j=0;j<min(ly,k);j++)
    20             {
    21                 pq.push(Node(nums1[i], nums2[j]));
    22             }
    23 
    24         while(k)
    25         {
    26             ans.push_back(pq.top().val);
    27             pq.pop();
    28             k--;
    29         }
    30         return ans;
    31 
    32     }
    33 };
    View Code

    378. Kth Smallest Element in a Sorted Matrix

    赤果果的heap。又不是不能用......

     1 //O(N*N*log(N*N))
     2 class Solution {
     3 public:
     4     int kthSmallest(vector<vector<int>>& matrix, int k)
     5     {
     6         int dx=matrix.size(),dy=matrix[0].size();
     7         priority_queue<int> pq;
     8         for(int i=0;i<dx;i++)
     9             for(int j=0;j<dy;j++)
    10                 pq.push(matrix[i][j]);
    11 
    12         int ans;
    13         k=dx*dy-k+1;
    14         while(k)
    15         {
    16             ans=pq.top();
    17             pq.pop();
    18             k--;
    19         }
    20         return ans;
    21     }
    22 };
    View Code

    另外三题heap都是妥妥超时的噢......hard就是hard

    2. Binary Search

    二分查找模板了解一下:

    under_construction

    时间复杂度:

    O(log(N))

    解题思路:

    用二分查找(有left, right, mid)来枚举第k小的值,然后看原数组里当前有多少是小于mid的,多了就减小right,少了就增大left。直到查找结束,此时left就是第k小值啦

    实现时先对原数组按行/列排个序,或者题目本身就是保证有序的

    时间复杂度是 O(log(X)*N) ,其中log(X)是二分查找的开销(X=right-left),N是在原数组里找小于mid的数的开销(二维数组的话最坏情况可能会N^2,不过一般也能过)

    例题:

    378. Kth Smallest Element in a Sorted Matrix

    就按上面的方法实现哒

    在原数组里count时用了一个函数upper_bound(matrix[i].begin(), matrix[i].end(), mid),返回matrix[i]这个从小到大有序的vector里小于等于mid的第一个数的指针

    这玩意底层是二分查找,时间复杂度是logN呢。

     1 //O(N*log(N)*log(max-min))
     2 //Binary Search
     3 class Solution {
     4 public:
     5     int kthSmallest(vector<vector<int>>& matrix, int k)
     6     {
     7         int dx=matrix.size(),dy=matrix[0].size();
     8         int ml=matrix[0][0], mr=matrix[dx-1][dy-1];
     9         int mid,cnt;
    10         while(ml<mr)
    11         {
    12             mid=(ml+mr)/2;
    13             cnt=0;
    14             for(int i=0;i<dx;i++)
    15             {
    16                 cnt+=upper_bound(matrix[i].begin(), matrix[i].end(), mid)-matrix[i].begin();
    17             }
    18             if(cnt>=k)
    19                 mr=mid;
    20             else if(cnt<k)
    21                 ml=mid+1;
    22         }
    23         return ml;
    24     }
    25 };
    View Code

    668. Kth Smallest Number in Multiplication Table

    上面的改巴改巴就行了......

     1 //Binary Search
     2 class Solution {
     3 public:
     4     //int kthSmallest(vector<vector<int>>& matrix, int k)
     5     int findKthNumber(int m, int n, int k) 
     6     {
     7         int ml=1, mr=m*n;
     8         int mid,cnt;
     9         while(ml<mr)
    10         {
    11             mid=(ml+mr)/2;
    12             cnt=0;
    13             for(int i=1;i<=m;i++)
    14             {
    15                 cnt+=min(mid/i,n);
    16             }
    17             if(cnt>=k)
    18                 mr=mid;
    19             else if(cnt<k)
    20                 ml=mid+1;
    21         }
    22         return ml;
    23     }
    24 };
    View Code

    719. Find K-th Smallest Pair Distance

    一样的套路噢...就是从一个matrix变成了两个list

     1 // Binary Search
     2 class Solution {
     3 public:
     4     int smallestDistancePair(vector<int>& nums, int k)
     5     {
     6         int n=nums.size();
     7         sort(nums.begin(), nums.end());
     8         int ml=nums[1]-nums[0],mr=nums[n-1]-nums[0],mid=0,cnt=0;
     9         for(int i=0;i<n-1;i++) ml=min(ml,nums[i+1]-nums[i]);
    10         while(ml<mr)
    11         {
    12             mid=(ml+mr)/2;
    13             cnt=0;
    14             for(int i=0;i<n;i++)
    15             {
    16                 int j=i+1;
    17                 while(j<n && nums[j]-nums[i]<=mid) j++;
    18                 cnt+=(j-i-1);
    19             }
    20             if(cnt<k)
    21                 ml=mid+1;
    22             else
    23                 mr=mid;
    24         }
    25         return ml;
    26     }
    27 };
    View Code

    这题其实还有个骚操作:因为list值的范围比较小,所以可以用counting sort:先用O(N^2)的时间把所有可能的pair和记下来,然后在counting sort的数组里从小到大数数即可

    不过感觉这方法通用性应该不强...

     1 //counting sort: O(N^2)
     2 class Solution {
     3 public:
     4     int smallestDistancePair(vector<int>& nums, int k) 
     5     {
     6         int val[1000010];
     7         memset(val,0,sizeof(val));
     8         int nl=nums.size();
     9         for(int i=0;i<nl;i++)
    10             for(int j=i+1;j<nl;j++)
    11             {
    12                 //cout<<abs(nums[j]-nums[i])<<endl;
    13                 val[abs(nums[j]-nums[i])]++;
    14             }
    15 
    16         int ans=0;
    17         while(k)
    18         {
    19             if(val[ans]>0) 
    20             {
    21                 //cout<<k<<" "<<ans<<endl;
    22                 val[ans]--;
    23                 k--;
    24             }
    25             else ans++;
    26         }
    27         return ans;
    28     }
    29 };
    View Code

    786. K-th Smallest Prime Fraction

    这题坑多一点,因为是分数,涉及到浮点数嘛

    涉及到浮点数就不能直接用=比较辣,要用r-l<eps。

    以及注意两个int做除法,要先强制转换成double

     1 //Binary Search, AC
     2 class Solution {
     3 public:
     4     vector<int> kthSmallestPrimeFraction(vector<int>& A, int k) 
     5     {
     6         vector<int> ans;
     7         int al=A.size();
     8         sort(A.begin(), A.end());
     9         double ml=0.0, mr=1.0, md=0.5;
    10         int dx,dy,cnt;
    11         double eps=1e-10;
    12         while(mr-ml>eps)
    13         {
    14             md=(ml+mr)/2;
    15             cnt=0;
    16             for(int i=0;i<al;i++)
    17             {
    18                 int j=al-1;
    19                 while(j>i && (double)A[i]/(double)A[j]<md) j--;
    20                 cnt+=(al-1-j);
    21             }
    22             cout<<ml<<","<<mr<<"=="<<md<<" "<<cnt<<endl;
    23             if(cnt<k)
    24                 ml=md;
    25             else
    26                 mr=md;
    27             
    28         }
    29         for(int i=0;i<al;i++)
    30             for(int j=i+1;j<al;j++)
    31                 if(abs((double)A[i]/(double)A[j] - ml)<eps)
    32                 {
    33                     dx=A[i]; dy=A[j];
    34                     ans.push_back(dx); ans.push_back(dy);
    35                     return(ans);
    36                 }
    37         return ans;
    38     }
    39 };
    View Code
  • 相关阅读:
    CSS3 Flex布局整理(二)-容器属性
    CSS3 Flex布局整理(一)
    .Net Core中文编码问题整理
    Karma:1. 集成 Karma 和 Jasmine 进行单元测试
    Protractor
    翻译: TypeScript 1.8 Beta 发布
    webpack 插件: html-webpack-plugin
    How I came to find Linux
    翻译:Angular 2
    在 Angular 中实现搜索关键字高亮
  • 原文地址:https://www.cnblogs.com/pdev/p/9435664.html
Copyright © 2011-2022 走看看