1

I'm trying to insert a Packet object into this binary search tree. But the problem is, I don't really know of a good-enough way of doing this or how to go about doing it. I'm looking for some pointers in the right direction and to be shown what to do to tackle this problem.

Please:

  • Ignore my usage of namespace std; because this is for educational purposes and I'm not rly (as of now) looking to go further than that!
  • Help me with my specific question and if possible, show me how I could fix this problem.

<< Take a look at my code >>

Main.cpp:

#include <iostream>

#include "BST.h"
#include "Packet.h"

// IGNORE the USAGE of namespace std. as this is purely a testing program for educational purposes.
// It is NOT implementation for a real program.
using namespace std;

int main() {
    cout << "-------------------------------------------------------" << endl;
    cout << "Testing BST" << endl;
    cout << "-------------------------------------------------------" << endl;
    BST test1;
    Packet packetTest(123, "This is a packet of cheese.", 12.95, 10);
    // test1.insert(How should I choose to insert Packet? That's the question.);
    system("pause");
}

BST.h:

#pragma once

#include "Packet.h"

using namespace std;

class BST {
    struct Node {
        Node() : rlink(nullptr), llink(nullptr) {};
        ~Node() {};

        // Store packet here (for instance Packet *data or something)...
        Node *rlink, *llink;
    };
    public:
        BST();
        // void insert(How should I choose to insert Packet? That's the question.);
        void insert(Node *&p, Node *newNode);
        void preorderTraversal() const;
        void destroyTree();
        ~BST();
    private:
        Node * root;
        void destroyTree(Node *&p);
        void preorderTraversal(const Node *p) const;
};

BST.cpp (need guidance here, see below code to see what I mean):

#include "BST.h"
#include <iostream>

BST::BST() : root(nullptr) {}

// Need guidance here. What should I do for this function? How can I insert this object called Packet into the BST? 
/*void BST::insert(How should I choose to insert Packet? That's the question.) {
    Node *newNode = new Node;
    ...
    insert(root, newNode);
}*/

void BST::insert(Node *&p, Node *newNode) {
    if (p == nullptr) {
        p = newNode;
    }/*else if (p's data's getPartId() > newNode's data's getPartId()){
        insert(p->llink, newNode);
    }*/else {
        insert(p->rlink, newNode);
    }
}

void BST::preorderTraversal() const {
    if (root == nullptr) {
        cerr << "There is no tree.";
    }
    else {
        preorderTraversal(root);
    }
}

void BST::preorderTraversal(const Node *p) const {
    if (p != nullptr) {
        // cout << p->data->getPartId() << " "; Need to handle Packet's data here. But we need to implement Packet insection first!
        preorderTraversal(p->llink);
        preorderTraversal(p->rlink);
    }
}

void BST::destroyTree(Node *&p) {
    if (p != nullptr) {
        destroyTree(p->llink);
        destroyTree(p->rlink);
        delete p;
        p = nullptr;
    }
}

void BST::destroyTree() {
    destroyTree(root);
}

BST::~BST() {
    destroyTree(root);
}

Packet.h:

#pragma once
#include <string>

using namespace std;

class Packet {
public:
    Packet(int partId, string description, double price, int partCount) :
        partId(partId), description(description), price(price), partCount(partCount) {}
    int getPartId() const { return partId; }

private:
    int partId;
    string description;
    double price;
    int partCount;
};

This was my previous implementation of insert in BST.cpp:

void BST::insert(Packet &data) {
    Node *newNode = new Node;
    newNode->data = &data;
    insert(root, newNode);
}

As you can see, I don't believe that this is ideal. I mean I had to use & reference twice. Is there a more elegant solution and may I get guidance in regards to that?

  • I like the rejected option at the end. Get rid of the storing pointers in `node` and put up with a bit of copying overhead and you've got a reasonably robust solution. Recommendation: Don't expose the Nodes. They should be a purely internal implementation detail. Handing out pointers to nodes violates encapsulation and allows folks to damage he internal state of the tree. That means `void BST::insert(Node *&p, Node *newNode)` should be `private`. – user4581301 Jun 20 '19 at 21:40
  • 1
    Sidenote: Booth uses of `&` in that last snippet mean different things. `Packet &data` means data is a reference. `&data` means get me the address of `data`. The danger here is the ownership of `data is blurry. If it is destroyed while `newNode` still exists... something bad happens. This arcs back to comment 1: store a copy. – user4581301 Jun 20 '19 at 21:45
  • How about using a std::unordered_map/set and pass in a custom hash function (or grossly implement a friend hash func in the std namespace) – Alex Hodges Jun 20 '19 at 23:47
  • Assume you have no access to Packet implementation, I think your implementation of BST is reasonable. As user4581301 said, you were not using & twice, they meant differently – Charlie Jun 21 '19 at 02:03

1 Answers1

1

Q: How can I insert this object called Packet into the BST?

A: To create a relationship between the BST and Packet class, you must define one in some sort of way. Best practice calls for an association which imposes the least amount of coupling between the related classes.

I have implemented an association in your solution in the place I found most suitable ie. the rlink and llink pointers of struct Node of class BST.

// Store packet here (for instance Packet *data or something)... Packet* rlink, * llink;

A relationship is the only way you will be able to access getPartId() from a Node or BST object. Albeit the Packet class does not manage any resources so it does not require memory management, association is just a fancy word for a loosely coupled relationship between classes, which is the case here.

Be careful when calling functions recursively, as you have in void BST::insert(Node *&p, Node *newNode). You shouldn't call a function recursively without an exit condition and never really use recursion unless you have to as iterations are a stack-memory saving alternative. I saw no need for recursion in your insert function so I took it out. I'm hoping what I replaced them with is of some use to you:

void BST::insert(Packet& p) {
    Packet* newPacket = new Packet(p);
    insert(root, newPacket);
}

void BST::insert(Node*& p, Packet* newPacket) {
    if (p == nullptr) {
        p = new Node;
        p->llink = newPacket;
    }else if ((p->llink->getPartId()) > newPacket->getPartId()){
        p->llink = newPacket;
    }else {
        p->rlink = newPacket;
    }
}

I then went on to say:

void BST::preorderTraversal(const Node* p) const {
    if (p != nullptr) {
        cout << p->llink->getPartId() << " \n";
    }
}

void BST::destroyTree(Node*& p) {
    if (p != nullptr) {
        delete p;
        p = nullptr;
    }
}

As I said, a relationship is the only way you will be able to access getPartId() from a Node or BST object.

Regarding the comments, I agree. Encapsulation requires keeping all data members private and only exposing methods when you have to. My solution allows you to keep the function

private:void insert(Node*& p, Packet* newPacket);

As you have kept Node completely hidden by overloading the preorderTraversal()

Good job and hope I helped!

AdamTV
  • 71
  • 7
  • Tysm your solution works out for me as a solution. Except the non-recursive part. That one doesn't mesh well with my program but the rest is great and solved my issue in the original post! – ii69outof247 Jun 25 '19 at 20:58
  • Glad I could help!!! :D Like I said you should really only use recursion if you have to, loops and containers can be much faster. – AdamTV Jul 04 '19 at 02:29
  • I could do it iteratively but then I would have to rewrite my code base, it seems. I've tried your code above and for some reason, it wouldn't work with my current program. Thanks for your efforts! – ii69outof247 Jul 05 '19 at 20:28