我有一个需要快速查找的记录表,因此决定进行哈希表查找。
现在,最终出现的问题是我必须根据多个键查找记录。
例如,下面的所有4个键都应指向相同的记录。
key1 -> a,b,c,d,e
key2 -> a,b,d
key3 -> a,b,e
key4 -> c
问题#1
然后,此模式显示与数据库查找的相似性,其中指定了多个键。那么,B树数据结构比多哈希表设计更适合使用吗?
问题#2
一个特殊的特里是否更适合这个问题。默认实现将要求所有键a + b + c + d + e作为查找键。如果我必须查找+ b + d,那么从这个主密钥必须跳过c& e,一边抬头一边看。但那么这个想法会起作用还是已经存在?
问题#3
另一个想法是我是否将东西插入到我的表中,并行地建立另一个带有索引的查找表到每个记录。这样我就可以为每个键设置多个掩码,并扫描此查找表以查找匹配的记录。我想是类似于CAM表的东西。但是如果我必须扫描整个表格,性能会下降。是否可以将哈希表+索引逻辑混合在一起,提供速度和最佳内存使用?
到目前为止,我们尝试过使用boost multi index,uthash,trie等来尝试实现一个适合所有4个问题但到目前为止还没有成功的设计。我喜欢boost multi index,但它有自己的一些问题,禁止我使用。
虽然我使用C语言编程和测试设计,但我对其他任何语言都很好,比如java,php,python。
非常感谢解决这个问题的任何其他想法。
我想要实现的解决方案的伪代码:
/* Keys */
struct key1_s {
int src;
int dst;
char name[10];
int t1;
int t2;
};
struct key2_s {
int src;
int dst;
char name[10];
};
struct key3_s {
int src;
int dst;
int t1;
};
struct key4_s {
int src;
int dst;
int t2;
};
/* Record */
struct record_s {
int src;
int dst;
char name[10];
int t1;
int t2;
int age;
int sex;
int mobile;
}
struct record_s record[2] = {
{1, 2, "jack", 5, 6, 50, 1, 1234567890},
{3, 4, "john", 7, 8, 60, 2, 1122334455}
};
table.insert(record[0]);
table.insert(record[1]);
/* search using key1 */
struct key1_s key1;
key1.src = 1;
key1.dst = 2;
strncpy(key1.name, "jack", 10);
key1.t1 = 5;
key1.t2 = 6;
table.find(key1); // should return pointer to record[0]
/* search using key2 */
struct key2_s key2;
key2.src = 1;
key2.dst = 2;
strncpy(key1.name, "jack", 10);
table.find(key2); // should return pointer to record[0]
/* search using key3 */
struct key3_s key3;
key3.src = 1;
key3.dst = 2;
key3.t1 = 5;
table.find(key3); // should return pointer to record[0]
如果find()返回一个成功的指针,那么我想更新记录的字段,如年龄,性别,移动。
答案 0 :(得分:0)
Boost Multi Index可以在这里提供帮助。
composite_keys.cpp
示例包含一个引人注目的示例。您只需要使用ordered
全局替换hashed
以获取您正在处理的内容(在您的情况下,密钥配置中也会有更多重叠)。
关于表演问题,我认为没有一个明确的答案;它(总是)取决于使用模式。您需要分析和平衡花在优化上的工作量。
我个人认为,当关注方便和快速结果时,Boost Multi Index是最佳选择。请注意,这绝不意味着BMI未经过优化(我相信它已经过高度优化);然而,它将/始终/取决于使用模式。 (考虑最初批量插入大量数据的应用程序,然后只读取;这样的应用程序可以从显式构建索引中获益一次,而不是自动更新每次插入时的所有索引)。
using namespace boost::multi_index;
/* A file record maintains some info on name and size as well
* as a pointer to the directory it belongs (null meaning the root
* directory.)
*/
struct file_entry
{
file_entry(
std::string name_,unsigned size_,bool is_dir_,const file_entry* dir_):
name(name_),size(size_),is_dir(is_dir_),dir(dir_)
{}
std::string name;
unsigned size;
bool is_dir;
const file_entry* dir;
friend std::ostream& operator<<(std::ostream& os,const file_entry& f)
{
os << f.name << "\t" << f.size;
if (f.is_dir)os << "\t <dir>";
return os;
}
};
/* A file system is just a multi_index_container of entries with indices on
* file and size (per directory).
*/
struct dir_and_name_key:composite_key<
file_entry,
BOOST_MULTI_INDEX_MEMBER(file_entry,const file_entry*,dir),
BOOST_MULTI_INDEX_MEMBER(file_entry,std::string,name)
>{};
struct dir_and_size_key:composite_key<
file_entry,
BOOST_MULTI_INDEX_MEMBER(file_entry,const file_entry* const,dir),
BOOST_MULTI_INDEX_MEMBER(file_entry,unsigned,size)
>{};
typedef multi_index_container<
file_entry,
indexed_by<
hashed_unique<dir_and_name_key>,
hashed_non_unique<dir_and_size_key>
>
> file_system;
/* typedef's of the two indices of file_system */
typedef nth_index<file_system,0>::type file_system_by_name;
typedef nth_index<file_system,1>::type file_system_by_size;
/* We build a rudimentary file system simulation out of some global
* info and a map of commands provided to the user.
*/
static file_system fs; /* the one and only file system */
static file_system_by_name& fs_by_name=fs; /* name index to fs */
static file_system_by_size& fs_by_size=get<1>(fs); /* size index to fs */
static const file_entry* current_dir=0; /* root directory */