I'm new to C++ and I'm trying to code a linked-list based queue. My test program worked fine until I added delete to the dequeue function. Then I get the error:
malloc: *** error for object 0x7f8a61403a00: pointer being freed was not allocated
Any help would be appreciated.
#include <string>
#include "queue.h"
#include <iostream>
using namespace std;
Queue::Queue() { // Constructs a new empty queue.
current_size = 0; // number of elements in queue
front_p = NULL; // first element in queue
back_p = NULL; // last element in queue
}
Queue::Queue( const Queue& q ) {// Copy constructor.
// nothing to copy if queue empty
if (q.current_size == 0) {
return;
}
// queue not empty
else {
node * p = q.front_p;
node * n;
// assign front pointer
n = new node(p -> data, NULL);
front_p = n;
p = p -> next;
// middle elements
while (p -> next != NULL) {
n -> next = p;
n = new node(p -> data, NULL);
p = p -> next;
}
// assign back pointer
n = new node(p -> data, NULL);
back_p = n;
current_size = q.current_size;
}
}
void Queue::enqueue( int item ) { // Enqueues <item> to back
node * n = new node(item, NULL);
// first item in queue, front & back ptrs point to same element
if (back_p == NULL) {
front_p = n;
back_p = n;
}
else {
back_p -> next = n;
back_p = n;
}
current_size++;
}
int Queue::dequeue() { // removes and returns the front item.
// empty queue
if (current_size == 0) {
return -1;
}
int item = front();
node * p = front_p;
if (current_size == 1) {
front_p = NULL;
back_p = NULL;
delete p;
current_size--;
return item;
}
else {
front_p = front_p -> next;
delete p;
current_size--;
return item;
}
}
test file
//test file
#include <iostream>
#include "queue.h"
using namespace std;
int main(void) {
Queue q1;
cout << "Create a new queue q1" << endl;
cout << "Size of q1 \t" << q1.size() << endl;
cout << "Is q1 empty? \t" << q1.empty() << endl;
cout << endl << endl;
cout << "enqueue \t1,2,3,4,5" << endl;
q1.enqueue(1);
q1.enqueue(2);
q1.enqueue(3);
q1.enqueue(4);
q1.enqueue(5);
cout << "front of q1 \t" << q1.front() << endl;
cout << "size of q1 \t" << q1.size() << endl << endl;
cout << "q2 is a deep copy of q1" << endl;
Queue q2(q1);
cout << "front of q2 \t" << q2.front() << endl;
cout << "size of q2 \t" << q2.size() << endl << endl;
cout << "removed 4 from q1" << endl;
q1.remove(4);
cout << "removed 2 from q2" << endl;
q2.remove(2);
cout << endl;
cout << "print out remaining elements of q1" << endl;
int N = q1.size();
for(int i=0; i < N; i++) {
cout << q1.dequeue() << " ";
}
cout << endl << endl;
cout << "print out remaining elements of q2" << endl;
N = q2.size();
for(int i=0; i < N; i++) {
cout << q2.dequeue() << " ";
}
cout << endl;
}
header file
class Queue
{
public:
Queue(); // Constructs a new empty queue.
Queue( const Queue& q );// Copy constructor.
~Queue();// Destructor.
void enqueue( int item ); // Enqueues <item>.
int dequeue(); // Dequeues the front item.
int front(); // Returns the front item without dequeuing it.
bool empty(); // Returns true iff the queue contains no items.
int size(); // Returns the current number of items in the queue.
bool remove(int item); // If <item> occurs in the queue, removes the
// first occurrence of <item> and returns true; otherwise returns false.
private:
class node // node type for the linked list
{
public:
node(int new_data, node * next_node){
data = new_data ;
next = next_node ;
}
int data ;
node * next ;
};
node * front_p ; // pointer to the (node containing the) next item
// which to be dequeud, or NULL if the queue is empty.
node * back_p ; // pointer to the (node containing the) last item
// which was enqueued, or NULL if the queue is empty.
int current_size ; // current number of elements in the queue.
};
There are other issues with the code, but the most immediate one is that your copy-constructor is broken.
In this cycle
while (p -> next != NULL) {
n -> next = p;
n = new node(p -> data, NULL);
p = p -> next;
}
p is a pointer to an element of a source queue q. Meanwhile, n is an element of the new queue. By doing n -> next = p; you are making the new queue nodes to link into the source queue node chain. This creates a completely nonsensical node linkage structure, which falls apart later.
Related
I am working on a Linked List assignment. The Load Bids function extracts the data from the CSV file. The Print List function is supposed to print all the bid entries in the list to the console. For some reason, whenever I select the Load Bids option, and then the Print List option from menu, the Print List function is not outputting data, even after I call the Load Bids function. It just prints the default values from the default constructor, which is basically nothing. The Print List function seems to be written correctly, but the data appears to be getting lost somewhere. Can someone help me with this?
#include <algorithm>
#include <iostream>
#include <time.h>
#include "CSVparser.hpp"
using namespace std;
//============================================================================
// Global definitions visible to all methods and classes
//============================================================================
// forward declarations
double strToDouble(string str, char ch);
// define a structure to hold bid information
struct Bid {
string bidId; // unique identifier
string title;
string fund;
double amount;
Bid() {
amount = 0.0;
}
};
//============================================================================
// Linked-List class definition
//============================================================================
/**
* Define a class containing data members and methods to
* implement a linked-list.
*/
class LinkedList {
private:
//Internal structure for list entries, housekeeping variables
struct Node {
Bid bid;
Node *next;
// default constructor
Node() {
next = nullptr;
}
// initialize with a bid
Node(Bid aBid) {
bid = aBid;
next = nullptr;
}
};
Node* head;
Node* tail;
int size = 0;
public:
LinkedList();
virtual ~LinkedList();
void Append(Bid bid);
void Prepend(Bid bid);
void PrintList();
void Remove(string bidId);
Bid Search(string bidId);
int Size();
};
/**
* Default constructor
*/
LinkedList::LinkedList() {
// FIXME (1): Initialize housekeeping variables
//set head and tail equal to null
head = nullptr;
tail = nullptr;
}
/**
* Destructor
*/
LinkedList::~LinkedList() {
// start at the head
Node* current = head;
Node* temp;
// loop over each node, detach from list then delete
while (current != nullptr) {
temp = current; // hang on to current node
current = current->next; // make current the next node
delete temp; // delete the orphan node
}
}
/**
* Append a new bid to the end of the list
*/
void LinkedList::Append(Bid bid) {
// FIXME (2): Implement append logic
//Create new node
Node* new_node = new Node;
//if there is nothing at the head...
if (head == nullptr) {
// new node becomes the head and the tail
head = new_node;
tail = new_node;
}
//else
else {
// make current tail node point to the new node
tail->next = new_node;
// and tail becomes the new node
tail = new_node;
}
//increase size count
size++;
}
/**
* Prepend a new bid to the start of the list
*/
void LinkedList::Prepend(Bid bid) {
// FIXME (3): Implement prepend logic
// Create new node
Node* new_node = new Node;
// if there is already something at the head...
if (head != nullptr) {
// new node points to current head as its next node
new_node->next = head;
}
// head now becomes the new node
head = new_node;
//increase size count
size++;
}
/**
* Simple output of all bids in the list
*/
void LinkedList::PrintList() {
// FIXME (4): Implement print logic
// start at the head
Node* current = head;
// while loop over each node looking for a match
while (current != nullptr) {
//output current bidID, title, amount and fund
cout << current->bid.bidId << " | ";
cout << current->bid.title << " | ";
cout << current->bid.amount << " | ";
cout << current->bid.fund << endl;
//set current equal to next
current = current->next;
}
}
/**
* Remove a specified bid
*
* #param bidId The bid id to remove from the list
*/
void LinkedList::Remove(string bidId) {
// FIXME (5): Implement remove logic
// special case if matching node is the head
if (head->bid.bidId == bidId) {
// make head point to the next node in the list
head->next;
//decrease size count
size--;
//return
return;
}
// start at the head
Node* current = head;
Node* temp = nullptr;
// while loop over each node looking for a match
while (current != nullptr) {
// if the next node bidID is equal to the current bidID
if (current->next->bid.bidId == current->bid.bidId) {
// hold onto the next node temporarily
temp = current->next;
}
// make current node point beyond the next node
current->next->next;
// now free up memory held by temp
free(temp);
// decrease size count
size--;
//return
return;
}
// curretn node is equal to next node
current = current->next;
}
/**
* Search for the specified bidId
*
* #param bidId The bid id to search for
*/
Bid LinkedList::Search(string bidId) {
// FIXME (6): Implement search logic
// special case if matching node is the head
if (head->bid.bidId == bidId) {
// make head point to the next node in the list
head->next;
//decrease size count
size--;
//return
return head->bid;
}
// start at the head of the list
Node* current = head;
// keep searching until end reached with while loop (next != nullptr
while (current != nullptr) {
// if the current node matches, return it
if (current->bid.bidId == bidId) {
return current->bid;
}
// else current node is equal to next node
else {
current = current->next;
}
}
//return bid
return current->bid;
}
/**
* Returns the current size (number of elements) in the list
*/
int LinkedList::Size() {
return size;
}
//============================================================================
// Static methods used for testing
//============================================================================
/**
* Display the bid information
*
* #param bid struct containing the bid info
*/
void displayBid(Bid bid) {
cout << bid.bidId << ": " << bid.title << " | " << bid.amount
<< " | " << bid.fund << endl;
return;
}
/**
* Prompt user for bid information
*
* #return Bid struct containing the bid info
*/
Bid getBid() {
Bid bid;
cout << "Enter Id: ";
cin.ignore();
getline(cin, bid.bidId);
cout << "Enter title: ";
getline(cin, bid.title);
cout << "Enter fund: ";
cin >> bid.fund;
cout << "Enter amount: ";
cin.ignore();
string strAmount;
getline(cin, strAmount);
bid.amount = strToDouble(strAmount, '$');
return bid;
}
/**
* Load a CSV file containing bids into a LinkedList
*
* #return a LinkedList containing all the bids read
*/
void loadBids(string csvPath, LinkedList *list) {
cout << "Loading CSV file " << csvPath << endl;
// initialize the CSV Parser
csv::Parser file = csv::Parser(csvPath);
try {
// loop to read rows of a CSV file
for (int i = 0; i < file.rowCount(); i++) {
// initialize a bid using data from current row (i)
Bid bid;
bid.bidId = file[i][1];
bid.title = file[i][0];
bid.fund = file[i][8];
bid.amount = strToDouble(file[i][4], '$');
// cout << bid.bidId << ": " << bid.title << " | " << bid.fund << " | " <<
bid.amount << endl;
// add this bid to the end
list->Append(bid);
}
} catch (csv::Error &e) {
std::cerr << e.what() << std::endl;
}
}
/**
* Simple C function to convert a string to a double
* after stripping out unwanted char
*
* credit: http://stackoverflow.com/a/24875936
*
* #param ch The character to strip out
*/
double strToDouble(string str, char ch) {
str.erase(remove(str.begin(), str.end(), ch), str.end());
return atof(str.c_str());
}
/**
* The one and only main() method
*
* #param arg[1] path to CSV file to load from (optional)
* #param arg[2] the bid Id to use when searching the list (optional)
*/
int main(int argc, char* argv[]) {
// process command line arguments
string csvPath, bidKey;
switch (argc) {
case 2:
csvPath = argv[1];
bidKey = "98109";
break;
case 3:
csvPath = argv[1];
bidKey = argv[2];
break;
default:
csvPath = "eBid_Monthly_Sales_Dec_2016.csv";
bidKey = "98109";
}
clock_t ticks;
LinkedList bidList;
Bid bid;
int choice = 0;
while (choice != 9) {
cout << "Menu:" << endl;
cout << " 1. Enter a Bid" << endl;
cout << " 2. Load Bids" << endl;
cout << " 3. Display All Bids" << endl;
cout << " 4. Find Bid" << endl;
cout << " 5. Remove Bid" << endl;
cout << " 9. Exit" << endl;
cout << "Enter choice: ";
cin >> choice;
switch (choice) {
case 1:
bid = getBid();
bidList.Append(bid);
displayBid(bid);
break;
case 2:
ticks = clock();
loadBids(csvPath, &bidList);
cout << bidList.Size() << " bids read" << endl;
ticks = clock() - ticks; // current clock ticks minus starting clock ticks
cout << "time: " << ticks << " milliseconds" << endl;
cout << "time: " << ticks * 1.0 / CLOCKS_PER_SEC << " seconds" << endl;
break;
case 3:
bidList.PrintList();
break;
case 4:
ticks = clock();
bid = bidList.Search(bidKey);
ticks = clock() - ticks; // current clock ticks minus starting clock ticks
if (!bid.bidId.empty()) {
displayBid(bid);
} else {
cout << "Bid Id " << bidKey << " not found." << endl;
}
cout << "time: " << ticks << " clock ticks" << endl;
cout << "time: " << ticks * 1.0 / CLOCKS_PER_SEC << " seconds" << endl;
break;
case 5:
bidList.Remove(bidKey);
break;
}
}
cout << "Good bye." << endl;
return 0;
}
I altered the Append function by adding a couple of lines after creating the new node and it looks like the problem was solved. Here is the new function.
void LinkedList::Append(Bid bid) {
// FIXME (2): Implement append logic
//Create new node
Node* new_node = new Node;
new_node->bid = bid;
new_node->next = nullptr;
//if there is nothing at the head...
if (head == nullptr) {
// new node becomes the head and the tail
head = new_node;
tail = new_node;
}
//else
else {
// make current tail node point to the new node
tail->next = new_node;
// and tail becomes the new node
tail = new_node;
}
//increase size count
size++;
}
This is an interactive test program for the List class as provided by the LinkedList.h.
The program responds to commands from the user to exercise commands as described in the menu() function.
The program will read and respond to commands that the user enters to manipulate an ordered list.
INPUT- The program reads commands to manipulate its List. These commands consist of a letter sometimes followed by an integer. For example, the command "i 25" tells the program to insert the value 25 into the List.
OUTPUT-The program writes instructions and a menu of commands to the terminal, it prompts for the user's input, and it outputs lists and elements of lists.
ERRORS-The program may assume that the input the user provides is correct; except that it will report if a position is beyond the end of a list (See the example below). Otherwise, it need not detect any errors.
This program responds to commands the user enters to manipulate an ordered list of integers, which is initially empty. In the following commands, k is a position in the list, and v is an integer.
e -- Re-initialize the list to be empty.
i v -- Insert the value v into the list.
r v -- Remove the value v from the list.
m -- Is the list empty?
l -- Report the length of the list.
p v -- Is the value v present in the list?
k k1 -- Report the k1th value in the list.
w -- Write out the list.
h -- See this menu.
q -- Quit.
header file
#ifndef LINKEDLIST_H
#define LINKEDLIST_H
#include <cstdlib> // Provides size_t
#include <iostream> // Provides ostream
using namespace std;
class List
{
public:
// TYPEDEF
typedef int Item; // What can go in a list
// CONSTRUCTORS
List( ) { first = NULL; } // Inline
List( const List& source ); // Copy constructor
// DESTRUCTOR
~List( );
// MODIFICATION MEMBER FUNCTIONS
void make_empty ( );
void insert ( const Item& entry );
void remove ( const Item& target );
void operator = ( const List& source );
// CONSTANT MEMBER FUNCTIONS
bool isEmptyList( ) const {return first==NULL;} // Inline
int length( ) const;
bool present ( const Item& target ) const;
Item kth (int k ) const;
void quit() const;
// FRIEND FUNCTION for the List class:
friend std::ostream& operator << ( std::ostream& out_s,
const List& l );
private:
// DATA MEMBERS
struct Node
{
Item data;
Node *next;
};
Node *first;
cpp file
#include "LinkedList.h"
#include <cstdlib>
#include <iostream>
#include <cassert>
using namespace std;
// Default constructor is inline.
// Copy constructor
List::List(const List& source)
{
Node* p; // Will traverse the source List.
Node* last; // Will always point to the new List's last Node.
if ( source.first == NULL ) // If the source list is empty ...
first = NULL;
else
{
first = get_node(source.first->data,NULL); // Copy the first Node.
last = first;
p = source.first->next;
while ( p != NULL ) // Copy remaining Nodes.
{
last->next = get_node(p->data,NULL);
last = last->next;
p = p->next;
}
}
}
// Destructor
List::~List( )
{
Node* temp;
while ( first != NULL )
{
temp = first;
first = first -> next;
delete temp;
}
} // end destructor
// Modification member functions
void List::make_empty ( )
{
Node* temp;
while ( first != NULL )
{
temp = first;
first = first -> next;
delete temp;
}
}
void List::insert ( const Item& entry )
{
Node *prev;
assert ( ! present(entry) );
if ( first == NULL || entry < first->data )
first = get_node(entry,first);
else
{
prev = first;
while ( prev->next != NULL && prev->next->data < entry )
prev = prev->next;
prev->next = get_node(entry,prev->next);
}
} // end insert
void List::remove ( const Item& target )
{
Node *temp;
Node *prev;
assert ( present(target) );
prev = first;
if ( prev->data == target )
{
first = first->next;
delete prev;
}
else
{
while ( prev->next != NULL && prev->next->data < target )
prev = prev->next;
temp = prev->next;
prev->next = temp->next;
delete temp;
}
} // end remove
// Constant member functions
int List::length( ) const
{
Node *p;
int k;
k= 0;
for(p = first; p != NULL; p = p->next)
{
++k;
p=p->next ;
}
return k;
} // end length
bool List::present ( const Item& target ) const
{
Node *p = first;
for(p!=NULL && p->data!=target;
p=p->next;) // The loop's body is empty.
{
return ( p != NULL );
}
}
void List::quit() const
{ return; }
List::Item List::kth ( int k ) const
{
Node *p;
int i;
assert ( 1 <= k && k <= length() );
p = first;
for (int i=1; i<k; ++i)
p = p->next;
return p->data;
}
// Friend Function
// This function writes out the list
ostream& operator << ( ostream& out_s, const List& l )
{
List::Node *p;
p = l.first;
while(p != NULL )
{
out_s << p->data << " ";
p = p->next;
}
return out_s; //return output stream.
}
// Private function
List::Node* List::get_node ( const Item& entry, Node* link )
{
Node *temp;
temp = new Node;
temp->data = entry;
temp->next = link;
return temp;
}
// PRIVATE FUNCTION
Node* get_node ( const Item& entry, Node* link );
};
#endif
main.cpp
#include "LinkedList.h"// ADT list operations
#include <cstdlib>
#include <iostream>
#include <iomanip>
#include <cassert>
#include <fstream>
#include <sstream>
#include <string>
using namespace std;
char command; // Each command letter
int v; // The value to be inserted
char e, i, r, m, l, p, w, h, q;
int k; // position in the list
List n; // An ordered list.
void menu()
//Postcondition: A menu of commands and instructions
//for their use has been written to out.
{
cout << endl;
cout << "This program responds to commands the user enters to" << endl;
cout << "manipulate ordered LinkedList which are initially empty." << endl;
cout << "In the following commands, v is any integer, and" << endl;
cout << "integers from 0 to " << endl << endl;
cout << " e -- Re-initialize list n to be empty." << endl;
cout << " i v -- Insert the value v into list n." << endl;
cout << " r v -- Remove the value v from list n." << endl;
cout << " m -- Report is the List n empty." << endl;
cout << " l -- Report the length of the list n." << endl;
cout << " p v -- Is the value present in the list n." << endl;
cout << " k -- Report the kth value in the List n." << endl;
cout << " w -- Write out the contents of LinkedList n." << endl;
cout << " h -- See this menu." << endl;
cout << " q -- Quit the program." << endl << endl;
}
int main()
{
menu(); //show the menu
do
{
cout << "--> "; // Issue a prompt.
cin >> command; // Read a command letter.
switch ( command ) // Carry out the command.
{
case 'e': n.make_empty();
cout << "The list is empty." << endl;
break;
case 'i': cin >> v ;
n.insert(v);
cout << "The new item is inserted into list." << n << endl;
break;
case 'r': cin >> v ;
n.remove(v);
cout << "The new item is removed from the list." << n << endl;
break;
case 'm': if (n.isEmptyList() ==true){
cout << "The list is empty." << endl;
}
else{
cout << "The list is NOT empty." << endl;
}
break;
case 'l': n.length();
cout<< "The length of the list is "<< n.length()<<endl;
break;
case 'p': cin >> v ;
if (n.present(v)==true){
cout << "The value" << v << "is present in the list." << endl;
}
else{
cout << "The value" << v << "is Not present in the list." << endl;
}
break;
case 'k': cin >> v;
cout << "LinkedList " << setw(1) << n << " contains "
<< setw(1) << n.kth(k) << " items." << endl;
cout << "The " << v << "is Not present in the list." << endl;
break;
case 'w': cout << "List:" <<n<< endl;
break;
case 'h': menu();
cout << "The progam menu is shown." << endl;
break;
case 'q': n.quit();
cout << "The program is quiting." << endl;
break;
default: ; // Null statement for an incorrect command
}
} while (command != 'q');
}
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;
}
I am doing an assignment of linked list of strings. Everything seems to work fine until I get to the sorting part. I am using classes as well. I already have 2 functions. The first one minimum(string) where it returns the string that would come first in alphabetical order.
This functions works fine. I also have a function remove(string) where it removes a node containing the given string from the linked list. It returns true if successful, otherwise false (if string was not in the list), in this case I have to erase the string returned by the minimum(string) function. The function I am having trouble is sort().
It wants me to define a StringNode pointer to be the head of a new list (the empty list) then I need a loop where I will call the functions of minimum(string) and remove(string). Inside the loop I also need to insert this node into the proper position in the new list (append it to the end). The old head pointer (now empty) must point to the new list.
I am very confused about this, so far I have this:
void StringList::sort()
{
StringNode *newList = new StringNode;
newList = NULL;
string mini;
bool removed;
while (newList->next != NULL)
{
StringNode *newList2 = new StringNode;
StringNode *p = head;
mini = minimum();
p->data = mini;
p->next = NULL;
newList2 = newList2->next;
newList2->next = p;
removed = remove(mini);
newList = newList2;
}
}
How I understand is: I need to create a new node which will be an empty list meaning newList->next = NULL; then on the loop I need to create another new node, and a pointer that will point to the head of new node inside the loop. I need to make the value given by the minimum be stored in the pointer p and the pointer to point back to the new node.
Any help will be greatly appreciated. Thanks!
Here is the hole program.
// StringList.cpp
#include <iomanip>
#include <iostream>
#include <sstream>
#include "StringList.h"
using namespace std;
//******************************************************************************
// StringList: creates an empty list
//******************************************************************************
StringList::StringList()
{
head = NULL;
}
//******************************************************************************
// StringList: deallocates all the nodes in StringList
//******************************************************************************
StringList::~StringList()
{
StringNode *p;
StringNode *n;
p = head;
while (p != NULL)
{
n = p->next;
delete p;
p = n;
}
}
//******************************************************************************
// count: returns the total number of nodes in the list.
//******************************************************************************
int StringList::count()
{
int count = 0;
StringNode *p;
p = head;
while ( p != NULL )
{
count++;
p = p->next;
}
return count;
}
//******************************************************************************
// add: adds a new node to the beginning of the list.
//******************************************************************************
void StringList::add(string movie)
{
StringNode *newNode = new StringNode;
newNode->data = movie;
newNode->next = head;
head = newNode;
}
//******************************************************************************
// remove: removes a node containing the string from linked list
// returns true if successful or false the string is not in the list
//******************************************************************************
bool StringList::remove(string movie)
{
StringNode *p = head;
StringNode *n = NULL;
while (p != NULL && p->data != movie )
{
n = p;
p = p->next;
if (p == NULL )
{
return false;
}
else if (p->data == movie)
{
n->next = p->next;
delete p;
return true;
}
}
}
//******************************************************************************
//display: Displays the strings in the list.
//******************************************************************************
void StringList::display()
{
StringNode *p;
p = head;
while (p != NULL)
{
cout << p->data << " ";
p = p->next;
}
cout << endl;
}
//******************************************************************************
//minimum: return the string in alphabetical order
//******************************************************************************
string StringList::minimum()
{
StringNode *p = head;
string minimum = p->data;
while (p->next != NULL)
{
p = p->next;
if(minimum > p->data)
{
minimum = p->data;
}
}
return minimum;
}
//******************************************************************************
//sort: will call the minimum function and remove function
//******************************************************************************
void StringList::sort()
{
StringNode* newhead; // create a new head pointer
string mini;
bool removed;
//adding the first node to the new list.
StringNode *newnode = new StringNode;
mini = minimum(); // get the minimum from the existing linked list
newnode->data = mini;
newnode->next = NULL;
newhead=newnode; //add the minimum node to the new list(with the newhead)
StringNode *p = newhead;
while (head != NULL) // loop should run until there's no node left in the original list
{
StringNode *newnode = new StringNode;
mini = minimum(); // get the minimum from the existing linked list
newnode->data = mini;
newnode->next = NULL;
p->next=newnode; //add the minimum node to the new list(with the newhead pointer)
removed = remove(mini);
p=p->next;
}
head=newhead; //finally change the head pointer, so that the head now points to sorted list.
}
// StringList.h
#ifndef STRINGLIST_H_INCLUDED
#define STRINGLIST_H_INCLUDED
#include <string>
using namespace std;
class StringList
{
private:
struct StringNode // the Nodes of the linked list
{
string data; // data is a string
StringNode *next; // points to next node in list
};
StringNode *head; // the head pointer
public:
StringList();
~StringList();
int count();
void add(string);
bool remove(string);
void display();
string minimum();
void sort();
};
#endif // STRINGLIST_H_INCLUDED
//Driver.cpp
#include<iostream>
#include<iomanip>
using namespace std;
#include "StringList.h"
int main()
{
//testing StringList
StringList slist;
string movie1 = "Star Wars";
string movie2 = "Fargo";
string movie3 = "Back to the Future";
string movie4 = "Titanic";
// Testing add/display/count
cout << "Testing add/display/count: " << endl;
cout << "count is: " << slist.count() << endl;
slist.add(movie1);
slist.display();
cout << "count is: " << slist.count() << endl;
slist.add(movie2);
slist.display();
cout << "count is: " << slist.count() << endl;
slist.add(movie3);
slist.add(movie4);
slist.display();
cout << "count is: " << slist.count() << endl;
// Testing remove
cout << endl;
cout << "Testing remove: " << endl;
bool delResult;
delResult = slist.remove(movie4);
cout << "remove result movie4 = " << boolalpha << delResult << endl;
delResult = slist.remove(movie3);
cout << "remove result movie3 = " << boolalpha << delResult << endl;
delResult = slist.remove("Not There");
cout << "remove result Not There = " << boolalpha << delResult << endl;
cout << "display after remove: " << endl;
slist.display();
cout << "count is: " << slist.count() << endl;
//Testing minimum
cout << endl;
cout << "Testing minimum: " << endl;
cout << "Test minimum 1: " << endl;
slist.display();
cout << "minimum: " << boolalpha << slist.minimum() << endl;
cout << "Test minimum 2: " << endl;
slist.add(movie4);
slist.display();
cout << "minimum: " << boolalpha << slist.minimum() << endl;
cout << "Test minimum 3: " << endl;
slist.add(movie3);
slist.display();
cout << "minimum: " << boolalpha << slist.minimum() << endl;
//Testing sort and display
cout << endl;
cout << "Testing sort/display: " << endl;
slist.sort();
slist.display();
cout << endl;
cout << "Testing sort/display after add: " << endl;
slist.add("Jurassic Park");
slist.display();
cout << "now sorted: " << endl;
slist.sort();
slist.display();
}
your remove function has small fault. You didn't take care of the possibility of removing the first node.
bool StringList::remove(string movie)
{
StringNode *p = head;
StringNode *n = NULL;
if(p->data==movie) //added this condition
{
head=head->next;
delete p;
return true;
}
while (p != NULL && p->data != movie )
{
n = p;
p = p->next;
if (p == NULL )
{
return false;
}
else if (p->data == movie)
{
n->next = p->next;
delete p;
return true;
}
}
}
final sort function. The objective is to sort the existing list by using the minimum and remove functions and get the new sorted list. You need to get minimum from the existing list and keep adding them at the end of the new list. And finally change the head pointer to point it to the new list.
The while loop should run until the existing list becomes empty.
void StringList::sort()
{
string mini;
bool removed;
//adding the first node to the new list.
StringNode *newnode1 = new StringNode;
mini = minimum(); // get the minimum from the existing linked list
newnode1->data = mini;
newnode1->next = NULL;
removed =remove(mini);
StringNode *p = newnode1;
while (head != NULL) // the loop should run until the original list is empty
{
StringNode *newnode = new StringNode;
mini = minimum(); // get the minimum from the existing linked list
newnode->data = mini;
newnode->next = NULL;
p->next=newnode; //add the minimum node to the new list
removed = remove(mini);
p=p->next;
}
head=newnode1; //finally change the head pointer, so that the head now points to sorted list.
}
I am having two issues with my c++ code (The test file is below):
I can't seem to figure out why its not breaking out of the while loop, when running, its stuck on the loop "7 versus 325".
So it should go into the next node of temp, which would be null, and then jump into the section where it adds it to the end of the queue. But its just looping and looping.
My second issue is with the the function I have commented out, queue1.back, whenever that is ran, it just errors out and gives what appears to be the address, but the .front() function works just fine.
The Test File I am working with is like this:
89 Alex
325 Rob
72 Joy
91 Bob
using namespace std;
class Person
{
friend class Pqueue;
public:
int priority;
string name;
};
class PQueue
{
friend class Person;
private:
//Structure for my linked list.
typedef struct node {
Person data;
struct node *next;
}Node, *NodePtr;
Node *head, *tail;
public:
//Prototype Functions
PQueue(void); //Initializer function
bool empty(void); //Test if empty
int size(void); //Return size
void enqueue(Person *); //Insert Node
void dequeue(void); //Remove Node
Person* front(void); //Access Next Node
Person* back(void); //Access last node
};
PQueue::PQueue()
{
head = NULL;
tail = NULL;
}
bool PQueue::empty(){
return (head == NULL);
}
void PQueue::enqueue(Person *myPerson){
NodePtr np = (NodePtr) malloc(sizeof(Node));
np->data = *myPerson;
np->next = NULL;
if(empty())
{
cout << "Making into creating the first node, of the linked list" <<endl;
head = np;
tail = np;
}
else { //Queue has more the one node
Node* temp = head;
if(np->data.priority > temp->data.priority) //If the priority is greater then the rest.
{
head = temp; //Saving my head pointer
head->data = np->data; //Assigning new Data to the head pointer
head->next = temp; //Assigning the rest of the linked list back into head.
cout << "Making into creating the first node again, having to reassign." <<endl;
}
else{
//Searching where to place the node.
while(temp->data.priority > np->data.priority) //Searching if the next priority is higher then the passed.
{
cout << "Inside the while loop: " << np->data.priority << " versus "<<temp->data.priority <<endl;
if(temp->next == NULL)
break;
temp = temp->next;
}
if(temp->next == NULL && np->data.priority < temp->data.priority) //Inserting at the end.
{
cout << "Making into creating the last node" <<endl;
tail->next = np;
cout << "Passing the function of creating the last node" <<endl;
}
else //Inserting into the middle of the function.
{
cout << "Inserting in the middle of the queue" <<endl;
np->next = temp->next;
temp->next = np;
}
}
}
}
void PQueue::dequeue(){
if(empty()){
cout << "\nAttempt to remove from an empty list." << endl;
exit(1);
}
Person hold = head->data;
NodePtr temp = head;
head=head->next;
if (head == NULL) tail = NULL;
free(temp);
}
Person* PQueue::front(){
//Person &temp = head->next->data;
//Person &temp = head->data;
Person &temp = head->data;
return &temp;
}
Person* PQueue::back(){
if(empty()){
cout << "\nNo entries in list." << endl;
exit(1);
}
Person &temp = tail->data;
return &temp;
}
int main() {
cout << "Starting main" << endl;
PQueue queue1; //Creating my queue.
cout << "Created Queue" << endl;
Person tempPerson;
ifstream inFile;
inFile.open("/tmp/temp");
cout << "going into while loop" << endl;
while (inFile >> tempPerson.priority >> tempPerson.name){
cout << "The priority is " << tempPerson.priority << " the name is " << tempPerson.name <<endl;
queue1.enqueue(&tempPerson);
}
//Testing Section, trying to get .front and .back to work.
Person *testPerson;
testPerson = queue1.front();
cout << "The TEST priority is " << testPerson->priority << " the TEST name is " << testPerson->name <<endl;
/**
Person *tailPerson;
testPerson = queue1.back();
cout << "The TEST priority is " << tailPerson->priority << " the TEST name is " << tailPerson->name <<endl;
**/
queue1.dequeue();
queue1.dequeue();
queue1.dequeue();
return 0;
}
When you add a new head entry to a non-empty list, you're mistakenly setting the next pointer to point right back at the node it's in, rather than setting it to point at the rest of the linked list like you intended.
head = temp; //Saving my head pointer
head->next = temp; //Assigning the rest of the linked list back into head.