已知整数值的快速优雅的单向映射

时间:2010-01-26 09:37:06

标签: c++

我必须将一组已知整数映射到另一组已知整数,1对1关系,所有预定义等等。所以,假设我有这样的东西(c ++,简化,但你会得到这个想法):

struct s { int a; int b; };

s theMap[] = { {2, 5}, {79, 12958 } };

现在给出一个输入整数,比如79,我需要从Map中找到相应的结果(显然是12958)。任何好的和快速的方法,而不是你的普通循环?其他数据结构建议也是受欢迎的,但地图应该易于手工编写。

两组中的值都在0到2 ^ 16的范围内,并且只有大约130对。我也追求的是一种静态初始化数据的简单方法。

15 个答案:

答案 0 :(得分:12)

使用地图

#include <map>
#include <iostream>

int main() {
   std::map <int, int> m;
   m[79] = 12958; 
   std::cout << m[79] << std::endl;
}

使用映射是最通用的解决方案,也是最便携的(C ++标准尚不支持散列表,但它们是非常常见的扩展)。尽管如此,这并非必要。二进制搜索和其他人建议的哈希映射解决方案可能(但不会)超出它。但是,对于大多数应用程序而言,这可能无关紧要。

答案 1 :(得分:8)

按键对数组排序并进行二进制搜索。

答案 2 :(得分:7)

如果您需要编译时间映射,可以使用以下模板:

// template to specialize
template<int T> struct int2int {};    

// macro for simplifying declaration of specializations
#define I2I_DEF(x, v) template<> struct int2int<x> { static const int value = v; };

// definitions
I2I_DEF(2, 5) I2I_DEF(79, 12958) I2I_DEF(55, 100) // etc.

// use
#include <iostream>    
int main()
{
  std::cout << int2int<2>::value << " " << int2int<79>::value << std::endl;

  return 0;
}

答案 3 :(得分:3)

如果源整数i的数量相对较高(以便直接搜索效率低下)但仍然可管理,则可以相对轻松地为输入整数构建完美的哈希函数hash(i)(例如,使用Pearson hashing,然后使用散列值作为输入表map

的条目
output = map[hash(i)];

当然,如果输入值的范围相对较小,您可以使用标识函数代替hash,只需将整个事物转换为直接重新映射

output = map[i];

(尽管如果是这种情况,你甚至不会问。)

答案 4 :(得分:2)

std::map<int, int> theMap;
theMap[2] = 5;
std::map<int, int>::const_iterator iter = theMap.find(2);
if (iter != theMap.end())
   iter->second; // found it

插入一对int,按键检索值,对数复杂度。如果你有一个非常大的数据集并且需要更快的检索,请使用std :: tr1 :: unordered_map或boost :: unordered_map(如果你的标准库没有TR1实现)。

答案 5 :(得分:2)

std::mapstd::unordered_map可能是最清洁的。不幸的是,C ++没有内置的关联数组。

std::map<int,int> mymap; // the same with unordered map

// one way of inserting
mymap.insert ( std::make_pair(2,5) );
mymap.insert ( std::make_pair(79,12958) );

// another
mymap[2] = 5;
mymap[79] = 12958;

检查

std::map<int,int>::const_iterator iter = mymap.find(2);
if ( iter != mymap.end() )
{
   // found
   int value = iter->second;
}

unordered_map的优势在于O(1)分摊的查询时间,而不是O(log n)的{​​{1}}。

答案 6 :(得分:2)

作为补充,如果您需要二进制搜索实现,请不要忽略C ++标准库。以下使用equal_range算法对您的结构类型的数组进行一个处理(对代码的某些hacky质量道歉)

#include <algorithm>
#include <iostream>
using namespace std;

struct S {
    int k, v;
};

bool operator <( const S & a, const S & b ) {
    return a.k < b.k;
};

// must be sorted in key order
S values[] = {{42,123},{666,27}};

int main() {

    S t;
    cin >> t.k;

    S * valend = &values[0] + sizeof(values) / sizeof(S);
    pair <S*,S*> pos = equal_range( &values[0], valend , t);

    if ( pos.first != pos.second ) {
        cout << pos.first->v << endl;
    }
    else {
        cout << "no" << endl;
    }
}

答案 7 :(得分:1)

为什么不是哈希地图?它会为您提供或多或少的任何密钥的检索时间。

答案 8 :(得分:1)

你有正确的想法,这是一张地图。使用std::map

答案 9 :(得分:1)

跳转表。如果你能够使用它,开关可能会设置它,否则你可能需要一些组装,但这可能是最快的方式。

答案 10 :(得分:1)

您可以使用boost :: assign。

#include <iostream>
#include <boost/assign.hpp>

int main()
{
    typedef std::map< int, int > int2int_t;
    typedef int2int_t::const_iterator int2int_cit;

    const int2int_t theMap
        = boost::assign::map_list_of
            ( 2, 5 )
            ( 79, 12958 )
            ;

    int2int_cit it = theMap.find( 2 );
    if ( it != theMap.end() )
    {
        const int result = it->second;
        std::cout << result << std::endl;
    }
}

答案 11 :(得分:0)

如果您100%确定theMap不会增加到超过1,000个条目(个人资料!),那么进行二分查找可能会更快。

如果a的值具有合理的界限(例如,低于1,000),则可以使用a作为保证O(1)复杂度的索引来创建一个简单数组。如果您正在使用gcc,则可以使用此语法(http://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html#Designated-Inits):

int theMap[256] = { [2] = 5, [79] = 12958 };

(遗憾的是,g ++不支持此功能)

在任何其他情况下,请使用std::unordered_map,如其他答案中所示。

答案 12 :(得分:0)

您的伪代码几乎是有效的C ++ 0x代码 - 但C ++ 0x需要更少!

map<int, int> theMap = { {2, 5}, {79, 12958 } };
assert ( theMap[ 2 ] == 5 );

在“普通”C ++中,你必须像这样初始化地图,仍然非常优雅:

pair< int, int > map_array[2] = { make_pair(2, 5), make_pair(79, 12958) };
map< int, int > theMap( &map_array[0], &map_array[2] ); // sorts the array
assert ( theMap[ 2 ] == 5 );

编写速度快,运行速度快!

编辑:不要让地图成为全局变量。 (虽然这在C ++ 0x中是安全的。)如果这样做,只有在编译器选择在map_array之后初始化它时才会正确初始化,这是非常不能保证的。如果您想成为全局用户,请使用theMap.assign( &map_array[0], &map_array[2] );初始化它。

答案 13 :(得分:0)

还有一种称为“xmacros”的技术,这是一种很好的方式来完成你所说的。但是,很容易滥用这项技术,所以我总是建议小心使用它。查看:http://en.wikipedia.org/wiki/C_preprocessor#X-Macros

基本要点是,你有一个文件,你列出你的映射说foo.txt,如下所示: MAP(2,5)
MAP(79,12958)
...

然后定义一个宏MAP(A,B),它接受这两个参数并为您进行初始化。然后#include文件(foo.txt)。如果您愿意,您甚至可以通过在文件的每个#include之间重新定义宏来进行多次传递。然后添加更多映射,只需将它们添加到foo.txt并重新编译即可。它非常强大,可以用于许多不同的事情。

答案 14 :(得分:0)

如果您不想出于某种原因使用地图(例如,您只想使用在编译时设置的数组),您还可以将functor与{结合使用} {1}}:

<algorithm>