I am having some trouble with this function I wrote to delete at some position in a double linked list. I feel like I am leaking memory and I am not doing this property for a double linked list.
Here is the code:
template <class T>
void DoubleLinkedLists<T>::deletePosition(int pos) {
Node* prev = new Node;
Node* current = head;
for(int i = 1; i < pos; i++) {
prev = current;
current = current->next;
}
prev->next = current->next;
}
This is pretty much what I did for a single linked list so I know this is not right. If anyone has any suggestions on how to do this for a double linked list I would really appreciate it.
EDIT:
I think this works correctly now:
template <class T>
void DoubleLinkedLists<T>::deletePosition(int pos) {
Node* temp = nullptr;
Node* current = head;
for(int i = 1; i < pos; i++) {
temp = current;
current = current->next;
}
temp->previous = current->previous;
temp->next = current->next;
}
Here is the entire code:
#ifndef DoubleLinkedLists_h
#define DoubleLinkedLists_h
template <class T>
class DoubleLinkedLists {
private:
struct Node {
T data;
Node* next;
Node* previous;
};
Node* head;
Node* tail;
public:
// Constructors
DoubleLinkedLists() : head(nullptr), tail(nullptr) {} // empty constructor
DoubleLinkedLists(DoubleLinkedLists const& value); // copy constructor
DoubleLinkedLists<T>(DoubleLinkedLists<T>&& move) noexcept; // move constuctor
DoubleLinkedLists<T>& operator=(DoubleLinkedLists&& move) noexcept; // move assignment operator
~DoubleLinkedLists(); // destructor
// Overload operators
DoubleLinkedLists& operator=(DoubleLinkedLists const& rhs);
friend std::ostream& operator<<(std::ostream& str, DoubleLinkedLists<T> const& data) {
data.display(str);
return str;
}
// Member functions
void swap(DoubleLinkedLists& other) noexcept;
void push(const T& theData);
void push(T&& theData);
void display(std::ostream& str) const;
void insertHead(const T& theData);
void insertTail(const T& theData);
void insertPosition(int pos, const T& theData);
void deleteHead();
void deleteTail();
void deletePosition(int pos);
bool search(const T& x);
};
template <class T>
DoubleLinkedLists<T>::DoubleLinkedLists(DoubleLinkedLists const& value) : head(nullptr), tail(nullptr) {
for(Node* loop = value->head; loop != nullptr; loop = loop->next) {
push(loop->data);
}
}
template <class T>
DoubleLinkedLists<T>::DoubleLinkedLists(DoubleLinkedLists<T>&& move) noexcept : head(nullptr), tail(nullptr) {
move.swap(*this);
}
template <class T>
DoubleLinkedLists<T>& DoubleLinkedLists<T>::operator=(DoubleLinkedLists<T> &&move) noexcept {
move.swap(*this);
return *this;
}
template <class T>
DoubleLinkedLists<T>::~DoubleLinkedLists() {
while(head != nullptr) {
deleteHead();
}
}
template <class T>
void DoubleLinkedLists<T>::swap(DoubleLinkedLists<T> &other) noexcept {
using std::swap;
swap(head,other.head);
swap(tail,other.tail);
}
template <class T>
void DoubleLinkedLists<T>::push(const T& theData) {
Node* newNode = new Node;
newNode->data = theData;
newNode->previous = tail;
if(head == nullptr) {
head = newNode;
}
else {
tail->next = newNode;
}
tail = newNode;
}
template <class T>
void DoubleLinkedLists<T>::push(T&& theData) {
Node* newNode = new Node;
newNode->data = theData;
newNode->previous = tail;
if(head == nullptr) {
head = newNode;
}
else {
tail->next = newNode;
}
tail = newNode;
}
template <class T>
void DoubleLinkedLists<T>::display(std::ostream &str) const {
for(Node* loop = head; loop != nullptr; loop = loop->next) {
str << loop->data << "\t";
}
str << "\n";
}
template <class T>
void DoubleLinkedLists<T>::insertHead(const T &theData) {
Node* newNode = new Node;
newNode->data = theData;
newNode->next = head;
head->previous = newNode;
head = newNode;
}
template <class T>
void DoubleLinkedLists<T>::insertTail(const T &theData) {
Node* newNode = new Node;
newNode->data = theData;
newNode->previous = tail;
tail->next = newNode;
tail = newNode;
}
template <class T>
void DoubleLinkedLists<T>::insertPosition(int pos, const T &theData) {
if (pos < 0) {
throw std::invalid_argument("pos is not a valid index");
}
Node* current = head;
Node* previous = nullptr;
while(pos-- > 0) {
if(!current) {
throw std::invalid_argument("pos is not a valid index");
}
previous = current;
current = current->next;
}
Node* newNode = new Node;
newNode->data = theData;
newNode->previous = previous;
newNode->next = current;
if(newNode->previous) {
newNode->previous->next = newNode;
}
else {
head = newNode;
}
if(newNode->next) {
newNode->next->previous = newNode;
}
else {
tail = newNode;
}
}
template <class T>
void DoubleLinkedLists<T>::deleteHead() {
if (head != nullptr) {
Node* old = head;
head = head->next;
delete old;
}
else {
throw std::invalid_argument("the list is empty!");
}
}
template <class T>
void DoubleLinkedLists<T>::deleteTail() {
if(head != nullptr) {
Node* prev = nullptr;
Node* current = head;
while(current->next != nullptr) {
prev = current;
current = current->next;
}
tail = prev;
prev->next = nullptr;
delete current;
}
else {
throw std::invalid_argument("The list is already empty, nothing to delete.");
}
}
template <class T>
void DoubleLinkedLists<T>::deletePosition(int pos) {
Node* temp = nullptr;
Node* current = head;
for(int i = 1; i < pos; i++) {
temp = current;
current = current->next;
}
temp->previous = current->previous;
temp->next = current->next;
}
template <class T>
bool DoubleLinkedLists<T>::search(const T &x) {
Node* current = head;
while(current != nullptr) {
if(current->data == x) {
return true;
}
current = current->next;
}
return false;
}
#endif /* DoubleLinkedLists_h */
And here is the main.cpp file that tests it:
#include <iostream>
#include "DoubleLinkedLists.h"
int main(int argc, const char * argv[]) {
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////// Double Linked List //////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
DoubleLinkedLists<int> obj;
obj.push(2);
obj.push(4);
obj.push(6);
obj.push(8);
obj.push(10);
std::cout<<"\n--------------------------------------------------\n";
std::cout<<"---------------Displaying All nodes---------------";
std::cout<<"\n--------------------------------------------------\n";
std::cout << obj << std::endl;
std::cout<<"\n--------------------------------------------------\n";
std::cout<<"----------------Inserting At Start----------------";
std::cout<<"\n--------------------------------------------------\n";
obj.insertHead(50);
std::cout << obj << std::endl;
std::cout<<"\n--------------------------------------------------\n";
std::cout<<"-----------------Inserting At End-----------------";
std::cout<<"\n--------------------------------------------------\n";
obj.insertTail(20);
std::cout << obj << std::endl;
std::cout<<"\n--------------------------------------------------\n";
std::cout<<"-------------Inserting At Particular--------------";
std::cout<<"\n--------------------------------------------------\n";
obj.insertPosition(5,60);
std::cout << obj << std::endl;
std::cout<<"\n--------------------------------------------------\n";
std::cout<<"----------------Deleting At Start-----------------";
std::cout<<"\n--------------------------------------------------\n";
obj.deleteHead();
std::cout << obj << std::endl;
std::cout<<"\n--------------------------------------------------\n";
std::cout<<"----------------Deleting At End-----------------";
std::cout<<"\n--------------------------------------------------\n";
obj.deleteTail();
std::cout << obj << std::endl;
std::cout<<"\n--------------------------------------------------\n";
std::cout<<"--------------Deleting At Particular--------------";
std::cout<<"\n--------------------------------------------------\n";
obj.deletePosition(5);
std::cout << obj << std::endl;
std::cout << std::endl;
obj.search(8) ? printf("Yes"):printf("No");
return 0;
}
So you would need to do something like this. Note that your code does not handle the case of an index out of range (i.e. the position specified is either negative or is longer the list). It looked like you don't maintain a count of your list length (in the linked code) so I added a check for current != nullptr into the for loop as well as after the for loop to handle the case of the pos being longer than the list. In that case, now the code will do nothing, but you could throw an out of range exception or something like that to indicate the invalid condition. In the case of removing the head you also need to take care to fix up the head pointer. I am assuming you might have a tail pointer as well, so you need to add checks to see if you are removing the tail and fix that up as well.
Note
I didn't compile this and so there may be a typo or two, but it should at least point you in the right direction.
void DoubleLinkedLists<T>::deletePosition(int pos) {
if (pos < 0) {} // Should do something in this case
Node* current = head;
// Added null check to keep from continuing past the end of the list
for(int i = 1; i < pos && current != nullptr; i++) {
current = current->next;
}
if (current != nullptr)
{
// If we are at the head, there isn't a previous
if (current != head)
{
current->previous->next = current->next;
}
else
{
// In this case, we are removing the head, need to reset head to the next Node
head = current->next;
}
if (current->next != nullptr)
{
current->next->previous = current->previous;
}
else if (current == tail)
{
// In this case we are removing the tail, need to reset tail pointer
tail = current->previous;
}
delete current; // Cleans up the node we are deleting
}
}
Related
So, I am trying to implement a stack using a linked list as the parent, and have written these two classes:
template <class T>
class LL {
public:
struct Node {
Node* next = nullptr;
T data = 0;
};
Node* head;
public:
LL();
~LL();
void insert(T item);
void append(T item);
virtual void disp();
};
template<class T>
LL<T>::LL(){ head = nullptr; }
template<class T>
void LL<T>::insert(T item)
{
Node* newNode = new Node;
newNode->data = item;
if (head == nullptr) head = newNode;
else
{
Node* loop = head;
Node* prev = nullptr;
while (loop != nullptr && loop->data < item) {
prev = loop;
loop = loop->next;
}
if (prev == nullptr) { //head
newNode->next = head;
head = newNode;
}
else { //insert
newNode->next = prev->next;
prev->next = newNode;
}
}
}
template<class T>
void LL<T>::append(T item)
{
Node* newNode = new Node;
newNode->data = item;
if (head == nullptr) head = newNode;
else
{
Node* loop = head;
while(loop->next != nullptr)
loop = loop->next;
loop->next = newNode;
}
}
template<class T>
void LL<T>::disp()
{
cout << "[";
if (head) {
if (head->next == nullptr)
cout << head->data;
else {
Node* loop = head;
while (loop->next != nullptr)
{
cout << loop->data << ",";
loop = loop->next;
}
cout << loop->data;
}
}
cout << "]" << endl;
}
template<class T>
LL<T>::~LL()
{
if (head != nullptr) {
while (head != nullptr) {
Node* temp = head;
head = head->next;
delete temp;
temp = nullptr;
}
}
}
template<class T>
class stack: public LL<T> {
private:
public:
stack();
~stack();
virtual void push(T item);
void pop(T ret);
bool empty();
void disp();
};
template<class T>
stack<T>::stack():LL<T>()
{
}
template<class T>
stack<T>::~stack()
{
}
template<class T>
void stack<T>::push(T item){ this->append(item); }
template<class T>
void stack<T>::pop(T ret)
{
ret = this->head->data;
this->head = this->head->next;
}
template<class T>
bool stack<T>::empty() { return false;}
template<class T>
void stack<T>::disp()
{
Node<T>* loop = this->head;
cout << "-" << endl;
while (loop) {
cout << loop->data << endl;
loop = loop->next;
}
cout << "-";
}
The trouble comes in the very last function, "disp()", where it does not give me access to the Node struct. It gives me the error "'Node': identifier not found". This prevents me from declaring the needed "loop" variable. As far as I know, the Node struct should be inherited from the LL class and protected in the stack class. Thus, I should be able to access it directly.
Any ideas?
Ok, so I got around it using this post:
Declaring a struct in a template class, undefined for member functions
My disp() function now looks like this:
template<class T>
void stack<T>::disp()
{
typename LL<T>::Node* loop = this->head;
cout << "-" << endl;
while (loop) {
cout << loop->data << endl;
loop = loop->next;
}
cout << "-";
}
Right now, I'm trying to write a reverse method for a Doubly Linked List. It's not just one of the methods where you reverse from start to finish, but specifically a part of the list starting from one point and ending at a point after the start. The method takes in ListNode pointer references called startPoint and endPoint, and they should point at the new start and end of the chain in the linked memory. The next member of the ListNode before the start of the sequence (so startPoint->prev) should point at the new start, and the prev member of the ListNode after the end of the sequence (so endPoint->next) should point to the new end.
A key aspect I should point out is that we may NOT allocate any new ListNodes.
In my List.h file, I have a private class ListNode. I provided that along with the rest of my List class:
template <class T>
class List {
private:
class ListNode {
public:
ListNode();
ListNode(const T & ndata);
ListNode* next;
ListNode* prev;
const T data;
};
public:
List();
List(const List<T>& other);
List<T>& operator=(const List<T>& rhs);
int size() const;
void print(ostream& os = std::cout) const;
bool empty() const;
~List();
void insertFront(const T& ndata);
void insertBack(const T& ndata);
void reverse();
void tripleRotate();
List<T> split(int splitPoint);
private:
ListNode* head_;
ListNode* tail_;
int length_;
void _copy(const List<T>& other);
void _destroy();
/**
* Helper function to reverse a sequence of linked memory inside a
* List, starting at startPoint and ending at endPoint. You are
* responsible for updating startPoint and endPoint to point to the
* new starting and ending points of the rearranged sequence of
* linked memory in question.
*
* #param startPoint A pointer reference to the first node in the
* sequence to be reversed.
* #param endPoint A pointer reference to the last node in the
* sequence to be reversed.
*/
void reverse(ListNode*& startPoint, ListNode*& endPoint);
};
In my List.hpp file, I have the reverse method itself. I am lost on how exactly to write it. Here's what I have so far (it obviously doesn't work):
template <typename T>
void List<T>::reverse(ListNode *& startPoint, ListNode *& endPoint) {
if (startPoint == endPoint) {
return;
}
//startPoint should point at the new start, endPoint should point at the new end.
ListNode* current = startPoint;
ListNode* before_start_point = startPoint->prev;
ListNode* after_end_point = endPoint->next;
while (current != after_end_point) {
ListNode* temp = current->next;
current->next = current->prev;
current->prev = temp;
if (temp == endPoint) {
endPoint = startPoint;
startPoint = current;
}
current = temp;
}
}
The rest of the List.hpp file:
template <class T>
List<T>::List() {
head_ = NULL;
tail_ = NULL;
length_ = 0;
}
template <typename T>
void List<T>::_destroy() {
ListNode* current = head_;
while (current != NULL) {
ListNode* temp = current->next;
delete current;
current = temp;
}
}
template <typename T>
void List<T>::insertFront(T const & ndata) {
ListNode* newNode = new ListNode(ndata);
//Case where there the list is empty
if (head_ == NULL) {
head_ = newNode;
tail_ = newNode;
newNode->next = NULL;
newNode->prev = NULL;
}
else {
newNode->next = head_;
newNode->prev = NULL;
head_->prev = newNode;
head_ = newNode;
}
length_++;
}
template <typename T>
void List<T>::insertBack(const T & ndata) {
ListNode* newNode = new ListNode(ndata);
if (tail_ == NULL) {
head_ = newNode;
tail_ = newNode;
newNode->next = NULL;
newNode->prev = NULL;
}
else {
newNode->prev = tail_;
newNode->next = NULL;
tail_->next = newNode;
tail_ = newNode;
}
length_++;
}
template <typename T>
typename List<T>::ListNode* List<T>::split(ListNode* start, int splitPoint) {
//There will be splitPoint number of nodes remaining in the current list
ListNode* curr = start;
if (splitPoint == 0) {
return curr;
}
//Takes you to the start of the new list
for (int i = 0; i < splitPoint && curr != NULL; i++) {
curr = curr->next;
}
if (curr != NULL) {
curr->prev->next = NULL;
curr->prev = NULL;
}
//Return the head of the new sublist
return curr;
}
template <typename T>
void List<T>::tripleRotate() {
if (length_ < 3) {
return;
}
else {
int third_element_counter = 1;
bool first_rotation = true;
int divisible_by_three = length_ % 3;
ListNode* current = head_;
while (current != NULL) {
if (third_element_counter != 3) {
third_element_counter++;
}
else {
ListNode* first = current->prev->prev;
ListNode* temp_first_prev = first->prev;
ListNode* second = current->prev;
ListNode* temp_current = current;
ListNode* temp_current_next = current->next;
second->prev = temp_first_prev;
if (temp_first_prev != NULL) {
temp_first_prev->next = second;
}
if (temp_current_next != NULL) {
temp_current_next->prev = first;
}
current->next = first;
first->next = temp_current_next;
first->prev = temp_current;
if (first_rotation) {
head_ = second;
first_rotation = false;
}
if (divisible_by_three == 0) {
tail_ = first;
}
current = first;
third_element_counter = 1;
}
current = current->next;
}
}
}
template <typename T>
void List<T>::reverse() {
reverse(head_, tail_);
}
Any sort of help is appreciated.
Given your code, I manually created a list of ten integers:
List<int> my_list;
for(int i = 0; i < 10; i++) {
my_list.insertBack(i);
}
Then, I printed the list:
std::cout << "Before reverse:" << std::endl;
my_list.print();
The output was:
Before reverse:
Node: 0
Node: 1
Node: 2
Node: 3
Node: 4
Node: 5
Node: 6
Node: 7
Node: 8
Node: 9
Then, I ran a slightly modified version of your reverse function:
template <typename T>
void List<T>::reverse(ListNode *& startPoint, ListNode *& endPoint) {
if (startPoint == endPoint) {
return;
}
//startPoint should point at the new start, endPoint should point at the new end.
ListNode* current = startPoint;
ListNode* before_start_point = startPoint->prev;
ListNode* after_end_point = endPoint->next;
while (current != after_end_point) {
std::cout << "Working with current node " << current->data << std::endl;
ListNode* temp = current->next;
std::cout << "Temp is " << (temp ? temp->data : 0) << std::endl;
current->next = current->prev;
std::cout << current->data << "'s new next is now " << (current->next ? current->next->data : 0) << std::endl;
current->prev = temp;
std::cout << current->data << "'s new prev is now " << (current->prev ? current->prev->data : 0) << std::endl;
if (temp == endPoint) {
endPoint = startPoint;
startPoint = temp;
}
current = temp;
}
}
And printed again:
std::cout << "After reverse:" << std::endl;
my_list.print();
The output was:
After reverse:
Node: 9
Node: 8
Node: 7
Node: 6
Node: 5
Node: 4
Node: 3
Node: 2
Node: 1
Node: 0
So it seems the only problem was in the if block at the end of the while loop, where you were assigning startPoint = current when instead it should be either startPoint = endPoint or startPoint = temp (because when the if is true, temp is equal to endPoint).
This is the complete code (also available in https://godbolt.org/z/nad7vT):
#include <iostream>
template <class T>
class List {
private:
class ListNode {
public:
ListNode();
ListNode(const T & ndata) : data{ndata} {};
ListNode* next;
ListNode* prev;
const T data;
};
public:
List();
List(const List<T>& other);
List<T>& operator=(const List<T>& rhs);
int size() const;
void print(std::ostream& os = std::cout) const;
bool empty() const;
~List() {};
void insertFront(const T& ndata);
void insertBack(const T& ndata);
void reverse();
void reverseNth(int n);
void tripleRotate();
List<T> split(int splitPoint);
private:
ListNode* head_;
ListNode* tail_;
int length_;
void _copy(const List<T>& other);
void _destroy();
/**
* Helper function to reverse a sequence of linked memory inside a
* List, starting at startPoint and ending at endPoint. You are
* responsible for updating startPoint and endPoint to point to the
* new starting and ending points of the rearranged sequence of
* linked memory in question.
*
* #param startPoint A pointer reference to the first node in the
* sequence to be reversed.
* #param endPoint A pointer reference to the last node in the
* sequence to be reversed.
*/
void reverse(ListNode*& startPoint, ListNode*& endPoint);
};
template <typename T>
void List<T>::reverse(ListNode *& startPoint, ListNode *& endPoint) {
if (startPoint == endPoint) {
return;
}
//startPoint should point at the new start, endPoint should point at the new end.
ListNode* current = startPoint;
ListNode* before_start_point = startPoint->prev;
ListNode* after_end_point = endPoint->next;
while (current != after_end_point) {
std::cout << "Working with current node " << current->data << std::endl;
ListNode* temp = current->next;
std::cout << "Temp is " << (temp ? temp->data : 0) << std::endl;
current->next = current->prev;
std::cout << current->data << "'s new next is now " << (current->next ? current->next->data : 0) << std::endl;
current->prev = temp;
std::cout << current->data << "'s new prev is now " << (current->prev ? current->prev->data : 0) << std::endl;
if (temp == endPoint) {
endPoint = startPoint;
startPoint = temp;
}
current = temp;
}
}
template <class T>
List<T>::List() {
head_ = NULL;
tail_ = NULL;
length_ = 0;
}
template <typename T>
void List<T>::_destroy() {
ListNode* current = head_;
while (current != NULL) {
ListNode* temp = current->next;
delete current;
current = temp;
}
}
template <typename T>
void List<T>::insertFront(T const & ndata) {
ListNode* newNode = new ListNode(ndata);
//Case where there the list is empty
if (head_ == NULL) {
head_ = newNode;
tail_ = newNode;
newNode->next = NULL;
newNode->prev = NULL;
}
else {
newNode->next = head_;
newNode->prev = NULL;
head_->prev = newNode;
head_ = newNode;
}
length_++;
}
template <typename T>
void List<T>::insertBack(const T & ndata) {
ListNode* newNode = new ListNode(ndata);
if (tail_ == NULL) {
head_ = newNode;
tail_ = newNode;
newNode->next = NULL;
newNode->prev = NULL;
}
else {
newNode->prev = tail_;
newNode->next = NULL;
tail_->next = newNode;
tail_ = newNode;
}
length_++;
}
template <typename T>
void List<T>::print(std::ostream& os) const {
for(const ListNode *it = head_; it != nullptr; it = it->next) {
os << "Node: " << it->data << std::endl;
}
}
template <typename T>
void List<T>::reverse() {
reverse(head_, tail_);
}
int main() {
List<int> my_list;
for(int i = 0; i < 10; i++) {
my_list.insertBack(i);
}
std::cout << "Before reverse:" << std::endl;
my_list.print();
my_list.reverse();
std::cout << "After reverse:" << std::endl;
my_list.print();
return 0;
}
I am having some trouble making the appropriate changes from a post I made on code review. Here is the original post I made here. Specifically, I am at the point where I am revising my insertPosition function.
Previously my function was like this:
template <class T>
void DoubleLinkedLists<T>::insertPosition(int pos, const T& theData) {
Node* prev = new Node;
Node* current = head;
Node* newNode = new Node;
for(int i = 1; i < pos; i++) {
prev = current;
current = current->next;
}
newNode->data = theData;
prev->next = newNode;
newNode->next = current;
}
But now, from what I have been told to do was make it more like this:
Node* cur_node = head;
int i = 0;
while (cur_node) {
if (i++ == pos) {
// do the deed
}
cur_node = cur_node->next;
}
I am not sure what to write in the // do the deed part. What I have tried is this:
template <class T>
void DoubleLinkedLists<T>::insertPosition(int pos, const T& theData) {
Node* current = head;
int i = 0;
while(current) {
if(i++ == pos) {
Node* newNode = new Node;
newNode->data = theData;
newNode->next = newNode->previous;
}
current = current->next;
}
}
But I do not get the code to work at all when I test it out. There are also various other things I need to change I am sure but I would like to get a second opinion of my code I have thus far.
Here is the header file:
#ifndef DoubleLinkedLists_h
#define DoubleLinkedLists_h
template <class T>
class DoubleLinkedLists {
private:
struct Node {
T data;
Node* next;
Node* previous;
};
Node* head;
Node* tail;
public:
// Constructors
DoubleLinkedLists() : head(nullptr), tail(nullptr) {} // empty constructor
DoubleLinkedLists(DoubleLinkedLists const& value); // copy constructor
DoubleLinkedLists<T>(DoubleLinkedLists<T>&& move) noexcept; // move constuctor
DoubleLinkedLists<T>& operator=(DoubleLinkedLists&& move) noexcept; // move assignment operator
~DoubleLinkedLists(); // destructor
// Overload operators
DoubleLinkedLists& operator=(DoubleLinkedLists const& rhs);
friend std::ostream& operator<<(std::ostream& str, DoubleLinkedLists<T> const& data) {
data.display(str);
return str;
}
// Member functions
void swap(DoubleLinkedLists& other) noexcept;
void createNode(const T& theData);
void createNode(T&& theData);
void display(std::ostream& str) const;
void insertHead(const T& theData);
void insertTail(const T& theData);
void insertPosition(int pos, const T& theData);
void deleteHead();
void deleteTail();
void deletePosition(int pos);
bool search(const T& x);
};
template <class T>
DoubleLinkedLists<T>::DoubleLinkedLists(DoubleLinkedLists const& value) : head(nullptr), tail(nullptr) {
for(Node* loop = value->head; loop != nullptr; loop = loop->next) {
createNode(loop->data);
}
}
template <class T>
DoubleLinkedLists<T>::DoubleLinkedLists(DoubleLinkedLists<T>&& move) noexcept : head(nullptr), tail(nullptr) {
move.swap(*this);
}
template <class T>
DoubleLinkedLists<T>& DoubleLinkedLists<T>::operator=(DoubleLinkedLists<T> &&move) noexcept {
move.swap(*this);
return *this;
}
template <class T>
DoubleLinkedLists<T>::~DoubleLinkedLists() {
while(head != nullptr) {
deleteHead();
}
}
template <class T>
DoubleLinkedLists<T>& DoubleLinkedLists<T>::operator=(DoubleLinkedLists const& rhs) {
DoubleLinkedLists copy(rhs);
swap(copy);
return *this;
}
template <class T>
void DoubleLinkedLists<T>::swap(DoubleLinkedLists<T>& other) noexcept {
using std::swap;
swap(head, other.head);
swap(tail, other.tail);
}
template <class T>
void DoubleLinkedLists<T>::createNode(const T& theData) {
Node* newData = new Node;
newData->data = theData;
newData->next = nullptr;
if(head == nullptr) {
newData->previous = nullptr;
head = newData;
tail = newData;
}
else {
newData->previous = tail;
tail->next = newData;
tail = newData;
}
}
template <class T>
void DoubleLinkedLists<T>::createNode(T&& theData) {
Node* newData = new Node;
newData->data = std::move(theData);
newData->next = nullptr;
if(head == nullptr) {
newData->previous = nullptr;
head = newData;
tail = newData;
}
else {
newData->previous = tail;
tail->next = newData;
tail = newData;
}
}
template <class T>
void DoubleLinkedLists<T>::insertHead(const T& theData) {
Node* newNode = new Node;
newNode->data = theData;
newNode->next = head;
head->previous = newNode;
head = newNode;
}
template <class T>
void DoubleLinkedLists<T>::insertTail(const T& theData) {
Node* newNode = new Node;
newNode->data = theData;
newNode->previous = tail;
tail->next = newNode;
tail = newNode;
}
//template <class T>
//void DoubleLinkedLists<T>::insertPosition(int pos, const T& theData) {
// Node* prev = new Node;
// Node* current = head;
// Node* newNode = new Node;
//
// for(int i = 1; i < pos; i++) {
// prev = current;
// current = current->next;
// }
// newNode->data = theData;
// prev->next = newNode;
// newNode->next = current;
//}
template <class T>
void DoubleLinkedLists<T>::insertPosition(int pos, const T& theData) {
Node* current = head;
int i = 0;
while(current) {
if(i++ == pos) {
Node* newNode = new Node;
newNode->data = theData;
newNode->next = newNode->previous;
}
current = current->next;
}
}
template <class T>
void DoubleLinkedLists<T>::display(std::ostream &str) const {
for(Node* loop = head; loop != nullptr; loop = loop->next) {
str << loop->data << "\t";
}
str << "\n";
}
template <class T>
void DoubleLinkedLists<T>::deleteHead() {
Node* old = head;
head = head->next;
delete old;
}
template <class T>
void DoubleLinkedLists<T>::deleteTail() {
Node* prev = nullptr;
Node* current = head;
while(current->next != nullptr) {
prev = current;
current = current->next;
}
tail = prev;
prev->next = nullptr;
delete current;
}
template <class T>
void DoubleLinkedLists<T>::deletePosition(int pos) {
Node* prev = new Node;
Node* current = head;
for(int i = 1; i < pos; i++) {
prev = current;
current = current->next;
}
prev->next = current->next;
}
template <class T>
bool DoubleLinkedLists<T>::search(const T &x) {
Node* current = head;
while(current != nullptr) {
if(current->data == x) {
return true;
}
current = current->next;
}
return false;
}
#endif /* DoubleLinkedLists_h */
Here is the main.cpp file to test the above implementation:
#include <iostream>
#include "DoubleLinkedLists.h"
int main(int argc, const char * argv[]) {
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////// Double Linked List //////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
DoubleLinkedLists<int> obj;
obj.createNode(2);
obj.createNode(4);
obj.createNode(6);
obj.createNode(8);
obj.createNode(10);
std::cout<<"\n--------------------------------------------------\n";
std::cout<<"---------------Displaying All nodes---------------";
std::cout<<"\n--------------------------------------------------\n";
std::cout << obj << std::endl;
std::cout<<"\n--------------------------------------------------\n";
std::cout<<"----------------Inserting At Start----------------";
std::cout<<"\n--------------------------------------------------\n";
obj.insertHead(50);
std::cout << obj << std::endl;
std::cout<<"\n--------------------------------------------------\n";
std::cout<<"-----------------Inserting At End-----------------";
std::cout<<"\n--------------------------------------------------\n";
obj.insertTail(20);
std::cout << obj << std::endl;
std::cout<<"\n--------------------------------------------------\n";
std::cout<<"-------------Inserting At Particular--------------";
std::cout<<"\n--------------------------------------------------\n";
obj.insertPosition(5,60);
std::cout << obj << std::endl;
std::cout<<"\n--------------------------------------------------\n";
std::cout<<"----------------Deleting At Start-----------------";
std::cout<<"\n--------------------------------------------------\n";
obj.deleteHead();
std::cout << obj << std::endl;
std::cout<<"\n--------------------------------------------------\n";
std::cout<<"----------------Deleting At End-----------------";
std::cout<<"\n--------------------------------------------------\n";
obj.deleteTail();
std::cout << obj << std::endl;
std::cout<<"\n--------------------------------------------------\n";
std::cout<<"--------------Deleting At Particular--------------";
std::cout<<"\n--------------------------------------------------\n";
obj.deletePosition(4);
std::cout << obj << std::endl;
std::cout << std::endl;
obj.search(8) ? printf("Yes"):printf("No");
return 0;
}
Update:
This is what I have done so far, I am pass in the data into a new node and then link previous node with the next node. Although, the code runs but nothing actually gets inserted into a position so I believe it does not even go through the while loop. I am not sure what the problem is.
template <class T>
void DoubleLinkedLists<T>::insertPosition(int pos, const T& theData) {
Node* current = head;
int i = 0;
while(current) {
if(i++ == pos) {
Node* newNode = new Node;
newNode->data = theData;
newNode->previous = newNode->next;
}
current = current->next;
}
}
So...
template <class T>
void DoubleLinkedLists<T>::insertPosition(int pos, const T& theData) {
Node* current = head;
int i = 0;
while (current != nullptr) {
if (i++ == pos) {
Node* newNode = new Node;
newNode->data = theData;
// Let's do the wiring
newNode->previous = current->previous;
newNode->next = current;
if (newNode->previous != nullptr) { // If the node is inserted at the end
newNode->previous->next = newNode;
}
current->previous = newNode;
return;
}
current = current->next;
}
}
I've been building on a template Linked List and recently implemented a user-defined copy constructor to handle deep copies of my Linked List. My current solution compiles just fine but when I execute, nothing is displayed.
#include <iostream>
template<typename T>
class LinkedList
{
private:
struct Node
{
T data;
Node* next;
};
Node* head;
Node* tail;
int size;
public:
LinkedList();
LinkedList(const LinkedList<T> &list);
~LinkedList();
LinkedList<T>& operator=(const LinkedList<T> list);
int getSize() const;
void display() const;
void push(const T &data);
void insert(const int pos, const T &data);
void remove(const int pos);
};
template<typename T>
LinkedList<T>::LinkedList() : head{ nullptr }, tail{ nullptr }, size{ 0 }
{
}
// TODO: User-defined copy constructor
template<typename T>
LinkedList<T>::LinkedList(const LinkedList<T> &list)
: head{ nullptr }, tail{ nullptr }, size{list.size}
{
std::cout << "In the copy constructor\n";
if (list.head == nullptr)
{
return;
}
else
{
Node* curNode = new Node{list.head->data, nullptr};
head = curNode; // sets the head member variable to first node in Linked List
Node* curListNode = list.head;
while (curListNode->next != nullptr)
{
curNode->next = new Node{curListNode->next->data, nullptr};
curListNode = curListNode->next;
curNode = curNode->next;
}
curNode->next = new Node{curListNode->next->data, nullptr};
tail = curNode->next;
}
}
template<typename T>
LinkedList<T>::~LinkedList()
{
Node* prevNode = head;
Node* curNode = head;
while(curNode != nullptr)
{
prevNode = curNode;
curNode = curNode->next;
delete prevNode;
}
head = nullptr;
tail = nullptr;
}
template<typename T>
LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T> list)
{
// make a copy of each node - much like the copy constructor
std::cout << "In the overloaded assignment\n";
}
template<typename T>
int LinkedList<T>::getSize() const
{
return size;
}
template<typename T>
void LinkedList<T>::display() const
{
Node* curNode = head;
while (curNode != nullptr)
{
std::cout << curNode->data << '\n';
curNode = curNode->next;
}
std::cout << '\n';
}
template<typename T>
void LinkedList<T>::push(const T &data)
{
Node* newNode = new Node{data, nullptr};
if (size == 0)
{
head = newNode;
tail = newNode;
}
else
{
tail->next = newNode;
tail = newNode;
}
++size;
}
template<typename T>
void LinkedList<T>::insert(const int pos, const T &data)
{
if (pos < 0 || pos > size)
{
throw "Index is out of range!";
}
else if (pos == size)
{
push(data);
}
else
{
Node* newNode = new Node{data, nullptr};
Node* prevNode = head;
Node* curNode = head;
int i = 0;
while (i != pos)
{
prevNode = curNode;
curNode = curNode->next;
++i;
}
prevNode->next = newNode;
newNode->next = curNode;
++size;
}
}
template<typename T>
void LinkedList<T>::remove(const int pos)
{
if (pos < 0 || pos > size)
{
throw "Index is out of range!";
}
else if (size == 0)
{
throw "List is empty!";
}
else
{
Node* prevNode = head;
Node* curNode = head;
int i = 1;
while (i != pos)
{
prevNode = curNode;
curNode = curNode->next;
++i;
}
prevNode->next = curNode->next;
delete curNode;
if (pos == size)
{
tail = prevNode;
}
--size;
}
}
int main()
{
LinkedList<int> list;
list.push(1);
list.push(3);
list.push(6);
list.display();
LinkedList<int> copyList{list};
copyList.display();
return 0;
}
The first LinkedList<int> list doesn't even display when I add the the copyList statement to main(). However, when I comment out the two copyList statements, list displays as normal.
I added print statements to see if either the copy constructor or the operator= functions were being called and neither of those are printed to the console when the copyList statements are added to my program (the code compiles just fine).
I could use any help to help diagnose what's causing this behavior.
A simple copy constructor like this one should suffice:
template<typename T>
LinkedList<T>::LinkedList(const LinkedList<T> &list)
: head{ nullptr }, tail{ nullptr }, size{0}
{
std::cout << "In the copy constructor\n";
Node* curNode = list.head;
while (curNode != nullptr)
{
this->push(curNode->data);
curNode = curNode->next;
}
}
Note that this is just your display method rewritten to insert elements in the current list.
I've gone through a bunch of threads trying to understand what is going on exactly with linked lists and bubblesort, and I think I get the bulk of it.
Right now my program is simply crashing when I get to the sort function and I am not sure why. Hopefully another set of eyes will see what I do not.
Any help is greatly appreciated.
DoublyList.h:
#include "listNode.h"
#ifndef DOUBLYLIST_H
#define DOUBLYLIST_H
template <typename T>
class DoublyList
{
public:
DoublyList();
~DoublyList();
void addFront(T d);
void addBack(T d);
T removeFront();
T removeBack();
T peak();
bool isEmpty();
int getSize();
void printList();
void sortList();
private:
ListNode<T> *front;
ListNode<T> *back;
int numOfElements;
};
template <typename T>
DoublyList<T>::DoublyList(){
front = NULL;
back = NULL;
numOfElements = 0;
}
template <typename T>
DoublyList<T>::~DoublyList(){
if(numOfElements!=0){
ListNode<T> *current;
current = front;
while (current != back)
{
ListNode<T> *temp = current;
current = current->next;
temp->next = NULL;
temp->prev = NULL;
delete temp;
numOfElements--;
}
//at this point current = back, now delete it
current->next = NULL;
current->prev = NULL;
delete current;
numOfElements--;
}
//this is a safeguard if you create a LL and then delete it without doing anything to it
else{
cout<<"deleted empty LL"<<endl;
delete front;
delete back;
}
}
template <typename T>
void DoublyList<T>::addFront(T d)
{
ListNode<T> *node = new ListNode<T>();
node->data = d;
if (isEmpty()){
back = node;
}
else{
front->prev = node;
}
node->next = front;
front = node;
++numOfElements;
}
template <typename T>
T DoublyList<T>::removeFront()
{
if (isEmpty()){
return T();
}
else
{
ListNode<T>* temp = front;
if (front->next == 0){
back = 0;
}
else
{
front->next->prev = 0;
}
front = front->next;
temp->next = 0;
T theData = temp->data;
delete temp;
--numOfElements;
return theData;
}
}
template <typename T>
void DoublyList<T>::addBack(T d)
{
ListNode<T> *node = new ListNode<T>();
node->data = d;
if (isEmpty()){
front = node;
}
else{
back->next = node;
}
node->prev = back;
back = node;
++numOfElements;
}
template <typename T>
T DoublyList<T>::removeBack()
{
if (isEmpty()) {
return T();
}
else
{
ListNode<T>* temp;
temp = back;
if (back->prev == 0){
front = 0;
}
else{
back->prev->next = 0;
}
back = back->prev;
temp->prev = 0;
T theData = temp->data;
delete temp;
--numOfElements;
return theData;
}
}
template <typename T>
T DoublyList<T>::peak()
{
if (isEmpty()) {
return T();
}
return front->data;
}
template <typename T>
int DoublyList<T>::getSize(){
return numOfElements;
}
template <typename T>
bool DoublyList<T>::isEmpty(){
if(numOfElements == 0){
return true;
}
else{
return false;
}
}
template <typename T>
void DoublyList<T>::printList(){
if(numOfElements!=0){
ListNode<T> *current = front;
while(current!=back)
{
cout<<current->data<<endl;
current = current->next;
}
cout<<back->data<<endl;
}
else{
cout<<"list is empty"<<endl;
}
}
template <typename T>
void DoublyList<T>::sortList(){
int size = getSize();
ListNode<T> *current;
ListNode<T> *dummy;
ListNode<T> *next;
if(current == NULL) return;
if(current -> next == NULL) return;
int swapped = 1;
while(swapped){
swapped = 0; //last pass unless there is a swap
while(current -> next != NULL){
if(current-> data < current -> next -> data){
swapped = 1; //swap, will need to re-enter while loop
//actual number swap
dummy -> data = current -> data;
current -> data = current -> next -> data;
current -> next -> data = dummy -> data;
}
current = current -> next;
}
}
}
#endif
listNode.h:
#include <iostream>
#ifndef LISTNODE_H
#define LISTNODE_H
using namespace std;
template <typename T>
class ListNode
{
public:
T data;//the data that we will store
ListNode();
ListNode(int d);
~ListNode();
ListNode *next;//int and ptr and the member variables
ListNode *prev;
};
template <typename T>
ListNode<T>::ListNode(int d){
data = d;
next = NULL;
prev = NULL;
}
template <typename T>
ListNode<T>::ListNode(){}
template <typename T>
ListNode<T>::~ListNode(){
delete next;
delete prev;
cout<<"deleted Node"<<endl;
}
#endif
testList.cpp
#include <iostream>
#include "doublyList.h"
#include "genericQueue.h"
int main(){
DoublyList<int> testQueue;
testQueue.addBack(3);
testQueue.addBack(5);
testQueue.addBack(2);
testQueue.addBack(10);
testQueue.addBack(1);
cout << "Before Sort: " << endl;
testQueue.printList();
cout << "After Sort: " << endl;
testQueue.sortList();
testQueue.printList();
}
The erors I could find so far are:
Your default ListNode() constructor doesn't null the next and prev pointers.
In void DoublyList<T>::sortList() you don't initialize dummy, so it just points into nowhere. Actually there is no reason to use a node list at all, you can just directly use a variable of type T.
You don't initialize current in the same function and you actually should reset current to e.g. front at the beginning of each outer loop.
You don't use next at all (and don't need to), so just remove it.
To sum it up, this is what void DoublyList<T>::sortList() could look like:
template <typename T>
void DoublyList<T>::sortList(){
int size = getSize();
ListNode<T> *current=front;
T dummy;
if (current == NULL) return;
if (current->next == NULL) return;
int swapped = 1;
while (swapped){
current = front;
swapped = 0; //last pass unless there is a swap
while (current->next != NULL){
if (current->data < current->next->data){
swapped = 1; //swap, will need to re-enter while loop
//actual number swap
dummy = current->data;
current->data = current->next->data;
current->next->data = dummy;
}
current = current->next;
}
}
}
and this is my suggestion for the ListNode constructor.
template <typename T>
ListNode<T>::ListNode() :
next(nullptr),
prev(nullptr),
data{}
{}
Besides that, I also agree with DaveB that swapping pointers is the approach you should actually use.
To start with you need to initialize current in your sort function,
current = first;
template <typename T>
void DoublyList<T>::sortList(){
ListNode<T> *current;
ListNode<T> *next;
T tmp;
current = front;
if(current == NULL) return;
if(current -> next == NULL) return;
int swapped = 1;
while(swapped){
swapped = 0; //last pass unless there is a swap
while(current->next != nullptr){
if(current->data < current->next->data){
swapped = 1; //swap, will need to re-enter while loop
//actual number swap
tmp = current->data;
current->data = current->next->data;
current->next->data = tmp;
}
current = current -> next;
}
if (swapped) // go back to start of list for next pass
current = front;
}
}