Home > database >  Generalization of tree creation
Generalization of tree creation

Time:12-16

I want to generalize this binary tree creation process in order to let different types of nodes to be included in the tree itself. For example, I want to let the user choose if he wants to build a tree with the structure city (as I did below) or with the structure people or any structure he wants to define in the source code.

Is there a simple way to implement those changes?

This is the code:

#include <iostream>

template <typename T>
struct node
{
    T infoStruct;
    // Pointers
    node* left = NULL;
    node* right = NULL;
};
struct city
{
    std::string cityName;
    int population;
};

struct people
{
    std::string name;
    std::string surname;
    int age;
    int weight;
};

node<city>* root;

void visualizeInOrder(node<city>*);
void insertNewNode(node<city>*, node<city>*);

int main()
{
    root = NULL;
    char choice;

    do
    {
        node<city>* tmp = new node<city>;
        std::cout << "Insert city name: ";
        getline(std::cin, tmp->infoStruct.cityName);
        std::cout << "Insert population: ";
        std::cin >> tmp->infoStruct.population;

        if (root)
            insertNewNode(root, tmp);
        else
            root = tmp;

        choice = 'N';
        std::cout << "Insert another city? [y|N]> ";
        std::cin >> choice;
        std::cin.ignore();
    } while (choice != 'N');

    visualizeInOrder(root);
}




void visualizeInOrder(node<city>* root)
{
    if (root->left) visualizeInOrder(root->left);
    std::cout << root->infoStruct.cityName << " has " << root->infoStruct.population << " population\n";
    if (root->right) visualizeInOrder(root->right);
}



void insertNewNode(node<city>* root, node<city>* leaf)
{
    if (root)
    {
        if (leaf->infoStruct.population < root->infoStruct.population)
            if (root->left)
                insertNewNode(root->left, leaf);
            else
                root->left = leaf;
        else
            if (root->right)
                insertNewNode(root->right, leaf);
            else
                root->right = leaf;
    }
}

CodePudding user response:

Most of the pieces are already there. The first step you can do is to simply change the signature of insertNewNode and visualizeInOrder to accept node<T> instead of node<city>.


So insertNewNode would become:

template<typename T>
void insertNewNode(node<T>* root, node<T>* leaf)
{
    if (root)
    {
        if (leaf->infoStruct.population < root->infoStruct.population)
            if (root->left)
                insertNewNode(root->left, leaf);
            else
                root->left = leaf;
        else
            if (root->right)
                insertNewNode(root->right, leaf);
            else
                root->right = leaf;
    }
}

However, the problem here is that a generic type T would not have a member population. So instead of using:

leaf->infoStruct.population < root->infoStruct.population

You could add a templated comparison function Comp comp to the signature, then use that to do the comparison, and pass it to the recursion call as well:

template<typename T, typename Comp>
void insertNewNode(node<T>* root, node<T>* leaf, Comp comp)
{
    if (root)
    {
        if (comp(leaf.infoStruct, root.infoStruct)
            if (root->left)
                insertNewNode(root->left, leaf, comp);
            else
                root->left = leaf;
        else
            if (root->right)
                insertNewNode(root->right, leaf, comp);
            else
                root->right = leaf;
    }
}

However, this is not the most effective way of adding a comparison function, since you would always have to add some function, such as a lambda, to the first call of insertNewNode in your main manually. So currently, you would be calling the function in the main like:

insertNewNode(root, tmp, [](const city& city_a, const city& city_b){
    return city_a.population < city_b.population;
});

This is quite verbose, and would straight up not working if population was a private member. Instead, you could use std::less as default, so the declaration would be:

template<typename T, typename Comp = std::less<>>
void insertNewNode(node<T>* root, node<T>* leaf, Comp comp = {});

Now you can either add a comparison function manually like what I had earlier, or you can add a operator< to your city class, and it would be automatically used in the insertNewNode function:

struct city
{
     ⋮
    bool operator< (const city& other) const
    {
        return population < other.population;
    }
};

Similarly, for visualizeInOrder, since cityName and population are unique to city, you can't use:

std::cout << root->infoStruct.cityName << " has " << root->infoStruct.population << " population\n";

Instead, you can overload the ostream& operator<< for your city class to print all the detail information. And inside visualizeInOrder, it would just become:

std::cout << root->infoStruct << '\n';
  • Related