【发布时间】:2019-06-05 08:12:26
【问题描述】:
作为申请过程的一部分,我被设置了一项家庭作业挑战(顺便说一句,我被拒绝了;否则我不会写这个),我将在其中实现以下功能:
// Store a collection of integers
class IntegerCollection {
public:
// Insert one entry with value x
void Insert(int x);
// Erase one entry with value x, if one exists
void Erase(int x);
// Erase all entries, x, from <= x < to
void Erase(int from, int to);
// Return the count of all entries, x, from <= x < to
size_t Count(int from, int to) const;
然后对这些功能进行了一系列测试,其中大部分都是微不足道的。最后的测试是真正的挑战,因为它执行了 500,000 次单次插入、500,000 次计数调用和 500,000 次单次删除。
IntegerCollection 的成员变量没有指定,所以我必须选择如何存储整数。自然地,STL 容器似乎是一个好主意,保持它的分类似乎是一种保持效率的简单方法。
这是我使用vector 的四个函数的代码:
// Previous bit of code shown goes here
private:
std::vector<int> integerCollection;
};
void IntegerCollection::Insert(int x) {
/* using lower_bound to find the right place for x to be inserted
keeps the vector sorted and makes life much easier */
auto it = std::lower_bound(integerCollection.begin(), integerCollection.end(), x);
integerCollection.insert(it, x);
}
void IntegerCollection::Erase(int x) {
// find the location of the first element containing x and delete if it exists
auto it = std::find(integerCollection.begin(), integerCollection.end(), x);
if (it != integerCollection.end()) {
integerCollection.erase(it);
}
}
void IntegerCollection::Erase(int from, int to) {
if (integerCollection.empty()) return;
// lower_bound points to the first element of integerCollection >= from/to
auto fromBound = std::lower_bound(integerCollection.begin(), integerCollection.end(), from);
auto toBound = std::lower_bound(integerCollection.begin(), integerCollection.end(), to);
/* std::vector::erase deletes entries between the two pointers
fromBound (included) and toBound (not indcluded) */
integerCollection.erase(fromBound, toBound);
}
size_t IntegerCollection::Count(int from, int to) const {
if (integerCollection.empty()) return 0;
int count = 0;
// lower_bound points to the first element of integerCollection >= from/to
auto fromBound = std::lower_bound(integerCollection.begin(), integerCollection.end(), from);
auto toBound = std::lower_bound(integerCollection.begin(), integerCollection.end(), to);
// increment pointer until fromBound == toBound (we don't count elements of value = to)
while (fromBound != toBound) {
++count; ++fromBound;
}
return count;
}
公司回复我说他们不会继续前进,因为我选择的容器意味着运行时复杂性太高。我还尝试使用list 和deque 并比较了运行时间。正如我所料,我发现list 很糟糕,而vector 比deque 更胜一筹。所以就我而言,我已经充分利用了糟糕的情况,但显然不是!
我想知道在这种情况下使用的正确容器是什么? deque 只有在我可以保证插入或删除到容器的末端并且 list 占用内存时才有意义。还有什么我完全忽略的吗?
【问题讨论】:
-
您似乎希望对您的容器进行排序,对吗?那么
std::multiset(或者如果你不想重复的话,或者简单的std::set)怎么样? -
如果您知道最小和最大可能输入的范围,您可以使用存储桶执行此操作并实现 O[1] 插入、删除等。占用的内存与 [max-min] 成正比。
-
void Erase(int from, int to);表示排序,如果没有枚举,无序集合不会给你。如果您的集合允许重复,std::multiset是一个可行的选择,否则std::set是候选。 -
Bjarne 说“使用向量”,所以如果公司拒绝了您的代码 - 向他们展示这个(非常有教育意义的)视频:youtube.com/watch?v=YQs6IC-vgmo
-
std::vector在性能上胜过其他容器,可以处理惊人的大数据集,这仅仅是因为它在缓存位置方面无与伦比。直观地说,std::set的某些变体会是一个更安全的选择,但值得衡量的是它开始击败std::vector的数据集大小。当您达到 500,000 个元素大小时,std::set很可能赶不上std::vector。但如果他们关心性能,他们就会指定/提供硬件。似乎他们只是对时间复杂度的顺序感兴趣。
标签: c++ containers