用c++中的唯一条目排队
Queue with unique entries in c++
我需要在C或C++中实现一个包含唯一条目(无重复项)的队列。我正在考虑维护队列中已经可用的元素的引用,但这似乎效率很低。
请告诉我你解决这个问题的建议。
如何使用辅助数据结构来跟踪唯一性:
std::queue<Foo> q;
std::set<std::reference_wrapper<Foo>> s;
// to add:
void add(Foo const & x)
{
if (s.find(x) == s.end())
{
q.push_back(x);
s.insert(std::ref(q.back())); // or "s.emplace(q.back());"
}
}
或者,或者,颠倒队列和集合的角色:
std::set<Foo> s;
std::queue<std::reference_wrapper<Foo>> q;
void add(Foo const & x)
{
auto p = s.insert(x); // std::pair<std::set<Foo>::iterator, bool>
if (s.second)
{
q.push_back(std::ref(*s.first)); // or "q.emplace_back(*s.first);"
}
}
排队:
- 使用std::set来维护您的一组唯一元素
- 将能够添加到std::set的任何元素添加到std::queue
出队:
- 从std::queue和std::set中删除元素
std::queue
是一个容器适配器,使用的底层Container
成员相对较少。您可以轻松地实现一个同时包含reference_wrapper<T>
的unordered_map
和deque<T>
的自定义容器。它至少需要成员front
和push_back
。当调用容器的push_back
时,请检查该hash_map
内部,并相应地拒绝(可能是throw)。举一个完整的例子:
#include <iostream>
#include <set>
#include <deque>
#include <queue>
#include <unordered_set>
#include <functional>
namespace std {
// partial specialization for reference_wrapper
// is this really necessary?
template<typename T>
class hash<std::reference_wrapper<T>> {
public:
std::size_t operator()(std::reference_wrapper<T> x) const
{ return std::hash<T>()(x.get()); }
};
}
template <typename T>
class my_container {
// important: this really needs to be a deque and only front
// insertion/deletion is allowed to not get dangling references
typedef std::deque<T> storage;
typedef std::reference_wrapper<const T> c_ref_w;
typedef std::reference_wrapper<T> ref_w;
public:
typedef typename storage::value_type value_type;
typedef typename storage::reference reference;
typedef typename storage::const_reference const_reference;
typedef typename storage::size_type size_type;
// no move semantics
void push_back(const T& t) {
auto it = lookup_.find(std::cref(t));
if(it != end(lookup_)) {
// is already inserted report error
return;
}
store_.push_back(t);
// this is important to not have dangling references
lookup_.insert(store_.back());
}
// trivial functions
bool empty() const { return store_.empty(); }
const T& front() const { return store_.front(); }
T& front() { return store_.front(); }
void pop_front() { lookup_.erase(store_.front()); store_.pop_front(); }
private:
// look-up mechanism
std::unordered_set<c_ref_w> lookup_;
// underlying storage
storage store_;
};
int main()
{
// reference wrapper for int ends up being silly
// but good for larger objects
std::queue<int, my_container<int>> q;
q.push(2);
q.push(3);
q.push(2);
q.push(4);
while(!q.empty()) {
std::cout << q.front() << std::endl;
q.pop();
}
return 0;
}
EDIT:您将希望使my_container
成为一个合适的容器模型(可能还有分配器),但这是另一个完整的问题。感谢Christian Rau指出错误。
有一点非常重要,您在问题中没有提到,那就是您的项目队列是排序还是有某种顺序(称为优先级队列),或未排序 如果您的队列未排序,那么在队列之外维护额外的数据结构将更有效。使用以某种方式排序的第二个结构来维护队列的内容,可以检查队列中是否已经存在项目,或者是否比扫描队列本身快得多。添加到未排序队列的末尾需要恒定的时间,并且可以非常有效地完成。 如果您的队列必须排序,那么将项目放入队列需要您知道项目在队列中的位置,这需要扫描队列。一旦你知道了一个项目的位置,你就知道这个项目是否是重复的,因为如果它是重复的话,那么一个项目就会已经存在于队列中的那个位置。在这种情况下,所有工作都可以在队列本身上以最佳方式执行,并且不需要维护任何辅助数据结构。 数据结构的选择取决于您。但是,对于(1),辅助数据结构应而不是为任何类型的列表或数组,否则扫描辅助索引的效率将不如扫描原始队列本身的效率高。
- 如何在 c++ 中确定一条指令(以字节为单位)在哪里结束,另一条指令从哪里开始?
- 如何在 Win32 中用一条线连接 2 个文本框?
- C++ 如何在同一行中和在一条线上?
- 如何在SDL2窗口上使用Cairo绘制一条白色直线
- 在Opengl中绘制一条不显示C++的线
- 为什么当 while 循环中的 if 条件变为 false 时,我的函数不输出最后一条语句?
- x86 - 为什么编译器在下一条指令中插入看似毫无意义的JMP?
- CMake似乎忽略了一条线
- 在C 中使用Legacy OpenGL(即时模式)绘制一条厚线
- 在图像标签上画一条线并计算距离
- switch 语句结束和下一条语句之间的时间是否具有确定性(与路径无关)
- 如何在OMNET++中收到来自前一个模块的所有消息后将一条消息发送到下一个模块
- 一条线和两行声明之间的差异
- C++ OpenCV:检测两条线而不是一条线(Canny & findContours)
- 如何在C++中将边列表转换为邻接矩阵,其中包含连接到顶点的边数而不是一条边?
- 为什么 fstream 打印出文件的最后一条记录
- 如何在另一条消息(M1)之前调用带有消息(M2)的lambda函数,但输出要"M2 M1"?
- 如何在子小部件而不是父窗口小部件上绘制一条线
- 在C++中,文件中的下一条记录被非法更新
- MFC:在 CRichEditCtrl 中突出显示一条线一段时间,然后恢复