-1

I have read many posts on BST and duplicates and I understand it isn't very possible / no clean way to allow duplicates especially for a complex type which I am using. So I need some help on how / if it's possible to implement a BST with duplicates with my scenario.

My scenario: I am using a Transaction class as my node keys, and the main data I am comparing is the 'amount' in the transaction class, so my binary search tree can allow you to enter an amount and output any transactions with its 'toString()' function to the user, that matches the search amount. However, now I face the issue that I won't be able to have a duplicate transaction amount. How could I resolve this? Could anyone provide an example? thank you.

Code to reproduce the problem to solve:

#include<iostream>
using namespace std;
#include <algorithm>
#include <cctype>
#include <string>
#include <memory>

// Complex type used for the BST
class Transaction
{
private:
    std::string desc;
    time_t timestamp;
    std::string value;
    bool isWithdrawal;

public:

    Transaction(const std::string& value, std::string reason = "None.")
    : desc(reason), timestamp(time(nullptr)), value(value) { // timestamp is current date/time based on current system

        // Lambda to convert reason to lower to we can identify elements easier
        std::transform(reason.begin(), reason.end(), reason.begin(),
            [](unsigned char c) { return std::tolower(c); });
    
        this->isWithdrawal = (reason.find("withdrawal") != std::string::npos) ? true : false;
    } 

    std::string toString() const {
        // convert timestamp to string form
        const char* string_timestamp = ctime(&timestamp);
    
        if(this->isWithdrawal) { return "-- " + desc + ": -£" + value + " on " + string_timestamp;}
        else {return "-- " + desc + ": £" + value + " on " + string_timestamp;}
    }
    
    // Gets the amount, converts it to a double and returns it
    double getAmount() const {
        return std::stod(this->value);
    }
};


// The binary search tree implementation
class BST {
    
    struct node {
        std::shared_ptr<Transaction> data;
        node* left;
        node* right;
    };

    node* root;

    node* makeEmpty(node* t) {
        if(t == NULL)
            return NULL;
        {
            makeEmpty(t->left);
            makeEmpty(t->right);
            delete t;
        }
        return NULL;
    }

    node* insert(std::shared_ptr<Transaction> x, node* t)
    {
        if(t == NULL)
        {
            t = new node;
            t->data = x;
            t->left = t->right = NULL;
        }
        else if(x->getAmount() < t->data->getAmount())
            t->left = insert(x, t->left);
        else if(x->getAmount() > t->data->getAmount())
            t->right = insert(x, t->right);
        return t;
    }

    node* findMin(node* t)
    {
        if(t == NULL)
            return NULL;
        else if(t->left == NULL)
            return t;
        else
            return findMin(t->left);
    }

    node* findMax(node* t) {
        if(t == NULL)
            return NULL;
        else if(t->right == NULL)
            return t;
        else
            return findMax(t->right);
    }

    void inorder(node* t) {
        if(t == NULL)
            return;
        inorder(t->left);
        cout << t->data->getAmount() << " ";
        inorder(t->right);
    }

    node* find(node* t, double x) {
        if(t == NULL)
            return NULL;
        else if(x < t->data->getAmount())
            return find(t->left, x);
        else if(x > t->data->getAmount())
            return find(t->right, x);
        else
            return t;
    }

public:
    BST() {
        root = NULL;
    }

    ~BST() {
        root = makeEmpty(root);
    }

    void insert(std::shared_ptr<Transaction> x) {
        root = insert(x, root);
    }

    void display() {
        inorder(root);
        cout << endl;
    }

    std::string search(double x) {
        node* result = find(root, x);
        if(result != NULL) { return result->data->toString(); }
        else { return "N/A"; }
    }
};

int main() {
    BST t;
    t.insert(std::make_shared<Transaction>("1500.50", "Deposit"));
    t.insert(std::make_shared<Transaction>("1600.98", "Deposit"));
    t.insert(std::make_shared<Transaction>("1400", "Withdrawal"));
    t.insert(std::make_shared<Transaction>("1400.59", "Deposit"));
    t.display();
    
    std::cout << t.search(1500.50);
    
    return 0; 
}

3 Answers3

1

I have read many posts on BST and duplicates and I understand it isn't very possible / no clean way to allow duplicates especially for a complex type which I am using.

This is incorrect you can use multimap or multiset for this case.

For example, cppreference

Multimap is an associative container that contains a sorted list of key-value pairs, while permitting multiple entries with the same key. Sorting is done according to the comparison function Compare, applied to the keys. Search, insertion, and removal operations have logarithmic complexity.

You just need to supply as a template parameter a Compare functor which says that for two equivalent keys, none is smaller than the other.

Ami Tavory
  • 74,578
  • 11
  • 141
  • 185
  • So I have a multimap of a transaction and count? but what if I want to call the toString() function in the search function, wouldn't it just call it once and not show the different "reasons" for the transactions. – paigelarry342 Dec 21 '21 at 12:09
  • @paigelarry342 You call any method you want from your Compare class. If the compare class returns that two objects are each not less than each other, they are equivalent as far as the multi-* are concerned. How you implement that, is up to you. – Ami Tavory Dec 21 '21 at 12:12
  • So I change my data member in the Node struct to std::multimap>? then in my insert i compare the first key and if it matches I inser the transaction into the data multimap? – paigelarry342 Dec 21 '21 at 12:29
  • using `double`s for decimal arithmetics is asking for trouble, take a look [here](https://github.com/user1095108/dpp). You have many issues in your code. – user1095108 Dec 21 '21 at 12:41
  • @user1095108 there is no arithmetic going on? just comparing – paigelarry342 Dec 21 '21 at 12:45
  • that's a topic for another question, please go ahead and write one, we will be happy to provide guidance. – user1095108 Dec 21 '21 at 12:51
  • @paigelarry342 you probably want `std::multiset`, where `CompareTransaction` is a class with a member `bool operator()(const Transaction&, const Transaction&) const` – Caleth Dec 21 '21 at 14:25
0

You can associate a BST node with multiple values by making your data member a container, for example, change:

std::shared_ptr<Transaction> data;

into

std::list<std::shared_ptr<Transaction>> data;

This is the same thing as associating a key with multiple values. In essence, this is what std::multimap and std::multiset do. You will have to update tree operations/iterator as well. You will have to zip individual std::lists together, when doing container traversal, for example.

EDIT:

An easy alternative would be to use std::multiset<std::tuple<std::string, time_t, std::string, bool>>.

user1095108
  • 14,119
  • 9
  • 58
  • 116
  • here's a sample [implementation](https://github.com/user1095108/sg). – user1095108 Dec 21 '21 at 12:13
  • Sorry a bit confused why are you using std::list here and not multimap? and also would std::map / std::unordered map do the same thing. Hope you can explain thanks. – paigelarry342 Dec 21 '21 at 12:17
  • well, you said yourself you wanted to be able to support duplicates and `std::list` can store duplicates. So, `data` is a data member of your `node` `struct` and the key is at the front of the list. – user1095108 Dec 21 '21 at 12:20
  • So are you basically suggesting use list instead of multimap? – paigelarry342 Dec 21 '21 at 12:23
  • no, I suggested a change in the code you posted, if your code is actually what you want to use. What the others are saying is, that you don't need to, you can use `std::multiset`. – user1095108 Dec 21 '21 at 12:24
  • So for my scenario I need to use my class as it links with our stuff in my full program, so please correct me iam wrong, I should change my data memeber to std::multiset>? and when inserting I comapre for exmaple the first value in the multiset and get its getAmount() if its equal to the new transaction, I just add to the multiset, if its not the same I just carry on. Then when I go to the search function I can just loop over the multiset and print toSting for each transaction? – paigelarry342 Dec 21 '21 at 12:35
  • I wrote what you need to change and it is, change `std::shared_ptr data` into `std::list> data`. But other changes are also necessary. It seems to me you need to read up on BSTs. Google for "open data structures". – user1095108 Dec 21 '21 at 12:39
  • why whats wrong with my current code? can you expand – paigelarry342 Dec 21 '21 at 12:45
0

A binary search tree lets you store keys and associated records. It allows efficient searches for a given key (with the purpose of retrieving the associated information).

As it also supports enumeration in sorted order, it allows retrieving all keys in a given range, and duplicate keys are not a problem.