So, i'm trying to initialize a LinkedList class using an initializer_list.
template<typename T>
SortedList<T>::SortedList(initializer_list<T> e){
head_= new Node<T>(*e.begin());
long intcheck = 0;
T old;
for (auto x : e){
if(intcheck > 0){
Node<T>* curr = new Node<T>(old);
if(head_ == curr){
head_->next_ = new Node<T>(x);
}
curr->next_ = new Node<T>(x);
}
old = x;
intcheck = 1;
}
}
I get a seg fault when trying to print head_->next_ (nothing wrong with my print function)
I'm assuming that you actually want the SortedList to be sorted. If so, this will accomplish that goal. It bails out early if the initializer_list is empty, but still leaves the object in a rational state.
template<typename T>
SortedList<T>::SortedList(initializer_list<T> e) : head_{nullptr} {
if (e.size() == 0)
return;
auto it = e.begin();
for (head_ = new Node<T>(*it); it != e.end(); ++it) {
Node<T> *n = new Node<T>(*it);
Node<T> *curr;
for (curr = head_; curr->next_ && curr->next_->data_ < *it; curr = curr->next_)
continue;
if (*it < curr->data_) {
n->next_ = curr;
head_ = n;
} else {
n->next_ = curr->next_;
curr->next_ = n;
}
}
}
For completeness, here's the destructor I used to test:
template<typename T>
SortedList<T>::~SortedList() {
while (head_->next_) {
Node<T> *t = head_->next_;
head_->next_ = t->next_;
delete t;
}
delete head_;
}
Your code says that head_->next_ will always be it's default value (we can't see the Node constructor so I can't say what that is).
for (auto x : e){
if(intcheck > 0){
Node<T>* curr = new Node<T>(old);
if(head_ == curr){
head_->next_ = new Node<T>(x);
}
It's OK do send your initializer list into the for loop this way. If it's empty you will just exit the loop immediately.
However your curr pointer is allocated right there, while your head_ pointer was allocated above. They will therefore never be equal because you are comparing two pointers that you are allocating in the same function.
Related
Hi for my c++ assignment i have to implement a bunch of functions and one of them requires me to copy the values from a forward list and store them in a linked list. My university uses a website called Edstem to run and mark the code. It goes through a bunch of tests in a file called tests.hpp
I wrote up my code for this specific function in another IDE and it works perfectly and i got it to print out all the nodes in the linked list.
here is my code that I did in repl.it to figure out how to apply it to my assignment
and this is what i get after running the test on the edstem website
this is from the tests.hpp
#include <iostream>
#include <map>
#include<forward_list>
using namespace std;
#include <iostream>
using namespace std;
class Node {
public:
int data;
Node* next = nullptr;
};
int main() {
forward_list<int> flist1;
flist1 = {1,2,3,4,5,6,5,3,7,3,5,2,6,3,6,34,3,65,6};
Node * head = nullptr;
Node * temp = head;
for (auto it = flist1.begin() ; it != flist1.end(); it++){
if (head == nullptr){
head = new Node();
head->data = *it;
temp = head;
}else{
if(temp != nullptr){
temp->next = new Node();
temp->next->data = *it;
temp = temp->next;
}
}
//temp->next = new Node();
//temp->next->data = *it;
// temp = temp->next;
}
temp = head;
while (temp != nullptr){
cout << temp->data<< endl;
temp = temp->next;
}
}
Here is the function that I have to implement
// Constructor from initializer list
// ***For you to implement
// This implements the functionality you see with, for example,
// std::forward_list<int> my_list = {1,2,3}
// which creates a new linked list where the first node holds 1, second 2, and
// third 3.
// The {1,2,3} here is of type std::initializer_list<int> and you
// see this is the argument to this constructor (with data of type T
// rather than just int).
// You can access the elements of a std::initializer_list via an iterator
// for example you can cycle through all the elements with
// for(auto it = input.begin(); it!= input.end(); ++it){Do something with *it}
template <typename T>
Forward_list<T>::Forward_list(std::initializer_list<T> input)
{
head_ = nullptr;
Node * temp = head_;
for (auto it = input.begin(); it != input.end(); it++){
if (head_ == nullptr){
head_ = new Node(*it);
temp = head_;
}else{
if(temp != nullptr){
temp->next = new Node(*it);
temp = temp->next;
}
}
temp = head_;
}
}
#endif
here is the class for the Linked List, the relevant parts
template <typename T>
class Forward_list
{
public:
class Node
{
public:
// A node will hold data of type T
T data{};
// next will point to the next node in the list
// we initialise next to nullptr
Node* next = nullptr;
// Because we have already intialised the variables
// the default constructor doesn't need to do anything
Node(){}
// To make life easier we also provide a constructor
// that takes the T data to be copied into the data member variable
// There is an optional second argument which is
// used to update the next pointer. This defaults to nullptr
// if the constructor is called with just one argument.
Node(T input_data, Node* next_node= nullptr)
{
data = input_data;
next = next_node;
}
// Destructor
~Node(){}
};
private:
// private member variables for Forward_list
// the trailing underscore is a stylistic choice to
// distinguish these as private member variables
unsigned size_ = 0;
Node* head_ = nullptr;
and the test function that is used
void test_initializer_list(void)
{
std::initializer_list<int> inputs = {5,23,1,105,-2,7,88,0};
Forward_list<int> my_list {inputs};
std::forward_list<int> real_list {inputs};
for(unsigned i=8; i>0; --i)
{
assert(my_list.front() == real_list.front());
assert(my_list.size() == i);
real_list.pop_front();
my_list.pop_front();
}
}
Just remove the line
temp = head_;
at the end of the loop body. Otherwise you reset the node you're working with to the head node in every iteration regardless of whether you're at the first element or not.
Imho an alternative loop that doesn't have to consider 2 states would be preferable.
Additionally size_ is never set in the constructor for some reason.
Here are both the version of your implementation with the bug mentioned fixed and a version implemented from scratch.
Your version with fix
template <typename T>
Forward_list<T>::Forward_list(std::initializer_list<T> input)
: size_(input.size())
{
head_ = nullptr;
Node* temp = head_;
for (auto it = input.begin(); it != input.end(); it++) {
if (head_ == nullptr) {
head_ = new Node(*it);
temp = head_;
}
else {
if (temp != nullptr) {
temp->next = new Node(*it);
temp = temp->next;
}
}
}
}
My own version
template <typename T>
Forward_list<T>::Forward_list(std::initializer_list<T> input)
: size_(input.size()),
head_(nullptr)
{
auto it = input.begin();
auto const end = input.end();
if (it != end)
{
head_ = new Node(*it);
++it;
for (auto temp = head_; it != end; ++it)
{
auto next = new Node(*it);
temp->next = next;
temp = next;
}
}
}
I'm currently trying to implement data structures from scratch as a way to better understand how they work while learning C++ and how to use raw pointers at the same time, and right now I'm working on a singly linked list:
#include <vector>
#include <iostream>
template <typename T>
struct Node {
T value;
Node* next;
};
template <typename T>
class SLL {
private:
Node<T>* m_head;
Node<T>* m_tail;
std::size_t m_size;
public:
explicit SLL(std::vector<T>& values);
Node<T>* get(int index);
void insert(int index, T value);
void print();
void set(int index, T value);
};
template<typename T>
SLL<T>::SLL(std::vector<T> &values) {
m_size = values.size();
for (int i = 0; i < values.size(); i++) {
switch (i) {
case 0: {
auto tmp0 = new Node<T>{values[0], nullptr};
m_head = tmp0;
m_tail = tmp0;
break;
}
case 1: {
auto tmp1 = new Node<T>{values[1], nullptr};
m_head->next = tmp1;
m_tail = tmp1;
break;
}
default: {
auto tmpDefault = new Node<T>{values[i], nullptr};
m_tail->next = tmpDefault;
m_tail = tmpDefault;
}
}
}
}
template<typename T>
Node<T>* SLL<T>::get(int index) {
if (0 <= index < m_size) {
int i = 0;
Node<T>* tmp = m_head;
while (i != index) {
tmp = tmp->next;
i++;
}
return tmp;
} else throw "Index out of bounds";
}
template<typename T>
void SLL<T>::insert(int index, T value) {
Node<T>* prev = get(index - 1);
Node<T>* next = prev->next;
auto toInsert = new Node<T>{value, nullptr};
toInsert->next = next;
prev->next = toInsert;
}
template<typename T>
void SLL<T>::print() {
Node<T>* tmp = m_head;
while (tmp->next != nullptr) {
std::cout << tmp->value << " ";
tmp = tmp->next;
}
std::cout << tmp->value << std::endl;
}
template<typename T>
void SLL<T>::set(int index, T value) {
if (0 <= index < m_size) {
int i = 0;
Node<T>* tmp = m_head;
while (i != index) {
tmp = tmp->next;
i++;
}
tmp->value = value;
} else throw "Index out of bounds";
}
int main ()
{
std::vector<int> vec{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
SLL<int> sll(vec);
sll.print();
sll.insert(4, 1337);
sll.print();
Node<int>* node = sll.get(4);
std::cout << node->value << std::endl;
sll.set(6, 600);
sll.print();
return 0;
}
There are likely many issues with my code that I'm unaware of, so identifying them would be greatly appreciated.
However, my main question was about the behavior of my code when I instantiated the temp Nodes in the SLL constructor on the stack rather than the heap (I saw there was a problem with this and switched to heap instantiation, but I don't understand what's going on well enough to understand the problem), with something like this:
template<typename T>
SLL<T>::SLL(std::vector<T> &values) {
m_size = values.size();
for (int i = 0; i < values.size(); i++) {
switch (i) {
case 0: {
// auto tmp0 = new Node<T>{values[0], nullptr};
// m_head = tmp0;
// m_tail = tmp0;
Node<T> tmp0{ values[0], nullptr };
m_tail->next = &tmp0;
m_tail = &tmp0;
break;
}
case 1: {
// auto tmp1 = new Node<T>{values[1], nullptr};
// m_head->next = tmp1;
// m_tail = tmp1;
Node<T> tmp1{ values[1], nullptr };
m_tail->next = &tmp1;
m_tail = &tmp1;
break;
}
default: {
// auto tmpDefault = new Node<T>{values[i], nullptr};
// m_tail->next = tmpDefault;
// m_tail = tmpDefault;
Node<T> tmpDefault{ values[i], nullptr };
m_tail->next = &tmpDefault;
m_tail = &tmpDefault;
}
}
}
}
When going through the debugger to figure out what was wrong with this, I saw that every time tmpDefault was instantiated after the first time (so for i = 3, 4, 5...) this would change the value of m_tail and m_tail->next, does this mean tmpDefault is instantiated in the same memory address each time? Also, if I understand correctly, tmpDefault is destroyed as soon as we leave the scope of the default case, so how come I don't see any change in the value of m_tail once I leave the scope in the debugger (shouldn't it be pointing to an unallocated block of memory resulting in value being equal to some random integer)? Thanks!
P.S. I'm not sure what would be a good, descriptive title for this post, so suggestions are welcome.
For my Computer Science class I am implementing a templated Priority Queue class and I am struggling to implement the enqueue function. I know the error is in the if(value < back) block of the function.
Here is the Function:
template<class T>
bool PriorityQueue<T>::enqueue(T& value){
if(!isEmpty()){
if(value < back()){
Node<T>* curr = f_ptr;
Node<T>* prev = NULL;
while(value > curr->data() && curr != NULL){
prev = curr;
curr = curr->m_next;
}
if(curr != NULL && value == curr->data()){
prev = curr;
curr = curr->m_next;
}
Node<T>* tmp = new Node<T>(value,curr);
prev->m_next = tmp;
return true;
}else{
b_ptr->m_next = new Node<T>(value);
b_ptr = b_ptr->m_next;
}
}else{
f_ptr = new Node<T>(value);
b_ptr = f_ptr;
return true;
}
}
The Helper functions are all functioning properly. Please explain what part of my logic is incorrect.
I am a beginner learning c++, and currently making a singly linked list. I have faced some problems and I thought for a very long time, searched a lot but still do not have an answer for this code so I am begging for some help..
So this is my linked.h
template <class T>
class Node {
public:
T data;
Node<T>* next;
};
template <class T>
class List {
private:
Node<T> *head;
public:
List() : head(NULL) {};
~List() {
Node<T>* ptr, tmp;
for(ptr = head->next; ptr == NULL; ptr = head->next) {
delete ptr;
}
}
List(T* arr, int n_nodes) {
head = NULL;
Node<T> *tmp = head;
for(int i = 0; i < n_nodes; i++) {
Node<T>* node = new Node<T>;
node->data = arr[i];
if(head == NULL) {
head->next = node;
tmp = node;
}
else {
tmp->next = node;
node->next = NULL;
tmp = node;
}
}
}
friend std::ostream& operator<<(std::ostream& out, List<T>& rhs) {
Node<T>* cur = rhs.head;
out << cur;
while(cur != NULL) {
if(cur->next != NULL) {
out << cur->data << ", ";
cur = cur->next;
}
else
out << cur->data << " ";
}
return out;
}
};
and this is my main.cc file.
#include <iostream>
#include "linked.h"
int main() {
int array[5] = {12, 7, 9, 21, 13};
List<int> li(array, 5);
std::cout << li;
return 0;
}
I keep on getting segmentation fault when running the constructor and I don't get why. Where am I making a mistake? Any help would be appreciated!
You could cover the issue with a pointer to pointer:
List(T* arr, int n_nodes)
{
Node<T>** tmp = &head; // tmp *pointing* to uninitialized(!) head pointer
for(int i = 0; i < n_nodes; i++)
{
Node<T>* node = new Node<T>();
node->data = arr[i];
// now the trick:
*tmp = node; // !!!
// you now have assigned the new node to whatever pointer
// the tmp pointer points to - which initially is - guess - head...
// but we now need to advance!
tmp = &node->next;
}
// tmp now points to latestly created node's next pointer
// (or still head, if no nodes where created because of n_nodes == 0)
// be aware that this one still is not initialized! so:
*tmp = nullptr;
}
Your destructor necessarily fails, too:
Node<T>* ptr, tmp;
for(ptr = head->next; ptr == NULL; ptr = head->next)
{
delete ptr; // you delete ptr, but advancing (ptr = head->next)
// is done AFTERWARDS, so you'd access already deleted memory
// undefined behaviour
}
Additionally, you don't delete the head node! And if head is nullptr, you again have undefined behaviour.
Try it this way:
while(head)
{
Node<T>* tmp = head; // need a copy of pointer
head = head->next; // need to advance BEFORE deleting
delete tmp; // now can delete safely
}
When I run valgrind, I get one error at method insert for operator new;
I know this probably means that I have to delete node n how I tried so many things to try to delete it but it just gives me even more errors. Please hel.
class key_value_sequences {
public:
struct node{
int key;
vector<int> values;
node* next;
node* prev;
};
key_value_sequences() {
}
~key_value_sequences() {
}
key_value_sequences(const key_value_sequences& A) {
n = A.n;
head = A.head;
tail = A.tail;
v = A.v;
}
key_value_sequences& operator=(const key_value_sequences& A) {
if (this == &A) return *this;
n = A.n;
head = A.head;
tail = A.tail;
v = A.v;
return *this;
}
// YOU SHOULD USE C++ CONTAINERS TO AVOID RAW POINTERS
// IF YOU DECIDE TO USE POINTERS, MAKE SURE THAT YOU MANAGE MEMORY PROPERLY
// IMPLEMENT ME: SHOULD RETURN SIZE OF A SEQUENCE FOR GIVEN KEY
// IF NO SEQUENCE EXISTS FOR A GIVEN KEY RETURN -1
int size(int key) const {
if (find(v.begin(), v.end(), key)!=v.end()) {
node* temp = head;
while(temp != NULL) {
if (temp->key == key) {
return temp->values.size();
}
else temp = temp->next;
}
}
else return -1;
}
// IMPLEMENT ME: SHOULD RETURN POINTER TO A SEQUENCE FOR GIVEN KEY
// IF NO SEQUENCE EXISTS FOR A GIVEN KEY RETURN nullptr
const int* data(int key) const {
if (find(v.begin(), v.end(), key)!=v.end()) {
node* temp = head;
while(temp != NULL) {
if (temp->key == key) {
return temp->values.data();
}
else temp = temp->next;
}
}
else return nullptr;
}
// IMPLEMENT ME: INSERT VALUE INTO A SEQUENCE IDENTIFIED BY GIVEN KEY
void insert(int key, int value) {
if(v.size() == 0) { //empty list
v.push_back(key);
n = new node;
n->prev = NULL;
n->key = key;
n->values.push_back(value);
head = n;
tail = n;
}
else if((find(v.begin(), v.end(), key)!=v.end())) { //if key exists already
node* temp = head;
while(temp != NULL) {
if (temp->key == key) {
temp->values.push_back(value);
break;
}
else temp = temp->next;
}
}
else { //if theres no existing key
v.push_back(key);
n = new node;
n->key = key;
n->values.push_back(value);
n->prev = tail;
tail->next = n;
tail = n;
tail->next = NULL;
}
}
private:
vector<int> v;
node* n;
node* head;
node* tail;
}; // class key_value_sequences
#endif // A3_HPP
In insert method:
if(v.size() == 0) { //empty list
v.push_back(key);
n = new node;
n->prev = NULL;
n->key = key;
n->values.push_back(value);
head = n;
tail = n;
}
You are not setting the head->next to NULL. I suspect that could be the problem while inserting second value.
while(temp != NULL) { // <<<<< Is temp uninitialized?
if (temp->key == key) {
temp->values.push_back(value);
break;
}
else temp = temp->next;
Its pretty dangerous to not initialize the pointer to NULL