我有一个std::map
个对象。键是实体ID(整数)并且它们的2D位置(向量)的值。目的是确定哪些实体在
同样的立场。
ID Position
1 {2,3}
5 {6,2}
12 {2,3}
54 {4,4}
92 {6,2}
我需要获得由键组成的向量向量,这些向量具有相等的值。
以上示例输入数据的输出:{1,12},{5,92}
我知道我可以将2D位置复制到向量矢量并循环第一级向量以找到相等的第二级向量的索引。然后通过索引选择向量并再次循环查找相应的键来返回查找键。
请为此建议更清洁的方法。
答案 0 :(得分:6)
std::map
的要点是提供有效的键值映射。你需要的是一个额外的关键映射值 - 可以通过多种方式实现:
从std::map
到Position
的额外std::vector<ID>
。
使用某种空间分区数据结构(例如,四叉树,空间哈希,网格),可以根据位置查找实体。< / p>
使用双向多地图,例如boost::bimap
。这将允许您对值集合进行双向映射,而无需使用多个数据结构。
&#34;我该如何选择?&#34;
这取决于您的优先事项。如果您想获得最佳性能,您应该尝试所有方法(可能使用某种模板化的包装器)和配置文件。如果您想要优雅/清洁,boost::bimap
似乎是最合适的解决方案。
答案 1 :(得分:1)
您可以将地图中的数据放入std::mutlimap
,其中Position
为关键字,ID
为值。
作为旁注,我想知道std::pair
是否可能比2d点的矢量更好。
答案 2 :(得分:1)
您需要提供反向映射。有很多方法可以做到这一点,包括multimap
,但如果你的映射在创建之后没有被修改,那么这是一种简单的方法,可以迭代地图并构建反向映射。在反向映射中,您映射值 - &gt;钥匙清单。
以下代码使用std::unordered_map
将std::pair<int, int>
(原始地图中的值)映射到std::vector<int>
(原始地图中的键列表)。反向地图的构建简单明了:
std::unordered_map<Point, std::vector<int>, hash> r;
for (const auto& item : m) {
r[item.second].push_back(item.first);
}
(请参阅hash
的定义的完整示例。)
无需担心密钥是否存在;当您尝试使用r[key]
表示法访问该键时,它将被创建(并且id的向量将初始化为空向量)。
此解决方案旨在简化;如果您需要这样做而不关心性能,内存使用或使用像Boost这样的第三方库,这是一个可行的解决方案。
如果你关心这些事情,或者你在两个方向进行查找时修改地图,你应该探索其他选择。
#include <iostream>
#include <map>
#include <unordered_map>
#include <vector>
// Define a point type. Use pair<int, int> for simplicity.
using Point = std::pair<int, int>;
// Define a hash function for our point type:
struct hash {
std::size_t operator()(const Point& p) const
{
std::size_t h1 = std::hash<int>{}(p.first);
std::size_t h2 = std::hash<int>{}(p.second);
return h1 ^ (h2 << 1);
}
};
int main() {
// The original forward mapping:
std::map<int, Point> m = {
{1, {2, 3}},
{5, {6, 2}},
{12, {2, 3}},
{54, {4, 4}},
{92, {6, 2}}
};
// Build reverse mapping:
std::unordered_map<Point, std::vector<int>, hash> r;
for (const auto& item : m) {
r[item.second].push_back(item.first);
}
// DEMO: Show all indices for {6, 2}:
Point val1 = {6, 2};
for (const auto& id : r[val1]) {
std::cout << id << " ";
}
std::cout << "\n";
// DEMO: Show all indices for {2, 3}:
Point val2 = {2, 3};
for (const auto& id : r[val2]) {
std::cout << id << " ";
}
std::cout << "\n";
}
答案 3 :(得分:1)
This answer似乎是最好的,但无论如何我都会提供我的代码。
鉴于
#include <iostream>
#include <map>
#include <vector>
// Some definiton of Vector2D
struct Vector2D { int x; int y; };
// and some definition of operator< on Vector2D
bool operator<(Vector2D const & a, Vector2D const & b) noexcept {
if (a.x < b.x) return true;
if (a.x > b.x) return false;
return a.y < b.y;
}
怎么样:
template <typename M>
auto calculate(M const & inputMap) -> std::vector<std::vector<typename M::key_type> > {
std::map<typename M::mapped_type,
std::vector<typename M::key_type> > resultMap;
for (auto const & vp : inputMap)
resultMap[vp.second].push_back(vp.first);
std::vector<std::vector<typename M::key_type> > result;
for (auto & vp: resultMap)
if (vp.second.size() > 1)
result.emplace_back(std::move(vp.second));
return result;
}
以下是测试方法:
int main() {
std::map<int, Vector2D> input{
{1, Vector2D{2,3}},
{5, Vector2D{6,2}},
{13, Vector2D{2,3}},
{54, Vector2D{4,4}},
{92, Vector2D{6,2}}
};
auto const result = calculate(input);
// Ugly print
std::cout << '{';
static auto const maybePrintComma =
[](bool & print) {
if (print) {
std::cout << ", ";
} else {
print = true;
}
};
bool comma = false;
for (auto const & v: result) {
maybePrintComma(comma);
std::cout << '{';
bool comma2 = false;
for (auto const & v2: v) {
maybePrintComma(comma2);
std::cout << v2;
}
std::cout << '}';
}
std::cout << '}' << std::endl;
}