Linked Lists – Part III

I spent some time enhancing and testing the SingleLinkList class. I will stop at this point. On the next post related linked lists will design and implement the DoubleLinkList class using the same methods that have been implemented on the SingleLinkList class. Of course, while testing one class or the other some new method may be added or an existing method may be modified. This is typical in software development.

Following is a screen capture of a console generated by Visual Studio 2013 when running the test code for the SingleLinkList class:

main <<< enqueue widget:  this: 0xe4896fead8 id: 3 name ==>widget_3<==

main <<< head:  this: 0xe4896fef60 id: 1 name ==>widget_1<==
main <<< tail:  this: 0xe4896fefc0 id: 3 name ==>widget_3<==

main <<< j: 0 p:  this: 0x2c5c0d81160 next: 0x2c5c0d81220 prev: 0x0
main <<< insertAfter widget:  this: 0xe4896ff0c8 id: 4 name ==>widget_4<==
main <<< j: 3 p:  this: 0x2c5c0d81720 next: 0x2c5c0d80690 prev: 0x0
main <<< insertAfter widget:  this: 0xe4896ff0c8 id: 5 name ==>widget_5<==
main <<< j: 0 p:  this: 0x2c5c0d81160 next: 0x2c5c0d81a60 prev: 0x0
main <<< insertAfter widget:  this: 0xe4896ff0c8 id: 6 name ==>widget_6<==
main <<< j: 3 p:  this: 0x2c5c0d81220 next: 0x2c5c0d81720 prev: 0x0
main <<< insertAfter widget:  this: 0xe4896ff0c8 id: 7 name ==>widget_7<==
main <<< j: 0 p:  this: 0x2c5c0d81160 next: 0x2c5c0d81d60 prev: 0x0
main <<< insertAfter widget:  this: 0xe4896ff0c8 id: 8 name ==>widget_8<==
main <<< j: 1 p:  this: 0x2c5c0d81fe0 next: 0x2c5c0d81d60 prev: 0x0
main <<< insertAfter widget:  this: 0xe4896ff0c8 id: 9 name ==>widget_9<==
main <<< j: 6 p:  this: 0x2c5c0d81ea0 next: 0x2c5c0d81720 prev: 0x0
main <<< insertAfter widget:  this: 0xe4896ff0c8 id: 10 name ==>widget_10<==
main <<< slq->isFull i: 7
main <<< slq->isFull i: 8
main <<< slq->isFull i: 9

main <<< pop widget:  this: 0xe4896fead8 id: 5 name ==>widget_5<==
main <<< pop widget:  this: 0xe4896fead8 id: 3 name ==>widget_3<==
main <<< pop widget:  this: 0xe4896fead8 id: 10 name ==>widget_10<==
main <<< pop widget:  this: 0xe4896fead8 id: 7 name ==>widget_7<==
main <<< pop widget:  this: 0xe4896fead8 id: 2 name ==>widget_2<==
main <<< pop widget:  this: 0xe4896fead8 id: 4 name ==>widget_4<==
main <<< pop widget:  this: 0xe4896fead8 id: 6 name ==>widget_6<==
main <<< pop widget:  this: 0xe4896fead8 id: 9 name ==>widget_9<==
main <<< pop widget:  this: 0xe4896fead8 id: 8 name ==>widget_8<==
main <<< pop widget:  this: 0xe4896fead8 id: 1 name ==>widget_1<==

main <<< enqueue widget:  this: 0xe4896fead8 id: 1 name ==>widget_1<==
main <<< j: 0 p:  this: 0x2c5c0d810e0 next: 0x2c5c0d80690 prev: 0x0
main <<< insertBefore widget:  this: 0xe4896ff318 id: 2 name ==>widget_2<==
main <<< j: 1 p:  this: 0x2c5c0d810e0 next: 0x2c5c0d80690 prev: 0x0
main <<< insertBefore widget:  this: 0xe4896ff318 id: 3 name ==>widget_3<==
main <<< j: 0 p:  this: 0x2c5c0d81620 next: 0x2c5c0d817e0 prev: 0x0
main <<< insertBefore widget:  this: 0xe4896ff318 id: 4 name ==>widget_4<==
main <<< j: 0 p:  this: 0x2c5c0d81ae0 next: 0x2c5c0d81620 prev: 0x0
main <<< insertBefore widget:  this: 0xe4896ff318 id: 5 name ==>widget_5<==
main <<< j: 0 p:  this: 0x2c5c0d81c20 next: 0x2c5c0d81ae0 prev: 0x0
main <<< insertBefore widget:  this: 0xe4896ff318 id: 6 name ==>widget_6<==
main <<< j: 2 p:  this: 0x2c5c0d81ae0 next: 0x2c5c0d81620 prev: 0x0
main <<< insertBefore widget:  this: 0xe4896ff318 id: 7 name ==>widget_7<==

main <<< push widget:  this: 0xe4896fead8 id: 1 name ==>widget_1<==
main <<< push widget:  this: 0xe4896fead8 id: 2 name ==>widget_2<==
main <<< push widget:  this: 0xe4896fead8 id: 3 name ==>widget_3<==
main <<< push widget:  this: 0xe4896fead8 id: 4 name ==>widget_4<==
main <<< push widget:  this: 0xe4896fead8 id: 5 name ==>widget_5<==
main <<< push widget:  this: 0xe4896fead8 id: 6 name ==>widget_6<==
main <<< push widget:  this: 0xe4896fead8 id: 7 name ==>widget_7<==
main <<< push widget:  this: 0xe4896fead8 id: 8 name ==>widget_8<==
main <<<  pop widget:  this: 0xe4896ff4e0 id: 8 name ==>widget_8<==
main <<<  pop widget:  this: 0xe4896ff4e0 id: 7 name ==>widget_7<==
main <<<  pop widget:  this: 0xe4896ff4e0 id: 6 name ==>widget_6<==
main <<<  pop widget:  this: 0xe4896ff4e0 id: 5 name ==>widget_5<==
main <<<  pop widget:  this: 0xe4896ff4e0 id: 4 name ==>widget_4<==
main <<<  pop widget:  this: 0xe4896ff4e0 id: 3 name ==>widget_3<==
main <<<  pop widget:  this: 0xe4896ff4e0 id: 2 name ==>widget_2<==
main <<<  pop widget:  this: 0xe4896ff4e0 id: 1 name ==>widget_1<==

Following is the test code written in C++:

// **** ****

#include &lt;iostream&gt;
#include &lt;time.h&gt;

// **** ****

using namespace std;

// **** header files (templates contain actual implementations) ****

#include "Widget.h"
#include "Node.h"
#include "SingleLinkList.h"
#include "Solution.h"

// **** constructor ****

Solution::Solution()
{}

// **** destructor ****

Solution::~Solution()
{}

/*
* TEST code.
*/
int main()
{

	// **** constant(s) ****

	const int MAX_ELEMENTS	= 10;

	char buffer[BUFSIZ];

	// ***** activity message ****

	cout &lt;&lt; "main &lt;&lt;&lt; testing widgets ..." &lt;&lt; endl;

	// **** create a new Widget ****

	Widget widget = Widget();

	// **** populate widget ****

	try {
		widget.setId(1234);
	} catch (int ex) {
		if (ex == widget.WidgetInvalidId) {
			cerr &lt;&lt; "main &lt;&lt;&lt; EXCEPTION widget.setId() - WidgetInvalidId" &lt;&lt; endl;
			exit(ex);
		}
	}
	
	try {
		widget.setName("widget_1234");
	} catch (int ex) {
		if (ex == widget.WidgetInvalidName) {
			cerr &lt;&lt; "main &lt;&lt;&lt; EXCEPTION widget.setName() - WidgetInvalidName" &lt;&lt; endl;
			exit(ex);
		}
	}

	// **** display the widget ****

	cout &lt;&lt; "main &lt;&lt;&lt; widget: " &lt;&lt; widget.toString() &lt;&lt; endl;

	// ***** activity message ****

	cout &lt;&lt; endl;
	cout &lt;&lt; "main &lt;&lt;&lt; testing nodes ..." &lt;&lt; endl;

	// **** instantiate a node ****

	Node&lt;Widget&gt; node = Node&lt;Widget&gt;(widget);

	// **** display the node ****

	cout &lt;&lt; "main &lt;&lt;&lt; node: " &lt;&lt; node.toString() &lt;&lt; endl;

	// **** display the widget ****

	cout &lt;&lt; "main &lt;&lt;&lt; widget: " &lt;&lt; node.data.toString() &lt;&lt; endl;

	// **** build a crude list of nodes ****

	Node&lt;int&gt; *list = nullptr;
	for (int i = 1; i &lt;= 5; i++) {

		// **** allocate a node ****

		Node&lt;int&gt; *n = new Node&lt;int&gt;(i);

		// **** insert node at tail of list ****

		if (list == nullptr) {
			list = n;
		} else {
			Node&lt;int&gt; *p = list;
			for ( ; p-&gt;next != nullptr; p = p-&gt;next) {}
			p-&gt;next = n;
		}
	}

	// **** traverse the crude list of nodes ****

	for (Node&lt;int&gt; *p = list; p != nullptr; p = p-&gt;next) {
		cout &lt;&lt; "main &lt;&lt;&lt; p data: " &lt;&lt; p-&gt;data &lt;&lt; " next: " &lt;&lt; p-&gt;next &lt;&lt; endl;
	}

	// **** delete all elements from the crude list of nodes ****

	for (Node&lt;int&gt; *p = list; p != nullptr; ) {
		Node&lt;int&gt; *n = p;
		p = p-&gt;next;
		cout &lt;&lt; "main &lt;&lt;&lt; deleting: " &lt;&lt; n-&gt;data &lt;&lt; endl;
		delete(n);
	}

	// ***** activity message ****

	cout &lt;&lt; endl;
	cout &lt;&lt; "main &lt;&lt;&lt; testing single link list ..." &lt;&lt; endl;

	// **** allocate single link queue ****

	SingleLinkList&lt;Widget&gt; *slq = new SingleLinkList&lt;Widget&gt;(MAX_ELEMENTS);
	cout &lt;&lt; "main &lt;&lt;&lt; slq: " &lt;&lt; slq-&gt;toString() &lt;&lt; endl;

	// **** loop inserting elements into the queue ****

	for (int i = 1; i &lt;= 5; i++) {

		// **** allocate a widget ****

		int status			= _itoa_s(i, buffer, sizeof(buffer), 10);
		string widgetName	= "widget_" + string(buffer);
		Widget widget		= Widget(i, widgetName);
		cout &lt;&lt; "main &lt;&lt;&lt; enqueue widget: " &lt;&lt; widget.toString() &lt;&lt; endl; // ***** enqueue widget **** slq-&gt;enqueue(widget);
	}
	cout &lt;&lt; "main &lt;&lt;&lt; slq: " &lt;&lt; slq-&gt;toString() &lt;&lt; endl;
	cout &lt;&lt; endl; // **** loop removing elements from the queue **** while (!slq-&gt;isEmpty()) {
		cout &lt;&lt; "main &lt;&lt;&lt; dequeue widget: " &lt;&lt; slq-&gt;dequeue().toString() &lt;&lt; endl;
	} 
	cout &lt;&lt; endl;

	// **** insert some widgets into the queue ****

	for (int i = 1; i &lt;= 3; i++) {
	
		// **** create a widget ****

		int status			= _itoa_s(i, buffer, sizeof(buffer), 10);
		string widgetName	= "widget_" + string(buffer);
		widget				= Widget(i, widgetName);
		cout &lt;&lt; "main &lt;&lt;&lt; enqueue widget: " &lt;&lt; widget.toString() &lt;&lt; endl; // **** enqueue the widget into the queue **** slq-&gt;enqueue(widget);
	}
	cout &lt;&lt; endl;

	// **** peek at the head of queue ****

	cout &lt;&lt; "main &lt;&lt;&lt; head: " &lt;&lt; slq-&gt;peekHead().toString() &lt;&lt; endl;

	// **** peek at the tail of the queue ****

	cout &lt;&lt; "main &lt;&lt;&lt; tail: " &lt;&lt; slq-&gt;peekTail().toString() &lt;&lt; endl &lt;&lt; endl;

	// **** initialize the pseudo random number generator ****

	srand((unsigned int)time(nullptr));

	// **** insert after ****

	int j	= 0;
	for (int i = 0; i &lt; 10; i++) { // **** check if queue is full **** if (slq-&gt;isFull()) {
			cout &lt;&lt; "main &lt;&lt;&lt; slq-&gt;isFull i: " &lt;&lt; i &lt;&lt; endl;
			continue;
		}

		// **** location to insert after in queue ****
	
		Node&lt;Widget&gt; *p;
		switch (i) {
			case 0:						// head of queue
				j = 0;
				p = slq-&gt;getHead();
			break;

			case 1:						// tail of queue
				j = slq-&gt;getCount() - 1;
				p = slq-&gt;getTail();
			break;

			default:					// random location in queue

				// **** generate random location in queue ****

				j = rand() % slq-&gt;getCount();

				// **** pointer to specified element in the queue ****

				p = slq-&gt;elementAt(j);
			break;
		}
		cout &lt;&lt; "main &lt;&lt;&lt; j: " &lt;&lt; j &lt;&lt; " p: " &lt;&lt; p-&gt;toString() &lt;&lt; endl; // **** create a widget **** int widgetId = slq-&gt;getCount() + 1;
		int status			= _itoa_s(widgetId, buffer, sizeof(buffer), 10);
		string widgetName	= "widget_" + string(buffer);
		Widget widget		= Widget(widgetId, widgetName);
		cout &lt;&lt; "main &lt;&lt;&lt; insertAfter widget: " &lt;&lt; widget.toString() &lt;&lt; endl; // **** insert widget into the queue AFTER the specified element **** try { slq-&gt;insertAfter(p, widget);
		} catch (int ex) {
			cerr &lt;&lt; "main &lt;&lt;&lt; EXCEPTION slq-&gt;insertAfter ex: " &lt;&lt; ex &lt;&lt; endl; 
			exit(ex);
		}
	}
	cout &lt;&lt; endl; // **** POP from the tail of the queue (all widgets) **** while (!slq-&gt;isEmpty()) {

		// **** pop next element from the queue ****

		widget = slq-&gt;pop();
		cout &lt;&lt; "main &lt;&lt;&lt; pop widget: " &lt;&lt; widget.toString() &lt;&lt; endl;
	}
	cout &lt;&lt; endl; // **** create a widget **** widget = Widget(1, "widget_1"); // **** enqueue widget **** slq-&gt;enqueue(widget);
	cout &lt;&lt; "main &lt;&lt;&lt; enqueue widget: " &lt;&lt; widget.toString() &lt;&lt; endl;

	// **** insert BEFORE ****

	for (int i = 2; (i &lt;= 7) &amp;&amp; !slq-&gt;isEmpty(); i++)
	{

		// **** check if queue is full ****

		if (slq-&gt;isFull())
		{
			cout &lt;&lt; "main &lt;&lt;&lt; slq-&gt;isFull" &lt;&lt; endl;
			continue;
		}

		// **** location to insert before in queue ****

		Node&lt;Widget&gt; *p;

		// **** generate random location in queue ****

		j = rand() % slq-&gt;getCount();

		// **** point to an element in the queue ****

		try {
			p = slq-&gt;elementAt(j);
		} catch (int ex) {
			cout &lt;&lt; "main &lt;&lt;&lt; EXCEPTION slq-&gt;elementAt j: " &lt;&lt; j &lt;&lt; " ex: " &lt;&lt; ex &lt;&lt; endl;
			exit(ex);
		}
		cout &lt;&lt; "main &lt;&lt;&lt; j: " &lt;&lt; j &lt;&lt; " p: " &lt;&lt; p-&gt;toString() &lt;&lt; endl; // **** create a widget **** int widgetId = slq-&gt;getCount() + 1;
		int status			= _itoa_s(widgetId, buffer, sizeof(buffer), 10);
		string widgetName	= "widget_" + string(buffer);
		Widget widget		= Widget(widgetId, widgetName);
		cout &lt;&lt; "main &lt;&lt;&lt; insertBefore widget: " &lt;&lt; widget.toString() &lt;&lt; endl; // **** insert widget into the queue BEFORE the specified element **** try { slq-&gt;insertBefore(p, widget);
		}
		catch (int ex)
		{
			cerr &lt;&lt; "main &lt;&lt;&lt; EXCEPTION slq-&gt;insertBefore ex: " &lt;&lt; ex &lt;&lt; endl;
			exit(ex);
		}
	}
	cout &lt;&lt; endl; // **** clear the queue **** while (!slq-&gt;isEmpty()) {
		slq-&gt;dequeue();
	}

	// **** push elements on the stack (queue) ****

	for (int i = 0; i &lt;= 7; i++) { // **** check if the stack is full **** if (slq-&gt;isFull()) {
			cout &lt;&lt; "main &lt;&lt;&lt; slq-&gt;isFull i: " &lt;&lt; i &lt;&lt; endl; continue; } // **** create a widget **** int widgetId = slq-&gt;getCount() + 1;
		int status			= _itoa_s(widgetId, buffer, sizeof(buffer), 10);
		string widgetName	= "widget_" + string(buffer);
		widget				= Widget(widgetId, widgetName);
		cout &lt;&lt; "main &lt;&lt;&lt; push widget: " &lt;&lt; widget.toString() &lt;&lt; endl; // **** push the widget on to the stack (queue) **** slq-&gt;push(widget);
	}

	// **** pop elements from the stack (queue) ****

	while (!slq-&gt;isEmpty()) {
		cout &lt;&lt; "main &lt;&lt;&lt;  pop widget: " &lt;&lt; slq-&gt;pop().toString() &lt;&lt; endl;
	}

	// **** all done ****

	return 0;
}

The SingleLinkList class uses a template; which allow us to use the queue with different types of objects. I tested it with integers and Widgets. The Widget class was solely created to test the operations of the queue. The Widgets class may have additional fields by the time the entire queue implementation is done.

The first set of items in the test code checks the operations of creating, populating and displaying Widgets.

The second set of operations in the test code checks creating, populating, displaying and creating a crude linked list with elements from the Node class. A list is created, nodes are inserted and deleted.

The third part of the test starts by checking out the enqueue method. The isEmpty method was created to eliminate the need to count the number of elements remaining in the queue. Before implementing the method I started by looping the same number of times as elements were inserted. The second pas used the getCount getter to determine if we should remove additional nodes. The current test implementation makes use of the isEmpty method. This is also typical of Agile and TDD approaches. You only implement what is needed and always generate test cases that do not compile before the actual code in the associated classes.

After testing the dequeue method a few widgets are inserted into the queue and we test the methods to get what is at the head and the tail of the queue.

We then test the insertAfter and insertBefore methods. These are used to insert elements at random positions in the queue. I believe we will be using them in a future implementation of a LFU Cache.

We then loop invoking the pop method. A queue can be used to implement FILO (queue) or FIFO (stack) objects. It is simpler and cleaner when using a double linked Node, but as you can verify, such operations also work with single linked lists.

Finally a few widgets are pushed and popped from a stack (implemented with the same queue).

The C++ code for the Widget class follows:

#include &lt;string&gt;

#include "Widget.h"

/*
	Constructor.
*/
Widget::Widget()
{
id		= 0;
name	= "";
}

/*
*/
Widget::Widget(int newId, std::string newName) {
	id		= newId;
	name	= newName;
}

/*
	Destructor.
*/
Widget::~Widget()
{}

// **** getters ****

int Widget::getId() {
	return id;
}

std::string Widget::getName() {
	return name;
}

// **** setters ****

void Widget::setId(int newId) {
	if (newId &lt;= 0) { throw WidgetInvalidId; } id = newId; } void Widget::setName(std::string newName) { if (newName == "") { throw WidgetInvalidName; } name = newName; } /* Return a string representation of the Widget object */ std::string Widget::toString() { std::string str = ""; char buffer[BUFSIZ]; errno_t status; status = _i64toa_s((long long)this, buffer, sizeof(buffer), 16); str += " this: 0x" + std::string(buffer); status = _itoa_s(id, buffer, sizeof(buffer), 10); str += " id: " + std::string(buffer); str += " name ==&gt;" + name + "&lt;==";
	return str;
}

// **** overload = operator ****

void Widget::operator=(const Widget&amp;obj) {
	(*this).id		= obj.id;
	(*this).name	= obj.name;
}

The C++ code for the Node class follows:

#pragma once

// **** ****

#include &lt;string&gt;
#include &lt;sstream&gt;


// **** vvvv class definition vvvv

template &lt;typename T&gt;
class Node
{

	// **** constants ****

public:
	const int NodeInvalidData = -1;

	// **** members ****

public: 
	T		data;
	Node&lt;T&gt;	*next;
	Node&lt;T&gt;	*prev;

	// **** constructor ****

public:
	Node();

	// **** constructor ****

public:
	Node(T myData);

	// **** destructor ****

public:
	~Node();

	// **** setters ***

public:
	void setData(T myData);

	// **** getters ****

public:
	T getData();

	// **** return a string with the contents of the node ****

public:
	std::string toString();

	// **** override the &lt;&lt; operator ****

public:
	friend std::ostream&amp; operator &lt;&lt;(std::ostream &amp;outputStream, const Node&lt;T&gt; &amp;node);
};

// **** vvvv class implementation vvvv

/*
	Constructor.
*/
template &lt;typename T&gt;
Node&lt;T&gt;::Node()
{
	next = (Node&lt;T&gt; *)nullptr;
	prev = (Node&lt;T&gt; *)nullptr;
}

// **** constructor ****

template &lt;typename T&gt;
Node&lt;T&gt;::Node(T myData)
{
	data = myData;
	next = (Node&lt;T&gt; *)nullptr;
	prev = (Node&lt;T&gt; *)nullptr;
}

// **** destructor ****

template &lt;typename T&gt;
Node&lt;T&gt;::~Node()
{}

// **** setters ****

template &lt;typename T&gt;
void Node&lt;T&gt;::setData(T myData)
{
	data = myData;
}

// **** getters ****

template &lt;typename T&gt;
T Node&lt;T&gt;::getData()
{
	return data;
}

// **** override &lt;&lt; operator ****

template &lt;typename T&gt;
std::ostream&amp; operator &lt;&lt;(std::ostream&amp; outputStream, const Node&lt;T&gt;&amp; node) {
	string str = (T)(node.data).toString();
	outputStream &lt;&lt; "data=" &lt;&lt; str;
	return outputStream;
}

/*
	Generate a string with the contents of the node.
*/
template &lt;typename T&gt;
std::string Node&lt;T&gt;::toString()
{
	std::string str = "";
	char buffer[BUFSIZ];
	errno_t status;

	status = _i64toa_s((long long)this, buffer, sizeof(buffer), 16);
	str += " this: 0x" + std::string(buffer);

	status = _i64toa_s((long long)next, buffer, sizeof(buffer), 16);
	str += " next: 0x" + std::string(buffer);

	status = _i64toa_s((long long)prev, buffer, sizeof(buffer), 16);
	str += " prev: 0x" + std::string(buffer);

	return str;
}

// **** override the &lt;&lt; operator ****

Note that I was about to override the “<<” operator but decided to leave it for when needed.

The C++ class for the SingleLinkList class follows:

#pragma once

// **** ****

#include &lt;string&gt;

// **** ****

#include "Node.h"


// **** vvvv class definition vvvv

template &lt;typename T&gt;
class SingleLinkList
{

// **** constants ****

public:
	const int QueueIsFull			= -1;
	const int QueueIsEmpty			= -2;
	const int QueueInvalidLocation	= -3;
	const int QueueNullPointer		= -4;
	const int QueueInvalidPointer	= -5;
	const int QueueInvalidMaxCount	= -6;

// **** members ****

private:
	int		count;
	int		maxCount;
	Node&lt;T&gt;	*head;
	Node&lt;T&gt;	*tail;

// **** constructor ****

public:
	SingleLinkList();

// **** constructor ****

public:
	SingleLinkList(int maxElements);

// **** destructor ****

public:
	~SingleLinkList();

// **** getters ****

public:
	int SingleLinkList&lt;T&gt;::getCount();

public:
	Node&lt;T&gt; *SingleLinkList&lt;T&gt;::getHead();

public:
	Node&lt;T&gt; *SingleLinkList&lt;T&gt;::getTail();

// **** setters ****


// **** generate string of queue ****

public:
	std::string SingleLinkList&lt;T&gt;::toString();

// **** enqueue element at tail of queue ****

public:
	void SingleLinkList&lt;T&gt;::enqueue(T myData);

// **** dequeue element at head of queue ****

public:
	T SingleLinkList&lt;T&gt;::dequeue();

// **** check if queue is empty ****

public:
	bool SingleLinkList&lt;T&gt;::isEmpty();

// ***** peek at the head of the queue ****

public:
	T SingleLinkList&lt;T&gt;::peekHead();
	
// **** peek at the tail of the queue ****

public:
	T SingleLinkList&lt;T&gt;::peekTail();

// **** return a pointer to the element in the queue at the specified location ****

public:
	Node&lt;T&gt; *SingleLinkList&lt;T&gt;::elementAt(int loc);

// **** check if pointer is valid in this queue ****

public:
	bool SingleLinkList&lt;T&gt;::isValidPointer(Node&lt;T&gt; *p);

// **** insert AFTER specified element ****

public:
	void SingleLinkList&lt;T&gt;::insertAfter(Node&lt;T&gt; *p, T myData);

// **** check if queue is full ****

public:
	bool SingleLinkList&lt;T&gt;::isFull();

// **** pop element from head of queue (FIFO - stack) ****

public:
	T SingleLinkList&lt;T&gt;::pop();

// **** insert BEFORE specified element ****

public:
	void SingleLinkList&lt;T&gt;::insertBefore(Node&lt;T&gt; *p, T myData);

// **** PUSH an element into a queue (stack) ****

public:
	void SingleLinkList&lt;T&gt;::push(T myData);
};


// **** vvvv class implementation vvvv

/*
	Base constructor.
*/
template &lt;typename T&gt;
SingleLinkList&lt;T&gt;::SingleLinkList()
{
	count		= 0;
	maxCount	= 0;
	head		= (Node&lt;T&gt; *)this;
	tail		= (Node&lt;T&gt; *)this;
}

/*
	Constructor indicating max count of element is the queue.
*/
template &lt;typename T&gt;
SingleLinkList&lt;T&gt;::SingleLinkList(int maxElements) {

	// **** check if the value is invalid ****

	if (maxElements &lt; 0) {
		throw QueueInvalidMaxCount;
	}

	// **** ****

	count		= 0;
	maxCount	= maxElements;
	head		= (Node&lt;T&gt; *)this;
	tail		= (Node&lt;T&gt; *)this;
}

/*
	Base destructor.
*/
template &lt;typename T&gt;
SingleLinkList&lt;T&gt;::~SingleLinkList()
{}

/*
	Build a string representation of the queue.
*/
template &lt;typename T&gt;
std::string SingleLinkList&lt;T&gt;::toString()
{
	std::string str = "";
	char buffer[64];
	int status;

	// **** collect data from the queue ****

	status = _ui64toa_s((long long)this, buffer, sizeof(buffer), 16);
	str += "this: 0x" + std::string(buffer);

	status = _itoa_s(count, buffer, sizeof(buffer), 10);
	str += " count: " + std::string(buffer);

	status = _itoa_s(maxCount, buffer, sizeof(buffer), 10);
	str += " maxCount: " + std::string(buffer);

	status = _ui64toa_s((long long)head, buffer, sizeof(buffer), 16);
	str += " head: 0x" + std::string(buffer);

	status = _ui64toa_s((long long)tail, buffer, sizeof(buffer), 16);
	str += " tail: 0x" + std::string(buffer);

	// **** collect data from the queue elements ****

	for (Node&lt;T&gt; *p = this-&gt;head; p != (Node&lt;T&gt; *)this; p = p-&gt;next)
	{
		str += "\n";

		status = _ui64toa_s((long long)p, buffer, sizeof(buffer), 16);
		str += "\t\tp: 0x" + std::string(buffer);

		//status = _itoa_s(p-&gt;data, buffer, sizeof(buffer), 10);
		//str += " data: " + std::string(buffer);
		str += " data: " + (p-&gt;data).toString();

		status = _ui64toa_s((long long)p-&gt;next, buffer, sizeof(buffer), 16);
		str += " next: 0x" + std::string(buffer);
	}

	// **** ****

	return str;
}

/*
	Enqueue element at the tail of the queue.
	The queue maintains a FIFO order (enqueue &amp; dequeue).
*/
template &lt;typename T&gt;
void SingleLinkList&lt;T&gt;::enqueue(T myData)
{

	// **** check if queue is full ****

	if ((maxCount != 0) &amp;&amp; (count &gt;= maxCount))
	{
		throw QueueIsFull;
	}

	// **** allocate new node ****

	Node&lt;T&gt; *node = new Node&lt;T&gt;(myData);

	// **** insert node in empty queue ****

	if (SingleLinkList&lt;T&gt;::count == 0)
	{
		SingleLinkList&lt;T&gt;::head = node;
	}

	// **** insert new element at the tail of the queue ****

	else
	{
		SingleLinkList&lt;T&gt;::tail-&gt;next = node;
	}

	// **** ****

	node-&gt;next = (Node&lt;T&gt; *)this;
	SingleLinkList&lt;T&gt;::tail = node;
	SingleLinkList&lt;T&gt;::count++;
}

/*
	Dequeue element at head of queue.
	The queue maintains a FIFO order (enqueue &amp; dequeue).
*/
template &lt;typename T&gt;
T SingleLinkList&lt;T&gt;::dequeue()
{

	// **** check if the queue is empty ****

	if (count &lt;= 0)
	{
		throw QueueIsEmpty;
	}

	// **** for ease of use ****

	Node&lt;T&gt; *node = head;

	// **** get the data from the head node ****

	T data = node-&gt;data;

	// **** update the head ****

	head = head-&gt;next;

	// **** free (head) node ****

	delete(node);

	// **** decrement queue count ****

	count--;

	// **** update tail (queue is empty) ****

	if (count == 0)
	{
		tail = (Node&lt;T&gt; *)this;
	}

	// **** return value ****

	return data;
}

/*
	Get the count of elements in the queue.
*/
template &lt;typename T&gt;
int SingleLinkList&lt;T&gt;::getCount()
{
	return count;
}

/*
	Get the HEAD from the queue.
*/
template &lt;typename T&gt;
Node&lt;T&gt; *SingleLinkList&lt;T&gt;::getHead()
{
	return head;
}

/*
	Get the TAIL from the queue.
*/
template &lt;typename T&gt;
Node&lt;T&gt; *SingleLinkList&lt;T&gt;::getTail()
{
	return tail;
}

/*
	Determine if the queue is EMPTY.
	Same but less elegant approach could be achieved with getCount() and a test.
*/
template &lt;typename T&gt;
bool SingleLinkList&lt;T&gt;::isEmpty()
{
	return (count &lt;= 0);
}

/*
	Determine if the queue is FULL.
	Same but less elegant approach could be achieved with getCount() and a test.
*/
template &lt;typename T&gt;
bool SingleLinkList&lt;T&gt;::isFull()
{
	return (count &gt;= maxCount);
}

/*
	Return the element at the HEAD of the queue.
	Returns null if queue is empty.
*/
template &lt;typename T&gt;
T SingleLinkList&lt;T&gt;::peekHead() {
	if (count &lt;= 0) { throw QueueIsEmpty; } else { return this-&gt;head-&gt;data;
	}
}

/*
	Return the element at the TAIL of the queue.
	Returns null if queue is empty.
*/
template &lt;typename T&gt;
T SingleLinkList&lt;T&gt;::peekTail() {
	if (count &lt;= 0) { throw QueueIsEmpty; } else { return this-&gt;tail-&gt;data;
	}
}

/*
*/
template &lt;typename T&gt;
Node&lt;T&gt; *SingleLinkList&lt;T&gt;::elementAt(int loc) {

	// **** check if location is out of range ****

	if ((loc &lt; 0) || (loc &gt; count)) {
		throw QueueInvalidLocation;
	}

	// **** traverse the list until specified location is found ****

	Node&lt;T&gt; *p = head;
	for (int i = 0; i &lt; loc; i++) { p = p-&gt;next;
	}

	// **** ****

	return p;
}

/*
	Check if the specified pointer a valid pointer in this queue.
*/
template&lt;typename T&gt;
bool SingleLinkList&lt;T&gt;::isValidPointer(Node&lt;T&gt; *p) {
	
	// **** check for null pointer ****

	if (p == nullptr) {
		throw QueueNullPointer;
	}

	// **** traverse the queue looking for the specified pointer ****

	for (Node&lt;T&gt; *q = head; q != (Node&lt;T&gt; *)this; q = q-&gt;next) {
		if (q == p) {

			// **** node found ****

			return true;
		}
	}

	// **** node NOT found ****

	return false;
}

/*
	Insert the specified element AFTER the specified one.
*/
template &lt;typename T&gt;
void SingleLinkList&lt;T&gt;::insertAfter(Node&lt;T&gt; *p, T myData) {

	// **** check if queue is full ****

	if ((maxCount != 0) &amp;&amp; (count &gt;= maxCount))
	{
		throw QueueIsFull;
	}

	// **** check for null pointer ****

	if (p == nullptr) {
		throw QueueNullPointer;
	}

	// **** check if specified pointer is NOT in the queue ****

	if (!isValidPointer(p)) {
		throw QueueInvalidPointer;
	}

	// **** allocate new node ****

	Node&lt;T&gt; *node = new Node&lt;T&gt;(myData);

	// **** insert new element after the specified one ****

	node-&gt;next	= p-&gt;next;
	p-&gt;next		= node;

	// **** update the queue ****

	if (p == tail) {
		tail = node;
	}
	count++;
}

/*
	Pop the element at tail of queue.
	A queue maintains a FIFO order (enqueue &amp; dequeue).
	A stack (implemented as a queue) maintains a FILO order (push &amp; pop).
*/
template &lt;typename T&gt;
T SingleLinkList&lt;T&gt;::pop()
{

	// **** check if the queue is empty ****

	if (count &lt;= 0)
	{
		throw QueueIsEmpty;
	}

	// **** for ease of use ****

	Node&lt;T&gt; *node = tail;

	// **** get the data (to be returned) from the tail node ****

	T data = node-&gt;data;


	// **** queue has a single element ****

	if (count == 1) {
	
		// **** update the head and tail pointers (queue will be empty) ****

		head = (Node&lt;T&gt; *)this;
		tail = (Node&lt;T&gt; *)this;
	}

	// **** queue has more than one element ****

	else {
	
		// **** get to the node before the tail ****

		Node&lt;T&gt; *p = head;
		for ( ; p-&gt;next != tail; p = p-&gt;next) {}

		// **** update tail pointer ****

		tail	= p;

		// **** update new tail node ****

		p-&gt;next = (Node&lt;T&gt; *)this;
	}

	// **** free (previous tail) node ****

	delete(node);

	// **** decrement queue count ****

	count--;

	// **** return value ****

	return data;
}

/*
	Insert the specified element BEFORE the specified one.
*/
template &lt;typename T&gt;
void SingleLinkList&lt;T&gt;::insertBefore(Node&lt;T&gt; *p, T myData)
{

	// **** check if queue is full ****

	if ((maxCount != 0) &amp;&amp; (count &gt;= maxCount))
	{
		throw QueueIsFull;
	}

	// **** check for null pointer ****

	if (p == nullptr)
	{
		throw QueueNullPointer;
	}

	// **** check if specified pointer is NOT in the queue ****

	if (!isValidPointer(p))
	{
		throw QueueInvalidPointer;
	}

	// **** allocate new node ****

	Node&lt;T&gt; *node = new Node&lt;T&gt;(myData);

	// **** node being inserted BEFORE p ****

	node-&gt;next = p;

	// **** insert BEFORE head ****

	if (p == head) {
		node-&gt;next = head;
		head = node;
	}

	// **** insert somewhere else ****

	else {
	
		// **** get to the node before p ****

		Node&lt;T&gt; *q = head;
		for ( ; q-&gt;next != p; q = q-&gt;next) {}

		// **** ****

		q-&gt;next = node;
	}

	// **** update the queue ****

	count++;
}

/*
	PUSH and element into a stack (implemented as a queue).
*/
template &lt;typename T&gt;
void SingleLinkList&lt;T&gt;::push(T myData)
{
	enqueue(myData);
}

As you can see in the last lines of the SingleLinkList class, the push method is implemented by calling the enqueue method.

Also note that the queue contains the count and maxCount members. The reason is to quickly be able to tell the count without traversing the queue and more important to be able to implement XON / XOFF. More on this in a future post.

If you have comments or questions regarding this or any other post in this blog, please feel free and send me a message. I will not use your name unless you explicitly allow me to do so.

John

john.canessa@gmail.com

Follow me on Twitter:  @john_canessa

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.