倒排索引C++实现

什么是倒排索引(反向索引)

以字或者词为关键字进行索引
《倒排索引C++实现》

正排索引是从文档到关键字的映射,已知文档求关键字。倒排索引是从关键字到文档的映射,已知关键字求文档。

百度搜索为什么这么快?

使用了倒排,当然具体的实现会更加复杂,这里只是简单实现倒排索引(inverted index)。

具体主要做了,把多个文档中的单词切出来(为了简单起见直接空格分开单词),然后建立如上图所示的数据结构,每个单词后面挂上一串文档。

这样用户输入一个单词,我们就能找到这个有哪些文档中出现过这个单词(为了简单起见,没有存单词在文档中具体的位置)

如何查找用户输入的单词,即单词库用什么存储,这里用了字典树。

实现

#include <algorithm>
#include <fstream>
#include <iostream>
#include <vector>
#include <string>

const std::string _CHARS = "abcdefghijklmnopqrstuvwxyz0123456789.:-_/";
const size_t MAX_NODES = 41;

class node
{
public:
    node() { clear(); }
    node(char z) { clear(); }
    ~node()
    {
        for (int x = 0; x < MAX_NODES; x++)
        {
            if (next[x])
                delete next[x];
        }
    }
    void clear()
    {
        for (int x = 0; x < MAX_NODES; x++)
        {
            next[x] = 0;
            isWord = false;
        }
    }
    bool isWord;                    // 是否是一个单词
    std::vector<std::string> files; // 文件列表
    node *next[MAX_NODES];          // 字典树
};

class Index
{
public:
    void add(std::string s, std::string fileName)
    {
        std::transform(s.begin(), s.end(), s.begin(), tolower);
        std::string h;
        for (std::string::iterator i = s.begin(); i != s.end(); i++)
        {
            if (*i == 32)
            { // 空格的ascii
                pushFileName(addWord(h), fileName);
                h.clear();
            }
            h.append(1, *i);
        }
        if (h.length())
        {
            pushFileName(addWord(h), fileName);
        }
    }
    void findWord(std::string s)
    {
        std::vector<std::string> v = find(s);
        if (!v.size())
        {
            std::cout << s + " was not found!\n";
            return;
        }
        std::cout << s << " found in:\n";
        for (std::vector<std::string>::iterator i = v.begin(); i != v.end(); i++)
        {
            std::cout << *i << "\n";
        }
        std::cout << "\n";
    }

private:
    node root;
    /*
        对输入的字符串s,遍历root下的字典树,并将该单词最后一个字符的位置设置isWord。
        返回最后一个字符的指针。
    */
    node *addWord(std::string s)
    {
        size_t idx;
        node *rt = &root, *n;
        for (std::string::iterator i = s.begin(); i != s.end(); i++)
        {
            idx = _CHARS.find(*i);
            if (idx < MAX_NODES)
            {
                n = rt->next[idx];
                if (n)
                {
                    rt = n;
                    continue;
                }
                n = new node(*i);
                rt->next[idx] = n;
                rt = n;
            }
        }
        rt->isWord = true;
        return rt;
    }
    /*
        在单词的字典树末尾节点下插入文档的名字。
    */
    void pushFileName(node *n, std::string fn)
    {
        std::vector<std::string>::iterator i = std::find(n->files.begin(), n->files.end(), fn);
        if (i == n->files.end())
            n->files.push_back(fn);
    }
    const std::vector<std::string> &find(std::string s)
    {
        size_t idx;
        std::transform(s.begin(), s.end(), s.begin(), tolower);
        node *rt = &root;
        for (std::string::iterator i = s.begin(); i != s.end(); i++)
        {
            idx = _CHARS.find(*i);
            if (idx < MAX_NODES)
            {
                if (!rt->next[idx])
                    return std::vector<std::string>();
                rt = rt->next[idx];
            }
        }
        if (rt->isWord)
            return rt->files;
        return std::vector<std::string>();
    }
};

int main(int argc, char *argv[])
{
    Index t;
    std::string s;
    std::string files[] = {"file1.txt", "f_text.txt", "text_1b.txt"};

    for (int x = 0; x < 3; x++)
    {
        std::ifstream f;
        f.open(files[x].c_str(), std::ios::in);
        if (f.good())
        {
            while (!f.eof())
            {
                f >> s;
                t.add(s, files[x]);
                s.clear();
            }
            f.close();
        }
    }

    while (true)
    {
        std::cout << "Enter one word to search for ,return to exit; ";
        std::getline(std::cin, s);
        if (!s.length())
            break;
        t.findWord(s);
    }

    return 0;
}

参考链接

  • https://www.jianshu.com/p/e036b87e3c66
  • https://www.rosettacode.org/wiki/Inverted_index#C.2B.2B
点赞