Why the last SPLIT function causes SEGMENTATION FAULT? How to fix this - c++

I wrote the typical program for the doubly-linked list in circular buffer (here: RING). I am using iterators (I have to, school project).
All seem to work, but there is something with the memory, and I can't find a way to fix it somehow. Probably the reason of segmentation fault is that I am accessing the "forbidden" part.
Whole code HERE
The problem is I can't find the place I should fix it.
Function SPLIT:
made two separate circular buffers from the original circular buffer.
For the 1st one I start iterating from the 1st position, for the 2nd- from the 2nd position. DIR means clockwise (if true), otherwise- false. LEN means the length of the RING we want to obtain.
We iterate every second element.
In snipped code you got real example.
/******* external function ********/
/****** Example of the split function:
split (r3,r1,true,3,r2,false,6)
r3= 1,2,3,4,5
r1= 1,3,5
r2= 2,5,3,1,4,2
********/
template <typename Key>
Ring<Key> split(const Ring<Key> &source,Ring<Key> &result1, bool dir1, int len1, Ring<Key> &result2, bool dir2, int len2){
typename Ring<Key>::iterator i1 = source.begin();
typename Ring<Key>::iterator i2 = source.begin();
/*I moved second iterator to the 2nd position in original Ring*/
i2++;
if (source.isEmpty()){
cout<<"Empty source Ring"<<endl;}
if (source.length()==1||source.length()==2){
return source;
}
if((len1 <= 0) || (len2 <= 0))
{
cout << "split():(incorrect lengths)" << endl;
}
if(!i1.isNULL()){
for(int i = 0; i < len1; i++)
{
result1.insertLast(i1.getKey());
if(dir1){
i1++;
i1++;;
}
else
{i1--;
i1--;
}}}
cout<<result1;
if(!i2.isNULL()){
for(int i = 0; i < len2; i++)
{
result2.insertLast(i2.getKey());
if(dir2){
i2++;
i2++;
}
else
{i2--;
i2--;
}}}
cout<<result2;}

I forked your code here: https://ideone.com/7af6D7
Corrected some bits. It no longer segfaults, seemingly, but I dunno if it does what you expect it to do. Read the notes on the comments. You should also specify whether you're working with C++11 or greater or old C++ code (I'm sorry if that's the case)
This should be catched by the compiler in forms of warnings at the very least (you should compile with -Werror flag).
Also, please indent your code properly.
Anyway, I dunno why are you returning a copy of the whole source on split function (it should return void and throw in error or a bool).
#include <iostream>
#include <stdlib.h>
using namespace std;
template <typename Key>
class Ring
{
struct Node
{
Key key;
Node *next= nullptr; // Always initialize. Worst case if you do it
// twice: the compiler will be smart enough to
// remove one of the occurrences
Node *prev= nullptr; // Ditto
};
Node *any= nullptr; // Use nullptr on C++11 and onwards
// Also, any is not a very good name, prefer using root or first
public:
/******* iterator class methods definitions ********/
class iterator
{
Node *el= nullptr; // Ditto
public:
iterator()= default; // or iterator() {}
~iterator()= default; // or ~iterator() {}
constexpr iterator(const iterator& copyIter)
: el(copyIter.el)
{
// Empty constructor gets to be constexpr
// Improves optimizing and can be evaluated at runtime
}
constexpr iterator(Node *copyEl)
: el(copyEl)
{
}
iterator &operator = (const iterator &copyIter)
{
el = copyIter.el;
return *this;
}
bool operator == (const iterator &comp) const
{
return el == comp.el;
}
bool operator != (const iterator &comp) const
{
return el != comp.el;
}
/* I don't think this operator is good practice
iterator operator + (const unsigned int number) const
{
iterator new_iter = *this; // Could be auto new_iter = *this in C++11
for(int i = 0; i < number; i++) { // Style: Always put brackets
new_iter++;
}
return new_iter;
}*/
iterator& operator ++ ()
{
if (el) {
el = el->next;
}
return *this;
// You had no return if it's null
// Also, no need for != nullptr or != NULL
// Usually, you return a reference to self
}
iterator operator ++ (int)
{
iterator copy_iter(el);
if (el) {
el = el->next;
}
return copy_iter; // You return the copy, no matter what
}
/* Ditto
iterator operator - (const unsigned int number) const
{
iterator new_iter = *this;
for(int i = 0; i < number; i++) {
new_iter--;
}
return new_iter;
}*/
iterator& operator --()
{
if (el) {
el = el->prev;
}
return *this;
}
iterator operator -- (int)
{
iterator copy_iter(el);
if (el) {
el= el->prev;
}
return copy_iter;
}
Key getKey() const
{
if (el) {
return el->key;
}
cerr << "getKey(): (iterator = NULL)" << endl;
return Key(); // Empty element, might aswell throw here
}
bool isNULL() const
{
return !el;
}
operator bool() const
{
return !isNULL();
}
};
/******* methods ********/
Ring();
~Ring();
Ring(const Ring<Key> &Ring);
friend ostream &operator << (ostream &o, const Ring<Key> &Ring){Ring.print(); return o;};
bool operator ==(const Ring<Key> &Ring);
bool operator != (const Ring<Key> &Ring);
Ring<Key> &operator = (const Ring<Key> &Ring);
Ring<Key> operator + (const Ring<Key> &second)const;
Ring<Key> operator - (const Ring<Key> &second)const;
bool insertFront(const Key &key);
bool insertLast(const Key &key);
bool insertAt(int pos, const Key &key);
bool insertAfter(const Key &where, const Key &key);
bool popByKey(const Key &key);
bool popLast();
bool popFront();
bool ifExists(const Key &key);
bool isEmpty()const;
int length()const;
void print()const;
bool clear();
void reverse();
/******* additional iterators definitions *******/
iterator begin() const
{
return iterator(any);
}
iterator end() const
{
return iterator(any? any->prev : nullptr); // Always check before dereference
}
};
/******* methods definitions ********/
template <typename Key>
Ring<Key>::Ring()
// : any(nullptr) // Prefer initializers. No need anyway because we did any= nullptr
{
cout << "Constructor: (Empty Ring created)" << endl;
}
template <typename Key>
Ring<Key>::~Ring()
{
if (!any) {
cout << "Destructor: ( Ring deleted )" << endl;
}
Node *curr = any; // auto
if (curr) {
while(any) {
this->popLast();
}
cout << "Destructor: ( Ring deleted )" << endl;
}
}
template <typename Key>
Ring<Key>::Ring(const Ring<Key> &ring) // Please be consistent. If you're using
// capital letters for classes, dont call the variable Ring; it should be ring
// : any(nullptr) // Prefer initializers. No need anyway because we did any= nullptr
{
if (ring.any) {
Node *curr = ring.any;
do {
this->insertLast(curr->key);
curr = curr->next;
} while(curr != ring.any);
}
}
template <typename Key>
bool Ring<Key>::popLast()
{
if (!any) {
return true;
}
Node *curr = any;
if (curr->next == curr) { // one Node
any = NULL;
delete curr; // Here is where you "hope" no one copied your class
// because you're deleting a pointer that other might be
// using, that's what's called a dangling pointer
return true;
}
if(curr->next->next == curr) // two Nodes
{
Node *temp = curr->next;
curr->next = curr;
curr->prev = curr;
delete temp;
return true;
}
do
{
if(curr->next->next == any) // Last Node
{
Node *temp = curr->next;
temp->next->prev = curr;
curr->next = temp->next;
delete temp;
return true;
}
curr = curr->next;
}
while(curr != any);
return false;
}
template <typename Key>
bool Ring<Key>::insertFront(const Key &key)
{
Node *newNode = new Node; // Either way
newNode->key = key; // Always set the key
if (!any) { // Empty
newNode->next = newNode;
newNode->prev = newNode;
any = newNode;
} else {
newNode->next= any; // Will be always before the "any"
newNode->prev= any->prev; // Will always steal any's previous
any->prev= newNode; // Any will always point at newNode as previous
any= newNode; // I'm the captain now
// Actually, for the above, We don't care if it's only one or more
}
return true;
}
template <typename Key>
bool Ring<Key>::operator == (const Ring<Key> &ring)
{
if (this->isEmpty() && ring.isEmpty()) {
return true;
}
if (this->length() != ring.length()) {
return false;
}
Node *curr1 = this->any;
Node *curr2 = ring.any;
do {
if (curr1->key != curr2->key) {
return false;
}
curr1 = curr1->next;
curr2 = curr2->next;
// No null check needed for non empty rings
} while(curr1 != this->any);
return true;
}
template <typename Key>
bool Ring<Key>::insertLast(const Key &key)
{
if (!any) { // no elements
this->insertFront(key);
return true;
}
Node *newNode = new Node;
newNode->key = key;
newNode->next = any;
Node* curr = any;
do {
if (curr->next == any) {
newNode->prev = curr;
any->prev = newNode;
curr->next = newNode;
return true;
}
curr= curr->next;
} while(curr != any);
return false;
}
template<typename Key>
bool Ring<Key>::isEmpty() const
{
// return !any;
if (!any) {
cout << "isEmpty(): (Ring empty)" << endl;
return true;
}
return false;
}
template <typename Key>
int Ring<Key>::length() const
{
int count = 0;
Node *curr = any;
if (!curr) {
return count;
}
do {
count++;
curr = curr->next;
} while(curr != any);
return count;
}
template<typename Key>
void Ring<Key>::print() const
{
Node * curr = any;
if(!curr) {
cout << "print(): (Ring empty)" << endl;
return;
}
do {
cout << "\t(" << curr->key<< ")";
curr = curr->next;
} while(curr != any);
cout << endl;
}
/******* external function ********/
/****** Example of the split function:
split (r3,r1,true,3,r2,false,6)
r3= 1,2,3,4,5
r1= 1,3,5
r2= 2,5,3,1,4,2
********/
template <typename Key>
Ring<Key> split(const Ring<Key> &source, Ring<Key> &result1, bool dir1,
int len1, Ring<Key> &result2, bool dir2, int len2)
{
if (source.isEmpty()) {
cout<<"Empty source Ring"<<endl;
return source;
}
if (source.length()==1 || source.length()==2) {
return source;
}
if (len1 <= 0 || len2 <= 0) {
cout << "split():(incorrect lengths)" << endl;
return source;
}
auto i1 = source.begin(); // typename Ring<Key>::iterator i1 = source.begin()
auto i2 = source.begin(); // typename Ring<Key>::iterator 21 = source.begin()
/* I moved second iterator to the 2nd position in original Ring */
++i2; // Prefer ++i to i++
if (i1) {
for (int i= 0; i < len1; ++i) {
result1.insertLast(i1.getKey());
if(dir1) {
++i1;
++i1;
} else {
--i1;
--i1;
}
}
}
cout << result1;
if (i2) {
for(int i = 0; i < len2; ++i) {
result2.insertLast(i2.getKey());
if(dir2) {
++i2;
++i2;
} else {
--i2;
--i2;
}
}
}
cout << result2;
return source; // You *ALWAYS* need to return a value.
// This was causing the SEGFAULT (and others like this might also)
}
int main()
{
Ring<int> R1,R2,R3,R4,R5;
R1.insertLast(2);
R1.insertLast(3);
R1.insertLast(4);
R1.insertLast(5);
R1.insertLast(6);
R1.insertLast(1);
R2.insertLast(10);
R2.insertLast(20);
R2.insertLast(30);
R2.insertLast(40);
R2.insertLast(50);
R2.insertLast(60);
R2.insertLast(70);
cout<<"Split function:"<<endl;
split(R1,R3,false,3,R4,false,6);
R5.insertLast(10);
R5.insertLast(20);
R5.insertLast(30);
R5.insertLast(50);
R5.insertLast(50);
R5.insertLast(60);
R5.insertLast(70);
R5.print();
cout << __FILE__ << ':' << __LINE__ << " I'm alive (no segfault)" << endl;
return 0;
}

Related

Doubly linked list insert method

I am currently doing an assignment to learn about doubly linked lists and iterators. I am making an insert() method that takes in an iterator type, as well as the data to add into the list. However, I'm getting an exception on the line I marked in the insert() method, saying:
Exception thrown: read access violation. iter.node was 0xFFFFFFFFFFFFFFEF
I'm not sure if this is a problem with the iterator or the linked list:
#include <cstdlib>
#include <iostream>
using namespace std;
class List {
struct Node {
int data;
Node* next = nullptr;;
Node* prev = nullptr;
};
friend ostream& operator<<(ostream& os, const List& rhs);
public:
class iterator {
friend class List;
Node* node = nullptr;
public:
iterator(Node* node) : node(node) {}
iterator& operator++() {
node = node->next;
return *this;
}
iterator& operator--() {
node = node->prev;
return *this;
}
bool operator==(const iterator& rhs) {
if (node != rhs.node) {
return false;
}
return true;
}
bool operator!=(const iterator& rhs) {
return !(*this == rhs);
}
int operator*() const {
return node->data;
}
iterator& operator->() {
return *this;
}
};
List() {
header = new Node();
trailer = new Node();
header->next = trailer;
header->prev = nullptr;
trailer->prev = header;
trailer->next = nullptr;
}
void push_back(int data) {
Node* newNode = new Node();
newNode->data = data;
newNode->prev = trailer->prev;
newNode->prev->next = newNode;
newNode->next = trailer;
trailer->prev = newNode;
}
void pop_back() {
Node* tempNode = trailer->prev->prev;
tempNode->next = trailer;
trailer->prev = tempNode;
}
void push_front(int data) {
Node* newNode = new Node();
newNode->data = data;
header->next->prev = newNode;
newNode->next = header->next;
newNode->prev = header;
header->next = newNode;
}
void pop_front() {
Node* tempNode = header->next->next;
tempNode->prev = header;
header->next = tempNode;
}
int& front() {
if (header->next == trailer) {
cerr << "List is empty" << endl;
}
return header->next->data;
}
int& back() {
if (trailer->prev == header) {
cerr << "List is empty" << endl;
}
return trailer->prev->data;
}
int front() const {
if (header->next == trailer) {
cerr << "List is empty" << endl;
}
return header->next->data;
}
int back() const {
if (trailer->prev == header) {
cerr << "List is empty" << endl;
}
return trailer->prev->data;
}
int size() const {
int count = 0;
for (iterator i = begin(); i != end(); ++i) {
++count;
}
return count;
}
int& operator[](int index) {
int ind = 0;
for (iterator i = begin(); i != end(); ++i) {
if (ind == index) {
return i.node->data;
}
++ind;
}
}
int operator[](int index) const {
int ind = 0;
for (iterator i = begin(); i != end(); ++i) {
if (ind == index) {
return i.node->data;
}
++ind;
}
}
iterator& begin() {
iterator iter(header->next);
return iter;
}
iterator& end() {
iterator iter(trailer);
return iter;
}
iterator begin() const {
iterator iter(header->next);
return iter;
}
iterator end() const {
iterator iter(trailer);
return iter;
}
iterator& insert(iterator& iter, int data) {
Node* newNode = new Node();
newNode->data = data;
iter.node->prev->next = newNode; //exception is thrown on this line
newNode->prev = iter.node->prev;
newNode->next = iter.node;
iter.node->prev = newNode;
iterator newIter(newNode);
return newIter;
}
void clear() {
Node* iter = header->next;
Node* next;
while (iter != trailer) {
next = iter->next;
delete iter;
iter = nullptr;
iter = next;
}
header->next = trailer;
trailer->prev = header;
}
iterator& erase(iterator& iter) {
for (iterator i = ++begin(); i != end(); ++i) {
if (i == iter) {
Node* tempNode = i.node;
tempNode->prev->next = tempNode->next;
tempNode->next->prev = tempNode->prev;
delete tempNode;
tempNode = nullptr;
return ++i;
}
}
}
public:
private:
Node* header;
Node* trailer;
};
ostream& operator<<(ostream& os, const List& rhs) {
for (int i : rhs) {
os << i << " ";
}
os << endl;
return os;
}
void printListInfo(const List& myList) {
cout << "size: " << myList.size()
<< ", front: " << myList.front()
<< ", back(): " << myList.back()
<< ", list: " << myList << endl;
}
int main() {
List myList;
for (int i = 0; i < 10; ++i) myList.insert(myList.end(), i * i);
printListInfo(myList);
myList.clear();
for (int i = 0; i < 10; ++i) myList.insert(myList.begin(), i * i);
printListInfo(myList);
}
iterator& iterator::begin() and iterator& iterator::end() are returning references to local variables. Since it's these functions that are being passed to List::insert(...) in main via myList.insert(myList.begin(), ..) etc, the insertion function is always going to be operating on an invalid iterator.
Similarly, iterator::insert has a similar issue in which it is returning a local variable by reference:
iterator& insert(...)
{
...
iterator newIter(newNode);
return newIter;
}
Once these functions exit, the local variables are destroyed and so those references that were returned are left dangling - pointing at memory where an object used to be.
A quick fix would be to make sure that those new iterators are not local to that function and are instead initialized on the heap e.g.
iterator& begin() {
iterator* iter = new iterator(header->next);
return *iter;
}
iterator& end() {
iterator* iter = new iterator(trailer);
return *iter;
}
However, this also means that you would have to introduce a way to keep track of those iterators so that you can free the memory once an iterator is no longer needed, perhaps by storing the iterators as a member variable of List instead of storing the begin/end Nodes of List and therefore only accessing the Nodes via the iterators.
I found this due to compiling with warnings on, which is highly recommended!
In member function 'List::iterator& List::begin()':
<source>:151:16: warning: reference to local variable 'iter' returned [-Wreturn-local-addr]
151 | return iter;
| ^~~~
<source>:150:18: note: declared here
150 | iterator iter(header->next);

Memory segmentation fault in the double linked list implementation

Below I have a implementation of Linked List.
While there may be more errors because I have not yet finished, let's focus at the four functions: pop_front, push_front, push_back, pop_back.
Everything seems working, until I try to implement push_back. The testing code throws segmentation fault. I don't have idea why, because like it's pretty symmetric operation to the push_front but just at the another end of the list. And the push_front just works.
#include <iostream>
#ifndef LINKED_LIST_H
#define LINKED_LIST_H
#include "AbstractList.hxx"
template <class T>
class LinkedList : public AbstractList<T>
{
public:
struct Node
{
T value;
Node *prev;
Node *next;
};
LinkedList()
{
guard = new Node;
guard->prev = nullptr;
guard->next = nullptr;
}
// copy,move construction and assignment are not relevant to the question
~LinkedList()
{
clear();
}
template <class U>
void push_front(U &&x)
{
Node *fresh = new Node;
fresh->value = std::forward<U>(x);
fresh->prev = nullptr;
fresh->next = guard->next;
guard->next = fresh;
if (empty())
{
guard->prev = fresh;
}
}
T pop_front()
{
if (empty())
throw std::out_of_range("List is empty");
Node *toDelete = guard->next;
int temp = toDelete->value;
guard->next = guard->next->next;
delete toDelete;
return temp;
}
template <class U>
void push_back(U &&x)
{
Node *fresh = new Node;
fresh->value = std::forward<U>(x);
fresh->next = nullptr;
guard->prev = fresh;
if (empty())
{
guard->next = fresh;
}
}
/*
T pop_back()
{
if (empty())
throw std::out_of_range("List is empty");
Node *toDelete = guard->prev;
int temp = toDelete->value;
guard->prev = guard->prev->prev;
delete toDelete;
return temp;
}
*/
struct Iterator
{
public:
Node *ptr;
Iterator(Node *ptr_) : ptr(ptr_){};
Iterator &operator++()
{
ptr = ptr->next;
return *this;
}
Iterator &operator++(int)
{
Iterator *temp = this;
ptr = ptr->next;
return *temp;
}
Iterator &operator--()
{
ptr = ptr->prev;
return *this;
}
Iterator &operator--(int)
{
Iterator *temp = this;
ptr = ptr->prev;
return *temp;
}
bool operator==(const Iterator &b)
{
return ptr == b.ptr;
}
bool operator!=(const Iterator &b)
{
return !operator==(b);
}
T &operator*()
{
return ptr->value;
}
};
Iterator find(const T &x)
{
Iterator point = begin();
while (point != end())
{
if (*point == x)
return point;
point++;
}
// or end()?
return nullptr;
}
// usuwa cala liste
void clear()
{
Node *current = guard->next;
while (current != nullptr)
{
Node *next = current->next;
delete current;
current = next;
}
guard->prev = nullptr;
guard->next = nullptr;
}
Iterator erase(Iterator x)
{
Iterator point = begin();
while (point != end())
{
if (point == x)
{
for (point; point != end(); point++)
{
point = ++x;
}
return ++x;
}
point++;
}
return nullptr;
}
template <class U>
Iterator insert(Iterator it, U &&x)
{
for (Iterator point = it; point != end(); point++)
{
(point + 1) = point;
}
it = std::forward<U>(x);
}
int remove(const T &x)
{
int count = 0;
Iterator point = begin();
Iterator last = end();
while (point != last)
{
Iterator next = point;
++next;
if (*point == x)
{
erase(point);
count++;
}
point = next;
}
return count;
}
int size()
{
Iterator point = begin();
int count = 0;
while (point != end())
{
count++;
point++;
}
return count;
}
bool empty()
{
return guard->next == nullptr;
}
Iterator begin() const
{
return Iterator(guard->next);
}
Iterator end() const
{
return Iterator(guard->prev);
}
private:
// head = next, prev = tail
Node *guard;
};
#endif
Testing code:
#include <iostream>
#include "./LinkedList.hxx"
int main(int argc, char *argv[])
{
LinkedList<int> l1;
std::cout << l1.size() << std::endl;
l1.push_front(1);
l1.push_front(2);
l1.push_front(3);
for (auto itr = l1.begin(); itr != l1.end(); itr++)
std::cout << *itr << std::endl;
std::cout << "fpops: " << l1.pop_front() << std::endl;
std::cout << "fpops: " << l1.pop_front() << std::endl;
std::cout << "if empty " << l1.empty() << std::endl;
std::cout << "fpops: " << l1.pop_front() << std::endl;
for (auto itr = l1.begin(); itr != l1.end(); itr++)
std::cout << *itr << std::endl;
std::cout << l1.size() << std::endl;
std::cout << "if empty " << l1.empty() << std::endl;
l1.push_back(1);
std::cout << l1.size() << std::endl;
}

Iterator while loop running extra loop

I have a Doubly linked List class wherein i have a function which uses my classes iterators to loop over the collection. However in 1 specific place it runs for an extra loop and I cannot figure out why.
current merge template
template <typename T>
void DlList<T>::merge(SortedList& other) {
iterator it = back_->prev_;
iterator oit = other.begin();
while (oit.current_->next_ != NULL) {
std::cout << *it << " " << *oit << std::endl;
it.current_->next_ = oit.current_;
back_->prev_ = oit.current_;
//back_->prev_->next_ = back_;
//other.back_->prev_ = other.back_->prev_->prev_;
it++;
oit++;
}
}
It always iterates and extra time and added a null node to the list and I don't understand why.
Any insight is greatly appricicated!
Edit Added full project examples to explain intention of function
I am working on a templated data structure class which is a doubly linked list which uses sentinel nodes.
The list is sorted based on insert() and I am working on a merge function wherein the nodes of each list must be combined into this->list. The nodes must be moved and not have new ones created. and if the same value exists in both lists the other node value must come after the current node value.
I coded what I thought was a logical implementation however the output I get is not as expected and the results do not make sense to me, so if anyone could explain how i am getting the results I have it would be greatly appreciated.
Class Definition
class SortedList {
struct Node {
T data_;
Node* next_;
Node* prev_;
Node(const T& data = T{}, Node* nx = nullptr, Node* pr = nullptr) {
data_ = data;
next_ = nx;
prev_ = pr;
}
};
Node* front_;
Node* back_;
public:
class const_iterator {
friend class SortedList;
Node* current_;
const_iterator(Node* n)
{
current_ = n;
}
public:
const_iterator() {
//Set to safe state
current_ = nullptr;
}
const_iterator& operator++() {
current_ = current_->next_;
return *this;
}
const_iterator operator++(int) {
const_iterator old = *this;
current_ = current_->next_;
return old;
}
const_iterator& operator--() {
current_ = current_->prev_;
return *this;
}
const_iterator operator--(int) {
const_iterator old = *this;
current_ = current_->prev_;
return old;
}
bool operator==(const_iterator rhs) {
return (current_ == rhs.current_) ? true : false;
}
bool operator!=(const_iterator rhs) {
return !(*this == rhs);
}
bool operator>(const_iterator rhs) {
return current_->data_ > rhs->current_->data_;
}
const T& operator*()const {
return current_->data_;
}
};
class iterator :public const_iterator {
friend SortedList;
iterator(Node* n) :const_iterator(n) {};
public:
iterator() : const_iterator() {};
//prefix
iterator& operator++() {
this->current_ = this->current_->next_;
return *this;
}
//post-fix
iterator operator++(int) {
iterator old = *this;
this->current_ = this->current_->next_;
return old;
}
iterator& operator--() {
this->current_ = this->current_->prev_;
return *this;
}
iterator operator--(int) {
iterator old = *this;
this->current_ = this->current_->prev_;
return old;
}
T& operator*() {
return this->current_->data_;
}
const T& operator*()const {
return this->current_->data;
}
};
SortedList(); //done
~SortedList();
SortedList(const SortedList& rhs);
SortedList& operator=(const SortedList& rhs);
SortedList(SortedList&& rhs);
SortedList& operator=(SortedList&& rhs);
iterator begin() {
return iterator(front_->next_);
}
iterator end() {
return iterator(back_);
}
const_iterator cbegin() const {
return const_iterator(front_->next_);
}
const_iterator cend() const {
return const_iterator(back_);
}
iterator insert(const T& data);
iterator search(const T& data);
const_iterator search(const T& data) const;
iterator erase(iterator it);
void merge(SortedList& other);
bool empty() const;
int size() const;
};
first merge function attempt
template <typename T>
void SortedList<T>::merge(SortedList& other) {
iterator it = this->begin();
iterator oit = other.begin();
while (oit != other.end()) {
std::cout << *oit << " " << *it << std::endl;
if (*oit < *it) {
oit.current_->prev_->next_ = oit.current_->next_;
oit.current_->next_->prev_ = oit.current_->prev_;
oit.current_->next_ = it.current_;
oit.current_->prev_ = it.current_->prev_;
it.current_->next_ = oit.current_;
}
else {
oit.current_->prev_->next_ = oit.current_->next_;
oit.current_->next_->prev_ = oit.current_->prev_;
oit.current_->next_ = it.current_->next_;
oit.current_->prev_ = it.current_;
it.current_->prev_ = oit.current_;
}
oit++;
it++;
}
}
main tester
int main() {
int num[] = { 3,5,1,2,6,8,9,11 };
int num2[] = { 1,5,4,6,12,7,8,9 };
SortedList<int> l;
SortedList<int> l2;
for (int i = 0; i < 8; i++)
{
l.insert(num[i]);
l2.insert(num2[i]);
}
SortedList<int>::iterator result;
SortedList<int>::iterator result2 = l2.begin();
result = l.begin();
while (result != l.end()) {
std::cout << *result << " " << *result2 << std::endl;
++result;
++result2;
}
l.merge(l2);
output
1 1
2 4
3 5
5 6
6 7
8 8
9 9
11 12
1 1
2 2
3 3
5 5
6 6
8 8
9 9
11 11
0 0
I dont understand why my second output is showing same the same values for *it and *oit I am pretty certain the error is in how I assign the the oit.current_->next & prev but i am unsure.
any insight is appriciated.
You seem to want to merge two sorted doubly linked lists together. There are several problems with your approach, and so I'll show you my code:
#include <iostream>
using namespace std;
struct node {
node* next;
node* prev;
int val;
node(int i_val)
: next(nullptr),
prev(nullptr),
val(i_val)
{}
};
void connect(node* a, node* b) {
if (a != nullptr) {
if (a->next != nullptr) {
a->next->prev = nullptr;
}
a->next = b;
}
if (b != nullptr) {
if (b->prev != nullptr) {
b->prev->next = nullptr;
}
b->prev = a;
}
}
struct DlList {
node* first_node;
node* last_node;
DlList()
: first_node(nullptr),
last_node(nullptr)
{}
~DlList() {
for (node* n = first_node; n != nullptr; n = n->next) {
delete n->prev;
}
delete last_node;
}
void push(int new_val) {
node* new_node = new node(new_val);
connect(last_node, new_node);
last_node = new_node;
if (first_node == nullptr) {
first_node = new_node;
}
}
void merge_sorted(DlList& other) {
node* this_node = first_node;
node* other_node = other.first_node;
node* n = nullptr; // goes through each node of the new list in order
while (this_node != nullptr || other_node != nullptr) {
node* next_n;
if (other_node == nullptr ||
(this_node != nullptr && this_node->val <= other_node->val)) {
// entered if other_node is nullptr or this_node comes before other_node
next_n = this_node;
this_node = this_node->next;
}
else {
// entered if this_node is nullptr or other_node comes before this_node
next_n = other_node;
other_node = other_node->next;
}
connect(n, next_n);
if (n == nullptr) { // first time through loop
first_node = next_n;
}
n = next_n;
}
last_node = n;
// *this takes ownership of all of other's nodes
other.first_node = nullptr;
other.last_node = nullptr;
}
};
int main() {
std::cout << "running test" << std::endl;
int num[] = { 1,2,3,5,6,8,9,11 };
int num2[] = { 1,4,5,6,7,8,9,12 };
DlList l;
DlList l2;
for (int i = 0; i < 8; i++)
{
l.push(num[i]);
l2.push(num2[i]);
}
l.merge_sorted(l2);
for (node* n = l.first_node; n != nullptr; n = n->next) {
std::cout << n->val << " ";
}
std::cout << std::endl;
}
You may add iterators and other higher-level abstractions later, but for now I think they complicate and obscure the logic. I also did not see a need for a "past-the-end-of-the-list" node in your case, as nullptr would suffice. Though of course these could rather easily be added in if you wanted them to, just for demonstration purposes they are omitted.
Notice how I made a dedicated connect function that does all the pointer assignments as they should be done for two nodes. It handles a bunch of combinations of nullptr cases, too, so you don't need to worry as much about checking for null pointers outside of the function. (Note how the first time through the merge loop, a null n pointer is connected to next_n). Now you hardly need to worry about pointer assignments, and it's clearer when you just say, "connect these two nodes."
My merger function goes through each node in the newly created list. It picks the next node from the two available nodes, from *this and other. It then connects the current node to the next node, and advances the current node to the next node. It has special handling when one or the other of the lists runs out (this_node or other_node becomes nullptr), which indeed happens in the given test case. It takes care to assign first_node and last_node in the correct places, and to clear other after the merge, so as to prevent double ownership issues.

Visual Studio 2019 Edits My C++ Code And Ruins My Iterator

I have spent some time recently designing an iterator for the AVL Tree (right now it just has the inserting mechanics though; haven't implemented tree balancing).
I wanted to test out the iterator, so I checked how to make it online and settled on making it by having a stack holding the tree nodes (e.g. in normal iteration stack would contain all nodes left of this->top node).
This is how the iteration is supposed to work:
for (auto it = tree.iterator(); it.hasNext(); it.next())
{
// process
}
However, VS changes (disables) my Iterator(const Iterator& it) and Iterator(Iterator&& it) constructors and then the iteration fails because the stack is always empty.
After setting Iterator() = delete;, I run into the issue of stack having an unusually large size with invisible parameters.
If extra information is needed, feel free to ask. I think that it's best if I just paste the relevant code because I do not understand this behaviour and do not know what details I should say:
avlTree<Key, Info>::iterator:
class Iterator
{
private:
std::vector<Node*> stack;
bool reverse;
Node* ptr;
std::vector<Node*> makeStack(Node* start, long height)
{
std::vector<Node*> newStack;
newStack.reserve(height);
while (start != nullptr)
{
newStack.push_back(start);
if (reverse)
start = start->right;
else
start = start->left;
}
return newStack;
}
Iterator(Node* start, long height, bool reverse = false) : reverse(reverse), ptr(nullptr)
{
stack = makeStack(start, height);
}
friend class avlTree;
public:
Iterator(Iterator&& iterator)
{
stack = move(iterator.stack);
ptr = nullptr;
}
Iterator(const Iterator& iterator)
{
stack = iterator.stack;
ptr = nullptr;
}
//Iterator() = delete;
bool hasNext()
{
return stack.size() > 0;
}
void next()
{
if (!stack.size()) throw "Empty iterator stack";
if (ptr == stack[stack.size() - 1])
{
stack.pop_back();
if (reverse) // fill the stack with the subsequent nodes (reverse or normal direction)
{
Node* start = ptr->left;
while (start != nullptr)
{
stack.push_back(start);
start = start->right;
}
}
else
{
Node* start = ptr->right;
while (start != nullptr)
{
stack.push_back(start);
start = start->left;
}
}
}
if (stack.size() > 0)
ptr = stack[stack.size() - 1];
}
const Key& getKey()
{
if (!ptr) throw "ptr is nullptr";
else return ptr->key;
}
Info& getInfo()
{
if (!ptr) throw "ptr is nullptr";
else return ptr->info;
}
};
main:
avlTree<char, int> tester;
for (char i = 'g'; i <= 'z'; ++i)
tester.insert(i);
for (char i = 'a'; i < 'g'; ++i)
tester.insert(i);
for (auto it = tester.iterator(); it.hasNext(); it.next())
{
std::cout << it.getKey() << " ";
}
Screenshot of the code & message I get while debugging: http://prntscr.com/qi79zd
How do I fix the issue and make the iteration work?
EDIT:
Complete code:
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <fstream>
#include <chrono>
#include <iterator>
#include <functional>
//#include <ctime>
template<typename T>
void swap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
template<typename Key, typename Info>
class avlTree
{
private:
struct Node
{
const Key key;
Info info;
Node* left;
Node* right;
long leftHeight, rightHeight;
Node(const Key& key, Info&& info = Info(), Node* left = nullptr, Node* right = nullptr)
: key(key), info(info), left(left), right(right), leftHeight(1), rightHeight(1) {}
Node& operator()(Node* nleft, Node* nright)
{
left = nleft;
right = nright;
return *this;
}
Node& operator()(long left, long right)
{
leftHeight = left;
rightHeight = right;
}
};
Node* top;
long length;
public:
class Iterator
{
private:
std::vector<Node*> stack;
bool reverse;
Node* ptr;
std::vector<Node*> makeStack(Node* start, long height)
{
std::vector<Node*> newStack;
newStack.reserve(height);
while (start != nullptr)
{
newStack.push_back(start);
if (reverse)
start = start->right;
else
start = start->left;
}
return newStack;
}
Iterator(Node* start, long height, bool reverse = false) : reverse(reverse), ptr(nullptr)
{
stack = makeStack(start, height);
}
friend class avlTree;
public:
Iterator(Iterator&& iterator)
{
stack = move(iterator.stack);
ptr = nullptr;
}
Iterator(const Iterator& iterator)
{
stack = iterator.stack;
ptr = nullptr;
}
bool hasNext()
{
return stack.size() > 0;
}
void next()
{
if (!stack.size()) throw "Empty iterator stack";
//stack.insert(stack.end(), vector.begin(), vector.end());
if (ptr == stack[stack.size() - 1])
{
stack.pop_back();
if (reverse)
{
Node* start = ptr->left;
while (start != nullptr)
{
stack.push_back(start);
start = start->right;
}
}
else
{
Node* start = ptr->right;
while (start != nullptr)
{
stack.push_back(start);
start = start->left;
}
}
}
if (stack.size() > 0)
ptr = stack[stack.size() - 1];
}
const Key& getKey()
{
if (!ptr) throw "ptr is nullptr";
else return ptr->key;
}
Info& getInfo()
{
if (!ptr) throw "ptr is nullptr";
else return ptr->info;
}
};
avlTree()
{
this->top = nullptr;
this->length = 0;
}
~avlTree()
{
recursiveDelete(top);
length = 0;
}
void printAsc()
{
for (auto it = iterator(); it.hasNext(); it.next())
{
std::cout << it.getKey() << " " << it.getInfo() << "\n";
}
}
void printDesc()
{
recDesc(top);
}
void printTop()
{
if (top) // != nullptr
{
std::cout << ".." << top->key << std::endl;
if (top->left)
std::cout << "." << top->left->key << "..";
else std::cout << ".0..";
if (top->right)
std::cout << top->right->key << std::endl;
else std::cout << "0" << std::endl;
}
}
void insert(const Key& key);
long height()
{
return !top ? 0 : top->leftHeight > top->rightHeight ? top->leftHeight : top->rightHeight;
}
private:
void recDesc(Node* parent);
void recursiveDelete(Node* parent);
void insertRecursive(Node* parent, const Key& key, int& depth);
// void rightRotation(Node* top, Node* parent = nullptr);
public:
Iterator iterator()
{
return Iterator(top, height());
}
};
std::vector<std::string> readFile(bool toDarwin = true);
/****************************************************************************/
int main()
{
// auto start = std::chrono::system_clock::now();
avlTree<std::string, int> counter;
avlTree<char, int> tester;
for (char i = 'g'; i <= 'z'; ++i)
tester.insert(i);
for (char i = 'a'; i < 'g'; ++i)
tester.insert(i);
for (auto it = tester.iterator(); it.hasNext(); it.next())
{
std::cout << it.getKey() << " ";
}
return 0;
}
/****************************************************************************/
template<typename Key, typename Info>
void avlTree<Key, Info>::recDesc(Node* parent)
{
if (parent->left != nullptr)
recAsc(parent->left);
std::cout << parent->key;
if (parent->right != nullptr)
recAsc(parent->left);
}
template<typename Key, typename Info>
void avlTree<Key, Info>::recursiveDelete(Node* parent)
{
if (!parent) return;
if (parent->left != nullptr)
recursiveDelete(parent->left);
if (parent->right != nullptr)
recursiveDelete(parent->right);
delete parent;
}
template<typename Key, typename Info>
void avlTree<Key, Info>::insertRecursive(Node* parent, const Key& key, int& depth)
{
if (parent->key == key)
++(parent->info);
else if (parent->key > key)
{
if (parent->left == nullptr)
{
parent->left = new Node(key);
++(parent->left->info);
++length;
depth = 1;
// (* parent->left)(depth, depth)
}
else
{
insertRecursive(parent->left, key, depth);
++depth;
parent->leftHeight = depth;
}
}
else if (parent->key < key)
{
if (parent->right == nullptr)
{
parent->right = new Node(key);
++(parent->right->info);
++length;
depth = 1;
// (* parent->right)(depth, depth)
}
else
{
insertRecursive(parent->right, key, depth);
++depth;
parent->rightHeight = depth;
}
}
}
template<typename Key, typename Info>
void avlTree<Key, Info>::insert(const Key& key)
{
int depth = 0;
if (!top)
{
top = new Node(key);
// (*top)(1, 1)
++length;
++(top->info);
}
else
{
insertRecursive(top, key, depth);
++depth;
top->key > key ? ++(top->leftHeight) : top->key < key ? ++(top->rightHeight) : NULL;
}
}
/* Irrelevant to the problem
std::vector<std::string> readFile(bool toDarwin)
{
// shrink_to_fit()
std::ifstream file;
std::string word;
std::vector<std::string> words;
words.reserve(1000000);
if (toDarwin == 1)
file.open("OnTheOriginOfSpecies.txt");
else
file.open("The_bible.txt");
while (file >> word)
{
words.push_back(word);
}
words.shrink_to_fit();
return words;
}
*/
I believe the problem is that you are not aware of RVO - return value optimization. Most compilers do so and in fact it is mandatory in C++17. What's RVO?
class A;
A func()
{
A a_infunc = {};
return a_infunc;
}
//use
A a_outsidefunc = func();
In this simple example at no point A::A(const A&) or A::A(A&&) is called. a_infunc is exactly the same variable as a_outsidefunc.
So in the for-loop:
for (auto it = tree.iterator(); it.hasNext(); it.next())
{
// process
}
There will be no calls to Iterator(const Iterator& it) or Iterator(Iterator&& it) due to RVO.

Unresolved External Symbol (Involves Templates with User-Defined Classes)

I am using Visiual Studio 2013. For some reason, I am getting the following error:
Error 1 error LNK2019: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Word &)" (??6#YAAAV?$basic_ostream#DU?$char_traits#D#std###std##AAV01#AAVWord###Z) referenced in function "protected: virtual void __thiscall BST<class Word>::visit(class BSTNode<class Word> *)" (?visit#?$BST#VWord####MAEXPAV?$BSTNode#VWord#####Z) C:\Users\Reuben\documents\visual studio 2013\Projects\CS321 Lab4\CS321 Lab4\main.obj CS321 Lab4
The error is due to this particular line:
BST<Word> tree;
If the line were as follows, then it seems to compile just fine:
BST<int> tree;OR BST<string> tree;
So for some reason, it's not liking my implementation of the Word class I defined. Here is the following code.
main.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include "Word.h"
#include "genBST.h"
using namespace std;
int main()
{
BST<Word> tree;
system("pause");
return 0;
}
Word.h
#include <string>
#include <set>
using namespace std;
class Word{
public:
string* word;
set<int>* lineNums;
void addLineNum(int);
Word(string*, int);
Word();
friend ostream& operator<<(ostream& out, Word& pr);
friend bool operator==(Word, Word);
friend bool operator!=(Word, Word);
friend bool operator<(Word, Word);
friend bool operator<=(Word, Word);
friend bool operator>=(Word, Word);
friend bool operator>(Word, Word);
};
Word::Word(string* myWord, int myLineNum) {
word = myWord;
set<int>* lineNums = new set<int>();
lineNums->insert(myLineNum);
}
Word::Word()
{
word = new string("");
set<int>* lineNums = new set<int>();
lineNums->insert(1);
}
void Word::addLineNum(int line)
{
lineNums->insert(line);
}
//overload comparison operators
//take note that the order of these are important
//since some of the operators are defined in terms of the previously defined ones
bool operator==(Word word1, Word word2)
{
if (*(word1.word) == *(word2.word))
{
return true;
}
return false;
}
bool operator!=(Word word1, Word word2)
{
return !(word1 == word2);
}
bool operator<=(Word word1, Word word2)
{
if (*(word1.word) <= *(word2.word))
{
return true;
}
return false;
}
bool operator<(Word word1, Word word2)
{
if (word1 <= word2 && word1 != word2)
return true;
return false;
}
bool operator>(Word word1, Word word2)
{
return !(word1 <= word2);
}
bool operator>=(Word word1, Word word2)
{
return !(word1 < word2);
}
std::ostream& operator<<(std::ostream& out, const Word& word1)
{
out << *(word1.word);
out << ": ";
set<int>::iterator it;
for (it = word1.lineNums->begin(); it != word1.lineNums->end(); it++)
{
out << *it << " ";
}
return out;
}
The last file is genBST.h. It's a long file, so I'm posting it last. This file was given to me as part of my assignment and we are not allowed to make changes to this file or else we will lose points.
//************************ genBST.h **************************
// generic binary search tree
#include <queue>
#include <stack>
#ifndef BINARY_SEARCH_TREE
#define BINARY_SEARCH_TREE
template<class T>
class Stack : public stack<T> {
public:
T pop() {
T tmp = top();
stack<T>::pop();
return tmp;
}
};
template<class T>
class Queue : public queue<T> {
public:
T dequeue() {
T tmp = front();
queue<T>::pop();
return tmp;
}
void enqueue(const T& el) {
push(el);
}
};
template<class T> class BST;
template<class T>
class BSTNode {
public:
BSTNode() {
left = right = 0;
}
BSTNode(const T& e, BSTNode<T> *l = 0, BSTNode<T> *r = 0) {
el = e; left = l; right = r;
}
T el;
BSTNode<T> *left, *right;
};
template<class T>
class BST {
public:
BST() {
root = 0;
}
~BST() {
clear();
}
void clear() {
clear(root);
root = 0;
}
bool isEmpty() const {
return root == 0;
}
void preorder() {
preorder(root);
}
void inorder() {
inorder(root);
}
void postorder() {
postorder(root);
}
void insert(const T&);
void recursiveInsert(const T& el) {
recursiveInsert(root, el);
}
T* search(const T& el) const {
return search(root, el);
}
T* recursiveSearch(const T& el) const {
return recursiveSearch(root, el);
}
void deleteByCopying(BSTNode<T>*&);
void findAndDeleteByCopying(const T&);
void deleteByMerging(BSTNode<T>*&);
void findAndDeleteByMerging(const T&);
void iterativePreorder();
void iterativeInorder();
void iterativePostorder();
void breadthFirst();
void MorrisPreorder();
void MorrisInorder();
void MorrisPostorder();
void balance(T*, int, int);
protected:
BSTNode<T>* root;
void clear(BSTNode<T>*);
void recursiveInsert(BSTNode<T>*&, const T&);
T* search(BSTNode<T>*, const T&) const;
T* recursiveSearch(BSTNode<T>*, const T&) const;
void preorder(BSTNode<T>*);
void inorder(BSTNode<T>*);
void postorder(BSTNode<T>*);
virtual void visit(BSTNode<T>* p)
{
cout << p->el << ' ';
}
};
template<class T>
void BST<T>::clear(BSTNode<T> *p) {
if (p != 0) {
clear(p->left);
clear(p->right);
delete p;
}
}
template<class T>
void BST<T>::insert(const T& el) {
BSTNode<T> *p = root, *prev = 0;
while (p != 0) { // find a place for inserting new node;
prev = p;
if (el < p->el)
p = p->left;
else p = p->right;
}
if (root == 0) // tree is empty;
root = new BSTNode<T>(el);
else if (el < prev->el)
prev->left = new BSTNode<T>(el);
else prev->right = new BSTNode<T>(el);
}
template<class T>
void BST<T>::recursiveInsert(BSTNode<T>*& p, const T& el) {
if (p == 0)
p = new BSTNode<T>(el);
else if (el < p->el)
recursiveInsert(p->left, el);
else recursiveInsert(p->right, el);
}
template<class T>
T* BST<T>::search(BSTNode<T>* p, const T& el) const {
while (p != 0)
if (el == p->el)
return &p->el;
else if (el < p->el)
p = p->left;
else p = p->right;
return 0;
}
template<class T>
T* BST<T>::recursiveSearch(BSTNode<T>* p, const T& el) const {
if (p != 0)
if (el == p->el)
return &p->el;
else if (el < p->el)
return recursiveSearch(p->left, el);
else return recursiveSearch(p->right, el);
else return 0;
}
template<class T>
void BST<T>::inorder(BSTNode<T> *p) {
if (p != 0) {
inorder(p->left);
visit(p);
inorder(p->right);
}
}
template<class T>
void BST<T>::preorder(BSTNode<T> *p) {
if (p != 0) {
visit(p);
preorder(p->left);
preorder(p->right);
}
}
template<class T>
void BST<T>::postorder(BSTNode<T>* p) {
if (p != 0) {
postorder(p->left);
postorder(p->right);
visit(p);
}
}
template<class T>
void BST<T>::deleteByCopying(BSTNode<T>*& node) {
BSTNode<T> *previous, *tmp = node;
if (node->right == 0) // node has no right child;
node = node->left;
else if (node->left == 0) // node has no left child;
node = node->right;
else {
tmp = node->left // node has both children;
previous = node; // 1.
while (tmp->right != 0) { // 2.
previous = tmp;
tmp = tmp->right;
}
node->el = tmp->el; // 3.
if (previous == node)
previous->left = tmp->left;
else previous->right = tmp->left; // 4.
}
delete tmp; // 5.
}
// findAndDeleteByCopying() searches the tree to locate the node containing
// el. If the node is located, the function DeleteByCopying() is called.
template<class T>
void BST<T>::findAndDeleteByCopying(const T& el) {
BSTNode<T> *p = root, *prev = 0;
while (p != 0 && !(p->el == el)) {
prev = p;
if (el < p->el)
p = p->left;
else p = p->right;
}
if (p != 0 && p->el == el)
if (p == root)
deleteByCopying(root);
else if (prev->left == p)
deleteByCopying(prev->left);
else deleteByCopying(prev->right);
else if (root != 0)
cout << "el " << el << " is not in the tree\n";
else cout << "the tree is empty\n";
}
template<class T>
void BST<T>::deleteByMerging(BSTNode<T>*& node) {
BSTNode<T> *tmp = node;
if (node != 0) {
if (!node->right) // node has no right child: its left
node = node->left; // child (if any) is attached to its parent;
else if (node->left == 0) // node has no left child: its right
node = node->right; // child is attached to its parent;
else { // be ready for merging subtrees;
tmp = node->left; // 1. move left
while (tmp->right != 0)// 2. and then right as far as possible;
tmp = tmp->right;
tmp->right = // 3. establish the link between the
node->right; // the rightmost node of the left
// subtree and the right subtree;
tmp = node; // 4.
node = node->left; // 5.
}
delete tmp; // 6.
}
}
template<class T>
void BST<T>::findAndDeleteByMerging(const T& el) {
BSTNode<T> *node = root, *prev = 0;
while (node != 0) {
if (node->el == el)
break;
prev = node;
if (el < node->el)
node = node->left;
else node = node->right;
}
if (node != 0 && node->el == el)
if (node == root)
deleteByMerging(root);
else if (prev->left == node)
deleteByMerging(prev->left);
else deleteByMerging(prev->right);
else if (root != 0)
cout << "el " << el << " is not in the tree\n";
else cout << "the tree is empty\n";
}
template<class T>
void BST<T>::iterativePreorder() {
Stack<BSTNode<T>*> travStack;
BSTNode<T> *p = root;
if (p != 0) {
travStack.push(p);
while (!travStack.empty()) {
p = travStack.pop();
visit(p);
if (p->right != 0)
travStack.push(p->right);
if (p->left != 0) // left child pushed after right
travStack.push(p->left); // to be on the top of the stack;
}
}
}
template<class T>
void BST<T>::iterativeInorder() {
Stack<BSTNode<T>*> travStack;
BSTNode<T> *p = root;
while (p != 0) {
while (p != 0) { // stack the right child (if any)
if (p->right) // and the node itself when going
travStack.push(p->right); // to the left;
travStack.push(p);
p = p->left;
}
p = travStack.pop(); // pop a node with no left child
while (!travStack.empty() && p->right == 0) { // visit it and all nodes
visit(p); // with no right child;
p = travStack.pop();
}
visit(p); // visit also the first node with
if (!travStack.empty()) // a right child (if any);
p = travStack.pop();
else p = 0;
}
}
template<class T>
void BST<T>::iterativePostorder() {
Stack<BSTNode<T>*> travStack;
BSTNode<T>* p = root, *q = root;
while (p != 0) {
for (; p->left != 0; p = p->left)
travStack.push(p);
while (p->right == 0 || p->right == q) {
visit(p);
q = p;
if (travStack.empty())
return;
p = travStack.pop();
}
travStack.push(p);
p = p->right;
}
}
template<class T>
void BST<T>::breadthFirst() {
Queue<BSTNode<T>*> queue;
BSTNode<T> *p = root;
if (p != 0) {
queue.enqueue(p);
while (!queue.empty()) {
p = queue.dequeue();
visit(p);
if (p->left != 0)
queue.enqueue(p->left);
if (p->right != 0)
queue.enqueue(p->right);
}
}
}
template<class T>
void BST<T>::MorrisInorder() {
BSTNode<T> *p = root, *tmp;
while (p != 0)
if (p->left == 0) {
visit(p);
p = p->right;
}
else {
tmp = p->left;
while (tmp->right != 0 &&// go to the rightmost node of
tmp->right != p) // the left subtree or
tmp = tmp->right; // to the temporary parent of p;
if (tmp->right == 0) { // if 'true' rightmost node was
tmp->right = p; // reached, make it a temporary
p = p->left; // parent of the current root,
}
else { // else a temporary parent has been
visit(p); // found; visit node p and then cut
tmp->right = 0; // the right pointer of the current
p = p->right; // parent, whereby it ceases to be
} // a parent;
}
}
template<class T>
void BST<T>::MorrisPreorder() {
BSTNode<T> *p = root, *tmp;
while (p != 0) {
if (p->left == 0) {
visit(p);
p = p->right;
}
else {
tmp = p->left;
while (tmp->right != 0 &&// go to the rightmost node of
tmp->right != p) // the left subtree or
tmp = tmp->right; // to the temporary parent of p;
if (tmp->right == 0) { // if 'true' rightmost node was
visit(p); // reached, visit the root and
tmp->right = p; // make the rightmost node a temporary
p = p->left; // parent of the current root,
}
else { // else a temporary parent has been
tmp->right = 0; // found; cut the right pointer of
p = p->right; // the current parent, whereby it ceases
} // to be a parent;
}
}
}
template<class T>
void BST<T>::MorrisPostorder() {
BSTNode<T> *p = new BSTNode<T>(), *tmp, *q, *r, *s;
p->left = root;
while (p != 0)
if (p->left == 0)
p = p->right;
else {
tmp = p->left;
while (tmp->right != 0 &&// go to the rightmost node of
tmp->right != p) // the left subtree or
tmp = tmp->right; // to the temporary parent of p;
if (tmp->right == 0) { // if 'true' rightmost node was
tmp->right = p; // reached, make it a temporary
p = p->left; // parent of the current root,
}
else { // else a temporary parent has been found;
// process nodes between p->left (included) and p (excluded)
// extended to the right in modified tree in reverse order;
// the first loop descends this chain of nodes and reverses
// right pointers; the second loop goes back, visits nodes,
// and reverses right pointers again to restore the pointers
// to their original setting;
for (q = p->left, r = q->right, s = r->right;
r != p; q = r, r = s, s = s->right)
r->right = q;
for (s = q->right; q != p->left;
q->right = r, r = q, q = s, s = s->right)
visit(q);
visit(p->left); // visit node p->left and then cut
tmp->right = 0; // the right pointer of the current
p = p->right; // parent, whereby it ceases to be
} // a parent;
}
}
template<class T>
void BST<T>::balance(T data[], int first, int last) {
if (first <= last) {
int middle = (first + last) / 2;
insert(data[middle]);
balance(data, first, middle - 1);
balance(data, middle + 1, last);
}
}
#endif
Any help would be appreciated! I'm still learning the basics of c++, I transferred schools so I'm trying to learn c++ (as opposed to Java which is what I was doing before at my other school). Thanks in advance!
EDIT: Oops, I was being dumb, the following line of code:
friend ostream& operator<<(ostream& out, Word& pr);
Should be (I think):
friend ostream& operator<<(ostream, const Word);
However, after this change, I still get the following error:
Error 1 error C2593: 'operator <<' is ambiguous c:\users\reuben\documents\visual studio 2013\projects\cs321 lab4\cs321 lab4\genbst.h 105 1 CS321 Lab4
Where the line 105 refers is the statement in the function (in the genBST.h file):
virtual void visit(BSTNode<T>* p)
{
cout << p->el << ' ';
}
EDIT v2:
Ok, I've changed the code and it seems to work now. I just placed the implementation of the << operator inside the declaration like so:
class Word{
public:
string* word;
set<int>* lineNums;
void addLineNum(int);
Word(string*, int);
Word();
friend ostream& operator<<(ostream& out, const Word& word1 )
{
out << *(word1.word);
out << ": ";
set<int>::iterator it;
for (it = word1.lineNums->begin(); it != word1.lineNums->end(); it++)
{
out << *it << " ";
}
return out;
};
friend bool operator==(Word, Word);
friend bool operator!=(Word, Word);
friend bool operator<(Word, Word);
friend bool operator<=(Word, Word);
friend bool operator>=(Word, Word);
friend bool operator>(Word, Word);
};
And it seems to work now.
I noticed that in your class definition you have:
class Word{
public:
string* word;
set<int>* lineNums;
void addLineNum(int);
Word(string*, int);
Word();
friend ostream& operator<<(ostream& out, Word& pr);
there is no const before Word & pr
however later in your code you have:
std::ostream& operator<<(std::ostream& out, const Word& word1)
see , there is a const here before Word& word1
should make them same( both have const), I think
I tried your code on my Visual Stdio 2010 ( I do not have 2013).
Before I adding the keyword const (before Word & pr) I got same error as you.
However, after I added the const, it was built successfully.
What is the error you got after you add the keyword const ? Post in detail here.