So I am trying to create my own implementation file which contains instructions for a Queue. I decided to use a linked list to implement the Queue class, meaning that I need to use my own Node struct. Unfortunately, I am stuck and don't know how to properly include this within the file.
This is what I have so far:
#include <string>
#ifndef NODE
template <class DataType>
struct Node
{
DataType data;
Node *next;
};
#endif
template <class DataType>
class Queue
{
public:
Queue();
bool isEmpty() const;
void push(const DataType& parameter);
bool peek(DataType& parameter) const;
bool pop(DataType& parameter);
void makeEmpty();
private:
Node<DataType>* front;
Node<DataType>* end;
};
template <class DataType>
Queue<DataType>::Queue()
: front(0), end(0)
{
}
template <class DataType>
bool Queue<DataType>::isEmpty() const {return 0 == front;}
template <class DataType>
void Queue<DataType>::push(const DataType& parameter)
{
Node<DataType>* node = new Node<DataType>;
node->data = parameter;
node->next = 0;
if (end) end->next = node;
else front = node;
end = node;
}
template <class DataType>
bool Queue<DataType>::peek(DataType& parameter) const
{
if (0 == front) return false; // failed
parameter = front->data;
return true; // success
}
template <class DataType>
bool Queue<DataType>::pop(DataType& parameter)
{
if (0 == front) return false; // failed
parameter = front->data;
Node<DataType>* p = front->next;
delete front;
front = p;
if (front == 0) end = 0;
return true; // success
}
template <class DataType>
void Queue<DataType>::makeEmpty()
{
end = 0;
Node<DataType>* p;
while (front)
{
p = front->next;
delete front;
front = p;
}
}
I'm not sure if I am enclosing the struct by the #ifndef correctly (i'm not even sure if this is the route I should be taking :/), should I be doing something similar to this or should I be doing something else with the code for the struct?
You can just drop the #ifdef/#endif entirely
This is a class template and it may occur many times in several tranlation units, as long as all the occurrences are identical (One Definition Rule)
Alternative
Since Node<> is purely a private concern, I'd make it a nested struct.
Here's a little demo making this more 'modern C++' style.
Edit Thanks to #R.MartinhoFernandes for showing a few more improvements and for reviewing this.
#include <memory>
template <typename T>
struct Queue {
Queue() : front(), end(/*nullptr*/) {}
// Copy-And-Swap idiom
// see http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Copy-and-swap
// or http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
void swap(Queue& q) noexcept {
using std::swap;
swap(q.front, front);
swap(q.end, end);
}
Queue(Queue const& q) : front(), end() {
for(auto it=q.front.get(); it; it=it->next.get())
push(it->data);
}
Queue& operator=(Queue q) {
std::swap(*this, q);
return *this;
}
// end Copy-and-swap
// prevent stack overflows in ~Node if the list grows large (say >1k elements)
~Queue() { clear(); }
bool isEmpty() const {
return !front;
}
void push(T const& data) {
Ptr node(new Node(data));
if (end)
end->next = std::move(node);
else
front = std::move(node);
end = node.get();
}
bool peek(T& data) const {
if(front) data = front->data;
return front.get();
}
bool pop(T& data) {
if(!front) return false;
data = front->data;
front = std::move(front->next);
if(!front) end = nullptr;
return true;
}
void clear() {
end = nullptr;
while(front) front = std::move(front->next);
}
private:
struct Node;
typedef std::unique_ptr<struct Node> Ptr;
struct Node {
Node(T data) : data(std::move(data)), next() {}
T data;
Ptr next;
};
Ptr front;
Node* end;
};
#include <iostream>
int main(int argc, const char *argv[]) {
Queue<int> test;
test.push(1);
test.push(2);
test.push(3);
test.push(5);
test.clear();
test.push(32028);
test.push(10842);
test.push(1839);
test.push(23493);
test.push(9857);
int x;
test.peek(x);
while(test.pop(x)) {
std::cout << x << '\n';
}
}
Note: Perhaps the code in push has been golfed a bit too far, but hey, it shows you how modern C++ requires much less handholding (even without std::make_unique).
Note how I think Clang correctly handles the following version (i.e. with implicit std::move):
void push(const DataType& parameter) {
end = ((end? end->next : front) = Ptr(new Node(parameter))).get();
}
I'm not quite sure why gcc rejects it.
Related
I am learning about linked lists, and I decided to try implementing linked lists in C++ on my own.
I made a Node class with attributes int val and Node* ptr.
Then I made a Linked_list class with the attribute first_node, and the constructor functions work.
The append() function 'appends' a node to the list (like in Python). I first thought of just making ptr a reference to the node's pointer and then changing it when its null, but references once made, can't be changed to refer to any other variable, so I made another variable prev_ptr that points to the Node's pointer (which makes it a Node**).
Every loop, it checks if ptr is NULL, if not, ptr and prev_ptr get updated to the next Node's pointer value, and the address of the next Node's pointer value, respectively.
This keeps happening until it finds a null pointer, and then changes it to the inputted node's address.
But I'm getting an error saying:
Exception thrown: write access violation. prev_ptr was 0x4.
I can't figure out what is wrong.
Classes:
#include <iostream>
#include <string>
#include <cmath>
class Node {
public:
int val;
Node* ptr = nullptr;
Node(int Val = NULL) {
val = Val;
}
};
class Linked_list {
public:
Node first_node;
Linked_list(int F) {
Node f(F);
first_node = f;
}
void append(Node& element) {
Node* ptr = first_node.ptr;
Node** prev_ptr = &first_node.ptr;
while (true) {
if (ptr == nullptr) {
*prev_ptr = &element;
break;
}
ptr = (*ptr).ptr;
prev_ptr = &((*ptr).ptr);
}
}
};
main()
int main() {
Linked_list list(5);
Node three(3);
list.append(three);
Node four(4);
list.append(four);
return 0;
}
ptr = (*ptr).ptr;
prev_ptr = &((*ptr).ptr);
First you advance ptr to the next node. Then, you use ptr again forgetting that it has already been advanced: (*ptr).ptr now points two nodes forward, and we don't know if we can go that much far.
Perhaps you need to swap the assignments.
prev_ptr = &((*ptr).ptr);
ptr = (*ptr).ptr;
(Further, why not ptr->ptr?)
Okay, you're doing a few things in an odd fashion. First, your Linked_List should probably NOT have a Node for firstNode. It should have a Node *. After all, an empty list is possible. So is (normally) deleting the first node. Also, there's an informal naming convention of calling it head. There's also a standard convention of calling the link in your Node next rather than ptr.
But there are two simpler methods for your append() method. First, you can also keep a Node * tail in Linked_List. This is common. It points to the last node in the list. If you do that, then append looks like:
void append(Node &nodeToAppend) {
if (head == nullptr) {
head = &nodeToAppend;
tail = &nodeToAppend;
}
else {
tail->next = nodeToAppend;
tail = &nodeToAppend;
}
}
However, it's also worthwhile to be able to insert anywhere or append without a tail:
void append(Node &nodeToAppend) {
if (head == nullptr) {
head = &nodeToAppend;
}
else {
Node *ptr = head;
while (ptr->next != nullptr) {
ptr = ptr->next;
}
ptr->next = &nodeToAppend;
}
}
An insert in some sort of sorted order would be nearly identical, although slightly different. The while-loop would look like:
while (ptr->next != nullptr && ptr->value < nodeToAppend.value) ...
but would otherwise be identical.
This code doesn't solve your immediate issue, but answers a question raised in the comments.
Linked lists are usually (where I taught) a 300-400 level assignment. There are a lot of principles that one must be competent in to write a decent linked list. First, I'll show the main.cpp and its output.
main.cpp
#include "list.hpp"
#include <iostream>
template <typename Container>
void print(Container& container, std::ostream& sout = std::cout)
{
for (auto i : container) {
sout << i << ' ';
}
sout << '\n';
}
int main()
{
List<int> list;
for (int i = 1; i <= 10; ++i) {
list.push_back(i);
}
print(list);
list.erase(list.find(4));
print(list);
list.erase(list.find(1));
print(list);
list.erase(list.find(10));
print(list);
}
Output:
1 2 3 4 5 6 7 8 9 10
1 2 3 5 6 7 8 9 10
2 3 5 6 7 8 9 10
2 3 5 6 7 8 9
It doesn't test every aspect of the linked list, but it serves to demonstrate what a user should be expected to work with. Users will want to interact directly with the list and its iterators in C++. You create a Node, and then add the Node to your list. That's a level of DIY that no user wants to be bothered with. In the code below, you'll see that a Node is still used, but it only exists within the List class. Users will never see a Node.
You can look in functions like push_back() (similar to your append) for specific answers related to your question.
To explain it a bit more, the pointers are key. Yes, I declare a local Node* that will go out of scope, but the object created continues to exist on the heap. And the list is able to keep track of these Nodes due to how linked lists work, namely that the Nodes know where their neighbors live (hold their addresses).
There is also a List<T>::iterator class. In the declaration, functions marked as // minimum are required if you want to use your linked list in a range-based for loop. The other functions do work toward satisfying the requirements of LegacyBidirectionalIterator; this is the level of iterator used by std::list in the C++ Standard Library.
The code below should only be considered a decent example (Hopefully not too presumptuous on my part). It is lacking some functionality that's found in std::list, and likely does a few things in non-optimal manners. A big thing that will need tweaking is removing the member function find() and make the class work with std::find().
list.hpp
#ifndef MY_LIST_HPP
#define MY_LIST_HPP
#include <algorithm> // std::swap
#include <cstddef> // std::size_t
/*
* Pre-declare template class and friends
*/
template <typename T>
class List;
template <typename T>
void swap(List<T>& lhs, List<T>& rhs);
/*
* List Class Declaration
*/
template <typename T>
class List {
public:
List() = default;
List(T val);
List(const List& other);
List(List&& other);
~List();
void push_front(T val);
void push_back(T val);
class iterator;
iterator begin();
iterator end();
iterator find(T val);
std::size_t size() const;
iterator erase(iterator toErase); // Implement
void clear();
bool operator=(List other);
friend void swap<T>(List& lhs, List& rhs);
private:
struct Node {
T data;
Node* prev = nullptr;
Node* next = nullptr;
Node(T val) : data(val) {}
};
Node* m_head = nullptr;
Node* m_tail = nullptr;
std::size_t m_size = 0;
// Helper functions
void make_first_node(T val);
Node* find_node(T val);
};
/*
* List Iterator Declaration
*/
template <typename T>
class List<T>::iterator {
public:
iterator() = default;
iterator(List<T>::Node* node); // minimum
T& operator*(); // minimum
iterator& operator++(); // minimum
iterator operator++(int);
iterator& operator--();
iterator operator--(int);
bool operator==(const iterator& other); // minimum
bool operator!=(const iterator& other); // minimum
private:
Node* m_pos = nullptr;
};
/*
* List Implementation
*/
template <typename T>
List<T>::List(T val) : m_head(new Node(val)), m_tail(m_head), m_size(1) {}
template <typename T>
List<T>::List(const List<T>& other) {
m_head = new Node((other.m_head)->data);
m_tail = m_head;
m_size = 1;
Node* walker = (other.m_head)->next;
while (walker) {
push_back(walker->data);
++m_size;
walker = walker->next;
}
}
template <typename T>
List<T>::List(List&& other) : List() {
swap(*this, other);
}
template <typename T>
List<T>::~List() {
clear();
}
template <typename T>
void List<T>::push_front(T val)
{
if (!m_head) {
make_first_node(val);
return;
}
Node* tmp = new Node(val);
tmp->next = m_head;
m_head->prev = tmp;
m_head = tmp;
++m_size;
}
template <typename T>
void List<T>::push_back(T val) {
if (!m_head) {
make_first_node(val);
return;
}
Node* tmp = new Node(val);
tmp->prev = m_tail;
m_tail->next = tmp;
m_tail = tmp;
++m_size;
}
template <typename T>
typename List<T>::iterator List<T>::begin() {
return iterator(m_head);
}
template <typename T>
typename List<T>::iterator List<T>::end() {
return iterator(nullptr);
}
template <typename T>
typename List<T>::iterator List<T>::find(T val) {
return iterator(find_node(val));
}
template <typename T>
std::size_t List<T>::size() const {
return m_size;
}
template <typename T>
typename List<T>::iterator List<T>::erase(typename List<T>::iterator toErase)
{
Node* node = find_node(*toErase);
if (node->prev) {
node->prev->next = node->next;
} else {
m_head = node->next;
}
if (node->next) {
node->next->prev = node->prev;
} else {
m_tail = node->prev;
}
Node* toReturn = node->next;
delete node;
return toReturn;
}
template <typename T>
void List<T>::clear() {
Node* tmp = m_head;
while (m_head) {
m_head = m_head->next;
delete tmp;
tmp = m_head;
}
m_tail = nullptr;
m_size = 0;
}
template <typename T>
bool List<T>::operator=(List other) {
swap(*this, other);
return *this;
}
template <typename T>
void List<T>::make_first_node(T val) {
m_head = new Node(val);
m_tail = m_head;
m_size = 1;
}
template <typename T>
typename List<T>::Node* List<T>::find_node(T val) {
if (!m_head) {
return nullptr;
}
Node* walker = m_head;
while (walker != nullptr && walker->data != val) {
walker = walker->next;
}
return walker;
}
template <typename T>
void swap(List<T>& lhs, List<T>& rhs) {
using std::swap;
swap(lhs.m_head, rhs.m_head);
swap(lhs.m_tail, rhs.m_tail);
swap(lhs.m_size, rhs.m_size);
}
/*
* List Iterator Implementation
*/
template <typename T>
List<T>::iterator::iterator(Node* node) : m_pos(node) {}
template <typename T>
T& List<T>::iterator::operator*() {
return m_pos->data;
}
template <typename T>
typename List<T>::iterator& List<T>::iterator::operator++() {
m_pos = m_pos->next;
return *this;
}
template <typename T>
typename List<T>::iterator List<T>::iterator::operator++(int) {
iterator tmp(m_pos);
++(*this);
return tmp;
}
template <typename T>
typename List<T>::iterator& List<T>::iterator::operator--() {
m_pos = m_pos->prev;
return *this;
}
template <typename T>
typename List<T>::iterator List<T>::iterator::operator--(int) {
iterator tmp(m_pos);
--(*this);
return tmp;
}
template <typename T>
bool List<T>::iterator::operator==(const iterator& other) {
return m_pos == other.m_pos;
}
template <typename T>
bool List<T>::iterator::operator!=(const iterator& other) {
return !(*this == other);
}
#endif
Code and explaination
I think this code should work as well, so there is no need to have two pointers. It's based on an example of “good taste” that Linus Torvalds gave in an interview.
void append(Node &element)
{
Node** cursor = &first_node.ptr;
while ((*cursor) != nullptr)
cursor = &(*cursor)->ptr;
*cursor = &element;
}
It eliminates the need for multiple pointers, it eliminates edge cases and it allows us to evaluate the condition of the while loop without having to let go of the pointer that points to the next element. This allows us to modify the pointer that points to NULL and to get away with a single iterator as opposed to ptr and prev_ptr.
Naming conventions
Also the norm is to call the first node in the linked list head and to call the pointer to the next node next instead of ptr, so I will rename them in the following code.
void append(Node &new)
{
Node** cursor = &head.next;
while ((*cursor) != nullptr)
cursor = &(*cursor)->next;
*cursor = &new;
}
I have a problem with creating base class for DoubleLinkedList.
Right now it's giving me this error
/tmp/cc3lORia.o:(.rodata._ZTV24AbstractDoubleLinkedListIiE[_ZTV24AbstractDoubleLinkedListIiE]+0x10):
undefined reference to
`AbstractDoubleLinkedList::createNewNode(int)' collect2: error:
ld returned 1 exit status
I've tried this and that as you can see by commented lines in code, but none of it works.
So how to define abstract template class with abstract method (factory method by the way) and then redefine it in children classes?
/*
* AbstractDoubleLinkedList.hpp
*
* Created on: Mar 2, 2015
* Author: michael
*/
#ifndef ABSTRACTDOUBLELINKEDLIST_H_
#define ABSTRACTDOUBLELINKEDLIST_H_
#include <vector>
using namespace std;
template <class T> class ListNode {
private:
void init();
public:
ListNode();
ListNode(T value);
ListNode *previous;
ListNode *next;
T value;
};
template <class T> void ListNode<T>::init() {
previous = nullptr;
next = nullptr;
}
template <class T> ListNode<T>::ListNode() {
init();
}
template <class T> ListNode<T>::ListNode(T value) {
init();
this->value = value;
}
template <class T> class AbstractDoubleLinkedList {
private:
void pullOutNode(ListNode<T> *node);
protected:
virtual ListNode<T>* createNewNode(T element);
public:
AbstractDoubleLinkedList();
void push_back(T element);
T front();
T back();
void insertBefore(ListNode<T> *node, ListNode<T> *beforeNode);
void insertAfter(ListNode<T> *node, ListNode<T> *afterNode);
void moveNodeAfter(ListNode<T> *node, ListNode<T> *afterNode);
vector<T> toVector();
ListNode<T> *frontNode;
ListNode<T> *backNode;
};
template <class T> void AbstractDoubleLinkedList<T>::push_back(T element) {
ListNode<T>* node = createNewNode(element);
node->previous = backNode;
if (backNode != nullptr) {
backNode->next = node;
node->previous = backNode;
}
else {
frontNode = node;
}
backNode = node;
}
template <class T> void AbstractDoubleLinkedList<T>::pullOutNode(ListNode<T> *node) {
if (node != frontNode) {
node->previous->next = node->next;
}
else {
frontNode = node->next;
}
if (node != backNode) {
node->next->previous = node->previous;
}
else {
backNode = node->previous;
}
}
template <class T> T AbstractDoubleLinkedList<T>::front() {
return frontNode->value;
}
template <class T> T AbstractDoubleLinkedList<T>::back() {
return backNode->value;
}
template <class T> void AbstractDoubleLinkedList<T>::insertAfter(ListNode<T> *node, ListNode<T> *afterNode) {
node->previous = afterNode;
node->next = afterNode->next;
afterNode->next = node;
if (afterNode == backNode) {
backNode = node;
}
}
template <class T> void AbstractDoubleLinkedList<T>::insertBefore(ListNode<T> *node, ListNode<T> *beforeNode) {
node->next = beforeNode;
beforeNode->previous->next = node;
beforeNode->previous = node;
if (beforeNode == frontNode) {
frontNode = node;
}
}
template <class T> void AbstractDoubleLinkedList<T>::moveNodeAfter(ListNode<T> *node, ListNode<T> *afterNode) {
pullOutNode(node);
node->previous = afterNode;
node->next = afterNode->next;
if (node->next == nullptr) {
backNode = node;
}
afterNode->next = node;
}
template <class T> vector<T> AbstractDoubleLinkedList<T>::toVector() {
ListNode<T>* node = frontNode;
vector<int> listAsVector;
bool shouldHaveAnother = (frontNode != nullptr);
while(shouldHaveAnother) {
listAsVector.push_back(node->value);
if (node->next != nullptr)
node = node->next;
else {
shouldHaveAnother = false;
}
}
return listAsVector;
}
template <class T> AbstractDoubleLinkedList<T>::AbstractDoubleLinkedList() {
frontNode = nullptr;
backNode = nullptr;
}
#endif /* ABSTRACTDOUBLELINKEDLIST_HPP_ */
/*
* DoubleLinkedList.hpp
*
* Created on: Feb 26, 2015
* Author: michael
*/
#ifndef DOUBLELINKEDLIST_HPP_
#define DOUBLELINKEDLIST_HPP_
#include "AbstractDoubleLinkedList.hpp"
template <class T> class DoubleLinkedList : public AbstractDoubleLinkedList<T> {
protected:
ListNode<T>* createNewNode(T element) {
return new ListNode<T>(element);
}
public:
~DoubleLinkedList() {
ListNode<T>* node = this->backNode;
bool shouldHaveAnother = (node != nullptr);
while(shouldHaveAnother) {
ListNode<T>* ptr = node->previous;
delete node;
if (ptr != nullptr)
node = ptr;
else {
shouldHaveAnother = false;
}
}
};
};
//template <class T> ListNode<T> DoubleLinkedList<T>::createNewNode(T element) {
// return new ListNode<T>(element);
//}
//template <class T> DoubleLinkedList<T>::~DoubleLinkedList() {
//
// ListNode<T>* node = this->backNode;
// bool shouldHaveAnother = (node != nullptr);
// while(shouldHaveAnother) {
// ListNode<T>* ptr = node->previous;
// delete node;
// if (ptr != nullptr)
// node = ptr;
// else {
// shouldHaveAnother = false;
// }
// }
//}
#endif /* DOUBLELINKEDLIST_HPP_ */
EDIT1:
So, there is a question "Why do I need my own container class and why not use list or vector?"
I need a data structure with constant time random access and constant time deletion and insertion. BUT I've already tried std::unordered_set and It's not good enough (while technically it meets the requirements) because of allocations and deallocations of memory (when deleting and inserting)
So I figured another way. I want to use a linked list with one "guard" element and the end. When I need to "delete" element from it I would move it after guard. And to check if list is "empty" I would check if the first element is the guard element. But I need a constant-time random access. This can be achieved by map of pointers to every element.
But to achieve maximum performance I need to also minimize cache misses. And I thinks that std::list would be scattered across the memory, because it is the normal behaviour for it. So I figured that the only way to do so - is to allocate vector> and then use this preallocated nodes to new elements.
So am I wrong somewhere? Can I achieve maximum performance more easier?
This is the first time I've ever played with an iterator so there's probably significant errors. I'm attempting to make an inorder iterative iterator class to work in conjunction with my threaded binary search tree. So the iterator is working over nodes. I only need the iterator to go through my tree in order so I can print all the values and the frequencies of each node. However my dereference doesn't seem to be working probably. This is the method that's giving me trouble:
//-------------------------- inOrderTraverse ------------------------------------
template <typename T>
void ThreadedBST<T>::inOrderTraverse() {
InorderIterator<T>* iter = new InorderIterator<T>(root);
++iter;
while ((*iter) != NULL)
{
cout << (*iter)->getItem() << " " << (*iter)->getFrequency() << endl;
}
}
Particularly the while loop is throwing compiler errors. Here is the exact error:
error C2678: binary '!=' : no operator found which takes a left-hand operand of type 'InorderIterator'
I figured the dereference would bring the node out so I'd actually be comparing the node != NULL but that's not what the error message leads me to believe. Here's the full Iterator class:
#ifndef INORDERITER_H
#define INORDERITER_H
#include <iostream>
#include "ThreadedBST.h"
using namespace std;
//---------------------------------------------------------------------------
// InorderIterator<T> class:
// --
//
// Assumptions:
// -- <T> implements it's own comparable functionality
//---------------------------------------------------------------------------
template <typename T>
class InorderIterator {
public:
InorderIterator(node<T> *); //constructor
InorderIterator<T>& operator++();
node<T>& operator*();
const node<T>& operator*() const;
private:
node<T>* begin;
node<T>* curr;
node<T>* prev;
node<T>* temp;
};
template <typename T>
InorderIterator<T>::InorderIterator(node<T>* root) {
begin = root;
temp = NULL;
while (begin->leftChild != NULL) {
begin = begin->leftChild;
}
}
template <typename T>
InorderIterator<T>& InorderIterator<T>::operator++() {
if (temp == NULL)
temp = begin;
else if (rightChildThread) {
prev = temp;
temp = temp->rightChild;
}
else {
prev = temp;
temp = temp->rightChild;
while (!temp->rightChildThread && (temp->leftChild->getItem() != prev->getItem())) {
temp = temp->leftChild;
}
}
curr = temp;
return *this;
}
template <typename T>
node<T>& InorderIterator<T>::operator*() {
return *curr;
}
template <typename T>
const node<T>& InorderIterator<T>::operator*() const {
return *curr;
}
#endif
Here's the node class if it's relevant for any reason:
#ifndef NODE_H
#define NODE_H
#include <iostream>
using namespace std;
//---------------------------------------------------------------------------
// node<T> class:
// --
//
// Assumptions:
// -- <T> implements it's own comparable functionality
//---------------------------------------------------------------------------
template <typename T>
class node {
public:
node<T>* leftChild;
node<T>* rightChild;
bool leftChildThread;
bool rightChildThread;
node(T value); //constructor
node(T value, node<T>*, node<T>*, bool, bool); //secondary constructor
node(const node<T>&); //copy constructor
void decrementFrequency(); //decrements by 1 the frequency
void incrementFrequency(); //increments by 1 the frequency
int getFrequency(); //returns the frequency
T getItem(); //returns the item
private:
T item;
int frequency;
};
//-------------------------- Constructor ------------------------------------
template <typename T>
node<T>::node(T value) {
item = value;
frequency = 1;
}
//-------------------------- Secondary Constructor ------------------------------------
template <typename T>
node<T>::node(T value, node<T>* left, node<T>* right, bool leftThread, bool rightThread) {
item = value;
frequency = 1;
leftChild = left;
rightChild = right;
leftChildThread = leftThread;
rightChildThread = rightThread;
}
//-------------------------- Copy ------------------------------------
template <typename T>
node<T>::node(const node<T>& copyThis) {
item = copyThis.value;
frequency = copyThis.frequency;
}
//-------------------------- decrementFrequency ------------------------------------
template <typename T>
void node<T>::decrementFrequency() {
frequency--;
}
//-------------------------- incrementFrequency ------------------------------------
template <typename T>
void node<T>::incrementFrequency() {
frequency++;
}
//-------------------------- getFrequency ------------------------------------
template <typename T>
int node<T>::getFrequency() {
return frequency;
}
//-------------------------- getItem ------------------------------------
template <typename T>
T node<T>::getItem() {
return item;
}
#endif
class const_iterator {
public:
Node *current;
const_iterator (Node *n) : current{n}
{
/* the body can remain blank, the initialization is carried
* the the constructor init list above
*/
}
/* copy assignment */
const_iterator operator= (const const_iterator& rhs) {
this->current = rhs.current;
return *this;
}
bool operator == (const const_iterator& rhs) const {
return this->current == rhs.current;
}
bool operator != (const const_iterator& rhs) const {
return this->current != rhs.current;
}
/* Update the current pointer to advance to the node
* with the next larger value
*/
const_iterator& operator++ () {
/*first step is to go left as far as possible(taken care of by begin())
once you go as left as possible, go right one step at a time*/
if(current->right != nullptr){
current = current->right;
//every step, go left again as far as possible
while(current->left != nullptr){
current = current->left;
}
}else{
bool upFromLeft = false;
bool upFromRight = false;
while(upFromLeft == false && upFromRight == false){
//if you have gone all the way up from the right
if(current->parent == nullptr){
upFromRight = true;
current = current->parent;
return *this;
}
//if you have gone all the way back up left
if(current->parent->left == current){
upFromLeft = true;
current = current->parent;
return *this;
}
current = current->parent;
}
}
return *this;
}
Z& operator *() const {
return current->data;
}
};
ADD these functions to your tree in order to use the begin() and end() with your iterator
const const_iterator begin() const {
if(rootPtr == nullptr){
return nullptr;
}
Node* temp = rootPtr;
while(temp->left != nullptr){
temp = temp->left;
}
return const_iterator(temp);
}
/* For the "end" marker
* we will use an iterator initialized to nil */
const const_iterator end() const {
return const_iterator(nullptr);
}
Here's an example of an in-order iterator I wrote in C++...
This iterator assumes that each node in your BST has a pointer to the parent node which is something I don't see in your node class. However, I am not sure its even possible to accomplish an inorder traversal without having a parent pointer.
In short, this example will work if you add a parent pointer to your nodes and update your parent pointers every time you do a node insertion or removal
I've been working on an assignment and now I'm stuck with buggy destructors. I have to create a generic binary tree with all the usual member functions and some special operators. There's also a restriction: everything must work iteratively so no nasty recursive hacks this time.
There is obviously something very wrong with the destructor of BinTreeNode class because if I delete the node like this:
BinTreeNode<int> * node = new BinTreeNode<int>();
delete node;
I can still access its data:
node->getData(); //should fail miserably
so deletion has no effect but I have no usable idea how I should correct the destructor.
It seems to me that the algorithm should be about right so I suspect there's something wrong with how I use pointers but at this point I'm so confused that I don't even understand my own code.
Code I have this far:
BinTree.h
#ifndef BINTREE_H_
#define BINTREE_H_
#ifndef NULL
#define NULL 0
#endif
#include "BinTreeNode.h"
template <class T>
class BinTree
{
private:
BinTreeNode<T> * root;
public:
//constructors and destructor
BinTree():
root(NULL){}
BinTree(T data):
root(new BinTreeNode<T>(data)){}
~BinTree();
//search
BinTreeNode<T> * search(T data);
//insert
bool insert(T data);
//remove
bool remove(T data);
};
template <class T>
BinTree<T>::~BinTree()
{
delete root;
}
template <class T>
BinTreeNode<T> * BinTree<T>::search(T data)
{
BinTreeNode<T> * node = new BinTreeNode<T>(data);
BinTreeNode<T> * current = root;
while (current != NULL)
{
if (*current == *node)
{
delete node;
return root;
}
else if (*node < *current)
{
current = current->getLeft();
}
else
{
current = current->getRight();
}
}
delete node;
return NULL;
}
template <class T>
bool BinTree<T>::insert(T data)
{
BinTreeNode<T> * node = new BinTreeNode<T>(data);
BinTreeNode<T> * current = root;
while (current != NULL)
{
if (*current == *node)
{
delete node;
return false;
}
else if (*node < *current)
{
if (current->getLeft() == NULL)
{
current->setLeft(node);
return true;
}
else
{
current = current->getLeft();
}
}
else
{
if (current->getRight() == NULL)
{
current->setRight(node);
return true;
}
else
{
current = current->getRight();
}
}
}
return false;
}
#endif
BinTreeNode.h
#ifndef BINTREENODE_H_
#define BINTREENODE_H_
#ifndef NULL
#define NULL 0
#endif
template <class T>
class BinTreeNode
{
private:
T data;
BinTreeNode<T> *left, *right, *parent;
public:
//constructors and destructor
BinTreeNode():
data(NULL), left(NULL), right(NULL), parent(NULL){}
BinTreeNode(T data):
data(data), left(NULL), right(NULL), parent(NULL){}
~BinTreeNode();
//set and get data member
T getData() const;
void setData(T data);
//set and get left and right branches
BinTreeNode<T> * getLeft() const;
BinTreeNode<T> * getRight() const;
void setLeft(BinTreeNode<T> * node);
void setRight(BinTreeNode<T> * node);
//set and get parent
BinTreeNode<T> * getParent() const;
void setParent(BinTreeNode<T> * node);
//comparison operators
bool operator<(const BinTreeNode<T>& node) const;
bool operator>(const BinTreeNode<T>& node) const;
bool operator==(const BinTreeNode<T>& node) const;
};
template <class T>
BinTreeNode<T>::~BinTreeNode()
{
BinTreeNode<T> * current = this;
BinTreeNode<T> * parent = NULL;
while (current != NULL)
{
parent = current->getParent();
if (current->getLeft() == NULL)
current = current->getLeft();
else if (current->getRight() == NULL)
current = current->getRight();
else
{
if (parent->getRight() == current)
parent->setRight(NULL);
else
parent->setLeft(NULL);
current = NULL; // this line (among others) is very suspicious
}
current = parent;
}
}
template <class T>
T BinTreeNode<T>::getData() const
{
return data;
}
template <class T>
void BinTreeNode<T>::setData(T data)
{
this->data = data;
}
template <class T>
BinTreeNode<T> * BinTreeNode<T>::getLeft() const
{
return left;
}
template <class T>
BinTreeNode<T> * BinTreeNode<T>::getRight() const
{
return right;
}
template <class T>
void BinTreeNode<T>::setLeft(BinTreeNode<T> * node)
{
node->setParent(this);
left = node;
}
template <class T>
void BinTreeNode<T>::setRight(BinTreeNode<T> * node)
{
node->setParent(this);
right = node;
}
template <class T>
BinTreeNode<T> * BinTreeNode<T>::getParent() const
{
return parent;
}
template <class T>
void BinTreeNode<T>::setParent(BinTreeNode<T> * node)
{
parent = node;
}
template <class T>
bool BinTreeNode<T>::operator<(const BinTreeNode<T>& node) const
{
return this->data < node.data;
}
template <class T>
bool BinTreeNode<T>::operator>(const BinTreeNode<T>& node) const
{
return this->data > node.data;
}
template <class T>
bool BinTreeNode<T>::operator==(const BinTreeNode<T>& node) const
{
return this->data == node.data;
}
#endif /* BINTREENODE_H_ */
Your BinTreeNode destructor should simply be:
template <class T>
BinTreeNode<T>::~BinTreeNode() {
delete left;
delete right;
}
That will call left and right's destructors recursively, freeing the memory allocated for those nodes and their child nodes. This will as a consequence free the entire tree.
Assigning NULL to a pointer does not free the memory pointed by it.
On the other hand, what you mention, that after deletion, this line:
node->getData();
Still returns data, is perfectly normal. Deletion frees the memory, but the data stored in it might still be available for a while, until something new is written in that memory address. Accessing an already free'd memory address implies undefined behaviour.
BTW, you should use "0"(without quotes) in C++ instead of NULL. Therefore, there it's not necessary to use the #ifndef NULL(...).
EDIT: I hadn't seen the "no recursion" comment. Here's a non-recursive algorithm:
#include <deque>
/* ... */
template <class T>
BinTreeNode<T>::~BinTreeNode() {
std::deque deq;
// we're going to delete our children
deq.push_back(this);
while(deq.size()) {
BinTreeNode<T> *ptr = deq.front();
deq.pop_front();
if(ptr) {
deq.push_back(ptr->left);
deq.push_back(ptr->right);
// we don't want the child nodes
// to double delete the children
ptr->left = 0;
ptr->right = 0;
// avoid deleteing ourselves
if(ptr != this)
delete ptr;
}
}
}
I haven't tested it, but it should work.
I have been struggling for too long a time now with a rather simple question about how to create a generic linked list in c++. The list should be able contain several types of structs, but each list will only contain one type of struct. The problem arises when I want to implement the getNode() function [see below], because then I have to specify which of the structs it should return. I have tried to substitute the structs with classes, where the getNode function returns a base class that is inherited by all the other classes, but it still does not do the trick, since the compiler does not allow the getNode function to return anything but the base class then.
So here is some code snippet:
typedef struct struct1
{
int param1;
(...)
} struct1;
typedef struct struct2
{
double param1;
(...)
} struct2;
typedef struct node
{
struct1 data;
node* link;
} node;
class LinkedList
{
public:
node *first;
int nbrOfNodes;
LinkedList();
void addNode(struct1);
struct1 getNode();
bool isEmpty();
};
LinkedList::LinkedList()
{
first = NULL;
nbrOfNodes = 0;
}
void LinkedList::addNode(struct1 newData)
{
if (nbrOfNodes == 0)
{
first = new node;
first->data = newData;
}
else
{
node *it = first;
for (int i = 0; i < nbrOfNodes; i++)
{
it = it->link;
}
node *newNode = new node;
newNode->data = newData;
it->link = newNode;
}
nbrOfNodes++;
}
bool LinkedList::isEmpty()
{
return !nbrOfNodes;
}
struct1 LinkedList::getNode()
{
param1 returnData = first->data;
node* deleteNode = first;
nbrOfNodes--;
if (nbrOfNodes)
first = deleteNode->link;
delete deleteNode;
return returnData;
}
So the question, put in one sentence, is as follows: How do I adjust the above linked list class so that it can also be used for struct2, without having to create a new almost identical list class for struct2 objects? As I said above, each instance of LinkedList will only deal with either struct1 or struct2.
Grateful for hints or help
There is already a generic link list available in C++, std::list. It will definitely be more efficient & should suffice for your usage.
If you still want to create your own generic link list
You should consider using templates and create a template implmentation of link list.
In c, where templates are not available the data node is stored in the form of a void* pointer. It takes advantage of the fact that a void pointer can point to any generic data type, You might consider that approach as well.
Basic tempaltes are easy.
Just declare the class as template with a templated type variable.
Now everywhere you have the declare type which you want to be generic, in the class, replace the explicit type name with the templated variable name.
For example, in your code, you want struct1 to be generic, so we replace it with T:
template<class T>
class LinkedList {
public:
node *first;
int nbrOfNodes; LinkedList();
void addNode(T);
T getNode();
bool isEmpty();
};
The STL source would be a piece of code to study.
You could also try https://github.com/simonask/ftl/blob/master/list.hpp
Both use templates, which should be understood to be able to make any generic classes.
Here is a generic implementation without using STL. You can create a templated class with a generic type and instantiate the singly_linked_list.
namespace api
{
template <typename T>
class i_list
{
public:
i_list() = default;
virtual ~i_list(){}
/* Add to front*/
virtual int push_front(T t_value) = 0;
/* Return the front item*/
virtual T top_front() = 0;
/* Remove and return the front elenment */
virtual T pop_front() = 0;
/* Add to the back */
virtual int push_back(T t_value) = 0;
/* Returns the back item */
virtual T top_back() = 0;
/* Removes and returns the back item */
virtual T pop_back() = 0;
/* Is key in the list */
virtual bool find(T t_value) = 0;
/* Remove the key from the list */
virtual int erase(T t_value) = 0;
/* Erases the entire list */
virtual void erase_all() = 0;
/* Is the list empty */
virtual bool empty() = 0;
/* return the size of the list */
virtual size_t size() = 0;
};
}
namespace list
{
template<typename T>
struct node
{
T m_data;
node* m_next;
node* m_jump;
int m_order;
node(T t_data) : m_data(t_data), m_next(nullptr) , m_jump(nullptr), m_order(-1){}
};
template<typename T>
class singly_linked_list : public api::i_list<T>
{
public:
singly_linked_list() : m_head(nullptr), m_size(0){}
~singly_linked_list() {erase_all();}
virtual int push_front(T t_value) override;
virtual T top_front() override;
virtual T pop_front() override;
virtual int push_back(T t_value) override;
virtual T top_back() override;
virtual T pop_back() override;
virtual bool find(T t_value) override;
virtual int erase(T t_value) override;
virtual void erase_all() override;
virtual bool empty() override;
virtual size_t size() override;
template<typename U>
friend node<U>* get_head(singly_linked_list<U>& t_list);
private:
node<T>* get_last_node();
node<T>* get_node_until(size_t t_index);
node<T>* find_node(T t_value);
node<T>* get_node_pointer(size_t t_position);
int find_node_index(T t_value);
singly_linked_list<T> get_all_addresses();
node<T>* m_head = nullptr;
size_t m_size;
};
/* O(1) */
template<typename T>
inline int singly_linked_list<T>::push_front(T t_value)
{
node<T>* new_node = new node<T>(t_value);
new_node->m_next = m_head;
m_head = new_node;
m_size++;
return 0;
}
/* O(1) */
template<typename T>
inline T singly_linked_list<T>::top_front()
{
if (empty())
{
std::cout << " list is empty " << std::endl;
return T();
}
return m_head->m_data;
}
/* O(1) */
template<typename T>
inline T singly_linked_list<T>::pop_front()
{
if (empty())
{
std::cout << " list is empty " << std::endl;
return T();
}
/* Value to be returned */
T value = m_head->m_data;
node<T>* next_node = m_head->m_next;
delete(m_head);
m_head = next_node;
m_size--;
return value;
}
/* O(N) */
template<typename T>
inline int singly_linked_list<T>::push_back(T t_value)
{
node<T>* new_node = new node<T>(t_value);
if (empty())
{
m_head = new_node;
m_size++;
return 0;
}
get_last_node()->m_next = new_node;
m_size++;
return 0;
}
/* O(N) */
template<typename T>
inline T singly_linked_list<T>::top_back()
{
if (empty())
{
std::cout << " list is empty " << std::endl;
return T();
}
return get_last_node()->m_data;
}
/* O(N) */
template<typename T>
inline T singly_linked_list<T>::pop_back()
{
T value;
if (empty())
{
std::cout << " list is empty " << std::endl;
return T();
}
if (size() == 1)
{
value = m_head->m_data;
delete(m_head);
m_head = nullptr;
m_size = 0;
return value;
}
node<T>* last_node = get_last_node();
node<T>* last_node_before = get_node_until(size() -1);
value = last_node->m_data;
delete(last_node);
last_node_before->m_next = nullptr;
m_size--;
return value;
}
/* O(N) - Worst case */
template<typename T>
inline bool singly_linked_list<T>::find(T t_value)
{
return find_node(t_value) != nullptr;
}
/* O(N) - Worst case */
template<typename T>
inline int singly_linked_list<T>::erase(T t_value)
{
/* Node with t_value deos not exists */
if (empty())
return -1;
if (size() == 1)
{
delete(m_head);
m_head = nullptr;
m_size = 0;
return 0;
}
int index = find_node_index(t_value);
if (index == -1)
return index;
node<T>* node_to_erase = get_node_until(index + 1);
node<T>* before_node_to_erase = get_node_until(index);
node<T>* after_node_to_erase = get_node_until(index + 2);
before_node_to_erase->m_next = after_node_to_erase;
delete(node_to_erase);
m_size--;
return 0;
}
/* O(N) - Worst case */
template<typename T>
inline void singly_linked_list<T>::erase_all()
{
while(m_head != nullptr)
{
node<T>* next_node = m_head->m_next;
delete(m_head);
m_size--;
m_head = next_node;
}
}
/* O(1) */
template<typename T>
inline bool singly_linked_list<T>::empty()
{
return (m_head == nullptr);
}
/* O(1) */
template<typename T>
inline size_t singly_linked_list<T>::size()
{
return m_size;
}
template<typename T>
inline node<T>* singly_linked_list<T>::get_last_node()
{
node<T>* start_node = m_head;
node<T>* last_node = nullptr;
/* Traverse until the end */
while (start_node != nullptr)
{
last_node = start_node;
start_node = start_node->m_next;
}
return last_node;
}
template<typename T>
inline node<T>* singly_linked_list<T>::get_node_until(size_t t_index)
{
node<T>* start_node = m_head;
node<T>* until_node = nullptr;
/* Traverse until the a node before last node */
size_t index(1);
while (start_node != nullptr)
{
until_node = start_node;
if (index == t_index)
{
return until_node;
}
start_node = start_node->m_next;
index++;
}
return nullptr;
}
template<typename T>
inline node<T>* singly_linked_list<T>::find_node(T t_value)
{
node<T>* start_node = m_head;
/* Traverse until the end */
while (start_node != nullptr)
{
if (t_value == start_node->m_data)
return start_node;
start_node = start_node->m_next;
}
return nullptr;
}
template<typename T>
inline int singly_linked_list<T>::find_node_index(T t_value)
{
node<T>* start_node = m_head;
/* Traverse until the end */
int index(0);
while (start_node != nullptr)
{
if (t_value == start_node->m_data)
return index;
start_node = start_node->m_next;
index++;
}
/* t_value not found*/
return -1;
}
/* Returns the address of the specified node position
form the linke list chain */
template<typename T>
node<T>* singly_linked_list<T>::get_node_pointer(size_t t_position)
{
auto start_node{m_head};
/* Traverse until the end */
size_t i(0);
while (start_node != nullptr)
{
if(i == t_position)
return start_node;
start_node = start_node->m_next;
i++;
}
return nullptr;
}
template<typename U>
node<U>* get_head(singly_linked_list<U>& t_list)
{
return t_list.m_head;
}
}
struct1 and struct2 have different size in bytes, so sizeof(struct1) != sizeof(struct2). Returning the struct from function requires copying it, so c++ requires you to specify the correct type for it so that correct amount of bytes can be copied. To start correct this problem you need to think in the very low level:
struct GenericStruct {
void *ptr;
size_t size;
type_info t;
};
struct1 extract_struct1(GenericStruct &s);
struct2 extract_struct2(GenericStruct &s)
{
if (s.size != sizeof(struct2)) throw -1;
if (s.t != typeid(struct2)) throw -1;
struct2 *s2 = (struct2*)s.ptr;
return *s2;
}
GenericStruct make_generic(const struct1 &ss)
{
GenericStruct s;
s.ptr = (void*)&ss;
s.size = sizeof(struct1);
s.t = typeid(struct1);
return s;
}
GenericStruct make_generic(const struct2 &ss);
the real problem is that these functions can fail on runtime, if the sizes or types do not match. The copying is obviously also needed:
GenericStruct Copy(const GenericStruct &s);
After these basic primitives exists, you can create a class which has copy constructor and assignment operator which uses these functions to implement proper generic struct support.