倒排索引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;
}

參考鏈接

本文由博客羣發一文多發等運營工具平臺 OpenWrite 發佈

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章