LinkedList sorting a-z - c++

Requirements for this function: If the full name (both the first and last name) is not equal to any full name currently in the list then add it and return true. Elements should be added according to their last name. Elements with the same last name should be added according to
their first names. Otherwise, make no change to the list and return false (indicating that the name is already in the list).
//this function add nodes to the list
//return true if fullname isn't in the list. Else return false if fullname is in the list.
//should be added according to last name.
bool OnlineDating::makeMatch(const std::string& firstName, const std::string& lastName, const OnlineType& value)
{
Node* p = head;
//are these nodes already set to firstName and lastName in this function
Node first;
Node last;
Node* temp = nullptr;
//if the list is empty just insert the fullname and value to the list
if (p == nullptr) {
//add values to the empty list
insertToRear(firstName, lastName, value);
return true;
}
else {
// so this loop is to check if fullname is in the list but first sort in alphebetial order
//sure its added in alphebetical order
//traverse the list after knowing where head is
while (p != nullptr) {
//checking to make sure theres at least another node in the list
if (p->next != nullptr) {
//its not going through ig loop?
//these are used to check and alphebetically selected names
if (p->last > p->next->last) {
insertToRear(p->first, p->last, p->value);
p->next = temp;
return true;
}
else if (p->next->last > p->last) {
insertToRear(p->first, p->last, p->value);
p->next = temp;
return true;
}
//check if full name is already in the list
if (p->last == p->next->last) {
insertToRear(p->first, p->last, p->value);
p->next = temp;
return true;
}
else if (p->first > p->next->first) {
insertToRear(p->first, p->last, p->value);
p->next = temp;
return true;
}
else {
//returns false if it passes through these checks
return false;
}
}
p = p->next;
}
}
}
Here is my main.cpp
int main()
{
OnlineDating clippersGonnaClip;
clippersGonnaClip.makeMatch("Kawhi", "Leonard", 2);
clippersGonnaClip.makeMatch("Paul", "George", 13);
clippersGonnaClip.makeMatch("Ivica", "Zubac", 40);
clippersGonnaClip.makeMatch("Reggie", "Jackson", 1);
clippersGonnaClip.makeMatch("Patrick", "Beverley", 21);
for (int n = 0; n < clippersGonnaClip.howManyMatches(); n++) {
string first;
string last;
int val;
clippersGonnaClip.confirmMatch(n, first, last, val);
cout << first << " " << last << " " << val << endl;
}
return 0;
}
honestly, I just want to create a ptr that will point to each node. Check as long as that node isn't pointing to nullptr, and sort the LinkedList in alphabetical order. finally link the nodes together using my *temp. Why won't it let me go through the if statements every time I compile I get a negative number? Every other function works for this program except this makeMatch(...).

Nothing in the requirements says anything about the data types you need to use. In this case using std::set makes most sense.
By providing a comparison operation on persons and a set you can guarantee uniqueness. Like this :
#include <set>
#include <string>
struct person_t
{
std::string name;
std::string last_name;
unsigned int age;
};
bool operator<(const person_t& lhs, const person_t& rhs)
{
if (lhs.last_name == rhs.last_name)
return lhs.name < rhs.name;
return lhs.last_name < rhs.last_name;
}
int main()
{
// set will ensure all persons are unique
std::set<person_t> persons;
persons.insert({ "Kawhi", "Leonard", 2 });
persons.insert({ "Paul", "George", 13 });
persons.insert({ "Ivica", "Zubac", 40 });
persons.insert({ "Reggie", "Jackson", 1 });
persons.insert({ "Patrick", "Beverley", 21 });
}

Related

How do you search for a specific string in a linked list and return that value?

I have a program that takes in input with 5 parameters. The inputs are video title, url, comment, length, and rating. Then sorts them based on title. The user will need to specify insert (to enter the video information), lookup (look up a video by title and print ONLY that video and its information associated with it), or print (just simply print everything).
for example
input:
insert
Arthur Benjamin: Lightning calculation and other "Mathemagic"
http://www.youtube.com/watch?v=M4vqr3_ROIk
Hard to believe.
15.25
4
lookup
Arthur Benjamin: Lightning calculation and other "Mathemagic"
output:
Arthur Benjamin: Lightning calculation and other "Mathemagic" , http://www.youtube.com/watch?v=M4vqr3_ROIk, Hard to believe., 15.25, 4
my problem is dealing with lookup in main
if(user == "lookup")
{
getline(cin, title);
if(vlistObj -> lookup(videoObj))
{
vlistObj->print();
}
}
and also lookup in my linked list
bool Vlist::lookup(Video *other)
{
Node *node = m_head;
return node->m_next -> m_video->GetTitle() == other-> GetTitle();
}
I am honestly very lost on how to make lookup search for a specific title (assuming lots of video title/info has been given) and only print what I ask (assuming it's in the list).
Here is the complete code:
#include <iostream>
#include <stdlib.h>
#include <cstring>
using namespace std;
class Video {
public:
Video(string video_title, string video_link, string video_comment, double video_length, int video_number);
void print();
const string& GetTitle() const { return title; }
private:
std::string title;
string link;
string comment;
double length;
int rating;
};
Video::Video(string video_title, string video_link, string video_comment, double video_length, int video_number)
: title(video_title), link(video_link), comment(video_comment), length(video_length), rating(video_number)
{
}
void Video::print(){
cout << title << ", " << link << ", " << comment << ", " << length << ", " << rating << endl;
}
class Vlist {
public:
Vlist() {m_head = nullptr; }
bool lookup(Video *other);
void Insert(Video *video);
void print();
private:
class Node {
public:
Node(Video *video, Node *next) {m_video = video; m_next = next; }
Video *m_video;
Node *m_next;
};
Node *m_head;
};
void Vlist::Insert(Video* video)
{
if (m_head == NULL || m_head->m_video -> GetTitle() > video->GetTitle())
{
m_head = new Node(video, m_head);
}
else
{
Node *node = m_head;
while (node->m_next != NULL && node->m_next -> m_video->GetTitle() < video->GetTitle())
{
node = node->m_next;
}
node->m_next = new Node(video, node->m_next);
}
}
bool Vlist::lookup(Video *other)
{
Node *node = m_head;
return node->m_next -> m_video->GetTitle() == other-> GetTitle();
}
void Vlist::print()
{
Video *video;
Node *node = m_head;
while(node != NULL)
{
node -> m_video-> Video::print();
node = node->m_next;
}
}
int main()
{
string sort_type, url, comment, title, user;
int rating;
double length;
int initial = 0, last = 0, number;
Vlist *vlistObj= new Vlist();
Video *videoObj;
while (getline(cin,user)) {
if(user == "insert")
{
getline(cin,title);
getline(cin, url);
getline(cin, comment);
cin >> length;
cin >> rating;
cin.ignore();
videoObj = new Video(title,url, comment, length, rating);
vlistObj->Insert(videoObj);
}
if(user == "lookup")
{
getline(cin, title);
if(vlistObj -> lookup(videoObj))
{
vlistObj->print();
}
}
if(user == "print")
{
vlistObj->print();
}
}
}
Also I do want to note that I am receiving a segmentation fault. But I do know that it is because of my code in lookup. The program runs and output correctly if I do not type lookup
Error is in the Vlist::lookup function, where the current node pointer points to m_next which then points to m_video: m_next is not required, m_head should point directly to m_video.
Here below the complete working code, I also changed something here and there to eliminate all warnings from my compiler
#include <iostream>
#include <stdlib.h>
#include <cstring>
using namespace std;
class Video {
public:
Video(string video_title, string video_link, string video_comment, double video_length, int video_number);
void print();
const string& GetTitle() const { return title; }
private:
string title;
string link;
string comment;
double length;
int rating;
};
Video::Video(string video_title, string video_link, string video_comment, double video_length, int video_number)
: title(video_title), link(video_link), comment(video_comment), length(video_length), rating(video_number)
{
}
void Video::print(){
cout << title << ", " << link << ", " << comment << ", " << length << ", " << rating << endl;
}
class Vlist {
public:
Vlist():m_head(nullptr) {} // init_list
bool lookup(const string& title); // gets user input directly
void Insert(Video *video);
void print();
Video* get(const string& title); // new:returns pointer in list with given title
private:
class Node {
public:
Node(Video *video, Node *next):m_video(video), m_next(next) {} // init_list
Video *m_video;
Node *m_next;
} *m_head; // declared directly together with class definition
};
void Vlist::Insert(Video* video)
{
if (m_head == NULL || m_head->m_video -> GetTitle() > video->GetTitle())
{
m_head = new Node(video, m_head);
}
else
{
Node *node = m_head;
while (node->m_next != NULL && node->m_next -> m_video->GetTitle() < video->GetTitle())
{
node = node->m_next;
}
node->m_next = new Node(video, node->m_next);
}
}
bool Vlist::lookup(const string& title)
{
Node *node = m_head;
while (node->m_next != NULL && node-> m_video->GetTitle() != title)
{
node = node->m_next;
}
return node-> m_video->GetTitle() == title; // there was one pointer too many here
}
void Vlist::print()
{
Node *node = m_head;
while(node != NULL)
{
node -> m_video-> Video::print();
node = node->m_next;
}
}
Video* Vlist::get(const string& title) // returns required item from list
{
Node *node = m_head;
while (node != NULL) {
if (node->m_video->GetTitle() == title)
return node->m_video;
node = node->m_next;
}
return nullptr;
}
int main()
{
string sort_type, url, comment, title, user;
int rating;
double length;
Vlist *vlistObj= new Vlist;
Video *videoObj;
while (getline(cin,user)) {
if(user == "insert")
{
getline(cin,title);
getline(cin, url);
getline(cin, comment);
cin >> length;
cin >> rating;
cin.ignore();
videoObj = new Video(title, url, comment, length, rating);
vlistObj->Insert(videoObj);
}
if(user == "lookup") // more than a few changes here
{
getline(cin, title);
if (vlistObj -> lookup(title))
{
videoObj = vlistObj->get(title);
videoObj->print();
} else {
cout << "not found!\n";
}
}
if(user == "print")
{
vlistObj->print();
}
}
}
BIG EDIT
Previous version did not traverse the Vlist correctly.
Now Vlist is properly searched by the lookup command, that thus finally prints the correct Video.
Overview
Before looking at the specific issues, your code shows you are struggling with putting all the pieces together and are, in a sense, guessing and not paying particular attention to every line in your code. You can't code by just "trying things and see if it works", that will just make you old, gray and frustrated. Take the time to know just exactly what your next lines of code needs to do, craft the line, and then craft a test to ensure it succeeds (or make friends with gdb and check there -- you indicated you are using g++)
Examples:
#include <iostream>
#include <limits>
// #include <stdlib.h>
// #include <cstring>
What were stdlib.h and cstring included for? And:
void Vlist::print()
{
// Video *video; /* unused */
and
// int initial = 0, last = 0, number = 0; /* unused */
How is other initialized? If you are passing a pointer to a Video object, that object must at minimum have the title initialized, so GetTitle() returns a meaningful value...
bool Vlist::lookup(Video *other)
{
Node *node = m_head;
return node->m_next -> m_video->GetTitle() == other-> GetTitle();
}
That doesn't really make much sense?
Take your time and slow-down, understand what you need to do, and then pick up the keyboard (not the other way around)
Be consistent with your use of syntax. You include std::string in places and then simply string in others relying on using namespace std;. See Why is “using namespace std;” considered bad practice?
Further, under no conditions in C++ is there ever a space surrounding ->. That is an operator that joins an object and its member, with nothing in between.
Specific Issues
It is clear that what you have will not output the Video object that matches a lookup when you are attempting to print a Vlist. (doesn't make much sense to print the entire list in response to finding one title of interest). Your lookup() function cannot return bool, instead it must return a pointer to the node containing the title (if found) or nullptr if not found. That means you must save and validate the return in order to know which Node contains the record you want to print. Within main() that looks like:
else if (user == "lookup") {
if (getline(std::cin, title)) {
videoObj = new Video (title); /* you must construct a new videoObj */
Video *video = nullptr; /* you want a Video* pointer returned */
if ((video = vlistObj->lookup(videoObj))) { /* lookup & assign return */
video->print(); /* output the video, not list */
}
else {
std::cout << "title not found: '" << title << "'.\n";
}
}
}
Now that videoObj points to a Video object that has title initialized, the lookup() function can do it's job -- returning a pointer to the node within the list that contains that title (and all the rest of the information) or returning nullptr if the title isn't found. (note the else indicating to the user that condition)
A rewrite of lookup() that does just that would be:
Video *Vlist::lookup (Video *other)
{
Node *node = m_head;
while (node) { /* iterate over nodes in list looking for title */
if (node->m_video->GetTitle() == other->GetTitle())
return node->m_video; /* return pointer to node if found */
node = node->m_next;
}
return nullptr; /* nullptr if not */
}
(Note: simply passing other as std::string makes more sense, but in case you need a Video object -- this is a minimal way to do it)
There were a litany of other cleanups needed, tweaks to initializations, syntax fixes -- removing spaces around ->, etc... that are too numerous to mention. Not to mention to need to ensure you are not leaking memory -- that is left to you (and your properly written destructors) Use valgrind to verify you are freeing all memory before your program exits.
Putting the syntax clean ups and initializations together, you could do something like:
#include <iostream>
#include <limits>
class Video {
public:
Video ( std::string video_title, std::string video_link, std::string video_comment,
double video_length, int video_number );
void print();
const std::string& GetTitle() const { return title; }
private:
std::string title {}, link {}, comment {};
double length;
int rating;
};
Video::Video ( std::string video_title = "",
std::string video_link = "",
std::string video_comment = "",
double video_length = 0, int video_number = 0)
: title(video_title), link(video_link), comment(video_comment),
length(video_length), rating(video_number)
{
}
void Video::print()
{
std::cout << title << ", " << link << ", " << comment << ", " <<
length << ", " << rating << '\n';
}
class Vlist {
public:
Vlist() { m_head = nullptr; }
Video *lookup (Video *other);
void Insert (Video *video);
void print();
private:
class Node {
public:
Node (Video *video = nullptr, Node *next = nullptr) {
m_video = video; m_next = next;
}
Video *m_video;
Node *m_next;
};
Node *m_head;
};
void Vlist::Insert (Video* video)
{
if (m_head == nullptr || m_head->m_video->GetTitle() > video->GetTitle()) {
m_head = new Node (video, m_head);
}
else {
Node *node = m_head;
while (node->m_next != nullptr &&
node->m_next->m_video->GetTitle() < video->GetTitle()) {
node = node->m_next;
}
node->m_next = new Node(video, node->m_next);
}
}
Video *Vlist::lookup (Video *other)
{
Node *node = m_head;
while (node) { /* iterate over nodes in list looking for title */
if (node->m_video->GetTitle() == other->GetTitle())
return node->m_video; /* return pointer to node if found */
node = node->m_next;
}
return nullptr; /* nullptr if not */
}
void Vlist::print()
{
Node *node = m_head;
while (node != nullptr) {
node->m_video->Video::print();
node = node->m_next;
}
}
int main (void) {
std::string sort_type {}, url {}, comment {}, title {}, user {};
int rating = 0;
double length = 0;
Vlist *vlistObj = new Vlist();
Video *videoObj = nullptr;
while (getline(std::cin, user)) {
if (user == "insert") {
if (getline (std::cin, title) &&
getline (std::cin, url) &&
getline (std::cin, comment) &&
std::cin >> length &&
std::cin >> rating) {
std::cin.ignore (std::numeric_limits<std::streamsize>::max(), '\n');
videoObj = new Video (title, url, comment, length, rating);
vlistObj->Insert(videoObj);
}
}
else if (user == "lookup") {
if (getline(std::cin, title)) {
videoObj = new Video (title); /* you must construct a new videoObj */
Video *video = nullptr; /* you want a Video* pointer returned */
if ((video = vlistObj->lookup(videoObj))) { /* lookup & assign return */
video->print(); /* output the video, not list */
}
else {
std::cout << "title not found: '" << title << "'.\n";
}
}
}
else if (user == "print") {
std::cout << "\nlist content:\n";
vlistObj->print();
}
}
}
Example Input File
To minimally exercise your code you need more than one node in your linked list. What about attempting to lookup a node that does not exist -- validate that code-path?
$ cat dat/ll_video2.txt
insert
Arthur Benjamin: Lightning calculation and other "Mathemagic"
http://www.youtube.com/watch?v=M4vqr3_ROIk
Hard to believe.
15.25
4
insert
Arthur Benjamin: Some Other "Mathemagic"
http://www.youtube.com/watch?v=SomeOther
Hard to swallow.
25.25
7
lookup
Arthur Benjamin: Lightning calculation and other "Mathemagic"
lookup
Arthur Benjamin: Some Other "Mathemagic"
lookup
Mickey & Minnie do Disney
print
Example Use/Output
The results of the three lookups are handled correctly and the list contents print as they should in result to the print command input as the last line of input above:
$ ./bin/ll_video <dat/ll_video2.txt
Arthur Benjamin: Lightning calculation and other "Mathemagic", http://www.youtube.com/watch?v=M4vqr3_ROIk, Hard to believe., 15.25, 4
Arthur Benjamin: Some Other "Mathemagic", http://www.youtube.com/watch?v=SomeOther, Hard to swallow., 25.25, 7
title not found: 'Mickey & Minnie do Disney'.
list content:
Arthur Benjamin: Lightning calculation and other "Mathemagic", http://www.youtube.com/watch?v=M4vqr3_ROIk, Hard to believe., 15.25, 4
Arthur Benjamin: Some Other "Mathemagic", http://www.youtube.com/watch?v=SomeOther, Hard to swallow., 25.25, 7
Look things over and let me know if you have further questions.

Inserting a basic singly linked list node seems to break my c++ code?

Singly Linked List and Node classes and the start of the main function, where I wrote a brief outline of the code functionality. The issue is toward the end of the main function. I wrote '...' in place of what I believe to be irrelevant code because it simply parses strings and assigns them to the string temp_hold[3] array.
#include <bits/stdc++.h>
using namespace std;
class Node {
public:
string value;
string attr;
string tagname;
Node *next;
Node(string c_tagname, string c_attr, string c_value) {
this->attr = c_attr;
this->value = c_value;
this->tagname = c_tagname;
this->next = nullptr;
}
};
class SinglyLinkedList {
public:
Node *head;
Node *tail;
SinglyLinkedList() {
this->head = nullptr;
this->tail = nullptr;
}
void insert_node(string c_tagname, string c_attr,string c_value) {
Node *node = new Node(c_tagname,c_attr, c_value);
if (!this->head) {
this->head = node;
} else {
this->tail->next = node;
}
this->tail = node;
}
};
int main(int argc, char **argv) {
/* storage is a vector holding pointers to the linked lists
linked lists are created and the linked list iterator sll_itr is incremented when
previous line begins with '</' and the currentline begins with '<'
linked lists have nodes, which have strings corresponding to tagname, value, and attribute
*/
SinglyLinkedList *llist = new SinglyLinkedList();
vector<SinglyLinkedList*> sllVect;
sllVect.push_back(llist);
auto sll_itr = sllVect.begin();
string temp_hold[3];
// to determine new sll creation
bool prev = false;
bool now = false;
//input
int num1, num2;
cin >> num1; cin >> num2;
//read input in
for (int i = 0; i <= num1; ++i) {
string line1, test1;
getline(cin, line1);
test1 = line1.substr(line1.find("<") + 1);
//determine to create a new linked list or wait
if (test1[0] == '/') {
prev = now;
now = true;
} else {
//make a node for the data and add to current linked list
if (i > 0) {
prev = now;
now = false;
//if last statement starts with '</' and current statment starts with '<'
// then start a new sll and increment pointer to vector<SinglyLinkedList*>
if (prev && !now) {
SinglyLinkedList *llisttemp = new SinglyLinkedList();
sllVect.push_back(llisttemp);
sll_itr++;
}
}
//parse strings from line
int j = 0;
vector<string> datastr;
vector<char> data;
char test = test1[j];
while (test) {
if (isspace(test) || test == '>') {
string temp_for_vect(data.begin(),data.end());
if (!temp_for_vect.empty()) {
datastr.push_back(temp_for_vect);
}
data.clear();
} else
if (!isalnum(test)) {
} else {
data.push_back(test);
}
j++;
test = test1[j];
}
//each node has 3 strings to fill
int count = 0;
for (auto itrs = datastr.begin(); itrs!=datastr.end(); ++itrs) {
switch (count) {
case 0:
temp_hold[count]=(*itrs);
break;
case 1:
temp_hold[count]=(*itrs);
break;
case 2:
temp_hold[count]=(*itrs);
break;
default:
break;
}
count++;
}
}
cout << "before storing node" << endl;
(*sll_itr)->insert_node(temp_hold[0], temp_hold[1], temp_hold[2]);
cout << "after" << endl;
}
cout << "AFTER ELSE" << endl;
return 0;
}
And here is the line that breaks the code. The auto sll_itr is dereferenced which means *sll_itr is now a SinglyLinkedList* and we can call the insert_node(string, string, string) to add a node to the current linked list. However when I keep the line, anything after the else statement brace does not run, which means the cout<<"AFTER ELSE"<< endl; does not fire. If I remove the insert_node line, then the program runs the cout<<"AFTER ELSE"<< endl; I am unsure what the issue is.
(*sll_itr)->insert_node(temp_hold[0],temp_hold[1],temp_hold[2]);
cout << "after" << endl;
} //NOT HANGING. This closes an else statement.
cout << "AFTER ELSE" << endl;
return 0;
}
Compiled as g++ -o myll mylinkedlist.cpp and then myll.exe < input.txt And input.txt contains
8 3
<tag1 value = "HelloWorld">
<tag2 name = "Name2">
</tag2>
</tag1>
<tag5 name = "Name5">
</tag5>
<tag6 name = "Name6">
</tag6>
Your linked list isn't the problem, at least not the problem here.
A recipe for disaster in the making: retaining, referencing, and potentially manipulating, an iterator on a dynamic collection that potentially invalidates iterators on container-modification. Your code does just that. tossing out all the cruft between:
vector<SinglyLinkedList*> sllVect;
sllVect.push_back(llist);
auto sll_itr = sllVect.begin();
....
SinglyLinkedList *llisttemp = new SinglyLinkedList();
sllVect.push_back(llisttemp); // HERE: INVALIDATES sll_iter on internal resize
sll_itr++; // HERE: NO LONGER GUARANTEED VALID; operator++ CAN INVOKE UB
To address this, you have two choices:
Use a container that doesn't invalidate iterators on push_back. There are really only two sequence containers that fit that description: std::forward_list and std::list.
Alter your algorithm to reference by index`, not by iterator. I.e. man your loop to iterate until the indexed element reaches end-of-container, then break.
An excellent discussion about containers that do/do-not invalidate pointers and iterators can be found here. It's worth a read.

Logic flaw in trie search

I'm currently working on a trie implementation for practice and have run into a mental roadbloack.
The issue is with my searching function. I am attempting to have my trie tree be able to retrieve a list of strings from a supplied prefix after they are loaded into the programs memory.
I also understand I could be using a queue/shouldnt use C functions in C++ ect.. This is just a 'rough draft' so to speak.
This is what I have so far:
bool SearchForStrings(vector<string> &output, string data)
{
Node *iter = GetLastNode("an");
Node *hold = iter;
stack<char> str;
while (hold->visited == false)
{
int index = GetNextChild(iter);
if (index > -1)
{
str.push(char('a' + index));
//current.push(iter);
iter = iter->next[index];
}
//We've hit a leaf so we want to unwind the stack and print the string
else if (index < 0 && IsLeaf(iter))
{
iter->visited = true;
string temp("");
stringstream ss;
while (str.size() > 0)
{
temp += str.top();
str.pop();
}
int i = 0;
for (std::string::reverse_iterator it = temp.rbegin(); it != temp.rend(); it++)
ss << *it;
//Store the string we have
output.push_back(data + ss.str());
//Move our iterator back to the root node
iter = hold;
}
//We know this isnt a leaf so we dont want to print out the stack
else
{
iter->visited = true;
iter = hold;
}
}
return (output.size() > 0);
}
int GetNextChild(Node *s)
{
for (int i = 0; i < 26; i++)
{
if (s->next[i] != nullptr && s->next[i]->visited == false)
return i;
}
return -1;
}
bool IsLeaf(Node *s)
{
for (int i = 0; i < 26; i++)
{
if (s->next[i] != nullptr)
return false;
}
return true;
}
struct Node{
int value;
Node *next[26];
bool visited;
};
The code is too long or i'd post it all, GetLastNode() retrieves the node at the end of the data passed in, so if the prefix was 'su' and the string was 'substring' the node would be pointing to the 'u' to use as an artificial root node
(might be completely wrong... just typed it here, no testing)
something like:
First of all, we need a way of indicating that a node represents an entry.
So let's have:
struct Node{
int value;
Node *next[26];
bool entry;
};
I've removed your visited flag because I don't have a use for it.
You should modify your insert/update/delete functions to support this flag. If the flag is true it means there's an actual entry up to that node.
Now we can modify the
bool isLeaf(Node *s) {
return s->entry;
}
Meaning that we consider a leaf when there's an entry... perhaps the name is wrong now, as the leaf might have childs ("y" node with "any" and "anywhere" is a leaf, but it has childs)
Now for the search:
First a public function that can be called.
bool searchForStrings(std::vector<string> &output, const std::string &key) {
// start the recursion
// theTrieRoot is the root node for the whole structure
return searchForString(theTrieRoot,output,key);
}
Then the internal function that will use for recursion.
bool searchForStrings(Node *node, std::vector<string> &output, const std::string &key) {
if(isLeaf(node->next[i])) {
// leaf node - add an empty string.
output.push_back(std::string());
}
if(key.empty()) {
// Key is empty, collect all child nodes.
for (int i = 0; i < 26; i++)
{
if (node->next[i] != nullptr) {
std::vector<std::string> partial;
searchForStrings(node->next[i],partial,key);
// so we got a list of the childs,
// add the key of this node to them.
for(auto s:partial) {
output.push_back(std::string('a'+i)+s)
}
}
} // end for
} // end if key.empty
else {
// key is not empty, try to get the node for the
// first character of the key.
int c=key[0]-'a';
if((c<0 || (c>26)) {
// first character was not a letter.
return false;
}
if(node->next[c]==nullptr) {
// no match (no node where we expect it)
return false;
}
// recurse into the node matching the key
std::vector<std::string> partial;
searchForStrings(node->next[c],partial,key.substr(1));
// add the key of this node to the result
for(auto s:partial) {
output.push_back(std::string(key[0])+s)
}
}
// provide a meaningful return value
if(output.empty()) {
return false;
} else {
return true;
}
}
And the execution for "an" search is.
Call searchForStrings(root,[],"an")
root is not leaf, key is not empty. Matched next node keyed by "a"
Call searchForStrings(node(a),[],"n")
node(a) is not leaf, key is not empty. Matched next node keyed by "n"
Call searchForStrings(node(n),[],"")
node(n) is not leaf, key is empty. Need to recurse on all not null childs:
Call searchForStrings(node(s),[],"")
node(s) is not leaf, key is empty, Need to recurse on all not null childs:
... eventually we will reach Node(r) which is a leaf node, so it will return an [""], going back it will get added ["r"] -> ["er"] -> ["wer"] -> ["swer"]
Call searchForStings(node(y),[],"")
node(y) is leaf (add "" to the output), key is empty,
recurse, we will get ["time"]
we will return ["","time"]
At this point we will add the "y" to get ["y","ytime"]
And here we will add the "n" to get ["nswer","ny","nytime"]
Adding the "a" to get ["answer","any","anytime"]
we're done

Do I need to set my destructor methods when I am using shared pointers?

I tried finding an answer but didn't see one for my particular problem. I am using shared pointers for a ternary search tree (to be used for a predictive text algorithm) and am running into some problems using shared pointers.
I've been away from C++ for 5 years, and let me tell you, Java does not help you learn pointers. I've had to relearn pointer material I learned in school 5-6 years ago over the past couple of days, and have successfully managed to destroy my code.
Here is most of the code I have:
// TernarySearchTree.cc
#include "stdafx.h"
#include "ternary_search_tree.h"
//Constructor
TernarySearchTree::TernarySearchTree() {
num_nodes_ = 0;
size_in_memory_ = 0;
root_node_ = nullptr;
}
TernarySearchTree::TernarySearchTree(const TernarySearchTree& other) {
num_nodes_ = other.num_nodes_;
size_in_memory_ = other.size_in_memory_;
TernarySearchTreeNode node;
node = *other.root_node_;
root_node_.reset(&node);
}
//Destructor
TernarySearchTree::~TernarySearchTree() {
}
//operators
TernarySearchTree& TernarySearchTree::operator=(const TernarySearchTree& other) {
//TODO: swap idiom - create a copy of the node then swap the new one with it
//do this first to provide exception safety
TernarySearchTreeNode node;
node = *other.root_node_;
root_node_.reset(&node);
num_nodes_ = other.num_nodes_;
size_in_memory_ = other.size_in_memory_;
return *this;
}
//Convert from string to c-style string
std::vector<char> TernarySearchTree::ConvertStringToCString(std::string str) {
std::vector<char> wordCharacters (str.begin(), str.end());
//remove newlines or tabs
if (wordCharacters.back() == '\n' || wordCharacters.back() == '\t') {
wordCharacters.pop_back();
}
wordCharacters.push_back('\0');
return wordCharacters;
}
//Insert a node
TernarySearchTreeNode TernarySearchTree::InsertNode(TernarySearchTreeNode &currentNode,
char character,
NodePosition position,
bool isRoot) {
TernarySearchTreeNode newNode;
newNode.set_character(character);
if (!isRoot) {
switch (position) {
case NODE_POS_LEFT:
currentNode.set_left_node(newNode);
break;
case NODE_POS_CENTRE:
currentNode.set_centre_node(newNode);
break;
case NODE_POS_RIGHT:
currentNode.set_right_node(newNode);
break;
default:
break;
}
}
return newNode;
}
//Insert a word
void TernarySearchTree::InsertWord(std::string word) {
std::vector<char> characters = ConvertStringToCString(word);
std::shared_ptr<TernarySearchTreeNode> currentNode = 0;
bool isFirstCharacter = true;
//Add each character to a node while traversing
//Base case where there is no root node
if (!root_node_) {
for(std::vector<char>::iterator it = characters.begin(); it != characters.end(); ++it) {
if (*it != '\0') {
//if it is the first character
//root_node_ is equal to the address of new node
if (isFirstCharacter) {
std::cout << "HIHI";
TernarySearchTreeNode node = InsertNode(*currentNode, *it, NODE_POS_CENTRE, true);
root_node_.reset(&node);
currentNode.reset(&node);
isFirstCharacter = false;
} else {
TernarySearchTreeNode node = InsertNode(*currentNode, *it, NODE_POS_CENTRE, false);
std::cout << std::endl << node.get_character();
currentNode.reset(&node);
}
}
}
//If not base case, then we need to compare each character
} else {
currentNode = root_node_;
for(std::vector<char>::iterator it = characters.begin(); it != characters.end(); ++it) {
if (*it != '\0') {
currentNode.reset(&SetNextNode(*currentNode, *it, *std::next(it, 1)));
} else {
currentNode->set_end_of_word(true);
}
}
}
}
//Recursive function for obtaining/adding the next node when inserting a word
TernarySearchTreeNode TernarySearchTree::SetNextNode(TernarySearchTreeNode &currentNode, const char currentChar, const char nextChar) {
//If characters match
if (currentChar == currentNode.get_character()) {
//if centre node exists
if (currentNode.get_centre_node()) {
return *(currentNode.get_centre_node());
//Otherwise, create a new node and recall method on that node
} else {
//If not the end of the word, make a new node with the next letter
if (nextChar != '\0') {
return InsertNode(currentNode, nextChar, NODE_POS_CENTRE, false);
} else {
return currentNode;
}
}
//If it is less, follow node on the left
} else if (currentChar < currentNode.get_character()) {
//if left node exists, recursive call
if (currentNode.get_left_node()) {
return SetNextNode(*(currentNode.get_left_node()), currentChar, nextChar);
//Otherwise, create a new node and recall method on that node
} else {
return SetNextNode(InsertNode(currentNode, currentChar, NODE_POS_LEFT, false), currentChar, nextChar);
}
//Otherwise it is bigger, so take right path
} else {
//if right node exists, recursive call
if (currentNode.get_right_node()) {
return SetNextNode(*(currentNode.get_right_node()), currentChar, nextChar);
//Otherwise, create a new node and recall method on that node
} else {
return SetNextNode(InsertNode(currentNode, currentChar, NODE_POS_RIGHT, false), currentChar, nextChar);
}
}
}
//Populate the TST from a word list/file
void TernarySearchTree::PopulateTreeFromTextFile(std::string fileName) {
std::ifstream file;
std::string line;
file.open(fileName);
if (file.is_open()) {
//Assume text file has one word per line
while (std::getline(file, line)) {
InsertWord(line);
}
}
}
//Search
bool TernarySearchTree::SearchForWord(std::string word) {
return false;
}
int _tmain(int argc, _TCHAR* argv[])
{
//Test
TernarySearchTree tst;
//Open file
tst.PopulateTreeFromTextFile("simple.txt");
//start at root and follow some paths
std::cout << tst.get_root_node();
/**std::vector<char> vec;
vec.push_back('a');
vec.push_back('c');
std::vector<char>::iterator it = vec.begin();
std::cout << *std::next(vec.begin(), 1);
std::cout << (*it < 'c');
it++;
std::cout << *std::next(it, 0);
std::cout << (*it < 'c');
**/
return 0;
}
and for the nodes:
/*TST node methods */
#include <iostream>
#include "ternary_search_tree_node.h"
/** ADD COPY CONSTRUCTOR*/
//Constructors
TernarySearchTreeNode::TernarySearchTreeNode() {
character_ = '\0';
end_of_word_ = false;
left_node_ = nullptr;
centre_node_ = nullptr;
right_node_ = nullptr;
}
TernarySearchTreeNode::TernarySearchTreeNode(const TernarySearchTreeNode& other) {
character_ = other.character_;
end_of_word_ = other.end_of_word_;
TernarySearchTreeNode leftNode;
leftNode = *other.left_node_;
left_node_.reset(&leftNode);
TernarySearchTreeNode centreNode;
centreNode = *other.centre_node_;
centre_node_.reset(&centreNode);
TernarySearchTreeNode rightNode;
rightNode = *other.right_node_;
right_node_.reset(&rightNode);
}
TernarySearchTreeNode::TernarySearchTreeNode(char character, bool end_of_word,
TernarySearchTreeNode left_node,
TernarySearchTreeNode centre_node,
TernarySearchTreeNode right_node) {
character_ = character;
end_of_word_ = end_of_word;
left_node_.reset(&left_node);
centre_node_.reset(&centre_node);
right_node_.reset(&right_node);
}
//Destructor
TernarySearchTreeNode::~TernarySearchTreeNode() {
left_node_.reset();
centre_node_.reset();
right_node_.reset();
}
//operators
TernarySearchTreeNode& TernarySearchTreeNode::operator=(const TernarySearchTreeNode& other) {
if (&other) {
TernarySearchTreeNode leftNode;
leftNode = *other.left_node_;
TernarySearchTreeNode centreNode;
centreNode = *other.centre_node_;
TernarySearchTreeNode rightNode;
rightNode = *other.right_node_;
left_node_.reset(&leftNode);
centre_node_.reset(&centreNode);
right_node_.reset(&rightNode);
character_ = other.character_;
end_of_word_ = other.end_of_word_;
}
return *this;
}
//printing
std::ostream& operator<<(std::ostream& os, const TernarySearchTreeNode& obj)
{
// write obj to stream
char c = obj.get_character();
bool b = obj.is_end_of_word();
os << c << "\t is end of word: " << b;
return os;
}
When I run in debug mode (Visual Studios), it is able to set the root node, but when it goes to input the second node, it crashes trying to delete "stuff" when currentNode calls .reset(&node) within the else statement of function InsertWord. Am I doing something wrong in the copy constructors or operator= methods, or the destructors? The cout line above it does print the correct letter, so it looks like the node is getting created properly.
The debug call stack shows:
TernarySearchTree.exe!std::_Ref_count_base::_Decref() Line 118 C++
TernarySearchTree.exe!std::_Ptr_base::_Decref()
Line 347 C++
TernarySearchTree.exe!std::shared_ptr::~shared_ptr()
Line 624 C++
TernarySearchTree.exe!std::shared_ptr::reset()
Line 649 C++
TernarySearchTree.exe!TernarySearchTreeNode::~TernarySearchTreeNode()
Line 50 C++ TernarySearchTree.exe!TernarySearchTreeNode::`scalar
deleting destructor'(unsigned int) C++
TernarySearchTree.exe!std::_Ref_count::_Destroy()
Line 161 C++ TernarySearchTree.exe!std::_Ref_count_base::_Decref()
Line 120 C++
TernarySearchTree.exe!std::_Ptr_base::_Decref()
Line 347 C++
TernarySearchTree.exe!std::shared_ptr::~shared_ptr()
Line 624 C++
TernarySearchTree.exe!std::shared_ptr::reset()
Line 649 C++
TernarySearchTree.exe!TernarySearchTreeNode::~TernarySearchTreeNode()
Line 50 C++
TernarySearchTree.exe!TernarySearchTree::InsertWord(std::basic_string,std::allocator
word) Line 105 C++ TernarySearchTree.exe!TernarySearchTree::PopulateTreeFromTextFile(std::basic_string,std::allocator
fileName) Line 182 C++ TernarySearchTree.exe!wmain(int argc, wchar_t * * argv) Line 200 C++
TernarySearchTree.exe!__tmainCRTStartup() Line 533 C
TernarySearchTree.exe!wmainCRTStartup() Line 377 C
kernel32.dll!7592338a() Unknown [Frames below may be incorrect
and/or missing, no symbols loaded for kernel32.dll]
ntdll.dll!77599f72() Unknown ntdll.dll!77599f45() Unknown
Thanks for any help you can provide! And let me know if there is anythign else you need me to provide (the text file I am reading in just has the word cornin it).
Your problem is that you're using Java style in C++. Unlike in Java where everything is essentially a pointer, in C++ you have to think about the difference between values, references, pointers, and object lifetime.
This function is bad:
TernarySearchTreeNode::TernarySearchTreeNode(char character, bool end_of_word,
TernarySearchTreeNode left_node,
TernarySearchTreeNode centre_node,
TernarySearchTreeNode right_node) {
character_ = character;
end_of_word_ = end_of_word;
left_node_.reset(&left_node);
centre_node_.reset(&centre_node);
right_node_.reset(&right_node);
}
You are taking TernarySearchTreeNode objects by value, then putting their address into a shared_ptr. The point of a shared_ptr to to take ownership of a dynamically allocated object (one created using new) and delete it when the reference count goes to zero. The objects above (left_node, etc) are stack objects that will go out of scope at the end of the function. When you put their address into a shared_ptr, it will then try to delete those objects later, but they no longer exist.
As far as recommending how to fix this, there is a whole lot going on here where the assumptions are just off. For instance, can a child node have more than one parent? Does it actually make sense to copy nodes?
I'll assume for the moment that copying nodes makes sense, so using shared_ptr is reasonable. In that case we might start here:
TernarySearchTreeNode TernarySearchTree::InsertNode(std::shared_ptr<TernarySearchTreeNode currentNode>,
char character,
NodePosition position,
bool isRoot) {
auto newNode = std::make_shared<TernarySearchTreeNode>();
newNode->set_character(character);
if (!isRoot) {
switch (position) {
case NODE_POS_LEFT:
currentNode->set_left_node(newNode);
Then all of your functions like set_left_node should also take std::shared_ptr<TernarySearchNode> as parameters. You should not be calling reset(), which exists to allow a shared_ptr to take ownership (refcount == 1) of a free pointer. shared_ptr works by incrementing the reference count on copy and dereferencing in the destructor. When you dereference the pointer and then take the address, you are working around the shared_ptr.

Searching a linked list, Different data types

I've been tasked with making a program that searches 400+ movies (linked together using a linked list) by title, genre, year, rating, lead actor, etc.
There is a catch though, we are only allowed ONE search function to preform the searches through the linked list. Also, within that search function, we are only allowed one while loop—which I assume in my case will be something like...
while (moviePtr != NULL)
Obviously their will be many different instances if its an actor search, or a genre search. In the case of actor, genre, rating, year, sub-genre, and supporting actor, it should output every single instance of it was found. (for example, if Kevin Bacon was in x-men and the notebook, it should output both not just one of them (to the output file not the screen)).
I've found myself completely stumped by these restrictions that we were given.
How will my search function handle different data types? (year and rating have to be declared as integers). How will it know what exactly I'm searching for? If I'm searching for actor, I don't want it to search for title as well.
Any suggestions on how to get started and get going are very appreciated.
EDIT:
Hi all thought id update you guys on what ive done. I have 3 different search functions. One for the numerical values (the year and the rating), 1 for the genre and actors, and lastly one for the title.
Here is the code for all three of them.
First off title seach.
void TitleSearched(MovieNode*head,
string titleSearched,
ofstream& outFile)
{
MovieNode* moviePtr;
bool found;
moviePtr = head;
found = false;
while (moviePtr !=NULL & !found)
{
if (moviePtr-> title == titleSearched)
{
found = true;
}
else
{
moviePtr = moviePtr -> next;
}
}
if (found)
{
cout << endl << titleSearched << " has been found!\n";
TitleOutput (moviePtr,outFile);
}
else
{
cout << endl << titleSearched << " was not found.\n";
}
}
now the year/ rating search.
int NumSearched(MovieNode* head, int numSearched)
{
int instances;
MovieNode* moviePtr;
ofstream outFile;
moviePtr = head;
instances = 0;
while (moviePtr !=NULL)
{
if (moviePtr-> year == numSearched)
{
instances = instances +1;
NumOutList(moviePtr,outFile,"year",numSearched,instances);
moviePtr = moviePtr -> next;
}
else if (moviePtr->rating == numSearched)
{
instances = instances +1;
NumOutList(moviePtr,outFile,"rating",numSearched,instances);
moviePtr = moviePtr -> next;
}
else
{
moviePtr = moviePtr ->next;
}
}
return instances;
}
lastly the genre/actors search.
int ItemSearch (MovieNode* head,string itemSearched, ofstream& outFile)
{
int instances;
MovieNode* moviePtr;
moviePtr = head;
instances = 0;
while (moviePtr !=NULL)
{
if (moviePtr-> genre == itemSearched || moviePtr ->subGenre == itemSearched)
{
instances = instances +1;
OutList(moviePtr,outFile,"Genre",itemSearched,instances);
moviePtr = moviePtr -> next;
}
else if (moviePtr->leadActor == itemSearched || moviePtr->supportActor == itemSearched)
{
instances = instances +1;
OutList(moviePtr,outFile,"Actor",itemSearched,instances);
moviePtr = moviePtr -> next;
}
else
{
moviePtr = moviePtr ->next;
}
}
return instances;
}
I wanted to remind you guys what my task was.
1. Combine these three search functions into one
2.have only ONE while loop in the search
3. only one return in any given function (however, id assume this would be a void function when combined)
My main issue i beileve is my ints and strings. I am not allowed to declare rating or year as strings. And just the format of the code in combing all three in general is giving me a head ache
You could write your search function in a way which accepts a predicate as a parameter. A predicate is some kind of "functionoid" (meaning, anything which has the ability to be "called" like a function - it could be a function, or a lambda, or a function object..)
in the C++ standard library, predicates are used for many of the standard algorithms, so it's common that you'll see code (using standard containers) like this:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cctype>
bool begins_with_s(std::string s)
{
return s.length() > 0 &&
std::toupper( s.at(0) ) == 'S';
}
bool contains_a_number(std::string s)
{
return std::find_if(s.begin(), s.end(), std::isdigit) != s.end();
}
int main()
{
std::string movies_array[] =
{
"King Kong",
"Singin in the Rain",
"Die Hard 2",
"Superman",
"Star Wars",
"Jaws 3"
};
std::vector<std::string> movies( std::begin(movies_array),
std::end(movies_array) );
// Use predicate - count if the movie name begins with "S"
std::cout << "Movies beginning with S: "
<< std::count_if(movies.begin(), movies.end(), begins_with_s)
<< std::endl;
// Use predicate - count if the movie name contains a number
std::cout << "Movies containing a number: "
<< std::count_if(movies.begin(), movies.end(), contains_a_number)
<< std::endl;
}
The way that C++ standard algorithms are implemented in this way is to accept a template argument representing the predicate, along the lines of
template< typename PredicateType >
void my_function(PredicateType predicate)
{
Movie my_movie;
predicate(my_movie);
}
It's a technique from the functional programming school of thinking - passing a function to a function (Treating a function as a "first class citizen").
You could have your search function take a "match" function as one its parameters, and call this match function on every movie to see whether the movie matches. You can then call your search function with different match functions.
Something like this:
template <typename MatchFunction>
void search_movies(movie* moviePtr, MatchFunction match)
{
while (moviePtr != NULL)
{
if (match(*moviePtr))
{
// output movie
}
moviePtr = moviePtr->next;
}
}
You could then declare match functions like this:
bool matches_actor(movie& m, const std::string& actor)
{
return m.actor == actor;
}
and call it with specific queries like this:
search_movies(moviePtr, std::bind(matches_actor, _1, "Morgan Freeman"));
(std::bind is a C++11 function from <functional>; you could equivalently use boost::bind or std::bind2nd)
Alternately, if you prefer a more C-style way of doing things, you could do something like this:
void search_movies(movie* moviePtr, bool (*match)(movie*, void*), void* match_arg)
{
while (moviePtr != NULL)
{
if (match(moviePtr, match_arg))
{
// output movie
}
moviePtr = moviePtr->next;
}
}
...
bool matches_actor(movie* m, void* actor)
{
return m.actor == *((std::string*)actor);
}
...
std::string actor = "Morgan Freeman";
search_movies(moviePtr, &matches_actor, (void*)(&actor));
Besides the option of passing a functor to check the match, there are other options. One such option would be taking a set of optional conditions to check (you can use boost::optional or a handcrafted approach, or use pointers. For example:
void print_matching( node* list, int * year, std::string * actor... ) {
// iterate over the list:
while (...) {
if ( ( !year || ptr->year == *year )
&& ( !actor || ptr->actor == *actor )
&& ...
)
{
// Film matches, print it
}
}
}
To simplify the function signature you can create a search_pattern type that encapsulates the fields that you want to test (example using a different approach: bools to determine optionality):
struct pattern {
bool check_actor;
std::string actor;
bool check_year;
int year;
};
void print_matching( node* list, pattern const & p ) {
// iterate over the list:
while (...) {
if ( ( !p.check_year || ptr->year == p.year )
&& ( !p.check_actor || ptr->actor == p.actor )
&& ...
)
{
// Film matches, print it
}
}
}
In this last case, you can actually move the test into the pattern object, and have a function:
bool pattern::matches( movie cosnt& m ) const {
return (!check_year || m.year == year )
&&(!check_actor || m.actor == actor );
}
void print_matching( node* list, pattern const & p ) {
// iterate over the list:
while (...) {
if ( p.matches( list->data ) )
{
// Film matches, print it
}
}
}