Inverted Indexes Algorithm: Working, Explanation, and Information Retrieval

Inverted indexes algorithm and working of this algorithm

I want to explain the inverted index the way I understood it while learning how search engines work. An inverted index is a data structure that maps content, usually words, to the locations (documents) where they appear, instead of mapping documents to the words they contain. I find this idea important because it’s the backbone of nearly every search engine I use, letting me search millions of documents for a keyword in milliseconds instead of scanning every document one by one.

History and Background

I found that inverted indexes have roots going back to manual concordances of religious and literary texts, where scholars listed words and the pages they appeared on, long before computers existed. In computing, the concept became essential with the rise of information retrieval systems in the 1960s and 1970s, and it became the foundation of modern search engines once Google and other companies scaled it to index the entire web starting in the late 1990s.

Problem Statement

The problem I need an inverted index to solve is fast keyword search across a huge collection of documents. If I searched by scanning every document for a keyword every time (a “forward” or linear search), it would take far too long as the collection grows. I need a way to precompute which documents contain each word, so a search becomes a fast lookup instead of a full scan.

Core Concepts

  • Term – a word or token extracted from documents.
  • Posting list – the list of document IDs (and often positions) where a term appears.
  • Tokenization – breaking text into individual words/terms.
  • Stemming/normalization – reducing words to their root form so “running” and “run” match the same term.
  • Index construction – the process of scanning all documents and building the term-to-posting-list mapping.
  • Query processing – using the index to quickly find documents matching a search query.

How It Works

  1. I collect a set of documents to index.
  2. I tokenize each document into individual words, removing punctuation and often stop words.
  3. For each word (term), I record which document IDs it appears in, building a posting list per term.
  4. I store these term-to-posting-list mappings in the inverted index structure.
  5. When a search query comes in, I tokenize the query the same way, look up each term’s posting list, and combine (intersect for AND queries, union for OR queries) to find matching documents.

Working Principle

The internal logic relies on inverting the natural document-to-words relationship into a word-to-documents relationship. Once built, this structure lets me answer “which documents contain word X” in near-constant time through a hash table or sorted lookup, rather than scanning through every document’s content at query time. The precomputation cost is paid once during indexing, and it pays off across every subsequent search.

Mathematical Foundation

I often describe search relevance using Term Frequency-Inverse Document Frequency (TF-IDF), which scores how important a term is to a document within a collection:

$$\text{TF-IDF}(t, d) = tf(t,d) \times \log\left(\frac{N}{df(t)}\right)$$

where $tf(t,d)$ is how many times term $t$ appears in document $d$, $N$ is the total number of documents, and $df(t)$ is the number of documents containing term $t$. This formula tells me that common words appearing in many documents get a lower weight, while rare, distinctive words get a higher weight, which helps rank search results by relevance.

Diagrams

flowchart TD
    A[Documents] --> B[Tokenize Text]
    B --> C[Extract Terms]
    C --> D[Build Term -> Posting List Mapping]
    D --> E[Inverted Index]
    F[Search Query] --> G[Tokenize Query]
    G --> E
    E --> H[Return Matching Document IDs]

Pseudocode

function BUILD_INDEX(documents):
    index = EMPTY_MAP()
    for doc_id, text in documents:
        terms = TOKENIZE(text)
        for term in UNIQUE(terms):
            if term not in index:
                index[term] = EMPTY_LIST()
            index[term].append(doc_id)
    return index

function SEARCH(index, query):
    query_terms = TOKENIZE(query)
    result = index[query_terms[0]]
    for term in query_terms[1:]:
        result = INTERSECT(result, index[term])
    return result

Step-by-Step Example

Suppose I have 3 documents:

  • Doc1: “the cat sat”
  • Doc2: “the dog ran”
  • Doc3: “the cat ran fast”
  1. Tokenizing gives terms: Doc1 → {the, cat, sat}, Doc2 → {the, dog, ran}, Doc3 → {the, cat, ran, fast}.
  2. Building the index: the → [1,2,3], cat → [1,3], sat → [1], dog → [2], ran → [2,3], fast → [3].
  3. If I search for “cat ran”, I look up cat → [1,3] and ran → [2,3].
  4. Intersecting these lists gives [3], meaning only Doc3 contains both “cat” and “ran”.

Time Complexity

Building the index takes $O(T)$ where $T$ is the total number of word tokens across all documents. Once built, a single-term query lookup takes $O(1)$ average case with a hash-based index, and multi-term queries take $O(m + n)$ time to intersect two posting lists of length $m$ and $n$ if they’re sorted.

Space Complexity

Space complexity is $O(T)$ in the worst case, since I store an entry for every occurrence of every term across all documents, though in practice, compression techniques (like delta encoding of document IDs) significantly reduce the actual storage needed.

Correctness Analysis

I consider the inverted index correct as long as every document that contains a term is included in that term’s posting list, and no document that doesn’t contain the term is incorrectly included. This directly follows from how the index is constructed — during indexing, I only add a document ID to a term’s list when that term is genuinely found in that document’s tokenized text.

Advantages

  • Extremely fast keyword lookups compared to scanning every document.
  • Scales well to millions or billions of documents when combined with distributed systems.
  • Supports advanced queries like boolean AND/OR/NOT and ranked relevance scoring.
  • Well-suited to precomputation since most collections change slower than they’re searched.

Disadvantages

  • Index construction and storage can be expensive for very large or frequently updated collections.
  • Handling deletions and updates to documents requires re-indexing or careful incremental updates.
  • Doesn’t inherently understand semantics or synonyms without additional processing (like stemming or embeddings).

Applications

I see inverted indexes used in web search engines, document retrieval systems, log search tools like Elasticsearch, library catalog systems, e-commerce product search, and email/message search features.

Implementation in C

Here is a simplified inverted index builder and searcher in C.

#include <stdio.h>
#include <string.h>

#define MAX_TERMS 50
#define MAX_DOCS 10
#define MAX_LEN 20

typedef struct {
    char term[MAX_LEN];
    int doc_ids[MAX_DOCS];
    int doc_count;
} IndexEntry;

IndexEntry index_table[MAX_TERMS];
int index_size = 0;

void add_to_index(char* term, int doc_id) {
    for (int i = 0; i < index_size; i++) {
        if (strcmp(index_table[i].term, term) == 0) {
            // avoid duplicate doc_id entries
            for (int j = 0; j < index_table[i].doc_count; j++) {
                if (index_table[i].doc_ids[j] == doc_id) return;
            }
            index_table[i].doc_ids[index_table[i].doc_count++] = doc_id;
            return;
        }
    }
    strcpy(index_table[index_size].term, term);
    index_table[index_size].doc_ids[0] = doc_id;
    index_table[index_size].doc_count = 1;
    index_size++;
}

void search(char* term) {
    for (int i = 0; i < index_size; i++) {
        if (strcmp(index_table[i].term, term) == 0) {
            printf("Documents containing '%s': ", term);
            for (int j = 0; j < index_table[i].doc_count; j++) {
                printf("Doc%d ", index_table[i].doc_ids[j]);
            }
            printf("\n");
            return;
        }
    }
    printf("Term '%s' not found in any document.\n", term);
}

int main() {
    char* docs[3] = {"the cat sat", "the dog ran", "the cat ran fast"};

    for (int d = 0; d < 3; d++) {
        char buffer[100];
        strcpy(buffer, docs[d]);
        char* token = strtok(buffer, " ");
        while (token != NULL) {
            add_to_index(token, d + 1);
            token = strtok(NULL, " ");
        }
    }

    search("cat");
    search("ran");
    search("fast");

    return 0;
}

Sample Input and Output

Input: Documents ["the cat sat", "the dog ran", "the cat ran fast"], searching for “cat”, “ran”, “fast”.

Output:

Documents containing 'cat': Doc1 Doc3
Documents containing 'ran': Doc2 Doc3
Documents containing 'fast': Doc3

Optimization Techniques

I improve inverted index performance by compressing posting lists using delta encoding (storing gaps between document IDs instead of full IDs), using skip lists within posting lists to speed up intersection, applying stemming and stop-word removal to reduce index size, and sharding the index across multiple machines for very large collections.

Common Mistakes

I’ve noticed people forget to normalize text consistently (case-sensitivity mismatches between indexing and querying), fail to handle duplicate terms within the same document correctly, don’t account for index updates when documents change, and use unsorted posting lists that make intersection slower than necessary.

Further Reading

  • Manning, Raghavan, Schütze, “Introduction to Information Retrieval” – https://nlp.stanford.edu/IR-book/information-retrieval-book.html
  • Apache Lucene Documentation – https://lucene.apache.org/core/documentation.html
  • Elasticsearch: The Definitive Guide – https://www.elastic.co/guide/en/elasticsearch/guide/current/index.html
  • Google’s original search paper, Brin & Page, “The Anatomy of a Large-Scale Hypertextual Web Search Engine” – http://infolab.stanford.edu/~backrub/google.html
Total
1
Shares

Leave a Reply

Previous Post
Splay tree algorithm and working of this algorithm

Splay Tree Algorithm: Working, Explanation, and Self-Adjusting Data Structure

Next Post
The Fourier transform algorithm and working of this algorithm

Fourier Transform Algorithm: Working, Explanation, and Signal Processing

Related Posts