I'm having a little trouble trying to sort a vector of pointers.
This is what I have done so far:
class Node
{
private:
vector <Node*> _children;
string _data;
...
public:
void Node::add_child(Node* child)
{
...
sort(_children.begin(), _children.end());
}
bool Node::operator<(const Node& node)
{
return (this->_data.compare(node._data) == -1);
}
};
My less-than operator works, if I write like this:
Node* root = new Node("abc");
Node* n = new Node("def");
cout << (*root<*n) << endl;
Why does sort never call the operator?? Any help would be appreciated!
Thanks.
madshov
Because you sort the pointer values, not the Nodes they point to.
You can use the third argument of the std::sort algorithm to specify a custom comparator.
For example :
bool comparePtrToNode(Node* a, Node* b) { return (*a < *b); }
std::sort(_children.begin(), _children.end(), comparePtrToNode);
(note that this code is just an indication - you'll have to add extra safety checks where needed)
Your less-than operator takes const Node& arguments, but your vector is sorting Node*s. You need to specify a comparison function as the third parameter to std::sort.
class Node
{
private:
vector <Node*> _children;
string _data;
struct PointerCompare {
bool operator()(const Node* l, const Node* r) {
return *l < *r;
}
};
public:
void add_child(Node* child)
{
sort(_children.begin(), _children.end(), PointerCompare());
}
bool operator<(const Node& node) const
{
return (this->_data.compare(node._data) == -1);
}
};
Also, your operator< needs to be declared const.
Your operator<() operates on references to Node objects; but the vector contains pointers to Node objects, which can't be compared with that function. You'll have to explicitly supply a proper function (one that accepts pointers as arguments) to the sort() algorithm.
Related
class Node{
public:
Node* back1 = nullptr;
Node* back2 = nullptr;
int value;
Node(int value) {
this->value = value;
}
//bool operator< (const Node& rhs) const {return this->value < rhs.value;}
bool operator< (const Node* rhs) const {return this->value < rhs->value;}
};
std::unordered_map<int, std::set<Node*>> nodes;
void subtractOneOrDouble(int current, int m, Node& prevNode) {
if (current != 0 && current < m && nodes[current % 10].find(current) == nodes[current % 10].end()) {
I have a set of Nodes* and not only do I want to sort them by the value of each node, I want to use find(), input a value, and get the pointer to the node that corresponds to that value. From what I think is going on, find() is taking in pointers, not actual values. What would I need to change so that find() takes in an integer value and gives me the pointer to the Node with that value?
The problem is with the expression
nodes[current % 10].find(current)
The index operator [] of the std::unordered_map will return a std::set (the value of the hash map). Then you apply the find function of the std::set. This set contains "Node*"s. But you are trying to find an int (current). This cannot work.
I am not sure what exactly you want to achieve, but your are searching with the wrong type. You need to give a const Node* as aparameter to the find function.
find takes as input the key type. In your std::unordered_map, that key type is int, and in your std::set that key type is Node*.
If you want to use your elements' sorting, then you can change your std::set's key to be Node instead of Node*. std::set's comparison uses operator < (via std::less) by default, so you can get this working with some tweaks to your code. Here's an example that shows how finding by value can work:
#include <iostream>
#include <set>
struct Node {
int value;
Node(int v):value(v) {}
bool operator <(const Node& rhs) const {
return value < rhs.value;
}
};
int main() {
std::set<Node> nodes;
nodes.emplace(1);
nodes.emplace(2);
nodes.emplace(3);
auto found = nodes.find(Node(2));
if (found != nodes.end()) {
std::cout << "Found node " << found->value << std::endl;
}
else {
std::cout << "Didn't find node!" << std::endl;
}
return 0;
}
This prints:
Found node 2
As it is in your question, your std::set will be sorted by memory address (because that's how Node* is sorted), and not by Node::value.
A class (dLinkedList) need to have different c-tors: [please don't suggest to use STL containers!]
explicit dLinkedList(const int value);
dLinkedList(const dLinkedList &rhs);
explicit dLinkedList(size_t numberOfNode, int initializationValue);
dLinkedList(const std::initializer_list<int> &arg);
However, the c-tor with initializer_list seems to be not working. For example, if we construct a class like this:
dLinkedList test1 {10, 108}; // intention is to create 10 elements of 108 value each
But my question is -- based on the arguments - this c-tor can well be considered as one with initializer_list<int>. How do I resolve this conflict?
The implementation of the last two constructors
and the structure of the class dLinkedList are as follow:
class dLinkedList
{
public:
//constructors will go here
explicit dLinkedList(const int value)
{
createFirstNode(value);
nodeCount = new size_t;
updateNodeCount();
}
dLinkedList(const dLinkedList &rhs)
{
Node *temp = rhs.head;
createFirstNode(temp->data);
temp = temp->next;
while(temp)
{
push_back(temp->data);
temp = temp->next;
}
nodeCount = new size_t;
updateNodeCount();
}
explicit dLinkedList(size_t numberOfNode, int initializationValue)
{
createFirstNode(initializationValue);
for(size_t i = 1; i < numberOfNode; ++i)
push_back(initializationValue);
nodeCount = new size_t;
updateNodeCount();
}
/* constructor with std::initializer_list is not working!!
dLinkedList(const std::initializer_list<int> &arg)
{
std::cout << "\n Here\t";
if(arg.size() == 0) dLinkedList(1);
else
{
for(auto it = arg.begin() + 1; it != arg.end(); ++it)
{
push_back(*it);
}
nodeCount = new size_t;
updateNodeCount();
}
}*/
//class destructor will go here
~dLinkedList()
{
clear();
delete nodeCount;
nodeCount = nullptr;
}
//member functions will go here
void push_back(int); // will attach a new node at the end of the list
void push_front(int); // will insert a new node at the beginning of the list
bool insertNode(int, int, bool, bool); // will insert a new node after the existing node (true = first occurrence from the head with value int OTHERWISE if false, then from the tail.)
bool deleteNode(int, bool); // will delete the existing node (true = first occurrence from the head with value int OTHERWISE if false, then from the tail.)
void pop_back(); // will delete the last node in the list
void pop_front(); // will delete the first node in the list
size_t size(); // will return the number of nodes/elements - experimental feature
void printList(bool); // will print the values of the data - (true for ordered list, false for reverse ordered list)
void swap(dLinkedList &rhs); // will swap this linked-list with rhs
//operator overloading go here
dLinkedList& operator = (const dLinkedList &rhs);
dLinkedList& operator + (const dLinkedList &rhs);
dLinkedList& operator += (const dLinkedList &rhs);
dLinkedList& operator >> (const size_t numberOfNodes);
dLinkedList& operator << (const dLinkedList &rhs);
private:
//defining the double linked-list structure
struct Node
{
int data; //this is a generic place holder - will be replaced later with some actual data-structures
Node *next;
Node *previous;
explicit Node(int x) : data(x), next(nullptr), previous(nullptr) {}
};
//member functions go here
void createFirstNode(int val); //will create the first node when the list is empty
void clear(); // will be called when class destructor is called
void updateNodeCount(); // keeps the nodeCount variable up-to-date
bool empty(); // returns true if the list is empty
//some experimental utility functions for internal use
void ectomizeAndClip(Node*);
Node *detectData(int, bool);
void insertBefore(Node*, int);
void insertAfter(Node*, int);
//member variables go here
Node *head {nullptr};
Node *tail {nullptr};
size_t *nodeCount {nullptr}; //experimental feature
};
You do not need to do anything to "resolve the conflict". The language does it for you; initialization in the form
dLinkedList test1 {10, 108};
will always call an initializer_list constructor if possible, falling back to a non-initializer_list constructor only if there are no viable initializer_list constructor candidates.
The problem is that the resolution of the potential conflict did not produce the outcome that you wanted. You want it to be resolved in favour of the two-element constructor.
It is possible to simulate what you want by having the initializer_list constructor delegate to another function only if it has two elements:
explicit dLinkedList(size_t numberOfNode, int initializationValue) {
initialize(numberOfNode, initializationValue);
}
dLinkedList(const std::initializer_list<int> &arg) {
if (arg.size() == 2) {
initialize(arg[0], arg[1]);
return;
}
// otherwise: initialize the list using the values in `arg`
}
private:
void initialize(size_t numberOfNode, int initializationValue) {
// create `numberOfNode` nodes with value `initializationValue`
}
However, I would strongly recommend that you not do this, because it will be terribly confusing for the user if the meanings of the following three declarations are not similar:
dLinkedList test1 {1};
dLinkedList test2 {2, 3};
dLinkedList test3 {4, 5, 6};
In fact, a constructor that takes the number of nodes as the first argument, and the value with which to initialize those nodes as the second argument, is a bad idea in general. It will lead to bugs when users forget which argument is which.
A better way is to create a struct that has the desired number of nodes and desired initial value as members:
struct dLinkedListInit {
int numberOfNodes;
int initialValue;
};
explicit dLinkedList(dLinkedListInit i) {
// ...
}
With C++20 designated initializers, this constructor could be used as follows:
dLinkedList l({.numberOfNodes = 10, .initialValue = 108});
Also, strike the constructor explicit dLinkedList(const int value); since the initializer list constructor can provide the same functionality. It is good to make users write out the braces explicitly to make it obvious that the value inside the braces is meant to be interpreted as an element value.
Let's say we have a priority_queue that holds a bunch of ListNode objects declared as below:
class ListNode {
int val;
ListNode *next;
public:
explicit ListNode(int v) : val(v), next(NULL) {}
inline bool operator<(const ListNode& rhs) const {
return val < rhs.val;
}
};
std::priority_queue<ListNode> pq;
By overriding operator< method or providing a sorting functor we can have the priority_queue hold the ListNode objects in val's ascending order.
My question is if the priority_queue holds the pointers to ListNode class instead can I have the pointers sorted so that the val's pointed are in ascending order. How do I do that?
std::priority_queue<ListNode *> pq1;
Thanks!
As you said, std::priority_queue accepts as third template parameter a comparison functor that it has to use to perform the comparisons.
Just write your own that dereferences the items before comparing them:
template<typename T>
struct PtrLess
{
bool operator()(const T* left, const T* right)
{
return *left < *right;
}
};
std::priority_queue<ListNode *, std::vector< ListNode * >, PtrLess< ListNode > > pq1;
A pointer to ListNode is like an everyday pointer. You cannot overload an operator between two pointers.
However, you can override the comparison operator for the purpose of the priority_queue. It would go something like this:
struct ListNodePtrLess {
bool operator()(const ListNode* a, const ListNode* b) {
return a->val < b->val;
}
};
typedef std::priority_queue<ListNode*, std::vector<ListNode*>, ListNodePtrLess> MyPriorityQueue;
(also: you will need to make ListNodePtrLess a friend of ListNode, or let it access the val field in some different way)
I have a Node class with the members
int weight;
Node *left;
Node *right;
I want to create a heap by using the STL functions
make_heap(Iterator , Iterator, comp)
pop_heap(Iterator, Iterator, comp)
to apply on a vector of Node pointers. How can I create a comparison object (or comparison function) for those functions?
struct node_comparison : public std::binary_function< const Node*, const Node*, bool >
{
bool operator()( const Node* const a, const Node* const b ) const
{
return a->weight < b->weight;
}
};
Note that this comparsion object compares only the weights, but I assume this is desired behaviour.
If you provide strict weak ordering via a operator< for your object you can call the overload of make_heap, pop_heap, etc which don't even need the third argument. comp is so you can provide a custom comparison if you choose.
class Node
{
int weight;
Node *left;
Node *right;
public:
bool operator<(const Node& rhs) const { return weight < rhs.weight; }
};
I have to implement a class that behaves like a map of strings using binary search tree. This is the class I implemented:
template<class T>
class StringMapper {
private:
// Pair
struct Pair {
std::string el1;
T el2;
};
// Nod
struct Node {
Pair* data;
Node* left;
Node* right;
Node()
{
data = new Pair;
}
~Node()
{
delete data;
}
int nod_size()
{
// code here
}
};
Node* root;
public:
StringMapper()
{
root = 0;
}
~StringMapper() {}
void insert(std::string from, const T& to)
{
// code here
}
bool find(std::string from,const T& to) const
{
return find(root, to);
}
bool find(Node* node, const T& value) const
{
// code here
}
bool getFirstPair(std::string& from, T& to)
{
if(root != 0)
{
from = root->data->el1;
to = root->data->el2;
return true;
}
return false;
}
bool getNextPair(std::string& from, T& to)
{
if(root != 0)
{
}
return false;
}
int size() const
{
return root->nod_size();
}
};
To be honest I don't know how to implement the function getNextPair().
If someone could help me I'd appreciate it.
Your interface is an internal iterator. You need to keep some kind of pointer to where you are in the iteration, and set it in getFirstPair().
Once you add this, getNextPair() just goes to the next one. It's somewhat difficult to do this, but that's your assignment, so I leave it to you.
The actual std::map uses an external iterator -- that keeps the state of the iteration separate from the data structure. The major advantage is being able to have more than one simultaneous iteration.
Without just throwing the algorithm for getNextPair, you will need to keep some kind of internal iterator which will point to the "current" pair. Once you got that, in order to figure the algorithm for the next pair draw yourself a tree with some nodes and see how one can find the next node in the tree given any node in the tree.