Loading dictionary words into a trie I have here a program created for pset5 of the CS50 course. The program compiles correctly and does as it should. However, is there any way to improve the run time of this program? Are there any pieces of code that are redundant and can be omitted? And is there anything that can be improved on in general?

/*
 * Implements a dictionary's functionality.
 */

#include &lt;stdbool.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;ctype.h&gt;

#include "dictionary.h"

typedef struct trie
{
    bool word;
    struct trie* children[27];
}trie;

trie* root;

unsigned int wordCount;

// Function that frees a node from the heap.
void freenode(trie* node)
{
    for (int i = 0; i &lt; 27; i++)
    {
        if (node-&gt;children[i] != NULL)
        {
            freenode(node-&gt;children[i]);
        }
    }

    free(node);
}

/**
 * Returns true if word is in dictionary else false.
 */
bool check(const char* word)
{
    int n = strlen(word);

    trie* node = root;

    for (int i=0; i &lt; n; i++)
    {
        char c = tolower(word[i]);

        if (c == '\'')
        {
            // ASCII a (97) + 26
            c = 123;
        }

        struct trie* current = node-&gt;children[c-97];

        if (current == NULL)
        {
            return false;
        }
        else
        {
            node = current;
        }
    }

    if (node-&gt;word == true)
    {
        return true;
    }

    return false;
}

/**
 * Loads dictionary into memory.  Returns true if successful else false.
 */
bool load(const char* dictionary)
{
    // Open the dictionary file
    FILE* d = fopen(dictionary, "r");
    if (d == NULL)
    {
        return false;
    }

    root = calloc(1, sizeof(trie));

    trie* node = root;

    for (char c = tolower(fgetc(d)); c != EOF; c = fgetc(d))
    {
        if (c == '\n')
        {
            if (node != root)
            {
                wordCount++;

                node-&gt;word = true;

                node = root;                
            }
        }
        else
        {
            if (c == '\'')
            {
                c = 123;
            }

            struct trie* current = node-&gt;children[c-97];
            if (current == NULL)
            {
                node-&gt;children[c-97] = calloc(1, sizeof(trie));

                node = node-&gt;children[c-97];
            }
            else
            {
                node = current;
            }

        }
    }

    fclose(d);

    return true;
}

/**
 * Returns number of words in dictionary if loaded else 0 if not yet loaded.
 */
unsigned int size(void)
{
    return wordCount;
}

/**
 * Unloads dictionary from memory.  Returns true if successful else false.
 */
bool unload(void)
{
    freenode(root);
    return true;
}
If you feel you need a comment like this:

    // ASCII a (97) + 26
    c = 123;


it means that the code is not clear. Consider

    c = 'a' + 26;


While we can figure out the significance of 26 (size of an English alphabet, right?) again it is better to be explicit. What you want is the first character beyond the [a-z] range, so:

    c = 'z' + 1;


Similarly, node-&gt;children[c-97] should be node-&gt;children[c-'a'].
The

    if (node-&gt;word == true) {
        return true;
    }

    return false;


is a long way to say

    return node-&gt;word;

Avoid globals. Make load return root (or NULL on failure), and pass root to check as a parameter.
Test what calloc returns.
root = calloc(1, sizeof(trie)); requires the knowledge that root is trie *, and therefore results a double maintenance. A preferred idiom is

root = calloc(1, sizeof(*root));

Branching factor of 27 adds a huge overhead. Most nodes will use just a few pointers, so most of the allocated memory is wasted. This in turn results in poor cache utilization, and may directly affect the performance. Consider allocating just as much as you need, and searching instead of indexing. It may or may not improve performance. Profile and compare.