I've been studying data structures and making implementations for different types. This is my linked list code. Please let me know if there's anything here that could be improved upon or changed for better performance. I should mention that it compiles and functions as intended.
Also, I'm not sure what I need the friend
line and class prototype line for. I think the class prototype line stems from some intricate detail of how templates work on compilation, but I'm not sure what that is. As for the friend line, it's either that or I make a get()
and set()
function in the Node
class. I'm not sure which would be better.
template <typename T> class LinkedList; // Not sure why this needs to be... LOOK INTO IT
// Node CLASS ===================================
template <typename T>
class Node {
friend class LinkedList<T>; // Not sure why this needs to be... LOOK INTO IT
public:
Node(T data);
~Node();
T getData();
private:
T mData; // Some data stored by user
Node<T>* mNextNode; // Points to the next node in the list
};
// ==============================================
// LinkedList CLASS =============================
template <typename T>
class LinkedList {
public:
LinkedList();
~LinkedList();
void print(); // Prints the contents of each node in the list
void insert(T data); // Crafts a new node using the argument then inserts it properly into the list
void remove(T data); // Removes/deletes a node from the list
private:
Node<T>* mHeadNode; // Leading node in the list
unsigned int mSize; // Number of nodes in the list
};
// ==============================================
// Node IMPLEMENTATION ==========================
template <typename T>
Node<T>::Node(T data) {
mData = data;
mNextNode = nullptr; // Creating a node has nothing to do with what comes after it
}
template <typename T>
Node<T>::~Node() {} // Don't delete mNextNode or you'll end up deleting essentially every node in the list
template <typename T>
T Node<T>::getData() {
return mData;
}
// ==============================================
// LinkedList IMPLEMENTATION ====================
template <typename T>
LinkedList<T>::LinkedList() {
mHeadNode = nullptr; // When a new list is created, there is no leading node
mSize = 0;
}
template <typename T>
LinkedList<T>::~LinkedList() {
if (mHeadNode) { // No need to delete anything if the list is empty
Node<T>* trailingNode = mHeadNode; // Deletes each node in the list
while (trailingNode) { // Keep traversing list until we've hit nullptr, or end of list
Node<T>* breadcrumbNode = trailingNode->mNextNode; // Iterator to traverse list. Directs trailingNode
// where to go after trailingNode deletes itself
delete trailingNode;
trailingNode = breadcrumbNode; // Move forward in the list
}
}
}
template <typename T>
void LinkedList<T>::print() {
Node<T>* traversalNode = mHeadNode; // Iterator to traverse list
while (traversalNode) { // Keep traversing list until we've hit nullptr, or end of list
cout << traversalNode->getData() << endl; // Print the contents of each node
traversalNode = traversalNode->mNextNode; // Point to next node in the list
}
}
template <typename T>
void LinkedList<T>::insert(T data) {
Node<T>* nodeToInsert = new Node<T>(data); // Craft the new node to be inserted using argument
if (!mHeadNode) { // If the list is empty, the new node should be the leading node
mHeadNode = nodeToInsert;
}
else { // Begin traversing the list to find the proper position for the new node
Node<T>* nodeToCompareTo = mHeadNode; // This traversal node's content will be compared with the new node's
// content. Represents the node that will lie just ahead of the new node
// after the new node is inserted
Node<T>* trailingNode = nullptr; // Represents the node that will lie just behind the new node. This traversal
// node will always trail behind nodeToCompareTo by one position
while (nodeToCompareTo) { // Traverse the list until we hit the end nullptr
if (nodeToCompareTo->getData() >= nodeToInsert->getData()) {
break; // Found the new node's proper position
}
else {
trailingNode = nodeToCompareTo; // Trailing node needs to move to catch up with comparison node
nodeToCompareTo = nodeToCompareTo->mNextNode;
}
}
if (nodeToCompareTo == mHeadNode) { // New node should be the new leading node
nodeToInsert->mNextNode = mHeadNode; // Don't loose sight of the old head node
mHeadNode = nodeToInsert; // Now that we have sight of the old head node, replace it with the new node
}
else { // New node needs to be positioned somewhere in the list, not at the head
nodeToInsert->mNextNode = nodeToCompareTo; // nodeToCompareTo represents the new node's front neighbor
trailingNode->mNextNode = nodeToInsert; // trailingNode represents the new node's back neighbor
}
mSize++;
}
}
template <typename T>
void LinkedList<T>::remove(T data) {
if (!mHeadNode) { // Check if the list is empty
cout << "List is empty..." << endl;
}
else {
Node<T>* nodeToCompareTo = mHeadNode; // Argument will be compared with this node's data. If there's a match,
// we will delete the node that this traversal node points to
Node<T>* trailingNode = nullptr; // Trails nodeToCompareTo. Keeps track of removed node's back neighbor
// so we can redirect its pointer after removal
while (nodeToCompareTo) { // Traverse the list until it ends
if (nodeToCompareTo->getData() == data) { // Does targeted node's data match argument data?
break;
}
else {
trailingNode = nodeToCompareTo; // Trail nodeToCompareTo as it iterates through the list
nodeToCompareTo = nodeToCompareTo->mNextNode;
}
}
if (!nodeToCompareTo) { // Check if the traversal node has fallen off the list without a match
cout << "Node not found..." << endl;
}
else if (nodeToCompareTo == mHeadNode) { // Check if the node to remove is the leading node
mHeadNode = mHeadNode->mNextNode; // Shift the head node one iteration further
delete nodeToCompareTo; // Delete the old head node from the list
mSize--;
}
else { // Node found was somewhere within the list, not the leading node
trailingNode->mNextNode = nodeToCompareTo->mNextNode; // Node to be removed's back neighbor should "skip"
// its mNext pointer to the node after the one that
// is about to be deleted
delete nodeToCompareTo; // Delete the targeted node from the list
mSize--;
}
}
}
// ==============================================