I am trying to learn trie and this is my first implementation. I started off with the idea of being able to handle different data types for key and value; however, I found the data structure a bit complicated and switched to just string for keys and int for values. So, parts of my code are specific to some data types while other parts are generic. My code is based on this link. Here is my current code:
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <cstddef>
#include <stack>
// Class Node
template<typename T, std::size_t N>
class Node
{
    T val;
    std::vector<Node<T, N>*> paths{N, nullptr};
public:
    //ctor
    Node() : val{T{}}
    {
    }
    // Set the value of node
    void set_val(T value)
    {
        val = value;
    }
    // Return the value of node
    T get_val() const
    {
        return val;
    }
    // Set a particular child node based on index
    void set_path(Node<T, N>* new_node, std::size_t index)
    {
        this -> paths[index] = new_node;
    }
    // Return a particular child node based on index
    Node* get_path(std::size_t index) const
    {
        return this -> paths[index];
    }
};
// Class Trie
template<typename U, typename T, std::size_t N>
class Trie
{
    Node<T, N>* root = new Node<T, N>{};
public:
    Trie();
    Trie(const std::list<U>, const T);
    ~Trie();
    void delete_all(Node<T, N>*);
    void insert(U, T);
    bool search(U, T&) const;
    void deletion(U);
};
// Ctor to initialize trie with a list
template<typename U, typename T, std::size_t N>
Trie<U, T, N>::Trie(const std::list<U> init_ls, const T val)
{
    for(auto it = init_ls.cbegin(); it != init_ls.cend(); it++)
    {
        this -> insert(*it, val);
    }
}
// Dtor for trie
template<typename U, typename T, std::size_t N>
Trie<U, T, N>::~Trie()
{
    Node<T, N>* node = root;
    this -> delete_all(node);
}
// Helper function for dtor
template<typename U, typename T, std::size_t N>
void Trie<U, T, N>::delete_all(Node<T, N>* node)
{
    if(node == nullptr)
    {
        return;
    }
    for(std::size_t i = 0; i < N; i++)
    {
        delete_all(node -> get_path(i));
    }
    delete node;
}
// Insert key - value pair in trie
template<typename U, typename T, std::size_t N>
void Trie<U, T, N>::insert(U key, T value)
{
    Node<T, N>* node = root;
    for(std::size_t i = 0; i < key.size(); i++)
    {
        std::size_t index = int(key[i]) - 97;
        if(node -> get_path(index) != nullptr)
        {
            node = node -> get_path(index);
        }
        else
        {
            Node<T, N>* new_node = new Node<T, N>{};
            node -> set_path(new_node, index);
            node = node -> get_path(index);
        }
        if(i == key.size() - 1)
        {
            node -> set_val(value);
        }
    }
}
// Search key - value pair in trie
template<typename U, typename T, std::size_t N>
bool Trie<U, T, N>::search(U key, T& ret_val) const
{
    Node<T, N>* node = root;
    for(std::size_t i = 0; i < key.size(); i++)
    {
        int index = int(key[i]) - 97;
        if(node -> get_path(index) == nullptr)
        {
            ret_val = T{};
            return false;
        }
        else
        {
            node = node -> get_path(index);
        }
        if(i == key.size() - 1)
        {
            if(node -> get_val() != T{})
            {
                ret_val = node -> get_val();
                return true;
            }
            else
            {
                ret_val = T{};
                return false;
            }
        }
    }
    return false;
}
// Delete key - value pair from trie
template<typename U, typename T, std::size_t N>
void Trie<U, T, N>::deletion(U key)
{
    Node<T, N>* node = root;
    std::stack<Node<T, N>*> node_stack;
    for(std::size_t i = 0; i < key.size(); i++)
    {
        int index = int(key[i]) - 97;
        if(node -> get_path(index) != nullptr)
        {
            node = node -> get_path(index);
            node_stack.push(node);
        }
        else
        {
            std::cout << "Key does not exist\n";
            return;
        }
        if(i == key.size() - 1)
        {
            if(node -> get_val() != T{})
            {
                node -> set_val(T{});
            }
        }
    }
    while(!node_stack.empty())
    {
        Node<T, N>* curr_node = node_stack.top();
        node_stack.pop();
        for(std::size_t i = 0; i < N; i++)
        {
            if(curr_node -> get_val() != T{} || curr_node -> get_path(i) != nullptr)
            {
                return;
            }
        }
        delete curr_node;
    }
}
// Main
int main()
{
    Trie<std::string, int, 26> t1{{"apple", "ball", "cat", "banana", "beach", "ballet", "dog"}, 1};
    int ret_val;
    bool success = t1.search("ballet", ret_val);
    std::cout << "Search result: " << success << "\t" << "Return Value: " << ret_val << "\n";
    t1.insert("elephant", 1);
    t1.insert("cats", 1);
    success = t1.search("cats", ret_val);
    std::cout << "Search result: " << success << "\t" << "Return Value: " << ret_val << "\n";
    success = t1.search("cat", ret_val);
    std::cout << "Search result: " << success << "\t" << "Return Value: " << ret_val << "\n";
    t1.deletion("cat");
    //t1.deletion("cats");
    success = t1.search("cats", ret_val);
    std::cout << "Search result: " << success << "\t" << "Return Value: " << ret_val << "\n";
    success = t1.search("cat", ret_val);
    std::cout << "Search result: " << success << "\t" << "Return Value: " << ret_val << "\n";
    success = t1.search("bald", ret_val);
    std::cout << "Search result: " << success << "\t" << "Return Value: " << ret_val << "\n";
    return 0;
}
The things I would like to know:
1) Is the approach and operations included correct, especially for the deletion of key?
2) When I think about it, template specialization seem to be one way to make it handle multiple data types. Are there any better suggestions?
3) Is the memory management part good?
4) Is there a check list I should follow while coding data structures?
5) General review and suggestion please.
Note: I would like to thank everyone who has helped me with my previous questions. I understand that I haven't been able to put into practice all the previous advice that I received. I am trying to get there in steps, through practice. Thank You!