The task is to test the the benefits of a Move-To-Front Linked List vs. a standard Linked List. It was also meant to practice inheritance and pointers.
The standard functionality is in LinkedList
, the derived MTFList
replaces contains()
to move the target to the front.
As I was late submitting my solution, I am not sure whether the poor grade is just for missing the deadline given in class, or reflects on my solution. Anyway, I don't know which errors I might have made.
I am hoping that some more experienced eyes can evaluate my use of pointers. Whether my constructors and destructors work appropriately or if I am allowing for memory links.
LinkedListStats.cpp
// Brandon Hoffman
// 10-21-2021
// Test program to evaluate linked list performance
// Written 10/4/19 by Michael Stiber
//
#include <iostream>
#include <string>
#include <random>
#include <vector>
#include <cassert>
// Replace the following include with alternative linked list class
//#include "LinkedList.h"
#include "MTFList.h"
using namespace std;
int main()
{
// Alter the following declaration to change the linked list class
// name.
MTFList theList;
const int numValues = 1000;
const int numAccesses = 100000;
// Create a linked list of the numbers 0..numValues-1
for (int i = numValues-1; i >= 0; i--)
theList.add(i);
// Reset the traversal counter, just in case
theList.resetTraverseCount();
// Now, access the elements randomly many times
int theNumber;
default_random_engine generator;
uniform_int_distribution<int> uniform(0, numValues-1);
normal_distribution<double> normal(numValues/2.0, numValues/5.0);
// As the statistic of comparison, we use a uniform
// distribution. For sequential search, even a "smart" algorithm
// shouldn't be able to improve performance.
for (int i = 0; i < numAccesses; i++) {
// Access a random item by value
theNumber = uniform(generator);
assert(theList.contains(theNumber));
}
cout << "Average number of nodes traversed per access (uniform): "
<< theList.getTraverseCount()/double(numAccesses)
<< endl;
// Reset the traversal counter.
theList.resetTraverseCount();
// We use a normal distribution so that some values are accessed
// much more frequently. It will be peaked around numValues/2 and fall off
// rapidly above and below. Note that there is some chance of
// generating a number outside the legal range, so we test and get a
// new number if that happens (this is because a uniform
// distribution goes to +/- infinity). A smart algorithm could in
// principle take advantage of the higher frequency of access of
// certain items to lower the average access time. On the other hand,
// without any "smarts", the mean number of nodes traversed should still
// be the mean of the distribution, the same as for the uniform distribution.
for (int i = 0; i < numAccesses; i++) {
theNumber = 0;
do {
theNumber = int(normal(generator));
} while ((theNumber<0) || (theNumber>=numValues));
assert(theList.contains(theNumber));
}
cout << "Average number of nodes traversed per access (normal): "
<< theList.getTraverseCount()/double(numAccesses)
<< endl;
} // end LinkedListStats
MTFList.cpp
// LinkedList.cpp
// Brandon Hoffman
// 10-21-2021
// Contains all of the functionality from members of MTFList.h
#include "MTFList.h"
//Returns true if anEntry is found in the List, otherwise returns false. For every Node traversed in
//search of anEntry, increases traverseCount by 1. Overwites functionality from Linked List
//Now moves node with first occurrence of anEntry match to become new first node
bool MTFList::contains(int anEntry) {
Node *p = first;
Node *q = nullptr;
while(p != nullptr) {
this->incrementTraverseCount();
if(anEntry == p -> data) {
if (q != nullptr) {
q -> next = p -> next;
p -> next = first;
first = p;
}
return true;
}
q = p;
p = p -> next;
}
return false;
}
MTFList.h
// LinkedList.h
// Brandon Hoffman
// 10-21-2021
// Contains the declarations for the MTFList.h class inheriteing from LinkedList.h
#ifndef MTF_LIST_
#define MTF_LIST_
#include "LinkedList.h"
class MTFList : public LinkedList
{
public:
//Returns true if anEntry is found in the List, otherwise returns false. For every Node traversed in
//search of anEntry, increases traverseCount by 1. Overwites functionality from Linked List
//Now moves node with first occurrence of anEntry match to become new first node
virtual bool contains(int anEntry);
};
#endif
LinkedList.cpp
// LinkedList.cpp
// Brandon Hoffman
// 10-21-2021
// Contains all of the functionality from members of LinkedList.h
#include <iostream>
#include <string>
#include "LinkedList.h"
//constructor
//takes an array an int n to denote the length of the array
//this is for manual testing purposes only
LinkedList::LinkedList(int A[], int n)
{
Node *t;
int i = 0;
first = new Node;
first -> data = A[0];
first -> next = nullptr;
last = first;
for(i = 1; i < n; i++) {
t = new Node;
t -> data = A[i];
t -> next = nullptr;
last -> next = t;
last = t;
}
};
//destructor
//The destructor deallocates all of the dynamic storage (each Node) and deletes the Node.
LinkedList::~LinkedList()
{
this->clear();
}
//displays data in each Node for manual testing purposes
void LinkedList::display()
{
Node *p = first;
while(p) {
std::cout << p -> data << " ";
p = p -> next;
}
std::cout << std::endl;
}
// Returns currentSize, the current number of nodes in the linked List.
int LinkedList::getCurrentSize() const
{
return currentSize;
}
//returns True if LinkedList is empty i.e. first is pointed nullptr
bool LinkedList::isEmpty() const
{
if (first == nullptr) {
return true;
}
else {
return false;
}
}
//Creates a new Node (dynamically allocated) with data = newEntry, adds it to the back of the
//List, and increases currentSize by 1.Returns true if the new Node with newEntry was added
//successfully, otherwise returns false.
bool LinkedList::add(int newEntry)
{
Node *temporary;
temporary = new Node;
temporary -> data = newEntry;
temporary -> next = nullptr;
if (first==nullptr) {
first = last = temporary;
}
else {
last -> next = temporary;
last = temporary;
}
this -> incrementCurrentSize();
return true;
}
//Searches and removes the first occurrence of anEntry in the List and decreases numItems by 1.
//Deallocates memory for the removed Node. Returns true if anEntry was removed successfully,
//otherwise returns false (if anEntry was not found in the List or the List was empty).
//calls decrementCurrentSize method to reduce currentSize by 1
bool LinkedList::remove(int anEntry)
{
Node *p = first;
Node *q = nullptr;
while (p != nullptr) {
if(anEntry == p -> data){
if (q == nullptr) {
q = first;
first = first -> next;
delete q;
}
else {
q -> next = p -> next;
delete p;
}
this->decrementCurrentSize();
return true;
;
}
q = p;
p = p -> next;
}
return false;
}
//Removes all items from the List and resets currentSize to 0. Deallocates memory for each Node removed.
void LinkedList::clear()
{
Node *p = first;
while (first) {
first = first -> next;
delete p;
p = first;
}
this->resetCurrentSize();
}
//Returns true if anEntry is found in the List, otherwise returns false. For every Node traversed in
//search of anEntry, increases traverseCount by 1.
bool LinkedList::contains(int anEntry)
{
Node *p = first;
while(p != nullptr) {
this->incrementTraverseCount();
if(anEntry == p -> data) {
return true;
}
p = p -> next;
}
return false;
}
LinkedList.h
// LinkedList.h
// Brandon Hoffman
// 10-21-2021
// Contains the declarations for the LinkedList class
#ifndef LINKED_LIST_
#define LINKED_LIST_
#include "IList.h"
class LinkedList: public IList
{
public:
//constructor
//The constructor initializes an empty LinkedList by setting both currentSize and traverseCount to
//0 and setting first and lst to nullptr.
//two constructors, first is standard for assignment, 2nd takes an array for testing purposes
LinkedList(){first=nullptr; last=nullptr; traverseCount=0; currentSize=0;}
LinkedList(int A[], int n);
//destructor
//The destructor deallocates all of the dynamic storage (each Node) and deletes the Node.
virtual ~LinkedList();
//accessors
//displays data in each Node for manual testing purposes
void display();
// Returns currentSize, the current number of nodes in the linked List.
virtual int getCurrentSize() const;
//mutators
//Sets traverseCount to 0.
void resetTraverseCount() {traverseCount=0;}
//Creates a new Node (dynamically allocated) with data = newEntry, adds it to the back of the
//List, and increases currentSize by 1.Returns true if the new Node with newEntry was added
//successfully, otherwise returns false.
virtual bool add(int newEntry);
//Searches and removes the first occurrence of anEntry in the List and decreases numItems by 1.
//Deallocates memory for the removed Node. Returns true if anEntry was removed successfully,
//otherwise returns false (if anEntry was not found in the List or the List was empty).
//calls decrementCurrentSize method to reduce currentSize by 1
virtual bool remove(int anEntry);
//Removes all items from the List and resets currentSize to 0. Deallocates memory for each Node removed.
virtual void clear();
//Returns true if anEntry is found in the List, otherwise returns false. For every Node traversed in
//search of anEntry, increases traverseCount by 1.
virtual bool contains(int anEntry);
//returns True if LinkedList is empty i.e. first is pointed nullptr
virtual bool isEmpty() const;
protected:
//standard Node structure
struct Node
{
int data;
struct Node *next;
};
struct Node *first, *last;
int currentSize = 0;
//mutators
void incrementCurrentSize() {currentSize++;}
void decrementCurrentSize() {currentSize--;}
void resetCurrentSize() {currentSize=0;}
void incrementTraverseCount() {traverseCount++;}
bool isValidEntry();
};
#endif
IList.h
// Modified from code created by Frank M. Carrano and Timothy M. Henry.
// Copyright (c) 2017 Pearson Education, Hoboken, New Jersey.
#ifndef I_LIST_
#define I_LIST_
class IList
{
public:
/** Constructor */
IList () : traverseCount(0) { }
/** Destroys object and frees memory allocated by object.
(See C++ Interlude 2) */
virtual ~IList () { }
/** Gets the current number of entries in this list.
@return The integer number of entries currently in the list. */
virtual int getCurrentSize() const = 0;
/** Sees whether this list is empty.
@return True if the list is empty, or false if not. */
virtual bool isEmpty() const = 0;
/** Adds a new entry to this list.
@post If successful, newEntry is stored in the list and
the count of items in the list has increased by 1.
@param newEntry The object to be added as a new entry.
@return True if addition was successful, or false if not. */
virtual bool add(int newEntry) = 0;
/** Removes one occurrence of a given entry from this list,
if possible.
@post If successful, anEntry has been removed from the list
and the count of items in the list has decreased by 1.
@param anEntry The entry to be removed.
@return True if removal was successful, or false if not. */
virtual bool remove(int anEntry) = 0;
/** Removes all entries from this list.
@post List contains no items, and the count of items is 0. */
virtual void clear() = 0;
/** Tests whether this list contains a given entry.
@param anEntry The entry to locate.
@return True if list contains anEntry, or false otherwise. */
virtual bool contains(int anEntry) = 0;
/** Get the count of number of nodes traversed.
@return The integer number of nodes traversed since last time the count was reset. */
virtual int getTraverseCount() const { return traverseCount; }
/** Reset the count of nodes traversed to zero. */
virtual void resetTraverseCount() { traverseCount = 0; }
protected:
int traverseCount;
}; // end IList
#endif