How can I manage memory deallocation in disjoint sets in C++? - c++

I have a set of classes to handle Disjoint sets in my C++ app. I have a hard time implementing the destructors for these classes. Can anyone help me on that?
What these basically do is: put nodes' pointers into NodeAddress[], every node is distinguished by its val. Every node has a pointer to the Item which is a placeholder for hd head and tl tail of the disjoint set.
I want to mention that I realize that there are some issues with e.g. : variable visibility ( public access ), constant size NodeAddress buffer but I want to focus here on memory deallocation.
And yes I want (need) to do it on pointer (no STL). If you have sny suggestions or questions feel free to comment.
This is the code :
header
class ListSet {
public:
unsigned int size;
node* NodeAddress[MAX_NUMBER_OF_LABELS];
struct Item;
class node {
public:
unsigned int val;
node *next;
Item *itemPtr;
node () : val(0), next(0), itemPtr(0) {}
node (const int& a) : val(a), next(0), itemPtr(0) {}
};
struct Item {
public:
node *hd, *tl;
Item(node *shd) : hd(shd), tl(shd) {}
void ListSet::Item::append (const Item* other);
//removal
ListSet::node* remove(node* old);
};
ListSet()
{
this->size = 0;
memset(NodeAddress, 0, sizeof(NodeAddress));
}
void setNodeAddress(const int& a, node* shd)
{
NodeAddress[a] = shd;
}
node* getNodeAddress(const int& a)
{
return NodeAddress[a];
}
ListSet::Item* ListSet::makeSet (const int& a) ;
ListSet::Item* ListSet::find (const int& a);
ListSet::Item* ListSet::unionSets (Item* s1, Item* s2);
void ListSet::unionSets (const int& a1, const int& a2);
};
source
void ListSet::Item::append (const Item* other) {
//join the tail of the set to head of the other set
tl->next = other->hd;
tl = other->tl;
for (node* cur = other->hd; cur; cur = cur->next) {
cur->itemPtr = this;
}
}
ListSet::Item* ListSet::makeSet (const int& a) {
if( a > this->size) {this->size = a;}
assert(!getNodeAddress(a));
node *shd = new node(a);
Item *newSet = new Item(shd);
setNodeAddress(a, shd);
shd->itemPtr = newSet;
return newSet;
}
ListSet::Item* ListSet::find (const int& a) {
node* ptr = getNodeAddress(a);
if (ptr)
return ptr->itemPtr;
return 0;
}
ListSet::Item* ListSet::unionSets (Item* s1, Item* s2) {
Item *set1 = s1;
Item *set2 = s2;
set2->append(set1);
delete set1;
return set2;
}
void ListSet::unionSets (const int& a1, const int& a2) {
Item* s1 = find(a1);
Item* s2 = find(a2);
if (s1 && s2) {
(void) unionSets(s1, s2);
}
}
*EDIT: *
there is something that I have but is not working
ListSet::node* ListSet::Item::remove(node* old) {
if (old == hd) {
if (old == tl) {
assert(! old->next);
return 0;
}
assert(old->next);
hd = old->next;
} else {
node* prev;
for (prev = hd; prev->next != old; prev = prev->next) {
assert(prev->next);
;
}
if (old == tl) {
assert(! old->next);
//
tl = prev;
prev->next = 0;
} else {
assert(old->next);
prev->next = old->next;
}
}
return hd;
}
ListSet::node::~node() {
if (itemPtr) {
if (! itemPtr->remove(this)) {
// Don't leak an empty set.
delete itemPtr;
}
}
}
void ListSet::remove(const int& a) {
node* ptr = getNodeAddress(a);
if (ptr) {
setNodeAddress(a, 0);
delete ptr;
}
// else error?
}

Your code is overly complicated. Here's my take on a disjoint set forest; all memory management can be handled from the outside by putting the sets in a vector. Note that no pointer manipulation is needed, because a set can be uniquely identified by a size_t-typed index into the forest.

Related

Iterator while loop running extra loop

I have a Doubly linked List class wherein i have a function which uses my classes iterators to loop over the collection. However in 1 specific place it runs for an extra loop and I cannot figure out why.
current merge template
template <typename T>
void DlList<T>::merge(SortedList& other) {
iterator it = back_->prev_;
iterator oit = other.begin();
while (oit.current_->next_ != NULL) {
std::cout << *it << " " << *oit << std::endl;
it.current_->next_ = oit.current_;
back_->prev_ = oit.current_;
//back_->prev_->next_ = back_;
//other.back_->prev_ = other.back_->prev_->prev_;
it++;
oit++;
}
}
It always iterates and extra time and added a null node to the list and I don't understand why.
Any insight is greatly appricicated!
Edit Added full project examples to explain intention of function
I am working on a templated data structure class which is a doubly linked list which uses sentinel nodes.
The list is sorted based on insert() and I am working on a merge function wherein the nodes of each list must be combined into this->list. The nodes must be moved and not have new ones created. and if the same value exists in both lists the other node value must come after the current node value.
I coded what I thought was a logical implementation however the output I get is not as expected and the results do not make sense to me, so if anyone could explain how i am getting the results I have it would be greatly appreciated.
Class Definition
class SortedList {
struct Node {
T data_;
Node* next_;
Node* prev_;
Node(const T& data = T{}, Node* nx = nullptr, Node* pr = nullptr) {
data_ = data;
next_ = nx;
prev_ = pr;
}
};
Node* front_;
Node* back_;
public:
class const_iterator {
friend class SortedList;
Node* current_;
const_iterator(Node* n)
{
current_ = n;
}
public:
const_iterator() {
//Set to safe state
current_ = nullptr;
}
const_iterator& operator++() {
current_ = current_->next_;
return *this;
}
const_iterator operator++(int) {
const_iterator old = *this;
current_ = current_->next_;
return old;
}
const_iterator& operator--() {
current_ = current_->prev_;
return *this;
}
const_iterator operator--(int) {
const_iterator old = *this;
current_ = current_->prev_;
return old;
}
bool operator==(const_iterator rhs) {
return (current_ == rhs.current_) ? true : false;
}
bool operator!=(const_iterator rhs) {
return !(*this == rhs);
}
bool operator>(const_iterator rhs) {
return current_->data_ > rhs->current_->data_;
}
const T& operator*()const {
return current_->data_;
}
};
class iterator :public const_iterator {
friend SortedList;
iterator(Node* n) :const_iterator(n) {};
public:
iterator() : const_iterator() {};
//prefix
iterator& operator++() {
this->current_ = this->current_->next_;
return *this;
}
//post-fix
iterator operator++(int) {
iterator old = *this;
this->current_ = this->current_->next_;
return old;
}
iterator& operator--() {
this->current_ = this->current_->prev_;
return *this;
}
iterator operator--(int) {
iterator old = *this;
this->current_ = this->current_->prev_;
return old;
}
T& operator*() {
return this->current_->data_;
}
const T& operator*()const {
return this->current_->data;
}
};
SortedList(); //done
~SortedList();
SortedList(const SortedList& rhs);
SortedList& operator=(const SortedList& rhs);
SortedList(SortedList&& rhs);
SortedList& operator=(SortedList&& rhs);
iterator begin() {
return iterator(front_->next_);
}
iterator end() {
return iterator(back_);
}
const_iterator cbegin() const {
return const_iterator(front_->next_);
}
const_iterator cend() const {
return const_iterator(back_);
}
iterator insert(const T& data);
iterator search(const T& data);
const_iterator search(const T& data) const;
iterator erase(iterator it);
void merge(SortedList& other);
bool empty() const;
int size() const;
};
first merge function attempt
template <typename T>
void SortedList<T>::merge(SortedList& other) {
iterator it = this->begin();
iterator oit = other.begin();
while (oit != other.end()) {
std::cout << *oit << " " << *it << std::endl;
if (*oit < *it) {
oit.current_->prev_->next_ = oit.current_->next_;
oit.current_->next_->prev_ = oit.current_->prev_;
oit.current_->next_ = it.current_;
oit.current_->prev_ = it.current_->prev_;
it.current_->next_ = oit.current_;
}
else {
oit.current_->prev_->next_ = oit.current_->next_;
oit.current_->next_->prev_ = oit.current_->prev_;
oit.current_->next_ = it.current_->next_;
oit.current_->prev_ = it.current_;
it.current_->prev_ = oit.current_;
}
oit++;
it++;
}
}
main tester
int main() {
int num[] = { 3,5,1,2,6,8,9,11 };
int num2[] = { 1,5,4,6,12,7,8,9 };
SortedList<int> l;
SortedList<int> l2;
for (int i = 0; i < 8; i++)
{
l.insert(num[i]);
l2.insert(num2[i]);
}
SortedList<int>::iterator result;
SortedList<int>::iterator result2 = l2.begin();
result = l.begin();
while (result != l.end()) {
std::cout << *result << " " << *result2 << std::endl;
++result;
++result2;
}
l.merge(l2);
output
1 1
2 4
3 5
5 6
6 7
8 8
9 9
11 12
1 1
2 2
3 3
5 5
6 6
8 8
9 9
11 11
0 0
I dont understand why my second output is showing same the same values for *it and *oit I am pretty certain the error is in how I assign the the oit.current_->next & prev but i am unsure.
any insight is appriciated.
You seem to want to merge two sorted doubly linked lists together. There are several problems with your approach, and so I'll show you my code:
#include <iostream>
using namespace std;
struct node {
node* next;
node* prev;
int val;
node(int i_val)
: next(nullptr),
prev(nullptr),
val(i_val)
{}
};
void connect(node* a, node* b) {
if (a != nullptr) {
if (a->next != nullptr) {
a->next->prev = nullptr;
}
a->next = b;
}
if (b != nullptr) {
if (b->prev != nullptr) {
b->prev->next = nullptr;
}
b->prev = a;
}
}
struct DlList {
node* first_node;
node* last_node;
DlList()
: first_node(nullptr),
last_node(nullptr)
{}
~DlList() {
for (node* n = first_node; n != nullptr; n = n->next) {
delete n->prev;
}
delete last_node;
}
void push(int new_val) {
node* new_node = new node(new_val);
connect(last_node, new_node);
last_node = new_node;
if (first_node == nullptr) {
first_node = new_node;
}
}
void merge_sorted(DlList& other) {
node* this_node = first_node;
node* other_node = other.first_node;
node* n = nullptr; // goes through each node of the new list in order
while (this_node != nullptr || other_node != nullptr) {
node* next_n;
if (other_node == nullptr ||
(this_node != nullptr && this_node->val <= other_node->val)) {
// entered if other_node is nullptr or this_node comes before other_node
next_n = this_node;
this_node = this_node->next;
}
else {
// entered if this_node is nullptr or other_node comes before this_node
next_n = other_node;
other_node = other_node->next;
}
connect(n, next_n);
if (n == nullptr) { // first time through loop
first_node = next_n;
}
n = next_n;
}
last_node = n;
// *this takes ownership of all of other's nodes
other.first_node = nullptr;
other.last_node = nullptr;
}
};
int main() {
std::cout << "running test" << std::endl;
int num[] = { 1,2,3,5,6,8,9,11 };
int num2[] = { 1,4,5,6,7,8,9,12 };
DlList l;
DlList l2;
for (int i = 0; i < 8; i++)
{
l.push(num[i]);
l2.push(num2[i]);
}
l.merge_sorted(l2);
for (node* n = l.first_node; n != nullptr; n = n->next) {
std::cout << n->val << " ";
}
std::cout << std::endl;
}
You may add iterators and other higher-level abstractions later, but for now I think they complicate and obscure the logic. I also did not see a need for a "past-the-end-of-the-list" node in your case, as nullptr would suffice. Though of course these could rather easily be added in if you wanted them to, just for demonstration purposes they are omitted.
Notice how I made a dedicated connect function that does all the pointer assignments as they should be done for two nodes. It handles a bunch of combinations of nullptr cases, too, so you don't need to worry as much about checking for null pointers outside of the function. (Note how the first time through the merge loop, a null n pointer is connected to next_n). Now you hardly need to worry about pointer assignments, and it's clearer when you just say, "connect these two nodes."
My merger function goes through each node in the newly created list. It picks the next node from the two available nodes, from *this and other. It then connects the current node to the next node, and advances the current node to the next node. It has special handling when one or the other of the lists runs out (this_node or other_node becomes nullptr), which indeed happens in the given test case. It takes care to assign first_node and last_node in the correct places, and to clear other after the merge, so as to prevent double ownership issues.

Implementation of a Doubly Linked List

I am trying to implement a linked list, and I am completely lost. I'm getting break points all over the place, specifically with the erase method. Whenever I alter the erase method, some error will inevitably spring up. I've got pointer errors, problems with the destructor that only occur when the erase method is called, and so on.
Here's what I have so far:
Header file:
#pragma once
class IntList {
private:
class IntNode {
public:
IntNode(int v, IntNode *pr, IntNode *nx);
~IntNode();
IntNode* previous;
IntNode* next;
class iterator {
public:
iterator(IntNode* t);
int& operator*();
iterator& operator++();
iterator& operator--();
bool operator!=(iterator other)const;
private:
IntNode* target;
};
private:
int value;
};
IntNode* head;
IntNode* tail;
int count;
public:
IntList();
~IntList();
void push_back(int v);
void pop_back();
int size() const { return count; }
typedef IntNode::iterator iterator;
iterator begin();
iterator end();
//unsigned int size() const;
void push_front(int value);
bool empty() const;
int& front();
int& back();
void clear();
iterator erase(iterator position);
};
Implementation:
#include "IntList.h"
#include <stdexcept>
IntList::IntList() : head{ nullptr }, tail{ nullptr }, count{ 0 }
{}
IntList::~IntList() {
while (head) {
head = head->next;
delete head;
}
}
void IntList::push_back(int v) {
tail = new IntNode{ v, tail, nullptr };
if (!head) { head = tail; }
count += 1;
}
void IntList::pop_back() {
tail = tail->previous;
delete tail->next;
count -= 1;
}
IntList::iterator IntList::begin()
{
return iterator{ head };
}
IntList::iterator IntList::end() {
return iterator{ nullptr };
}
void IntList::push_front(int value) {
head = new IntNode{ value, nullptr, head };
if (!tail) { tail = head; }
count += 1;
}
bool IntList::empty() const{
return (count==0);
}
int& IntList::front() {
return *begin();
}
int& IntList::back() {
return *begin();
}
void IntList::clear() {
head = nullptr;
tail = nullptr;
count = 0;
}
IntList::iterator IntList::erase(iterator position) {
int midpointL = 0;
for (iterator index = begin(); index != position; ++index) {
midpointL++;
}
if (midpointL == 0) {
head = head->next;
}
else if (midpointL == count) {
tail = tail->previous;
}
else {
// Move head to get a reference to the component that needs to be deleted
for (int i = 0; i < midpointL; i++) {
head = head->next;
}
// Change the previous and next pointers to point to each other
(head->previous)->next = (head->next);
(head->next)->previous = (head->previous);
for (int i = midpointL-1; i > 0; i++) {
head = head->previous;
}
}
count-=1;
return position;
}
IntList::IntNode::IntNode(int v, IntNode * pr, IntNode * nx)
: previous{ pr }, next{ nx }, value{ v }
{
if (previous) { previous->next = this; }
if (next) { next->previous = this; }
}
IntList::IntNode::~IntNode() {
if (previous) previous->next = next;
if (next) next->previous = previous;
}
IntList::IntNode::iterator::iterator(IntNode* t)
: target{ t }
{}
int& IntList::IntNode::iterator::operator*() {
if (!target) { throw std::runtime_error{ "Deferenced sentinel iterator." }; }
return target->value;
}
IntList::IntNode::iterator& IntList::IntNode::iterator::operator++()
{
if (target) { target = target->next; }
return *this;
}
IntList::IntNode::iterator& IntList::IntNode::iterator::operator--()
{
if (target) { target = target->previous; }
return *this;
}
bool IntList::IntNode::iterator::operator!=(iterator other)const
{
return (!(target == other.target));
}
Could anyone help point me in the right direction?
Thanks!
Let's make a kind of quick review here:
IntList::~IntList() {
while (head) {
head = head->next;
delete head;
}
}
you should do instead:
IntList::~IntList() {
while (head) {
IntNode* newHead = head->next;
delete head;
head = newHead;
}
}
as you are deleting the "next" object and then you are trying to get access to it in the next iteration.
void IntList::pop_back() {
tail = tail->previous;
delete tail->next;
count -= 1;
}
Here you are not checking if tail is null or if it is pointing to head..(what's the empty condition?), maybe count!=0? in case you may delete a not existing next-node
IntList::iterator IntList::end() {
return iterator{ nullptr };
}
..end is null? ebd should be your tail...
int& IntList::back() {
return *begin();
}
that's begin..not back.
void IntList::clear() {
head = nullptr;
tail = nullptr;
count = 0;
}
a clear should release all the objects in the list. You are generating garbage here (leaks).
I stopped here, sorry for that, it's just a coffee break. But you should look carefully at:
* null pointer usage
* delete your node list item when not needed
* pay attention to do not use invalid pointers (like head->previous->next I saw somewhere)
You have to review your code, bottom up. Hope that those first hints help you through your learning process.
Have fun,
Ste

for_each for Node manually c++

How can I implement for_each for that implementation of Node and List?
I'm sure it's not long code, can some share a few strokes of his knowledge please? Do I need to implement it as template or just as inner function.
Thanks for helping.
class Node {
int data;
Node *next;
public:
Node() {}
void SetData(int aData) { data = aData; }
void SetNext(Node *aNext) {
next = aNext;
}
int Data() { return data; }
Node* Next() { return next; }
};
// List class
class List {
Node *head;
public:
List() { head = nullptr; }
void Append(int data){
// Create a new node
Node *newNode = new Node();
newNode->SetData(data);
newNode->SetNext(nullptr);
// Create a temp pointer
Node *tmp = head;
if ( tmp != nullptr ) {
// Nodes already present in the list
// Parse to end of list
while ( tmp->Next() != nullptr) {
tmp = tmp->Next();
}
// Point the last node to the new node
tmp->SetNext(newNode);
}
else {
// First node in the list
head = newNode;
}
}
void Delete(int data){
// Create a temp pointer
Node *tmp = head;
// No nodes
if ( tmp == nullptr ) return;
// Last node of the list
if ( tmp->Next() == nullptr ) {
delete tmp;
head = nullptr;
}
else {
// Parse thru the nodes
Node* prev;
do {
if ( tmp->Data() == data ) break;
prev = tmp;
tmp = tmp->Next();
} while ( tmp != nullptr );
// Adjust the pointers
if (tmp->Next()!=nullptr) prev->SetNext(tmp->Next());
else prev->SetNext(nullptr);
// Delete the current node
if (tmp!=nullptr) delete tmp;
}
}
};
EDIT:
This is use of iterator:
for (List<Pair, CompareFunction>::myIterator it = this->_pairs.begin();
it != this->_pairs.end(); ++it) {
Pair cur_pair = *it;
if (cur_pair.first == key) {
this->_pairs.Delete(cur_pair);
this->_size--;
}
}
This is Pair Class as sub-class in another template class:
template <class KeyType, class ValueType, class CompareFunction = std::less<KeyType> >
class MtmMap {
public:
class Pair {
public:
Pair():first(KeyType()){} ////////////////////
Pair(const KeyType& key, const ValueType& value)
: first(key), second(value) {}
const KeyType first;
ValueType second;
~Pair() = default;
In our concrete case KeyType and ValueType run as both int.
Add the following code into your List class:
class List{
...
class myIterator
{
public:
typedef myIterator self_type;
typedef Node* pointer;
myIterator(pointer ptr) : ptr_(ptr) { }
self_type operator++() {
self_type i = *this;
if (ptr_) ptr_ = ptr_->Next();
return i;
}
int operator*() { if (ptr_ ) return ptr_->Data(); else return 0; }
bool operator==(const self_type& rhs) {
if (!ptr_ && !rhs.ptr_) return true;
return (ptr_ && rhs.ptr_ && rhs.ptr_->Data()==ptr_->Data());
}
bool operator!=(const self_type& rhs) {
return !(*this==rhs);
}
private:
pointer ptr_ = nullptr;
};
myIterator begin() { return myIterator(head); }
myIterator end() { return myIterator(nullptr); }
};
You may then use normal iterators like in the following example:
List l;
l.Append(2);
l.Append(5);
l.Append(7);
l.Append(11);
for (int i: l) std::cout << i << std::endl;
//or
for (myIterator it=l.begin(); it!=l.end(); ++it) std::cout << *it << std::endl;

C++ List is not copying via copy ctor or assignment operator

I have a program that has a list manually put into it, its just a bunch of names tossed in like this: list.PushBack("Frank"); or list.PushFront("George");
My current problem is that when I attempt to run the already made list through my copy constructor and assignment operator, the list comes out "" (as defined in an isEmpty function)
Ignore all of the "TODO" commented spots, those are mental notes for myself.
There are also a couple of random cout statements that I used for debugging purposes, and would like to keep there until everything is working properly.
Here is the relevant code:
List.h
class Node;
class List
{
public:
List();
List(const char* p);
//Copy constructor
List(const List& str);
//Deep Copy
List& operator=(const List& str);
~List();
void Clear();
//Adds to the front
void PushFront(std::string data);
//adds to the back
void PushBack(std::string data);
//removes from the front
void PopFront();
//removes from the back
void PopBack();
//gets the back value
std::string& Back() const;
//gets the from value
std::string& Front() const;
bool Empty() const {return m_pFront == 0;}
ostream& OutPut(ostream& os);
private:
Node* m_pFront;
Node* m_pBack;
char* m_pData;
};
List.cpp
List::List() : m_pFront(0), m_pBack(0), m_pData(0)
{}
List::~List()
{
Clear();
}
void List::Clear()
{
//delete
if(!m_pFront)
{
return;
}
delete m_pFront;
delete m_pData;
m_pFront = 0;
m_pBack = 0;
}
void List::PushFront(std::string data)
{
//create a new node
Node* p = new Node(data);
//Empty list
if(!m_pFront)
{
m_pFront = p;
m_pBack = p;
}
else //Not empty list
{
p -> m_pNext = m_pFront;
m_pFront -> m_pPrev = p;
m_pFront = p;
}
}
void List::PushBack(std::string data)
{
Node* p = new Node(data);
if(!m_pBack)
{
m_pFront = p;
m_pBack = p;
}
else
{
p -> m_pPrev = m_pBack;
m_pBack -> m_pNext = p;
m_pBack = p;
}
}
void List::PopFront()
{
if(m_pFront == 0)
{
//TODO: we need to handle this problem
return;
}
if(m_pBack == m_pFront)
{
Clear();
return;
}
Node* p = m_pFront;
m_pFront = m_pFront -> m_pNext;
p -> m_pNext = 0;
m_pFront -> m_pPrev = 0;
delete p;
}
void List::PopBack()
{
if(m_pBack == 0)
{
//TODO: we need to handle this problem
return;
}
if(m_pBack == m_pFront)
{
Clear();
return;
}
Node* p = m_pBack;
m_pBack = m_pBack -> m_pPrev;
p -> m_pPrev = 0;
m_pBack -> m_pNext = 0;
delete p;
}
ostream& List::OutPut(ostream& os)
{
if(Empty() == true)
{
os << "<empty>";
}
else
{
m_pFront -> OutputNode(os);
}
return os;
}
std::string& List::Back() const
{
if(m_pBack == 0)
{
//TODO: we need to handle this problem
}
return m_pBack -> GetData();
}
std::string& List::Front() const
{
if(m_pFront == 0)
{
//TODO: we need to handle this problem
}
return m_pFront -> GetData();
}
//Copy Constructor
List::List(const List& str)
{
if(str.m_pData)
{
m_pData = new char[strlen(str.m_pData) + 1];
strcpy(m_pData, str.m_pData);
}
else
{
m_pData = 0;
}
}
//Deep copy
List& List::operator=(const List& str)
{
//Check for self assignment
if(this == &str)
{
return *this;
}
//Deallocate any value the string is holding
delete [] m_pData;
//Now we need to deep copy m_pData
if(str.m_pData)
{
//Allocate memory for the copy
m_pData = new char[strlen(str.m_pData) + 1];
//Copy the parameter to the newly allocated memory
strcpy(m_pData, str.m_pData);
}
else
{
m_pData = 0;
}
return *this;
}
You can change char* m_pData; to std::string m_sData, Otherwise, you need to allocate memory for m_pData manually.
and m_pData = str.m_pData; will cause problem, because you let m_pData point to str.m_pData, if str.m_pData got delete it. m_pData point to unknown place.
In your copy constructor
//Copy Constructor
List::List(const List& str)
{
if(Empty() == true)
{
m_pData = 0;
return;
}
m_pData = str.m_pData;
strcpy(m_pData, str.m_pData);
}
You are using Empty() which is bool Empty() const {return m_pFront == 0;} and m_pFront is not initialized so far.
And m_pData = str.m_pData there is no need for strcpy.
Rather make copy of this string (first allocated and then copy) or use std::string instead.

Turn An Unsorted Link List Into A Sorted Link List? (C++)

I am having problems trying to turn the INSERT function into a function where it sorts the nodes alphabetically. I have written down what I've tried so far...but it only checks the name of the first node to see if it is bigger than the given name of the new node in the function's argument. Can someone please give me an idea of how I would be able to move through each node and be able to compare their keys (names) and place them left and right accordingly? Below is what I have in my code and my INSERT function so far...
// UnSortedLnkList.h
//----------------------------
#include <iostream>
#include <afxwin.h>
using namespace std;
#define new DEBUG_NEW
struct Node {
string m_name; // key
int m_age;
Node* m_next;
Node(const string& name, int age, Node* next = NULL);
};
ostream& operator<< (ostream&, const Node&);
class ULnkList {
friend ostream& operator<< (ostream&, const ULnkList&); // 1.5
public:
ULnkList();
// copy ctor
ULnkList(const ULnkList& existingList ); // 5
~ULnkList(); // 4
bool IsEmpty() const;
int Size() const;
bool Insert(const string& name, int age); // 1
bool Delete(const string& name); // 3
bool Lookup(const string& name, int& age) const; // 2
ULnkList& operator =(const ULnkList& list2); // 6
bool Delete2(const string& name);
private:
Node* m_head; // points to head of the list
int m_num; // the number of entries in the list
// helper functions:
void Clear(); // 7
void Copy(const ULnkList& list2); // 8
};
// UnSortedLnkList.cpp
//----------------------------
#include "UnSortedLnkList.h"
#include <iostream>
#include <string>
using namespace std;
Node::Node(const string& name, int age, Node* next)
: m_name(name), m_age(age), m_next(next)
{}
ostream& operator<< (ostream& os, const Node& n) // cout << n;
{
os << "Name: " << n.m_name << "\tAge: " << n.m_age;
return os;
}
ULnkList::ULnkList()
: m_head(new Node("",-99,NULL)), m_num(0)
{
//m_head = new Node("",-99,NULL);
}
//
ULnkList::ULnkList(const ULnkList& existingList )
{
Copy(existingList);
}
void ULnkList::Copy(const ULnkList& existingList)
{
m_num = existingList.m_num;
// create dummy node
m_head = new Node("",-99,NULL);
// traverse existing list
Node *pe = existingList.m_head->m_next;
Node *pThis = m_head;
while( pe != 0)
{
// create a copy of the Node in OUR list
pThis->m_next = new Node(pe->m_name,pe->m_age,0);
// update pointers
pe = pe->m_next;
pThis = pThis->m_next;
}
}
void ULnkList::Clear()
{
Node *p = m_head->m_next;
Node *tp = m_head; // trail pointer
while( p != 0)
{
delete tp;
// update pointers
tp = p; //
p = p->m_next;
}
delete tp;
}
ULnkList& ULnkList::operator =(const ULnkList& list2) // list1 = list2;
{
// list1 = list1; // check for self-assignment
if( this != &list2 )
{
this->Clear(); // normally Clear();
this->Copy(list2);
}
// l1 = l2 = l3;
return *this;
}
bool ULnkList::IsEmpty() const
{
return m_num == 0;
// return m_head->m_next == NULL;
}
int ULnkList::Size() const
{
return m_num;
}
//
ULnkList::Insert(const string& name, int age)
{
Node *current = m_head->m_next;
Node *previous = m_head;
if (m_head->m_next == NULL)
{
m_head->m_next = new Node(name,age,m_head->m_next);
m_num++;
return true;
}
if (name < m_head->m_next->m_name)
{
m_head->m_next = new Node(name,age,m_head->m_next);
m_num++;
return true;
}
return true;
}
//
ostream& operator<< (ostream& os, const ULnkList& list) // cout << list;
{
Node *p = list.m_head->m_next; // first node with data
while( p != 0 )
{
cout << *p << endl; // ????
// update p
p = p->m_next;
}
cout << "--------------------------------------" << endl;
return os;
}
// input: name
//// output: age if found
bool ULnkList::Lookup(const string& name, int& age) const
{
// linear search
Node *p = m_head->m_next;
while( p != 0)
{
if( name == p->m_name )
{
// found it
age = p->m_age;
return true;
}
// update p
p = p->m_next;
}
return false;
}
//
bool ULnkList::Delete(const string& name)
{
Node *p = m_head->m_next;
Node *tp = m_head; // trail pointer
while( p != 0)
{
if( name == p->m_name )
{
// found it, so now remove it
// fix links
tp->m_next = p->m_next;
// delete the node
delete p;
return true;
}
// update pointers
tp = p; // tp = tp->m_next;
p = p->m_next;
}
return false;
}
bool ULnkList::Delete2(const string& name)
{
Node *p = m_head;
while( p->m_next != 0 ) // ?????
{
if( p->m_next->m_name == name )
{
Node *save = p->m_next;
// remove the node
// fix links
p->m_next = p->m_next->m_next;
// delete memory
delete save;
return true;
}
// update pointers
p = p->m_next;
}
return false;
}
//
ULnkList::~ULnkList()
{
Clear();
}
//
I have a word of caution for this code
if(name > m_head->m_name ){
while(name > current->m_next->m_name){
current = current->m_next;
}
// add temp = current->next
current->m_next = new Node(name,age)
// current->next->next = temp
You don't want to lose the list after where you insert.
I'm guessing this is homework, so I'm not going to write the code. I would suggest that your Insert() function could walk the list from the head until it gets to the 'right' spot, by comparing the input string, and then doing the insert logic. That is assuming that you have to use a literal list as your data structure. If you want better insert performance on average, you could use a binary tree as your underlying structure, but that would make list traversal more complex.
Your problem lies in this code:
if (name < m_head->m_next->m_name)
{
m_head->m_next = new Node(name,age,m_head->m_next);
m_num++;
return true;
}
In order to insert in a sorted manner, I would probably some sort of helper function with a recursive function call.
but this might work:
Node *current = m_head;
if(name > m_head->m_name ){
while(name > current->m_next->m_name){
current = current->m_next;
}
current->m_next = new Node(name,age,current->m_next);
}
else{
m_head = new Node(name,age,m_head);
}
This will insert in an ascendingly sorted manner.
I haven't tested it, but let me know if it works! hope this helps!