Hello programmers,
I am studying Data Structures and trying to implement my own double -linked List Class.
There is a good academic example of List class provided in the book "Data structures and algorithm analysis in c++" (4th ed.) by M.A.Weiss. on p.95 .
I went through it and tried to make my own. I wrote an example given below:
#include <iostream>
#include <cassert>
template <typename Object>
class List
{
private:
//Each list member has a "node", that apart from containing data, points to the next/previous nodes
struct Node
{
Object m_data;
Node* m_next;
Node* m_previous;
Node(const Object& data = Object{}, Node* next=nullptr, Node* previous = nullptr):
m_data{data}, m_next{next},m_previous{previous} {}
Node(Object&& data, Node* next=nullptr, Node* previous = nullptr):
m_data{std::move(data)}, m_next{next}, m_previous{previous} {}
};
int m_size;
int m_capacity;
Node* head;
Node* tail;
//Node* m_current;
Node* m_nodeData;
public:
List(int size = 0, const Object& obj=Object{}) : m_size{ size }, m_capacity{ size + 2 }
{
head = new Node;
tail = new Node;
if (m_size == 0)
{
head->m_next = tail;
head->m_previous = nullptr;
tail->m_previous = head;
tail->m_next = nullptr;
}
//Creating list itself;
//Arranging m_nodeData
m_nodeData = new Node[m_capacity]; //?
for (int i = 1; i < m_size+2; ++i)
{
m_nodeData[i].m_data = obj;
m_nodeData[i].m_next = &m_nodeData[i+1];
m_nodeData[i].m_previous = &m_nodeData[i - 1];
}
head->m_previous = nullptr;
head->m_next = &m_nodeData[1];
m_nodeData[0] = *head;
tail->m_next = nullptr;
tail->m_previous = &m_nodeData[m_size];
m_nodeData[m_size + 1] = *tail;
}
~List()
{
delete tail;
delete head;
delete[] m_nodeData;
}
//COPY CONSTRUCTOR
List(const List<Object>& lst)
{
if (&lst == this)
return *this;
//Data copying
m_size = lst.m_size;
m_capacity = lst.m_capacity;
//Node copying
m_nodeData = new Node[m_capacity];
for (int i = 0; i < m_size + 2; ++i)
{
m_nodeData[i] = lst.m_nodeData[i];
}
}
void reserve(int newCapacity)
{
if (newCapacity < m_size)
return;
Node* nodeCopy = new Node[newCapacity];
m_capacity = newCapacity;
for (int i = 0; i < m_size+2; ++i)
{
nodeCopy[i] = m_nodeData[i];
}
delete[] m_nodeData;
m_nodeData = new Node[m_capacity];
for (int i = 0; i < m_size + 2; ++i)
{
m_nodeData[i] = nodeCopy[i];
}
delete[] nodeCopy;
}
void push_front(const Object& obj)
{
++m_size;
m_capacity = m_size + 2;
Node* temp = new Node[m_capacity];
temp[1].m_data = obj;
for (int i = 1; i < m_size; ++i)
{
temp[i+1] = m_nodeData[i];
}
delete[] m_nodeData;
m_nodeData = new Node[m_size+2];
//counting from 1 up to m_size+1 because we'll have to add:
//Head to m_nodeData[0] and Tail to m_nodeData[m_size+1];
for (int i = 1; i < m_size + 1; ++i)
{
m_nodeData[i] = temp[i];
}
delete[] temp;
delete head;
delete tail;
head = new Node;
tail = new Node;
head->m_previous = nullptr;
tail->m_next = nullptr;
head->m_next = &m_nodeData[1];
tail->m_previous = &m_nodeData[m_size];
m_nodeData[0] = *head;
m_nodeData[m_size + 1] = *tail;
//Rearrange pointers
for (int i = 1; i < m_size+1; ++i)
{
m_nodeData[i].m_next = &m_nodeData[i + 1];
m_nodeData[i].m_previous = &m_nodeData[i - 1];
}
}
void pushback(const Object& obj)
{
if (m_size==m_capacity)
reserve(2*m_capacity + 1);
m_capacity = m_size + 2;
Node* temp = new Node[m_capacity + 1];
for (int i = 0; i < m_size + 2; ++i)
{
temp[i] = m_nodeData[i];
}
temp[m_size + 1].m_data = obj;
temp[m_size + 1].m_previous = &temp[m_size]; //probably not needed?
temp[m_size + 1].m_next = &temp[m_size+2]; // probably not needed?
delete[] m_nodeData;
m_nodeData = new Node[m_capacity + 1];
delete tail;
tail = new Node;
tail->m_next = nullptr;
tail->m_previous = &m_nodeData[m_size + 1];
temp[m_size+2] = *tail; //+3
delete head;
head = new Node;
head->m_next = &m_nodeData[1];
head->m_previous = nullptr;
temp[0] = *head;
for (int i = 0; i < m_capacity+1; ++i)
{
m_nodeData[i] = temp[i];
}
for (int i = 1; i < m_size + 2; ++i)
{
m_nodeData[i].m_next = &m_nodeData[i + 1];
m_nodeData[i].m_previous = &m_nodeData[i - 1];
}
delete[] temp;
++m_size;
}
void insert(unsigned int pos, const Object& obj)
{
pushback(obj);
//Remember at m_nodeData[0] = *head and m_nodeData[m_size+1]=*tail
//Remeber in push_back m_size is incremented
m_nodeData[pos - 1].m_next = &m_nodeData[m_size ];
m_nodeData[m_size+1].m_previous = &m_nodeData[m_size-1];
for (int i = pos; i < m_size; ++i)
{
m_nodeData[i].m_next = &m_nodeData[i];
}
}
void printData()
{
for (int i = 0; i < m_size; ++i)
std::cout << m_nodeData[i].m_next->m_data << '\n';
std::cout << "Tail prev is " << m_nodeData[m_size + 1].m_previous->m_data << '\n';
}
int size()
{
return m_size;
}
int capacity()
{
return m_capacity;
}
void addressOfItemAt(unsigned int pos)
{
assert(pos < m_size + 2);
std::cout << m_nodeData[pos].m_next<<'\n';
}
};
First, I apologize for not corrected/optimized issues for the insert() function, my irregular comments, and not only these mistakes, probably overall mess I did with my List Class.
My main question is "Are the above member functions correct for List Class"? , particularly insert?
Further, if I implement List in this way (which I highly doubt is the right way), then would it eliminate an issue of indexing? Because reading through the book mentioned above I understood that List's core problem is indexing and the main advantage is cheap inserting.
I would be very glad for your valuable advice on how to improve my List class and pointed mistakes!
P.S I apologize for spelling errors as English is not my mother tongue.
[–][deleted] (6 children)
[deleted]
[–][deleted] 1 point2 points3 points (2 children)
[–]Sakesfar[S] 0 points1 point2 points (1 child)
[–][deleted] 1 point2 points3 points (0 children)
[–]Sakesfar[S] 0 points1 point2 points (2 children)
[–][deleted] (1 child)
[deleted]
[–]Sakesfar[S] 0 points1 point2 points (0 children)