集合求交集中,vector, set ,unordered_set,的对比试验

集合求交集中,vector, set ,unordered_set,的对比试验

introduction: 对集合进行求交集,是我们常用的操作。STL 也给我们提供了对于常规容器(vector, set等)的intersection 操作

sort(a.begin(), a.end());
sort(b.begin(), b.end());
vector<int> res;
res.reserve(a.size() + b.size());
set_intersection(a.begin(), a.end(), b.begin() , b.end(), back_insert_iterator<vector<int>>(res));

用法如上所示,这里的 back_insert_iterator 仅仅对于具有push_back()操作的容器有效。

而在实际场合中,我们使用的数据容器多种多样,有具备随机访问vector、 有基于红黑树set,也有基于hashSetunordered_set。下面将对这三中容器进行定量试验,测试在不同数据数量范围,不同容器的选择对求交集的影响。

Intersection Method for unordered_set

STL 是没有提供对于unordered_set 的求交集操作的。

因此在这里,我使用了一种简单的实现,时间复杂度为O(n);

vector<int> intertsect_hashset(vector<int> _a, vector<int> _b)
{
    unordered_set<int> a(_a.begin(), _a.end());
    unordered_set<int> b(_b.begin(), _b.end());
    vector<int> res;
    res.reserve(a.size() + b.size());
    for (int n : a){
        if (b.find(n) != b.end()){
            res.push_back(n);
            b.erase(n);
        }
    }
    return res;
}

Interseciton Method for vector & set

对于set_intersection函数来说,传入的迭代器对应的容器应该是有序的,内部实现原理类似如下:

template <typename InIt1, typename InIt2, typename OutIt>
OutIt unordered_set_intersection(InIt1 b1, InIt1 e1, InIt2 b2, InIt2 e2, OutIt out) {
    while (!(b1 == e1)) {
        if (!(std::find(b2, e2, *b1) == e2)) {
            *out = *b1;
            ++out;
        }

        ++b1;
    }

    return out;
}

于是在实现代码中,对于vector数组,需要进行额外的排序,当然,这也应该被统计在总的时间内。

Trials

实验的环境为 Intel Core i7-9700 @ 8x 4.7GHzUbuntu 20.04Clang 6.0.0O3优化

_vector_set_hashset
100003.36.34.3
1000000482.11707.3610.2
10000000060857.5297929.7103751.4

可以明显发现,在不同数据数量范围,均呈现线性增长。

对于基于红黑树实现的set容器,由于构造set是O(NlogN),求交为O(N)所以总体时间复杂度为O(NlogN)

对于vector, 排序时间复杂度为O(NlogN),求交为O(N),因此时间复杂度也为O(NlogN)

对于hashset,虽然其构造理论时间复杂度为O(1),但是发生冲突的时候会衰减至O(N),理想状态下,总时间复杂度为O(N).

对比试验可以发现,实际运行中,使用vector 的随机访问器的速度最快,而set的速度最慢,这是因为set不能随机访问,且访问的时间复杂度为O(logN). 相比之下, hashSet 的时间复杂度并不能达到最优,而是慢于vector, 如果改进hash函数,也许能够达到更好的效果。

Code

#include "timeTest.h"
#include <bits/stdc++.h>
#include <iostream>
using namespace std;

vector<int> intersect_vector(vector<int> a, vector<int> b)
{
    sort(a.begin(), a.end());
    sort(b.begin(), b.end());
    vector<int> res;
    res.reserve(a.size() + b.size());
    set_intersection(a.begin(), a.end(), b.begin() , b.end(), back_insert_iterator<vector<int>>(res));
    return res;
}


vector<int> intersect_set(vector<int> _a, vector<int> _b)
{
    set<int> a(_a.begin(), _a.end());
    set<int> b(_b.begin(), _b.end());
    vector<int> res;
    res.reserve(a.size() + b.size());
    set_intersection(a.begin(), a.end(), b.begin() , b.end(), back_insert_iterator<vector<int>>(res));
    return res;

}
vector<int> intertsect_hashset(vector<int> _a, vector<int> _b)
{
    unordered_set<int> a(_a.begin(), _a.end());
    unordered_set<int> b(_b.begin(), _b.end());
    vector<int> res;
    res.reserve(a.size() + b.size());
    for (int n : a){
        if (b.find(n) != b.end()){
            res.push_back(n);
            b.erase(n);
        }
    }
    return res;
}

int main()
{
    int sz1 = 100000000;
    vector<int> a, b;
    for (int i = 0; i < sz1 * 1.5; ++i)
    {
        if (i < sz1)
        {
            a.push_back(i);
        }
        else if (i > sz1 / 2)
        {
            b.push_back(i);
        }
    }
    random_shuffle(a.begin(), a.end());
    random_shuffle(b.begin(), b.end());
    vector<int> res1, res2, res3;
   {
        TestTimeVar
    TestTimeTic
    res1 = intersect_vector(a, b);
    TestTimeToc("intersect_vector")
   }
   {
        TestTimeVar
    TestTimeTic
    res2 = intersect_set(a, b);
    TestTimeToc("intersect_set")
   }
   {
        TestTimeVar
    TestTimeTic
    res3 = intertsect_hashset(a, b);
    TestTimeToc("intertsect_hashset")
   }
    return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值