在数组中找到第k个最大元素,两个不同的priority_queue解决方案时间复杂度

时间:2015-07-18 06:59:02

标签: algorithm sorting heap priority-queue

我对使用priority_queue的两个解决方案感兴趣。虽然它们都使用priority_queue,但我认为它们具有不同的时间复杂度。

解决方案1:

int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int> pq(nums.begin(), nums.end()); //O(N)
        for (int i = 0; i < k - 1; i++) //O(k*log(k))
            pq.pop(); 
        return pq.top();
}

时间复杂度:O(N)+ O(k * log(k))

编辑:对不起,应该是O(N)+ O(k * log(N))感谢指出!

解决方案2:

int findKthLargest(vector<int>& nums, int k) {
    priority_queue<int, vector<int>, greater<int>> p;
    int i = 0;
    while(p.size()<k) {

        p.push(nums[i++]);
    }

    for(; i<nums.size(); i++) {

        if(p.top()<nums[i]){
            p.pop();
            p.push(nums[i]);
        }

    }

    return p.top();
}

时间复杂度:O(N * log(k))

所以在大多数情况下,第一种解决方案比第二种解决方案好得多?

2 个答案:

答案 0 :(得分:2)

在第一种情况下,复杂度为O(n)+ klog(n)而不是O(n)+ klog(k),因为堆中有n个元素。在最坏的情况下,k可以和n一样大,因此对于无界数据,O(nlog(n))是正确的最坏情况复杂度。

在第二种情况下,优先级队列中至少有k个项,因此复杂度为O(nlog(k)),对于无界数据,k也可以与n一样大,因此它是O(nlog) (N))。

对于较小的k,第二个代码将运行得更快,但随着k变大,第一个代码变得更快。我做了一些实验,结果如下:

k=1000
Code 1 time:0.123662
998906057
Code 2 time:0.03287
998906057
========
k=11000
Code 1 time:0.137448
988159929
Code 2 time:0.0872
988159929
========
k=21000
Code 1 time:0.152471
977547704
Code 2 time:0.131074
977547704
========
k=31000
Code 1 time:0.168929
966815132
Code 2 time:0.168899
966815132
========
k=41000
Code 1 time:0.185737
956136410
Code 2 time:0.205008
956136410
========
k=51000
Code 1 time:0.202973
945313516
Code 2 time:0.236578
945313516
========
k=61000
Code 1 time:0.216686
934315450
Code 2 time:0.27039
934315450
========
k=71000
Code 1 time:0.231253
923596252
Code 2 time:0.293189
923596252
========
k=81000
Code 1 time:0.246896
912964978
Code 2 time:0.321346
912964978
========
k=91000
Code 1 time:0.263312
902191629
Code 2 time:0.343613
902191629
========

我修改了第二个代码,使其类似于code1:

int findKthLargest2(vector<int>& nums, int k) {
    double st=clock();
    priority_queue<int, vector<int>, greater<int>> p(nums.begin(), nums.begin()+k);

    int i=k;
    for(; i<nums.size(); i++) {
        if(p.top()<nums[i]){
            p.pop();
            p.push(nums[i]);
        }

    }
    cerr<<"Code 2 time:"<<(clock()-st)/CLOCKS_PER_SEC<<endl;
    return p.top();
}
int findKthLargest1(vector<int>& nums, int k) {
        double st=clock();
        priority_queue<int> pq(nums.begin(), nums.end()); //O(N)
        for (int i = 0; i < k - 1; i++) //O(k*log(k))
            pq.pop(); 

        cerr<<"Code 1 time:"<<(clock()-st)/CLOCKS_PER_SEC<<endl;
        return pq.top();
}

int main() {   

 READ("in");
 vector<int>v;
 int n;
 cin>>n;
 repl(i,n)
 {
     int x;
     scanf("%d",&x);
     v.pb(x);
 }

 for(int k=1000;k<=100000;k+=10000)
 {
     cout<<"k="<<k<<endl;
    cout<<findKthLargest1(v,k)<<endl;
    cout<<findKthLargest2(v,k)<<endl;
    puts("========");
  }
}

我使用了0到10 ^ 9之间的1000000个随机整数作为数据集,由C ++ rand()函数生成。

答案 1 :(得分:1)

好吧,首先是O(N)+ O(k * log(N)),因为pop是O(log(N))

int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int> pq(nums.begin(), nums.end()); //O(N)
        for (int i = 0; i < k - 1; i++) //O(k*log(N))
            pq.pop(); // this line is O(log(N))
        return pq.top();
}

在大多数情况下,它仍然优于第二种。