I want to implement a trie using a vector to store the nodes but somehow my insert method doesn't work. I've managed to build the trie data structure using a different implementation but I would like to understand why my current implementation doesn't work.
Works (not index based storing of childs/references):
struct Trie {
struct Trie *references[26];
bool end; //It is true if node represents end of word.
};
DOESN'T WORK (index based storing of childs/references):
struct node {
int references[26] = {0};
bool end;
};
It doesn't work because of a faulty insert function.
void insert_word(string s){
node *current_node = &trie[0];
// current_node->references[4] = 9999 WORKS! Node in Trie is UPDATED
for(int i=0;i<s.size();i++){
print_trie();
int letter_num = static_cast<int>(tolower(s[i])) - static_cast<int>('a');
int next_index = current_node->references[letter_num];
cout << "letter num: " << letter_num << " next index: " << next_index << endl;
if(next_index == 0){
node new_node;
trie.push_back(new_node);
current_node->references[letter_num] = trie.size()-1; // DOESN'T WORK! Node in Trie is NOT UPDATED
cout << "new value: ";
for(auto c:current_node->references)
cout << c << " ";
cout << endl;
cout << "in for" << endl;
print_trie();
current_node = &trie.back();
} else{
current_node = &trie[next_index];
}
}
current_node->end = true;
}
The problem is that when I access current_node as a reference to an object ob the trie vector and I change its value. The object/node in the trie vector isn't always updated. It works in the second line but further down it somehow stops working. I would like to understand why.
Here is a short debug program I wrote to simplify the problem. Here everything seems to work fine.
n1.references[0] = 1;
n2.references[0] = 2;
n3.references[0] = 3;
trie.push_back(n1);
trie.push_back(n2);
trie.push_back(n3);
node *n = &trie[0];
n->references[0] = 10; // Tree is updated properly
n = &trie[1];
n->references[0] = 11; // Tree is updated properly
Can you help me understand why the insert function doesn't work properly?
EDIT: Minimal working example
#include <vector>
#include <string>
#include <iostream>
using namespace std;
struct node
{
int num_words;
int references [26] = {0};
bool end;
};
vector<node> trie;
int n;
void print_trie(){
cout << "#### NEW PRINT TRIE ##### " << endl;
for(int i=0;i<trie.size();i++){
cout << "node " << i << ": ";
for(int j=0;j<26;j++)
cout << trie[i].references[j] << " ";
cout << endl;
}
}
void insert_word(string s){
node *current_node = &trie[0];
// current_node->references[4] = 9999 WORKS! Node in Trie is UPDATED
for(int i=0;i<s.size();i++){
print_trie();
int letter_num = static_cast<int>(tolower(s[i])) - static_cast<int>('a');
int next_index = current_node->references[letter_num];
cout << "letter num: " << letter_num << " next index: " << next_index << endl;
if(next_index == 0){
node new_node;
trie.push_back(new_node);
current_node->references[letter_num] = trie.size()-1; // DOESN'T WORK! Node in Trie is NOT UPDATED
cout << "new reference value of node: ";
for(auto c:current_node->references)
cout << c << " ";
cout << endl;
current_node = &(trie[trie.size()-1]);
} else{
current_node = &trie[next_index];
}
}
current_node->end = true;
}
int main()
{
node root;
trie.push_back(root);
insert_word("hallohallo");
return 0;
}
Anytime a std::vector<T> undergoes a resizing operation all iterators and pointers to elements are invalidated. Using your mcve as an example of where this goes off the rails, consider the marked lines:
void insert_word(string s){
node *current_node = &trie[0]; // **HERE
for(int i=0;i<s.size();i++){
print_trie();
int letter_num = static_cast<int>(tolower(s[i])) - static_cast<int>('a');
int next_index = current_node->references[letter_num];
cout << "letter num: " << letter_num << " next index: " << next_index << endl;
if(next_index == 0){
node new_node;
trie.push_back(new_node); //** RESIZE
current_node->references[letter_num] = trie.size()-1;
cout << "new reference value of node: ";
for(auto c:current_node->references)
cout << c << " ";
cout << endl;
current_node = &(trie[trie.size()-1]); // **HERE
} else{
current_node = &trie[next_index]; // **HERE
}
}
current_node->end = true;
}
In each location marked with // **HERE, you're storing a pointer to an object hosted in your vector. but the line marked with // **RESIZE can (and will) resize via copy/move/etc the entire vector once the capacity is reached. This means current_node no longer points to a valid object, is a dangling pointer, but your code is none-the-wiser and marches on into undefined behavior.
There are a couple of ways to address this. You could reserve the capacity from inception if you know it ahead of time, but for a more robust solution don't use pointers to begin with. if you enumerate via index instead of pointer your solution becomes the following:
void insert_word(std::string s)
{
size_t idx = 0;
for(int i=0;i<s.size();i++){
print_trie();
int letter_num = static_cast<int>(tolower(s[i])) - static_cast<int>('a');
size_t next_index = trie[idx].references[letter_num];
std::cout << "letter num: " << letter_num << " next index: " << next_index << std::endl;
if(next_index == 0){
trie.emplace_back();
trie[idx].references[letter_num] = trie.size()-1;
std::cout << "new reference value of node: ";
for(auto c : trie[idx].references)
std::cout << c << ' ';
std::cout << std::endl;
idx = trie.size()-1;
} else{
idx = next_index;
}
}
trie[idx].end = true;
}
Notice how all instances of current_node have been replaced with trie[idx]. And changing the "current node" is now just a matter of changing the value of idx, which is relevant even when the underlying vector resizes.
that might be caused by type mismatch int is assigned size_t
try ... = (int)trie.size()-1
#include <vector>
#include <iostream>
using namespace std;
struct node{
int num_words;
int references [26] = {}; //........... int
bool end;
};
vector<node> trie;
int n;
void print_trie(){
cout << "#### NEW PRINT TRIE ##### " << endl;
for(int i=0;i<trie.size();i++){
cout << "node " << i << ": ";
for(int j=0;j<26;j++)
cout << trie[i].references[j] << " ";
cout << endl;
}
}
void insert_word(const string& s){
node *current_node = &trie[0];
// current_node->references[4] = 9999 WORKS! Node in Trie is UPDATED
for(int i=0;i<s.size();i++){
print_trie();
int letter_num = int(tolower(s[i]) - 'a');
int next_index = current_node->references[letter_num];
cout << "letter num: " << letter_num << " next index: " << next_index << endl;
if(next_index == 0){
node new_node;
trie.push_back(new_node);
current_node->references[letter_num] = (int)trie.size()-1; //....size_t DOESN'T WORK! Node in Trie is NOT UPDATED
cout << "new reference value of node: ";
for(auto c:current_node->references)
cout << c << " ";
cout << endl;
current_node = &(trie[trie.size()-1]);
} else{
current_node = &trie[next_index];
}
}
current_node->end = true;
}
int main()
{
node root;
trie.push_back(root);
insert_word("hallohallo");
return 0;
}
Related
I have a bunch of tuples (int array[N], string message) to store. I want to be able to add/delete a lot of elements from this array very quickly but, most importantly, given another array array2, I want to find every string such that for all i : array[i] <= array2[i] (not implemented yet).
Thus, I thought about using a tree of height N where a leaf is a message. If it is a leaf, it should contain a vector if it's a node, it should contain a map.
I am using an union to manage whether a tree is a leaf or a node.
My delete function should delete the leaf and all the nodes that lead only to this leaf.
I can insert a message (or multiple different messages). However, I can't reinsert a message that I previously deleted. It raises a bad_alloc error.
#include <iostream>
#include <map>
#include <vector>
using namespace std;
struct Node{
enum{LEAF, NODE} tag;
union {
std::map<int, struct Node*> map;
std::vector<std::string> msg;
};
Node(std::string m){
tag = LEAF;
cout << "Flag 1 : Crashing here, for some reasons a map is allocated" << "\n";
msg.push_back(m);
cout << "Flag 2 : Did you manage to fix it ?" << "\n";
}
Node(){
tag = NODE;
map = std::map<int, struct Node*>();
}
~Node(){
if (tag==NODE){
map.~map();
} else {
msg.~vector();
}
}
};
void insert(int* array, int size, Node* node, std::string msg){
cout << "Insert\n";
if (size > 1){
if (!node -> map.count(array[0])){
node->map[array[0]] = new Node();
}
insert(array+1, size-1, node->map[array[0]], msg);
} else {
if (!node->map.count(array[0])){
cout << "Case 1\n";
node -> map[array[0]] = new Node(msg);
}
else{
cout << "Case 2\n";
node -> map[array[0]]->msg.push_back(msg);
}
}
}
bool find(int * array, int size, Node * node){
if (!node -> map.count(array[0])){
return false;
}
if (size==1){
return true;
}
return find(array+1, size-1, node->map[array[0]]);
}
std::vector<std::string> find_vec(int * array, int size, Node * node){
if (!node -> map.count(array[0])){
return std::vector<std::string>();
}
if (size==1){
if (!node -> map.count(array[0])){
return std::vector<std::string>();
}
return node -> map[array[0]]->msg;
}
return find_vec(array+1, size-1, node->map[array[0]]);
}
void print_array(std::vector<std::string> v){
for (auto & elem : v){
cout << elem << " ";
}
cout << "\n";
}
void erase(int * array, int size, Node * node){
std::vector<Node*> vec;
int i = 0;
Node *t = node;
while (i < size){
if (t -> map.count(array[i])){
vec.push_back(t);
t = t-> map[array[i]];
} else
break;
i++;
}
if (i == size){
// Deleting the leaf
cout << "Deleting Leaf\n";
delete t;
cout << "Deleting vec [" << size-1 << "] elem " << array[size-1] << "\n";
cout << "Deleted ? " << vec[size-1]->map.erase(array[size-1]) << "\n";
// Deleting the path if it has no other leaf
cout << "Delete Path\n";
for (i = size-1; i > 0; i--){
//cout << "debut loop " << i << "\n";
//vec[i-1]->map.erase(array[i-1]);
if (!vec[i] -> map.size()){
delete vec[i];
cout << "Deleting vec [" << i-1 << "] elem " << array[i-1] << "\n";
cout << "Deleted ? " << vec[i-1]->map.erase(array[i-1]) << "\n";
}
else
break;
//cout << "fin loop\n";
}
}
}
int main()
{
Node * Tree = new Node;
for (int k = 0; k < 2; k++){
cout << "k = " << k << "\n---------------------------------------------------------------------------------------------\n";
int size = 4;
int array[4] = {0,1,2,3};
std::string m1 = "Random message that I want to store as many times as I want";
insert(array, size, Tree, m1);
cout << "find : " << find(array, size, Tree) << "\n";
std::vector<std::string> vec1 = find_vec(array, size, Tree);
cout << "vec ";
print_array(vec1);
cout << "-------------------\n";
erase(array, size, Tree);
cout << "We should find the message \n";
print_array(vec1);
cout << "-------------------\n";
cout << "We should not find the message \n";
vec1 = find_vec(array, size, Tree);
print_array(vec1);
cout << "-------------------\n";
}
return 0;
}
A union should be treated with care, especially when used with non-trivial members like in your example. Specifically of interest is this passage from cppreference:
If a union contains a non-static data member with a non-trivial
special member function (copy/move constructor, copy/move assignment,
or destructor), that function is deleted by default in the union and
needs to be defined explicitly by the programmer.
If a union contains a non-static data member with a non-trivial
default constructor, the default constructor of the union is deleted
by default unless a variant member of the union has a default member
initializer .
The map member is not constructed and therefore you cannot just start using it.
I recommend using std::variant as a safe alternative to a raw union. Your example would look like the following without a need for your enum:
struct Node {
std::variant<std::map<int, Node*>, std::vector<std::string>> data;
Node(std::string m){
data.emplace<1>();
cout << "Flag 1 : Crashing here, for some reasons a map is allocated" << "\n";
std::get<1>(data).push_back(m);
cout << "Flag 2 : Did you manage to fix it ?" << "\n";
}
// ...
};
Goal state: I'm supposed to display a result where by randomized e.g. Set S = {dog, cow, chicken...} where randomized size can be 1-12 and animals cannot be replicated so once there is cow, there cannot be another cow in Set S anymore.
Error: I've been displaying a correct randomized size of 1-12. However I have duplicated animals even though I tried to check whether the animal exist in set S before I insert it into Set S.
UPDATE: I couldnt get it to run after the various updates by stackoverflow peers.
Constraints: I have to use pointers to compare with pointers - dynamically.
"Important Note
All storages used for the arrays should be dynamically created; and delete them when
they are no longer needed.
When accessing an element of the array, you should access it via a pointer, i.e. by
dereferencing this pointer. Using the notation, for example set [k] or *(set + k)
accessing to the kth element of the set is not allowed."
Do hope to hear your advice, pals!
Best regards,
MM
/*
MarcusMoo_A2.cpp by Marcus Moo
Full Time Student
I did not pass my assignment to anyone in the class or copy anyone’s work;
and I'm willing to accept whatever penalty given to you and
also to all the related parties involved
*/
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <ctime>
using namespace std;
/* Global Declaration */
const int MAX = 12; // 12 animals
const int MAXSTR = 10;
typedef char * Element;
static Element UniversalSet [MAX] = {"Rat", "Ox", "Tiger", "Rabbit", "Dragon",
"Snake", "Horse", "Sheep", "Monkey", "Rooster", "Dog", "Pig"};
/* Functions */
// Construct a set
void option0(int); // Menu Option 0
void constructSet (Element *, int); // Construct a set
bool checkElement (Element *, Element *, int); // Check element for replicates
int main()
{
// Declarations
int mainSelect;
int size=rand()%12+1; // Random construct
srand (time(NULL)); // Even better randomization
cout << "Welcome to MARCUS MOO Learning Center" << endl;
do
{
cout << "0. An example of set" << endl;
cout << "1. Union" << endl;
cout << "2. Intersection" << endl;
cout << "3. Complement" << endl;
cout << "4. Subset of" << endl;
cout << "5. Equality" << endl;
cout << "6. Difference " << endl;
cout << "7. Distributive Law" << endl;
cout << "9. Quit" << endl;
cout << endl;
if (mainSelect==0)
{
option0(size);
}
cout << "Your option: ";
cin >> mainSelect;
cout << endl;
} while(mainSelect!=9);
return 0;
}
/* Functions */
// Option 0 - An example of set
void option0 (int size)
{
// Mini Declaration
int again;
Element *S;
do
{
cout << "Here is an example on set of animals" << endl;
cout << endl;
// Build set S
constructSet (S,size);
// Display set S
Element *S = &S[0];
cout << "Set S = {";
for (int i = 0; i < size; i++)
{
if (i!=size)
{
cout << *S
<< ", ";
}
else
{
cout << *S
<< "}"
<< endl;
}
S++;
}
cout << endl;
cout << "Note that elements in S are distinct are not in order" << endl;
cout << endl;
// Option 0 2nd Part
cout << "Wish to try the following operations?" << endl;
cout << "1. Add an element to the set" << endl;
cout << "2. Check the element in the set" << endl;
cout << "3. Check the cardinality" << endl;
cout << "9. Quit" << endl;
cout << endl;
cout << "Your choice: ";
cin >> again;
} while (again!=9);
}
// Construct a set
void constructSet (Element *set, int size)
{
// Declarations
Element *ptrWalk;
ptrWalk = &set[0];
int randomA=0;
for (int i = 0;i<size;i++)
{
bool found = true;
while (found)
{
randomA = rand()%MAX; // avoid magic numbers in code...
*ptrWalk = UniversalSet [randomA];
// Ensure no replicated animals in set S
found = checkElement (ptrWalk, set, i);
}
set=ptrWalk;
set++;
}
}
bool checkElement (Element *ptrWalk, Element *set, int size)
{
for (int j=0; j<size;j++)
{
if (ptrWalk==&set[j])
{
return true;
}
}
return false;
}
You have 2 different major problems in your code. First has already be given by Federico: checkElement should return true as soon as one element was found. Code should become simply (but please notice the < in j<size):
bool checkElement (char *ptrWalk, int size)
{
for (int j=0; j<size;j++)
{
if (ptrWalk==S[j])
{
return true;
}
}
return false;
}
The second problem is that you should not search the whole array but only the part that has already been populated. That means that in constructSet you should call checkElement(ptrWalk, i) because the index of current element is the number of already populate items. So you have to replace twice the line
found = checkElement (*ptrWalk, size);
with this one
found = checkElement (*ptrWalk, i);
That should be enough for your program to give expected results. But if you want it to be nice, there are still some improvements:
you correctly declared int main() but forgot a return 0; at the end of main
you failed to forward declare the functions while you call them before their definition (should at least cause a warning...)
you make a heavy use of global variables which is not a good practice because it does not allow easy testing
your algorithms should be simplified to follow the Dont Repeat Yourself principle. Code duplication is bad for future maintenance because if forces to apply code changes in different places and omission to do so leads to nasty bugs (looks like this is bad but I've already fixed it - yes but only in one place...)
constructSet could simply be:
// Construct a set
void constructSet (Element *set, int size)
{
// Declarations
//Element *ptrBase;
voidPtr *ptrWalk;
ptrWalk = &set[0];
int randomA=0;
for (int i = 0;i<size;i++)
{
bool found = true;
while (found) {
randomA = rand()%MAX; // avoid magic numbers in code...
*ptrWalk = UniversalSet [randomA];
// Ensure no replicated animals in set S
found = checkElement (*ptrWalk, i);
}
ptrWalk++;
}
}
Main problem is that 'break' is missing in checkElement() once it finds the element. If you do not break the loop, it will compare with other indices and overwrite the 'found' flag.
if (ptrWalk==S[j])
{
found = true;
break;
}
Also, use ptrWalk as temporary variable to hold the string. Add the string to S only after you make sure that it is not present already.
void constructSet (Element *set, int size)
{
// Declarations
//Element *ptrBase;
Element ptrWalk;
//ptrWalk = &set[0];
int randomA=0;
int randomB=0;
bool found = false;
for (int i = 0;i<size;i++)
{
randomA = rand()%12;
ptrWalk = UniversalSet [randomA];
// Ensure no replicated animals in set S
found = checkElement (ptrWalk, i);
if (found==true)
{
do
{
// Define value for S
randomB = rand()%12;
ptrWalk = UniversalSet [randomB];
found = checkElement (ptrWalk, i);
} while(found==true);
S[i] = UniversalSet [randomB];
//ptrWalk++;
}
else
{
// Define value for S
S[i] = UniversalSet [randomA];
//ptrWalk++;
}
}
}
You need to optimize your code by removing unnecessary variables and making it less complex.
I have fixed this with the guidance of my C++ lecturer! You guys may take a reference from this to solve your pointers to pointers dilemma next time! Cheers!
/*
MarcusMoo_A2.cpp by Marcus Moo
Full Time Student
I did not pass my assignment to anyone in the class or copy anyone’s work;
and I'm willing to accept whatever penalty given to you and
also to all the related parties involved
*/
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <ctime>
using namespace std;
/* Global Declaration */
const int MAX = 12; // 12 animals
const int MAXSTR = 10;
typedef char * Element;
static Element UniversalSet [MAX] = {"Rat", "Ox", "Tiger", "Rabbit", "Dragon",
"Snake", "Horse", "Sheep", "Monkey", "Rooster", "Dog", "Pig"};
/* Functions */
// Construct a set
void option0(int); // Menu Option 0
void constructSet (Element *, int); // Construct a set
bool checkElement (Element, Element *, int); // Check element for replicates
// This function is to get a random element
// with storage allocated
Element getAnElement ()
{
Element *p = &UniversalSet [0];
int k = rand () % MAX;
for (int i = 0; i < k; i++)
++p;
Element e = new char [MAXSTR];
strcpy (e, *p);
return e;
}
int main()
{
// Declarations
int mainSelect;
int size=rand()%12; // Random construct
srand (time(NULL)); // Even better randomization
cout << "Welcome to MARCUS MOO Learning Center" << endl;
do
{
cout << "0. An example of set" << endl;
cout << "1. Union" << endl;
cout << "2. Intersection" << endl;
cout << "3. Complement" << endl;
cout << "4. Subset of" << endl;
cout << "5. Equality" << endl;
cout << "6. Difference " << endl;
cout << "7. Distributive Law" << endl;
cout << "9. Quit" << endl;
cout << endl;
if (mainSelect==0)
{
option0(size);
}
cout << "Your option: ";
cin >> mainSelect;
cout << endl;
} while(mainSelect!=9);
return 0;
}
/* Functions */
// Option 0 - An example of set
void option0 (int size)
{
// Mini Declaration
int again;
Element *S;
// You need to assign storage
S = new Element [MAX];
for (int i = 0; i < MAX; i++)
S [i] = new char [MAXSTR];
do
{
cout << "Here is an example on set of animals" << endl;
cout << endl;
// Build set S
constructSet (S,size);
// Display set S
Element *p = &S[0]; // Change to p
cout << "Set S = {";
for (int i = 0; i < size; i++)
{
if (i!=size-1)
{
cout << *p
<< ", ";
}
else
{
cout << *p
<< "}"
<< endl;
}
p++;
}
cout << endl;
cout << "Note that elements in S are distinct are not in order" << endl;
cout << endl;
// Option 0 2nd Part
cout << "Wish to try the following operations?" << endl;
cout << "1. Add an element to the set" << endl;
cout << "2. Check the element in the set" << endl;
cout << "3. Check the cardinality" << endl;
cout << "9. Quit" << endl;
cout << endl;
cout << "Your choice: ";
cin >> again;
} while (again!=9);
}
// Construct a set
void constructSet (Element *set, int size)
{
// Declarations
Element *ptrWalk;
ptrWalk = &set[0];
int randomA=0;
Element temp = new char [MAXSTR];
for (int i = 0;i<size;i++)
{
bool found = true;
while (found)
{
// randomA = rand()%MAX; ..
temp = getAnElement ();
// Ensure no replicated animals in set S
found = checkElement (temp, set, i);
}
// set=ptrWalk;
// set++;
strcpy (*ptrWalk, temp);
++ptrWalk;
}
}
bool checkElement (Element ptrWalk, Element *set, int size)
{
Element *p = &set[0];
for (int j=0; j<size;j++)
{
if (strcmp (ptrWalk, *p) == 0)
{
return true;
}
p++;
}
return false;
}
I am trying to create a doubly linked list:
class DblLinkedBag
{
private:
struct node{
string data;
node* next;
node* prev;
}*start=NULL;
int itemCount;
string item;
node *head;
node *tail;
public:
DblLinkedBag();
~DblLinkedBag();
int getCurrentSize();
bool isEmpty();
string add(string value);
bool remove(string item);
void clear();
bool contains(string target);
int getFrequencyOf();
string toVector();
string getItem();
void display();
};
So far, I have gotten add, isEmpty, getCurrentSize and clear to work. Now I just need the rest to work and I am having a hard time. My professor gave us a requirement that the class had to be implemented like this:
#include <iostream>
#include <string>
#include "LinkedBag.h"
using namespace std;
void displayBag(LinkedBag<string>& bag)
{
cout << "The bag contains " << bag.getCurrentSize()
<< " items:" << endl;
vector<string> bagItems = bag.toVector();
int numberOfEntries = (int) bagItems.size();
for (int i = 0; i < numberOfEntries; i++)
{
cout << bagItems[i] << " ";
} // end for
cout << endl << endl;
} // end displayBag
void copyConstructorTester()
{
LinkedBag<string> bag;
string items[] = {"zero", "one", "two", "three", "four", "five"};
for (int i = 0; i < 6; i++)
{
cout << "Adding " << items[i] << endl;
bool success = bag.add(items[i]);
if (!success)
cout << "Failed to add " << items[i] << " to the bag." << endl;
}
displayBag(bag);
LinkedBag<string> copyOfBag(bag);
cout << "Copy of bag: ";
displayBag(copyOfBag);
cout << "The copied bag: ";
displayBag(bag);
} // end copyConstructorTester
void bagTester()
{
LinkedBag<string> bag;
cout << "Testing the Link-Based Bag:" << endl;
cout << "isEmpty: returns " << bag.isEmpty()
<< "; should be 1 (true)" << endl;
displayBag(bag);
string items[] = {"one", "two", "three", "four", "five", "one"};
cout << "Add 6 items to the bag: " << endl;
for (int i = 0; i < 6; i++)
{
bag.add(items[i]);
} // end for
displayBag(bag);
cout << "isEmpty: returns " << bag.isEmpty()
<< "; should be 0 (false)" << endl;
cout << "getCurrentSize: returns " << bag.getCurrentSize()
<< "; should be 6" << endl;
cout << "Try to add another entry: add(\"extra\") returns "
<< bag.add("extra") << endl;
cout << "contains(\"three\"): returns " << bag.contains("three")
<< "; should be 1 (true)" << endl;
cout << "contains(\"ten\"): returns " << bag.contains("ten")
<< "; should be 0 (false)" << endl;
cout << "getFrequencyOf(\"one\"): returns "
<< bag.getFrequencyOf("one") << " should be 2" << endl;
cout << "remove(\"one\"): returns " << bag.remove("one")
<< "; should be 1 (true)" << endl;
cout << "getFrequencyOf(\"one\"): returns "
<< bag.getFrequencyOf("one") << " should be 1" << endl;
cout << "remove(\"one\"): returns " << bag.remove("one")
<< "; should be 1 (true)" << endl;
cout << "remove(\"one\"): returns " << bag.remove("one")
<< "; should be 0 (false)" << endl;
cout << endl;
displayBag(bag);
cout << "After clearing the bag, ";
bag.clear();
cout << "isEmpty: returns " << bag.isEmpty()
<< "; should be 1 (true)" << endl;
} // end bagTester
int main()
{
copyConstructorTester();
bagTester();
return 0;
} // end main
So far this is what I have.
#include <iostream>
#include <string>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
class DblLinkedBag
{
private:
struct node{
string data;
node* next;
node* prev;
}*start=NULL;
int itemCount;
string item;
node *head;
node *tail;
public:
DblLinkedBag();
~DblLinkedBag();
int getCurrentSize();
bool isEmpty();
string add(string value);
bool remove(string item);
void clear();
bool contains(string target);
int getFrequencyOf();
string toVector();
string getItem();
void display();
};
DblLinkedBag::DblLinkedBag()
{
itemCount=0;
item;
}
string DblLinkedBag::add(string value)
{
node* n;
cout<<itemCount<<endl;
if(itemCount==0)
{
n=new node;
n->data=value;
n->prev=NULL;
head=n;
tail=n;
}
if(itemCount>0 && itemCount<7)
{
n= new node;
n->data=value;
n->prev=tail;
tail->next=n;
tail=n;
}
itemCount++;
return value;
}
void DblLinkedBag::display()
{
struct node* temp=start;
while(temp != NULL)
{
cout<<temp->data<<endl;
temp=temp->next;
}
}
int DblLinkedBag::getCurrentSize()
{
return itemCount;
}
bool DblLinkedBag::contains(string target)
{
//need help here, supposed to tell if the linked list contains a certain //string
bool found= false;
node* curPtr=start;
int i=0;
while (!found && (curPtr!=NULL)&& (i<itemCount))
{
if(target==curPtr->data)
{
found=true;
}
else
{
i++;
curPtr=curPtr->next;
}
}
return found;
}
bool DblLinkedBag::isEmpty()
{
bool empty;
if (itemCount==0)
{
empty=true;
}
else
empty=false;
return empty;
}
void DblLinkedBag::clear()
{
node* nodeToDelete=start;
while (start != NULL)
{
start=start->next;
nodeToDelete->next=NULL;
delete nodeToDelete;
}
itemCount=0;
}
bool DblLinkedBag::remove(string item)
{
//need help here
}
string DblLinkedBag::toVector()
{
//need help here this is supposed to send the linked list to a vector
vector<string> vct;
node* curPtr= start;
int counter = 0;
while ((curPtr != NULL) && (counter < itemCount))
{
vct.push_back(curPtr->data);
curPtr = curPtr->next;
counter++;
}
}
int DblLinkedBag::getFrequency()
{//supposed to show how many of a certain item are in the linked list
DblLinkedBag::~DblLinkedBag()
{
}
Any help implementing these class functions to create the other functions my professor gave me would be appreciated, I have tried all different kinds of implementations and I cannot seem to figure it out.
First: your method DblLinkedBag::clear has a error, nodeToDelete never change (just deletes first node)
bool DblLinkedBag::remove(string item)
{
node* curPtr=start;
while (curPtr!=NULL)
{
if(item==curPtr->data)
{
if(curPtr->prev) curPtr->prev->next = curPtr->next;
if(curPtr->next) curPtr->next->prev = curPtr->prev;
delete curPtr;
itemCount--;
return true;
}
else
{
curPtr=curPtr->next;
}
}
return false;
}
What do you expect getFrequency() to do?
I have a small program in which I have two structs:
Person - Consists of id, and basic methods
Ppl - Consists of an array of people with some methods to operate on the array.
struct Person {
const int id;
Person();
Person(int a);
};
Person::Person(int a) : id(a) {}
This is the Person struct with its methods.
const int MAX = 5;
Sets limit on array length
struct Ppl {
static int current; //Keeps track of current position in array
Person *ppls[MAX]; //Creates an array of Person structure pointers
void add(int a); //Adds a person with id to the next available position
//void remove(int b);
int searchid(int c); //Searches ppls for an id c.
Ppl(); //Constructor
};
int Ppl::current = 0; //Initializing static variable
void Ppl::add(int a) {
int ret = searchid(a); //Determine if id a already exists in ppls
//cout << "Ret: " << ret << endl;
if (ret > MAX) { //If value returned is > MAX, value exists
cout << "User " << a << " already exists" << endl;
} else { //else, add it to the next available spot
Person p(a);
ppls[current] = &p;
cout << "Added user: " << a << " at index: " << current << endl;
current++;
}
}
Ppl::Ppl() {
current = 0;
int i = 0;
while (i < MAX) { //Sets all Person pointers to NULL on initialization
ppls[i] = NULL;
cout << "ppls[" << i << "] is NULL" << endl;
i++;
}
}
int Ppl::searchid(int c) {
int i = 0;
bool found = false;
while(i < MAX) {
if (ppls[i] == NULL) { //If NULL, then c wasn't found in array because
break; //there is no NULL between available spots.
} else {
if (ppls[i]->id == c) {
found = true;
}
}
i++;
}
if (found == true) {
return 10; //A number higher than MAX
} else {
return 1; //A number lower than MAX
}
}
The main function is:
int main() {
Ppl people;
people.add(21);
cout << people.ppls[0]->id << endl;
people.add(7);
cout << people.ppls[0]->id << " ";
cout << people.ppls[1]->id << endl;
people.add(42);
cout << people.ppls[0]->id << " ";
cout << people.ppls[1]->id << " ";
cout << people.ppls[2]->id << endl;
people.add(21);
cout << people.ppls[0]->id << " ";
cout << people.ppls[1]->id << " ";
cout << people.ppls[2]->id << " ";
cout << people.ppls[3]->id << endl;
}
The output that I get is:
ppls[0] is NULL
ppls[1] is NULL
ppls[2] is NULL
ppls[3] is NULL
ppls[4] is NULL
Added user: 21 at index: 0
21
Added user: 7 at index: 1
7 0
Added user: 42 at index: 2
42 0 0
Added user: 21 at index: 3
21 0 0 0
Why is it adding all new entries to the beginning of the array while keeping the rest NULL?
Why isn't it detecting that 21 was already added.
I have been going crazy trying to figure this out. Any help would be much appreciated.
Thanks a lot community.
EDIT
I have fixed it so that it adds the elements to the array and recognizes when an id exists.
I made changes to the Ppl struct by adding a destructor:
Ppl::~Ppl() {
int i = 0;
while (i < MAX) {
delete ppls[i];
i++;
}
}
and by changing the add method.
void Ppl::add(int a) {
int ret = searchid(a);
//cout << "Ret: " << ret << endl;
if (ret > MAX) {
cout << "User " << a << " already exists" << endl;
} else {
**Person *p = new Person(a);
ppls[current] = p;**
cout << "Added user: " << a << " at index: " << current << endl;
current++;
}
}
So the output now is
ppls[0] is NULL
ppls[1] is NULL
ppls[2] is NULL
ppls[3] is NULL
ppls[4] is NULL
Added user: 21 at index: 0
21
Added user: 7 at index: 1
21 7
Added user: 42 at index: 2
21 7 42
User 21 already exists
Segmentation fault (core dumped)
What is a segmentation fault and how can I fix it?
Thanks again
Person p(a);
ppls[current] = &p;
is a problem. You are storing a pointer to a local variable. Your program is subject to undefined behavior.
Use
Person* p = new Person(a);
ppls[current] = p;
Make sure to delete the objects in the destructor of Ppl.
Suggestion for improvement
It's not clear what's your objective with this program but you can make your life a lot simpler by using
std::vector<Person> ppls;
instead of
Person *ppls[MAX];
I wrote this testing code for my 8 puzzle project. The problem is the line23 string currentPath can't read anything from the Node. The message for debug is "Cannot evaluate function -- may be inlined". As the result the function cant go through the switch loop.
I dont similar project before, they all work well. Any one know what is the problem?
In the Line 3 of the screen, it suppose to print the string variable "currentPath", but right now it's nothing. As the result the error statement pop out.
#include
#include
#include
#include // string::size_type
#include // std::swap
using namespace std;
struct Node{
string state;
string path;
int depth;
};
Node dequeue;
stack<Node> enqueue;
stack<Node> visitedList;
void AddNextPath(Node *&listpointer){
Node *temp, *newNode;
temp = listpointer;
newNode = new Node;
string currentPath = listpointer->path;
cout << currentPath << endl;
if(currentPath.length() == 9){
string::size_type location = currentPath.find("0"); // Finde char index of '0' in the string
char tempCharArray[9];
strcpy(tempCharArray, currentPath.c_str()); // Convert the string to char array
string newState;
// The required order for traverse each state is U,R,D,L.
// When add new node to stack, this order has to done in reverse order: L,D,R,U
switch (location){
// Each case represents a location of 3x3 graphic map
case 0:
// Move down
swap(tempCharArray[0],tempCharArray[3]);
newState = tempCharArray;
newNode->state = newState;
newNode->path = temp->path.append("D");
newNode->depth = temp->depth + 1;
enqueue.push(*newNode);
// Move right
swap(tempCharArray[0],tempCharArray[1]);
newState = tempCharArray;
newNode->state = newState;
newNode->path = temp->path.append("R");
newNode->depth = temp->depth + 1;
enqueue.push(*newNode);
break;
// case 1:
}
}
else{
cout << "The length of the current state is " << currentPath.length() << endl;
cout << "Warning: state length error!\r\nExit." << endl;
exit(0);
}
}
int main()
{
Node *testNode = new Node;
testNode->depth = 0;
testNode->state = "087654321";
testNode->path = "";
cout << "The current state is " << testNode->state << endl;
cout << "The path is " << testNode->path << endl;
enqueue.push(*testNode);
AddNextPath(testNode);
cout << "The size of enqueue is" << enqueue.size() << endl;
Node topNode;
topNode = enqueue.top();
cout << "The top state is " << topNode.state << endl;
cout << "The path is " << topNode.path << endl;
cout << "The depth is " << topNode.depth << endl;
return 0;
}
#include <stack>
#include <iostream>
using namespace std;
struct Node{
string state;
string path;
int depth;
};
Node dequeue;
stack<Node> enqueue;
stack<Node> visitedList;
void AddNextPath(Node *&listpointer){
Node *temp, *newNode;
temp = listpointer;
newNode = new Node;
string currentPath = listpointer->path;
cout << currentPath.c_str() << endl;
if(currentPath.length() == 9){
string::size_type location = currentPath.find("0"); // Finde char index of '0' in the string
char tempCharArray[10];
strcpy(tempCharArray, currentPath.c_str()); // Convert the string to char array
string newState;
// The required order for traverse each state is U,R,D,L.
// When add new node to stack, this order has to done in reverse order: L,D,R,U
switch (location){
// Each case represents a location of 3x3 graphic map
case 0:
// Move down
swap(tempCharArray[0],tempCharArray[3]);
newState = tempCharArray;
newNode->state = newState;
newNode->path = temp->path.append("D");
newNode->depth = temp->depth + 1;
enqueue.push(*newNode);
// Move right
swap(tempCharArray[0],tempCharArray[1]);
newState = tempCharArray;
newNode->state = newState;
newNode->path = temp->path.append("R");
newNode->depth = temp->depth + 1;
enqueue.push(*newNode);
break;
// case 1:
}
}
else{
cout << "The length of the current state is " << currentPath.length() << endl;
cout << "Warning: state length error!\r\nExit." << endl;
exit(0);
}
}
int main()
{
Node *testNode = new Node;
testNode->depth = 0;
testNode->state = "087654321";
testNode->path = "0dddddddd";
cout << "The current state is " << testNode->state.c_str() << endl;
cout << "The path is " << (testNode->path).c_str() << endl;
enqueue.push(*testNode);
AddNextPath(testNode);
cout << "The size of enqueue is" << enqueue.size() << endl;
Node topNode;
topNode = enqueue.top();
cout << "The top state is " << topNode.state.c_str() << endl;
cout << "The path is " << topNode.path.c_str() << endl;
cout << "The depth is " << topNode.depth << endl;
return 0;
}
I fixed three things.
.c_str()
char tempCharArray[9]; -> char tempCharArray[10];
testNode->path = "0dddddddd";
I might misunderstand your question. but that code is working.